[
  {
    "path": ".github/workflows/build_windows_app.yml",
    "content": "name: Build Windows app\n\non:\n  push:\n    branches:\n      - main\n      - develop\n      - main-pc\n\njobs:\n  build:\n    runs-on: windows-latest\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v2\n\n      - name: Set up Flutter\n        uses: subosito/flutter-action@v2.10.0\n        with:\n          flutter-version: \"3.19.2\"  # Set flutter version here\n\n      - name: Build Windows app\n        #run: flutter build windows --release\n        run: dart run msix:create --release -v --output-path build/windows/runner --output-name AIdea --install-certificate false\n\n      # - name: Copy dependencies\n      #   run: copy .\\windows\\sqlite3.dll .\\build\\windows\\runner\\Release\\\n\n      - name: Upload artifact\n        uses: actions/upload-artifact@v2\n        with:\n          name: aidea\n          path: build/windows/runner/AIdea.msix"
  },
  {
    "path": ".gitignore",
    "content": "# Miscellaneous\n*.class\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.buildlog/\n.history\n.svn/\nmigrate_working_dir/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.iws\n.idea/\n\n# The .vscode folder contains launch configuration and tasks you configure in\n# VS Code which you may wish to be included in version control, so this line\n# is commented out by default.\n#.vscode/\n\n# Flutter/Dart/Pub related\n**/doc/api/\n**/ios/Flutter/.last_build_id\n.dart_tool/\n.flutter-plugins\n.flutter-plugins-dependencies\n.packages\n.pub-cache/\n.pub/\n/build/\n\n# Symbolication related\napp.*.symbols\n\n# Obfuscation related\napp.*.map.json\n\n# Android Studio will place build artifacts here\n/android/app/debug\n/android/app/profile\n/android/app/release\n\nMakefile.local"
  },
  {
    "path": ".metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled.\n\nversion:\n  revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0\n  channel: stable\n\nproject_type: app\n\n# Tracks metadata for the flutter migrate command\nmigration:\n  platforms:\n    - platform: root\n      create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0\n      base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0\n    - platform: android\n      create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0\n      base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0\n    - platform: ios\n      create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0\n      base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0\n    - platform: linux\n      create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0\n      base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0\n    - platform: macos\n      create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0\n      base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0\n    - platform: web\n      create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0\n      base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0\n    - platform: windows\n      create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0\n      base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0\n\n  # User provided section\n\n  # List of Local paths (relative to this file) that should be\n  # ignored by the migrate tool.\n  #\n  # Files that are not part of the templates will be ignored by default.\n  unmanaged_files:\n    - 'lib/main.dart'\n    - 'ios/Runner.xcodeproj/project.pbxproj'\n"
  },
  {
    "path": "AppRun",
    "content": "#!/bin/sh\n\ncd \"$(dirname \"$0\")\"\nexec ./askaide\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM nginx:1.25\n\nCOPY build/web/ /data/webroot\nCOPY nginx.conf /etc/nginx/conf.d/default.conf"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023 管宜尧\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "\nipa:\n\tflutter build ipa --no-tree-shake-icons --release \n\topen build/ios/ipa\n\nrun:\n\tflutter run --release \n\nbuild-all: build-android ipa build-dmg\n\trm -fr build/release \n\tmkdir -p build/release\n\tmv build/app/outputs/flutter-apk/app-release.apk build/release/aidea-android.apk\n\tmv build/ios/ipa/askaide.ipa build/release/aidea-ios.ipa\n\tmv build/macos/Build/Products/Package/AIdea-Installer.dmg build/release/aidea-macos.dmg\n\topen build/release\n\nbuild-android:\n\tflutter build apk --release --no-tree-shake-icons\n\nbuild-and-sync-android: build-android \n\tmv build/app/outputs/flutter-apk/app-release.apk /Users/mylxsw/ResilioSync/ResilioSync/临时文件/aidea-release.apk\n\nbuild-macos:\n\tflutter build macos --no-tree-shake-icons --release\n\tcodesign -f -s \"Developer ID Application: YIYAO  GUAN (N95437SZ2A)\" build/macos/Build/Products/Release/AIdea.app\n\nbuild-appimage:\n\tflutter build linux --no-tree-shake-icons --release \n\tmkdir -p aidea_app.AppDir\n\tcp -r build/linux/x64/release/bundle/* aidea_app.AppDir\n\tcp assets/app.png aidea_app.AppDir/\n\tcp AppRun aidea_app.AppDir/\n\tcp askaide.desktop aidea_app.AppDir/\n\tappimagetool aidea_app.AppDir/\n\nbuild-dmg: build-macos\n\trm -fr build/macos/Build/Products/Package\n\tmkdir -p build/macos/Build/Products/Package && cp -r build/macos/Build/Products/Release/AIdea.app build/macos/Build/Products/Package\n\tcreate-dmg --volname \"AIdea Installer\" \\\n\t\t--volicon \"install.icns\" \\\n\t\t--background \"background.jpg\" \\\n\t\t--window-pos 200 120 \\\n\t\t--window-size 600 320 \\\n\t\t--icon-size 100 \\\n\t\t--icon \"AIdea.app\" 170 130 \\\n\t\t--hide-extension \"AIdea.app\" \\\n\t\t--app-drop-link 430 130 \\\n\t\t--sandbox-safe \\\n\t\t--no-internet-enable \\\n\t\t\"build/macos/Build/Products/Package/AIdea-Installer.dmg\" \\\n\t\t\"build/macos/Build/Products/Package\"\n\topen build/macos/Build/Products/Package/\n\nbuild-web:\n\t#flutter build web --web-renderer canvaskit --release --dart-define=FLUTTER_WEB_CANVASKIT_URL=https://resources.aicode.cc/canvaskit/\n\tflutter build web --web-renderer canvaskit --release\n\tcd scripts && go run main.go ../build/web/main.dart.js && cd ..\n\nbuild-web-samehost:\n\tflutter build web --web-renderer canvaskit --release --dart-define=API_SERVER_URL=/\n\tcd scripts && go run main.go ../build/web/main.dart.js && cd ..\n\ndeploy-web: build-web\n\tcd build && tar -zcvf web.tar.gz web\n\tscp build/web.tar.gz huawei-1:/data/webroot\n\tssh huawei-1 \"cd /data/webroot && tar -zxvf web.tar.gz && rm -rf web.tar.gz app && mv web app\"\n\trm -fr build/web.tar.gz\n\n.PHONY: run build-android build-macos ipa build-web-samehost build-web deploy-web build-dmg build-all build-and-sync-android\n"
  },
  {
    "path": "README.md",
    "content": "# AIdea - AI Chat, Collaboration & Image Generation\n\nEnglish | [中文](./README.zh-CN.md)\n\n[![FOSSA Status](https://app.fossa.com/api/projects/custom%2B39727%2Fgithub.com%2Fmylxsw%2Faidea.svg?type=shield&issueType=license)](https://app.fossa.com/projects/custom%2B39727%2Fgithub.com%2Fmylxsw%2Faidea?ref=badge_shield)\n![GitHub release (by tag)](https://img.shields.io/github/downloads/mylxsw/aidea/1.0.4/total)\n![GitHub](https://img.shields.io/github/license/mylxsw/aidea)\n\n<a href=\"https://trendshift.io/repositories/1013\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/1013\" alt=\"mylxsw%2Faidea | Trendshift\" style=\"width: 250px; height: 55px;\" width=\"250\" height=\"55\"/></a>\n\nAn APP that integrates mainstream large language models and image generation models, built with Flutter, with fully open-source code.\n\nDownload & Try:\n\nhttps://aidea.aicode.cc\n\nOpen Source Repositories:\n\n- Client: https://github.com/mylxsw/aidea\n- Server: https://github.com/mylxsw/aidea-server\n- Docker Deployment: https://github.com/mylxsw/aidea-docker\n\n## Development & Build Environment\n\nThe default branch `main` is the v2 version, currently under active development. If you need to self-host, please switch to the [v1.x](https://github.com/mylxsw/aidea/tree/v1.x) branch.\n\n```bash\ngit checkout v1.x\n```\n\nTo set up a development environment for compiling and packaging the APP, you can refer to the following articles (more articles will be added over time):\n\n- [AIdea Development Environment Tutorial (1): Flutter Frontend Setup](https://mp.weixin.qq.com/s/bgAIH6s7t5IREusK_WtpRg)\n- [AIdea Development Environment Tutorial (2): Golang Server Setup](https://mp.weixin.qq.com/s?__biz=MzA3NTU1NDk4Mg==&mid=2454663711&idx=1&sn=c2c66abc20f8e0900afe645ff1f552ac&chksm=88d55bd6bfa2d2c063ea15a4e8864c197009b49233c710b85725f1aa946836e15a26439c69a7&scene=178&cur_album_id=3204997940193296389#rd)\n- [AIdea Development Environment Tutorial (3): Windows Build Environment Setup](https://mp.weixin.qq.com/s?__biz=MzA3NTU1NDk4Mg==&mid=2454663731&idx=1&sn=2aa4841daeb8dc4132e8abe63f585996&chksm=88d55bfabfa2d2ecce8224dcf23da6f911d3d8324121d141fd5c0324197c6f4845dd63639ac2&scene=178&cur_album_id=3204997940193296389#rd)\n- [Flutter App Windows Installer Creation Tutorial](https://mp.weixin.qq.com/s?__biz=MzA3NTU1NDk4Mg==&mid=2454663689&idx=1&sn=73c93edd9ddacb2d4c36061cc79be059&chksm=88d55bc0bfa2d2d6ecaa7979835431467105d9572953f1e96c0f735df3fe60d4f6d6137f041d&scene=178&cur_album_id=3204997940193296389#rd)\n\n> Some users encounter build failures, which can be very frustrating. This is not a bug -- it is a known characteristic of Flutter. Build failures are common as the Flutter version changes.\n> To be safe, you can refer to my local environment configuration:\n>\n> ![Local Environment Configuration](./build-environment.png)\n\n## Self-Hosting / Private Deployment\n\nIf you do not want to use the managed cloud service, you can deploy the server yourself. [See deployment instructions here](https://github.com/mylxsw/aidea-server/blob/main/docs/deploy.md).\n\nIf you prefer not to set it up yourself, you can contact me for assisted deployment. See [VIP Deployment Service](https://github.com/mylxsw/aidea-server/blob/main/docs/deploy-vip.md) for details.\n\n## Community & Support\n\n- WeChat Tech Group: Add WeChat ID `x-prometheus` as a friend for an invitation to the group.\n- WeChat Official Account:\n\n    <img src=\"https://github.com/mylxsw/aidea-server/assets/2330911/376a3b9f-eacd-45c6-9630-39eb720ba097\" width=\"500\" />\n\n## Screenshots\n\nMobile\n\n![Mobile](./images/v2-mobile-preview.png)\n\nMacOS\n\n![MacOS](./images/v2-macos-preview.png)\n\nWindows\n\n![Windows](./images/v2-windows-preview.png)\n\n## License\n\nMIT\n\nCopyright (c) 2025, mylxsw\n"
  },
  {
    "path": "README.zh-CN.md",
    "content": "# AIdea - AI 聊天、协作、图像生成\n\n[English](./README.md) | 中文\n\n[![FOSSA Status](https://app.fossa.com/api/projects/custom%2B39727%2Fgithub.com%2Fmylxsw%2Faidea.svg?type=shield&issueType=license)](https://app.fossa.com/projects/custom%2B39727%2Fgithub.com%2Fmylxsw%2Faidea?ref=badge_shield)\n![GitHub release (by tag)](https://img.shields.io/github/downloads/mylxsw/aidea/1.0.4/total)\n![GitHub](https://img.shields.io/github/license/mylxsw/aidea)\n\n<a href=\"https://trendshift.io/repositories/1013\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/1013\" alt=\"mylxsw%2Faidea | Trendshift\" style=\"width: 250px; height: 55px;\" width=\"250\" height=\"55\"/></a>\n\n一款集成了主流大语言模型以及绘图模型的 APP， 采用 Flutter 开发，代码完全开源。\n\n下载体验地址：\n\nhttps://aidea.aicode.cc\n\n开源代码：\n\n- 客户端：https://github.com/mylxsw/aidea\n- 服务端：https://github.com/mylxsw/aidea-server\n- Docker 部署：https://github.com/mylxsw/aidea-docker\n\n## 开发、编译运行环境\n\n默认分支 `main` 是 v2 版本，当前正在开发中，如需自己部署，请切换到 [v1.x](https://github.com/mylxsw/aidea/tree/v1.x) 分支。\n\n```bash\ngit checkout v1.x\n```\n\n搭建开发环境，用来编译和打包 APP，可以参考下面的文章，更多文章后面有时间了会持续更新。\n\n- [AIdea 项目开发环境部署教程（一）前端 Flutter 环境搭建](https://mp.weixin.qq.com/s/bgAIH6s7t5IREusK_WtpRg)\n- [AIdea 项目开发环境部署教程（二）服务端 Golang 环境搭建](https://mp.weixin.qq.com/s?__biz=MzA3NTU1NDk4Mg==&mid=2454663711&idx=1&sn=c2c66abc20f8e0900afe645ff1f552ac&chksm=88d55bd6bfa2d2c063ea15a4e8864c197009b49233c710b85725f1aa946836e15a26439c69a7&scene=178&cur_album_id=3204997940193296389#rd)\n- [AIdea 项目开发环境部署教程（三）Windows 编译环境搭建](https://mp.weixin.qq.com/s?__biz=MzA3NTU1NDk4Mg==&mid=2454663731&idx=1&sn=2aa4841daeb8dc4132e8abe63f585996&chksm=88d55bfabfa2d2ecce8224dcf23da6f911d3d8324121d141fd5c0324197c6f4845dd63639ac2&scene=178&cur_album_id=3204997940193296389#rd)\n- [Flutter 应用 Windows 安装包创建教程](https://mp.weixin.qq.com/s?__biz=MzA3NTU1NDk4Mg==&mid=2454663689&idx=1&sn=73c93edd9ddacb2d4c36061cc79be059&chksm=88d55bc0bfa2d2d6ecaa7979835431467105d9572953f1e96c0f735df3fe60d4f6d6137f041d&scene=178&cur_album_id=3204997940193296389#rd)\n\n> 有些小伙伴在编译的时候总是失败，非常令人抓狂，这并不是什么问题，而是 Flutter 特有的特性，随着 Flutter 版本的变化，编译失败是常态。\n> 保险起见，你可以参考我的本地环境配置：\n>\n> ![本地环境配置](./build-environment.png)\n\n## 私有化部署\n\n如果你不想使用托管的云服务，可以自己部署服务端，[部署请看这里](https://github.com/mylxsw/aidea-server/blob/main/docs/deploy.md)。\n\n不想自己折腾，可以找我来帮你部署，详情参考 [服务器代部署说明](https://github.com/mylxsw/aidea-server/blob/main/docs/deploy-vip.md)。\n\n## 技术交流\n\n- 微信技术交流群：请添加微信号 `x-prometheus` 为好友，拉你进群。\n- 微信公众号\n\n    <img src=\"https://github.com/mylxsw/aidea-server/assets/2330911/376a3b9f-eacd-45c6-9630-39eb720ba097\" width=\"500\" />\n\n## 产品截图\n\n移动端\n\n![移动端](./images/v2-mobile-preview.png)\n\nMacOS 端\n\n![MacOS 端](./images/v2-macos-preview.png)\n\nWindows 端\n\n![Windows 端](./images/v2-windows-preview.png)\n\n## License\n\nMIT\n\nCopyright (c) 2025, mylxsw\n"
  },
  {
    "path": "analysis_options.yaml",
    "content": "# This file configures the analyzer, which statically analyzes Dart code to\n# check for errors, warnings, and lints.\n#\n# The issues identified by the analyzer are surfaced in the UI of Dart-enabled\n# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be\n# invoked from the command line by running `flutter analyze`.\n\n# The following line activates a set of recommended lints for Flutter apps,\n# packages, and plugins designed to encourage good coding practices.\ninclude: package:flutter_lints/flutter.yaml\n\nlinter:\n  # The lint rules applied to this project can be customized in the\n  # section below to disable rules from the `package:flutter_lints/flutter.yaml`\n  # included above or to enable additional rules. A list of all available lints\n  # and their documentation is published at\n  # https://dart-lang.github.io/linter/lints/index.html.\n  #\n  # Instead of disabling a lint rule for the entire project in the\n  # section below, it can also be suppressed for a single line of code\n  # or a specific dart file by using the `// ignore: name_of_lint` and\n  # `// ignore_for_file: name_of_lint` syntax on the line or in the file\n  # producing the lint.\n  rules:\n    # avoid_print: false  # Uncomment to disable the `avoid_print` rule\n    # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule\n\n# Additional information about this file can be found at\n# https://dart.dev/guides/language/analysis-options\n"
  },
  {
    "path": "android/.gitignore",
    "content": "gradle-wrapper.jar\n/.gradle\n/captures/\n/gradlew\n/gradlew.bat\n/local.properties\nGeneratedPluginRegistrant.java\n\n# Remember to never publicly share your keystore.\n# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app\nkey.properties\n**/*.keystore\n**/*.jks\n"
  },
  {
    "path": "android/app/build.gradle",
    "content": "plugins {\n    id \"com.android.application\"\n    id \"kotlin-android\"\n    // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.\n    id \"dev.flutter.flutter-gradle-plugin\"\n}\n\ndef localProperties = new Properties()\ndef localPropertiesFile = rootProject.file('local.properties')\nif (localPropertiesFile.exists()) {\n    localPropertiesFile.withReader('UTF-8') { reader ->\n        localProperties.load(reader)\n    }\n}\n\ndef flutterVersionCode = localProperties.getProperty('flutter.versionCode')\nif (flutterVersionCode == null) {\n    flutterVersionCode = '1'\n}\n\ndef flutterVersionName = localProperties.getProperty('flutter.versionName')\nif (flutterVersionName == null) {\n    flutterVersionName = '1.0'\n}\n\ndef keystoreProperties = new Properties()\ndef keystorePropertiesFile = rootProject.file('key.properties')\nif (keystorePropertiesFile.exists()) {\n    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))\n}\n\nandroid {\n    namespace 'cc.aicode.flutter.askaide.askaide'\n    compileSdkVersion flutter.compileSdkVersion\n    // ndkVersion flutter.ndkVersion\n    ndkVersion \"27.0.12077973\"\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n\n    kotlinOptions {\n        jvmTarget = JavaVersion.VERSION_1_8\n    }\n\n    defaultConfig {\n        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).\n        applicationId \"cc.aicode.flutter.askaide.askaide\"\n        // You can update the following values to match your application needs.\n        // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.\n        // minSdkVersion flutter.minSdkVersion\n        minSdkVersion 23\n        targetSdkVersion flutter.targetSdkVersion\n        versionCode flutter.versionCode\n        versionName flutterVersionName\n    }\n\n    signingConfigs {\n       release {\n           keyAlias keystoreProperties['keyAlias']\n           keyPassword keystoreProperties['keyPassword']\n           storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null\n           storePassword keystoreProperties['storePassword']\n       }\n   }\n   buildTypes {\n       release {\n           signingConfig signingConfigs.release\n       }\n   }\n}\n\nflutter {\n    source '../..'\n}"
  },
  {
    "path": "android/app/proguard-rules.pro",
    "content": "-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivity$g\n-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter$Args\n-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter$Error\n-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter\n-dontwarn com.stripe.android.pushProvisioning.PushProvisioningEphemeralKeyProvider"
  },
  {
    "path": "android/app/src/debug/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"cc.aicode.flutter.askaide.askaide\">\n    <!-- The INTERNET permission is required for development. Specifically,\n         the Flutter tool needs it to communicate with the running application\n         to allow setting breakpoints, to provide hot reload, etc.\n    -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n</manifest>\n"
  },
  {
    "path": "android/app/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"cc.aicode.flutter.askaide.askaide\">\n   <application\n        android:label=\"AIdea\"\n        android:name=\"${applicationName}\"\n        android:icon=\"@mipmap/launcher_icon\"\n        android:requestLegacyExternalStorage=\"true\"\n        android:usesCleartextTraffic=\"true\">\n        <activity\n            android:name=\".MainActivity\"\n            android:exported=\"true\"\n            android:launchMode=\"singleTop\"\n            android:theme=\"@style/LaunchTheme\"\n            android:configChanges=\"orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode\"\n            android:hardwareAccelerated=\"true\"\n            android:windowSoftInputMode=\"adjustResize\">\n            <!-- Specifies an Android theme to apply to this Activity as soon as\n                 the Android process has started. This theme is visible to the user\n                 while the Flutter UI initializes. After that, this theme continues\n                 to determine the Window background behind the Flutter UI. -->\n            <meta-data\n              android:name=\"io.flutter.embedding.android.NormalTheme\"\n              android:resource=\"@style/NormalTheme\"\n              />\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"${applicationId}.FlutterActivity\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <data\n                    android:host=\"${applicationId}\"\n                    android:path=\"/\"\n                    android:scheme=\"wechatextmsg\" />\n            </intent-filter>\n        </activity>\n        <!-- Set up the Sign in with Apple activity, such that it's callable from the browser-redirect -->\n        <activity\n            android:name=\"com.aboutyou.dart_packages.sign_in_with_apple.SignInWithAppleCallback\"\n            android:exported=\"true\"\n        >\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n\n                <data android:scheme=\"signinwithapple\" />\n                <data android:path=\"callback\" />\n            </intent-filter>\n        </activity>\n        <!-- Don't delete the meta-data below.\n             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->\n        <meta-data\n            android:name=\"flutterEmbedding\"\n            android:value=\"2\" />\n        <meta-data\n            android:name=\"weChatAppId\"\n            android:value=\"wx52cc036cc770406d\" />\n    </application>\n\n    <queries>\n        <intent>\n            <action android:name=\"${applicationId}.FlutterActivity\" />\n        </intent>\n        <intent>\n            <action android:name=\"android.intent.action.VIEW\" />\n            <data\n                android:host=\"${applicationId}\"\n                android:path=\"/\"\n                android:scheme=\"wechatextmsg\" />\n        </intent>\n        <intent>\n            <action android:name=\"android.intent.action.TTS_SERVICE\" />\n        </intent>\n    </queries>\n    \n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>\n    <uses-permission android:name=\"android.permission.RECORD_AUDIO\" />\n    <uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />\n    <uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\" />\n</manifest>\n"
  },
  {
    "path": "android/app/src/main/kotlin/cc/aicode/flutter/askaide/askaide/MainActivity.kt",
    "content": "package cc.aicode.flutter.askaide.askaide\n\nimport io.flutter.embedding.android.FlutterActivity\nimport io.flutter.embedding.android.FlutterFragmentActivity\n\nclass MainActivity: FlutterFragmentActivity() {\n}"
  },
  {
    "path": "android/app/src/main/res/drawable/launch_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item>\n        <bitmap android:gravity=\"fill\" android:src=\"@drawable/background\"/>\n    </item>\n    <item>\n        <bitmap android:gravity=\"center\" android:src=\"@drawable/splash\"/>\n    </item>\n</layer-list>\n"
  },
  {
    "path": "android/app/src/main/res/drawable-v21/launch_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item>\n        <bitmap android:gravity=\"fill\" android:src=\"@drawable/background\"/>\n    </item>\n    <item>\n        <bitmap android:gravity=\"center\" android:src=\"@drawable/splash\"/>\n    </item>\n</layer-list>\n"
  },
  {
    "path": "android/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->\n    <style name=\"LaunchTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n        <!-- Show a splash screen on the activity. Automatically removed when\n             the Flutter engine draws its first frame -->\n        <item name=\"android:windowBackground\">@drawable/launch_background</item>\n        <item name=\"android:forceDarkAllowed\">false</item>\n        <item name=\"android:windowFullscreen\">true</item>\n        <item name=\"android:windowDrawsSystemBarBackgrounds\">true</item>\n        <item name=\"android:windowLayoutInDisplayCutoutMode\">shortEdges</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n\n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"Theme.MaterialComponents\">\n        <item name=\"android:windowBackground\">?android:colorBackground</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "android/app/src/main/res/values-night/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->\n    <style name=\"LaunchTheme\" parent=\"Theme.AppCompat.DayNight.NoActionBar\">\n        <!-- Show a splash screen on the activity. Automatically removed when\n             the Flutter engine draws its first frame -->\n        <item name=\"android:windowBackground\">@drawable/launch_background</item>\n        <item name=\"android:forceDarkAllowed\">false</item>\n        <item name=\"android:windowFullscreen\">true</item>\n        <item name=\"android:windowDrawsSystemBarBackgrounds\">true</item>\n        <item name=\"android:windowLayoutInDisplayCutoutMode\">shortEdges</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n\n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"Theme.MaterialComponents\">\n        <item name=\"android:windowBackground\">?android:colorBackground</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "android/app/src/main/res/values-night-v31/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Black.NoTitleBar\">\n        <item name=\"android:forceDarkAllowed\">false</item>\n        <item name=\"android:windowFullscreen\">true</item>\n        <item name=\"android:windowDrawsSystemBarBackgrounds\">true</item>\n        <item name=\"android:windowLayoutInDisplayCutoutMode\">shortEdges</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n         \n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"@android:style/Theme.Black.NoTitleBar\">\n        <item name=\"android:windowBackground\">?android:colorBackground</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "android/app/src/main/res/values-v31/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Light.NoTitleBar\">\n        <item name=\"android:forceDarkAllowed\">false</item>\n        <item name=\"android:windowFullscreen\">true</item>\n        <item name=\"android:windowDrawsSystemBarBackgrounds\">true</item>\n        <item name=\"android:windowLayoutInDisplayCutoutMode\">shortEdges</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n         \n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"@android:style/Theme.Light.NoTitleBar\">\n        <item name=\"android:windowBackground\">?android:colorBackground</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "android/app/src/profile/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"cc.aicode.flutter.askaide.askaide\">\n    <!-- The INTERNET permission is required for development. Specifically,\n         the Flutter tool needs it to communicate with the running application\n         to allow setting breakpoints, to provide hot reload, etc.\n    -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n</manifest>\n"
  },
  {
    "path": "android/build.gradle",
    "content": "allprojects {\n    repositories {\n        google()\n        mavenCentral()\n    }\n\n    subprojects {\n        afterEvaluate { project ->\n            if (project.hasProperty('android')) {\n                project.android {\n                    if (namespace == null) {\n                        namespace project.group\n                    }\n                }\n            }\n\n            if (project.hasProperty(\"android\")) {\n                project.android {\n                    compileSdkVersion = 34\n                }\n            }\n        }\n    }\n}\n\nrootProject.buildDir = \"../build\"\nsubprojects {\n    project.buildDir = \"${rootProject.buildDir}/${project.name}\"\n}\nsubprojects {\n    project.evaluationDependsOn(\":app\")\n}\n\ntasks.register(\"clean\", Delete) {\n    delete rootProject.buildDir\n}\n\n"
  },
  {
    "path": "android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.10-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "android/gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError\nandroid.useAndroidX=true\nandroid.enableJetifier=true\nkotlin.jvm.target.validation.mode = IGNORE"
  },
  {
    "path": "android/settings.gradle",
    "content": "pluginManagement {\n    def flutterSdkPath = {\n        def properties = new Properties()\n        file(\"local.properties\").withInputStream { properties.load(it) }\n        def flutterSdkPath = properties.getProperty(\"flutter.sdk\")\n        assert flutterSdkPath != null, \"flutter.sdk not set in local.properties\"\n        return flutterSdkPath\n    }()\n\n    includeBuild(\"$flutterSdkPath/packages/flutter_tools/gradle\")\n\n    repositories {\n        google()\n        mavenCentral()\n        gradlePluginPortal()\n    }\n}\n\nplugins {\n    id \"dev.flutter.flutter-plugin-loader\" version \"1.0.0\"\n    id \"com.android.application\" version '8.7.2' apply false\n    id \"org.jetbrains.kotlin.android\" version \"1.9.20\" apply false\n}\n\ninclude \":app\"\n"
  },
  {
    "path": "askaide.desktop",
    "content": "[Desktop Entry]\nVersion=1.0\nName=Ask aide\nComment=Ask aide!\nIcon=app\nExec=askaide %U\nTerminal=false\nType=Application\nCategories=Utility;\nKeywords=Internet;\nStartupNotify=false\n"
  },
  {
    "path": "assets/lottie/empty_status.json",
    "content": "{\"v\":\"4.5.9\",\"fr\":25,\"ip\":0,\"op\":100,\"w\":248,\"h\":187,\"ddd\":0,\"assets\":[],\"layers\":[{\"ddd\":0,\"ind\":0,\"ty\":4,\"nm\":\"\\rtail\",\"ks\":{\"o\":{\"a\":0,\"k\":100},\"r\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":0,\"s\":[21],\"e\":[-60]},{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":12,\"s\":[-60],\"e\":[21]},{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":25,\"s\":[21],\"e\":[-60]},{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":37,\"s\":[-60],\"e\":[21]},{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":50,\"s\":[21],\"e\":[-60]},{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":62,\"s\":[-60],\"e\":[21]},{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":74,\"s\":[21],\"e\":[-60]},{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"n\":[\"0p833_0p833_0p167_0p167\"],\"t\":87,\"s\":[-60],\"e\":[21]},{\"t\":99}]},\"p\":{\"a\":0,\"k\":[80,44.429,0]},\"a\":{\"a\":0,\"k\":[8.473,9.453,0]},\"s\":{\"a\":0,\"k\":[100,100,100]}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0],[-1.739,1.217],[0,0],[2.934,-2.055],[0.434,-0.631],[0.467,0.666],[0,0],[-2.054,-2.934],[-1.467,-0.418],[0,0]],\"o\":[[0,0],[1.521,0.11],[2.933,-2.055],[0,0],[-0.666,0.466],[-0.192,-0.741],[-2.054,-2.934],[0,0],[1.218,1.738],[0,0],[0,0]],\"v\":[[-0.161,5.232],[-0.161,5.232],[5.32,4.011],[8.702,-2.465],[1.462,-1.499],[-0.177,0.187],[-1.14,-1.958],[-7.614,-5.342],[-6.649,1.901],[-1.916,4.923],[-1.916,4.923]],\"c\":true}},\"nm\":\"路径 1\",\"mn\":\"ADBE Vector Shape - Group\"},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.769,0.769,0.851,1]},\"o\":{\"a\":0,\"k\":100},\"r\":1,\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\"},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[8.952,5.591],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"组 1\",\"np\":2,\"mn\":\"ADBE Vector Group\"}],\"ip\":0,\"op\":750,\"st\":0,\"bm\":0,\"sr\":1},{\"ddd\":0,\"ind\":1,\"ty\":4,\"nm\":\"\\reye\",\"ks\":{\"o\":{\"a\":0,\"k\":100},\"r\":{\"a\":0,\"k\":0},\"p\":{\"a\":0,\"k\":[145.857,82.715,0]},\"a\":{\"a\":0,\"k\":[5.964,5.965,0]},\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":42,\"s\":[100,100,100],\"e\":[100,1,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":45,\"s\":[100,1,100],\"e\":[100,100,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":48,\"s\":[100,100,100],\"e\":[100,100,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":93,\"s\":[100,100,100],\"e\":[100,1,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":96,\"s\":[100,1,100],\"e\":[100,100,100]},{\"t\":99}]}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ks\":{\"a\":0,\"k\":{\"i\":[[-3.156,0],[0,-3.156],[3.156,0],[0,3.156]],\"o\":[[3.156,0],[0,3.156],[-3.156,0],[0,-3.156]],\"v\":[[0,-5.715],[5.714,-0.001],[0,5.715],[-5.714,-0.001]],\"c\":true}},\"nm\":\"路径 1\",\"mn\":\"ADBE Vector Shape - Group\"},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.671,0.671,0.761,1]},\"o\":{\"a\":0,\"k\":100},\"r\":1,\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\"},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[5.964,5.965],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"组 1\",\"np\":2,\"mn\":\"ADBE Vector Group\"}],\"ip\":0,\"op\":750,\"st\":0,\"bm\":0,\"sr\":1},{\"ddd\":0,\"ind\":2,\"ty\":4,\"nm\":\"\\reyes white\",\"ks\":{\"o\":{\"a\":0,\"k\":100},\"r\":{\"a\":0,\"k\":0},\"p\":{\"a\":0,\"k\":[143.714,82.714,0]},\"a\":{\"a\":0,\"k\":[11.679,11.678,0]},\"s\":{\"a\":0,\"k\":[100,100,100]}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ks\":{\"a\":0,\"k\":{\"i\":[[-6.312,0],[0,6.312],[6.312,0],[0,-6.311]],\"o\":[[6.312,0],[0,-6.311],[-6.312,0],[0,6.312]],\"v\":[[0,11.428],[11.429,-0.001],[0,-11.428],[-11.429,-0.001]],\"c\":true}},\"nm\":\"路径 1\",\"mn\":\"ADBE Vector Shape - Group\"},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[1,1,1,1]},\"o\":{\"a\":0,\"k\":100},\"r\":1,\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\"},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[11.679,11.678],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"组 1\",\"np\":2,\"mn\":\"ADBE Vector Group\"}],\"ip\":0,\"op\":750,\"st\":0,\"bm\":0,\"sr\":1},{\"ddd\":0,\"ind\":3,\"ty\":4,\"nm\":\"\\rbody\",\"ks\":{\"o\":{\"a\":0,\"k\":100},\"r\":{\"a\":0,\"k\":0},\"p\":{\"a\":0,\"k\":[123.719,72.664,0]},\"a\":{\"a\":0,\"k\":[60.153,37.965,0]},\"s\":{\"a\":0,\"k\":[100,100,100]}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ks\":{\"a\":0,\"k\":{\"i\":[[31.5,0],[10.189,-7.981],[1.68,1.422],[0,0],[0,0],[4.418,-6.529],[0,-12.88],[0,0],[-3.015,-6.029],[0,0],[-12.453,0],[-6.911,9.934]],\"o\":[[-13.946,0],[-1.536,1.204],[0,0],[0,0],[-0.153,2.463],[-6.908,9.775],[0,0],[10.063,-5.593],[0,0],[9.428,-2.285],[20.38,0],[-2.476,-30.863]],\"v\":[[0.097,-37.715],[-36.872,-24.975],[-42.158,-26.982],[-43.403,-27.495],[-44.57,-27.098],[-48.937,-12.301],[-59.903,22.286],[-59.499,29.285],[-33.617,30.857],[-1.617,30.857],[23.526,37.715],[59.903,17.423]],\"c\":true}},\"nm\":\"路径 1\",\"mn\":\"ADBE Vector Shape - Group\"},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.769,0.769,0.851,1]},\"o\":{\"a\":0,\"k\":100},\"r\":1,\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\"},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[60.153,37.965],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"组 1\",\"np\":2,\"mn\":\"ADBE Vector Group\"}],\"ip\":0,\"op\":750,\"st\":0,\"bm\":0,\"sr\":1},{\"ddd\":0,\"ind\":4,\"ty\":4,\"nm\":\"\\rfin\",\"ks\":{\"o\":{\"a\":0,\"k\":100},\"r\":{\"a\":0,\"k\":0},\"p\":{\"a\":0,\"k\":[104.536,103.099,0]},\"a\":{\"a\":0,\"k\":[24.733,0.25,0]},\"s\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":0,\"s\":[100,100,100],\"e\":[100,70,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":6,\"s\":[100,70,100],\"e\":[100,100,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":12,\"s\":[100,100,100],\"e\":[100,56,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":18,\"s\":[100,56,100],\"e\":[100,100,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":24,\"s\":[100,100,100],\"e\":[100,70,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":30,\"s\":[100,70,100],\"e\":[100,100,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":36,\"s\":[100,100,100],\"e\":[100,56,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":43,\"s\":[100,56,100],\"e\":[100,100,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":49,\"s\":[100,100,100],\"e\":[100,70,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":55,\"s\":[100,70,100],\"e\":[100,100,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":61,\"s\":[100,100,100],\"e\":[100,56,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":67,\"s\":[100,56,100],\"e\":[100,100,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":74,\"s\":[100,100,100],\"e\":[100,70,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":80,\"s\":[100,70,100],\"e\":[100,100,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":87,\"s\":[100,100,100],\"e\":[100,56,100]},{\"i\":{\"x\":[0.833,0.833,0.833],\"y\":[0.833,0.833,0.833]},\"o\":{\"x\":[0.167,0.167,0.167],\"y\":[0.167,0.167,0.167]},\"n\":[\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\",\"0p833_0p833_0p167_0p167\"],\"t\":94,\"s\":[100,56,100],\"e\":[100,100,100]},{\"t\":100}]}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[15.429,0],[3.714,7.429]],\"o\":[[-9.429,2.286],[-12,0],[0,0]],\"v\":[[21,-8.572],[-9,8.572],[-11,-8.572]],\"c\":true}},\"nm\":\"路径 1\",\"mn\":\"ADBE Vector Shape - Group\"},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.769,0.769,0.851,1]},\"o\":{\"a\":0,\"k\":100},\"r\":1,\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\"},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[21.25,8.822],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"组 1\",\"np\":2,\"mn\":\"ADBE Vector Group\"}],\"ip\":0,\"op\":750,\"st\":0,\"bm\":0,\"sr\":1},{\"ddd\":0,\"ind\":5,\"ty\":4,\"nm\":\"\\rbelly5\",\"ks\":{\"o\":{\"a\":0,\"k\":100},\"r\":{\"a\":0,\"k\":0},\"p\":{\"a\":0,\"k\":[88.286,85.293,0]},\"a\":{\"a\":0,\"k\":[24.536,34.528,0]},\"s\":{\"a\":0,\"k\":[100,100,100]}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ks\":{\"a\":0,\"k\":{\"i\":[[18.68,0.296],[2.348,-3.47],[0,-12.88],[-2.993,-7.135],[-3.347,0],[0,18.935]],\"o\":[[-0.485,2.668],[-6.908,9.775],[0,8.221],[3.058,0.894],[18.935,0],[0,-18.751]],\"v\":[[-9.448,-34.279],[-13.32,-24.878],[-24.286,9.708],[-19.636,32.896],[-10,34.279],[24.286,-0.007]],\"c\":true}},\"nm\":\"路径 1\",\"mn\":\"ADBE Vector Shape - Group\"},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.973,0.973,0.988,1]},\"o\":{\"a\":0,\"k\":100},\"r\":1,\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\"},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[24.536,34.528],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"组 1\",\"np\":2,\"mn\":\"ADBE Vector Group\"}],\"ip\":0,\"op\":750,\"st\":0,\"bm\":0,\"sr\":1},{\"ddd\":0,\"ind\":6,\"ty\":4,\"nm\":\"\\rbelly4\",\"ks\":{\"o\":{\"a\":0,\"k\":100},\"r\":{\"a\":0,\"k\":0},\"p\":{\"a\":0,\"k\":[98.286,85.659,0]},\"a\":{\"a\":0,\"k\":[34.536,44.162,0]},\"s\":{\"a\":0,\"k\":[100,100,100]}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ks\":{\"a\":0,\"k\":{\"i\":[[20.602,3.796],[2.903,-2.274],[1.714,0.858],[6.177,-9.127],[0,-12.88],[-5.829,-9.25],[-5.292,0],[0,24.458]],\"o\":[[-3.298,1.715],[-2.142,1.678],[-1.714,-0.857],[-6.908,9.775],[0,11.742],[4.705,1.702],[24.458,0],[0,-21.701]],\"v\":[[-1.929,-43.912],[-11.254,-37.921],[-18.286,-41.231],[-23.32,-25.246],[-34.286,9.341],[-25.071,41.278],[-10,43.912],[34.286,-0.373]],\"c\":true}},\"nm\":\"路径 1\",\"mn\":\"ADBE Vector Shape - Group\"},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.945,0.945,0.973,1]},\"o\":{\"a\":0,\"k\":100},\"r\":1,\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\"},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[34.536,44.162],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"组 1\",\"np\":2,\"mn\":\"ADBE Vector Group\"}],\"ip\":0,\"op\":750,\"st\":0,\"bm\":0,\"sr\":1},{\"ddd\":0,\"ind\":7,\"ty\":4,\"nm\":\"\\rbelly3\",\"ks\":{\"o\":{\"a\":0,\"k\":100},\"r\":{\"a\":0,\"k\":0},\"p\":{\"a\":0,\"k\":[108.286,87.383,0]},\"a\":{\"a\":0,\"k\":[44.535,52.439,0]},\"s\":{\"a\":0,\"k\":[100,100,100]}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ks\":{\"a\":0,\"k\":{\"i\":[[19.58,8.198],[9.016,-7.063],[1.714,0.857],[6.178,-9.128],[0,-12.88],[-10.446,-10.797],[-6.088,0],[0,29.981]],\"o\":[[-12.096,0.951],[-2.141,1.677],[-1.715,-0.857],[-6.907,9.775],[0,16.21],[5.464,1.848],[29.982,0],[0,-22.556]],\"v\":[[10.948,-52.189],[-21.255,-39.644],[-28.286,-42.954],[-33.321,-26.969],[-44.286,7.617],[-27.4,49.323],[-10.001,52.189],[44.286,-2.097]],\"c\":true}},\"nm\":\"路径 1\",\"mn\":\"ADBE Vector Shape - Group\"},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.91,0.914,0.961,1]},\"o\":{\"a\":0,\"k\":100},\"r\":1,\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\"},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[44.535,52.439],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"组 1\",\"np\":2,\"mn\":\"ADBE Vector Group\"}],\"ip\":0,\"op\":750,\"st\":0,\"bm\":0,\"sr\":1},{\"ddd\":0,\"ind\":8,\"ty\":4,\"nm\":\"\\rbelly2\",\"ks\":{\"o\":{\"a\":0,\"k\":100},\"r\":{\"a\":0,\"k\":0},\"p\":{\"a\":0,\"k\":[118.286,92.286,0]},\"a\":{\"a\":0,\"k\":[54.535,57.536,0]},\"s\":{\"a\":0,\"k\":[100,100,100]}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ks\":{\"a\":0,\"k\":{\"i\":[[0,35.504],[7.996,10.717],[13.435,0],[10.189,-7.981],[1.714,0.857],[6.177,-9.128],[0,-12.879],[-19.548,-9.89],[-3.867,0]],\"o\":[[0,-14.399],[-10.001,-7.459],[-13.946,0],[-2.141,1.677],[-1.715,-0.857],[-6.907,9.775],[0,23.406],[3.679,0.655],[35.504,0]],\"v\":[[54.286,-7],[41.553,-45.409],[5.715,-57.286],[-31.255,-44.547],[-38.286,-47.857],[-43.321,-31.872],[-54.286,2.714],[-21.327,56.27],[-10.001,57.286]],\"c\":true}},\"nm\":\"路径 1\",\"mn\":\"ADBE Vector Shape - Group\"},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.886,0.886,0.937,1]},\"o\":{\"a\":0,\"k\":100},\"r\":1,\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\"},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[54.535,57.536],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"组 1\",\"np\":2,\"mn\":\"ADBE Vector Group\"}],\"ip\":0,\"op\":750,\"st\":0,\"bm\":0,\"sr\":1},{\"ddd\":0,\"ind\":9,\"ty\":4,\"nm\":\"\\rbelly1\",\"ks\":{\"o\":{\"a\":0,\"k\":100},\"r\":{\"a\":0,\"k\":0},\"p\":{\"a\":0,\"k\":[124,95,0]},\"a\":{\"a\":0,\"k\":[60.25,60.25,0]},\"s\":{\"a\":0,\"k\":[100,100,100]}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ks\":{\"a\":0,\"k\":{\"i\":[[-33.137,0],[0,33.137],[33.137,0],[10.189,-7.981],[1.714,0.857],[6.177,-9.127],[0,-12.88]],\"o\":[[33.137,0],[0,-33.137],[-13.946,0],[-2.141,1.677],[-1.714,-0.858],[-6.908,9.776],[0,33.137]],\"v\":[[0,60],[60,0],[0,-60],[-36.969,-47.261],[-44,-50.571],[-49.034,-34.587],[-60,0]],\"c\":true}},\"nm\":\"路径 1\",\"mn\":\"ADBE Vector Shape - Group\"},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.839,0.843,0.906,1]},\"o\":{\"a\":0,\"k\":100},\"r\":1,\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\"},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[60.25,60.25],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"组 1\",\"np\":2,\"mn\":\"ADBE Vector Group\"}],\"ip\":0,\"op\":750,\"st\":0,\"bm\":0,\"sr\":1}]}"
  },
  {
    "path": "build-win-msix.bat",
    "content": "dart run msix:create --release -v --output-path build/windows/runner --output-name AIdea"
  },
  {
    "path": "build-win.bat",
    "content": "flutter build windows --release"
  },
  {
    "path": "devtools_options.yaml",
    "content": "description: This file stores settings for Dart & Flutter DevTools.\ndocumentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states\nextensions:\n"
  },
  {
    "path": "docker-build.sh",
    "content": "#!/usr/bin/env bash\n\nVERSION=1.0.14\n\nrm -fr build/web\n\nflutter build web --web-renderer canvaskit --release --dart-define=API_SERVER_URL=/\ndocker buildx build --platform=linux/amd64,linux/arm64 -t mylxsw/aidea-web:$VERSION . --push\n\n"
  },
  {
    "path": "flutter_launcher_icons.yaml",
    "content": "# flutter pub run flutter_launcher_icons\nflutter_launcher_icons:\n  image_path: \"assets/app.png\"\n\n  android: \"launcher_icon\"\n  # image_path_android: \"assets/icon/icon.png\"\n  min_sdk_android: 21 # android min sdk min:16, default 21\n  # adaptive_icon_background: \"assets/icon/background.png\"\n  # adaptive_icon_foreground: \"assets/icon/foreground.png\"\n  # adaptive_icon_monochrome: \"assets/icon/monochrome.png\"\n\n  ios: true\n  # image_path_ios: \"assets/icon/icon.png\"\n  remove_alpha_channel_ios: true\n  # image_path_ios_dark_transparent: \"assets/icon/icon_dark.png\"\n  # image_path_ios_tinted_grayscale: \"assets/icon/icon_tinted.png\"\n  # desaturate_tinted_to_grayscale_ios: true\n\n  web:\n    generate: true\n    # image_path: \"path/to/image.png\"\n    background_color: \"#hexcode\"\n    theme_color: \"#hexcode\"\n\n  windows:\n    generate: true\n    image_path: \"assets/app-macos.png\"\n    icon_size: 256 # min:48, max:256, default: 48\n\n  macos:\n    generate: true\n    image_path: \"assets/app-macos.png\"\n"
  },
  {
    "path": "install.iss",
    "content": "; Script generated by the Inno Setup Script Wizard.\n; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!\n\n#define MyAppName \"AIdea\"\n#define MyAppVersion \"2.0.0\"\n#define MyAppPublisher \"Shenzhen Gulu Artificial Intelligence Technology Co., Ltd.\"\n#define MyAppURL \"https://ai.aicode.cc/\"\n#define MyAppExeName \"AIdea.exe\"\n\n[Setup]\n; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.\n; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)\nAppId={{F9E7E323-8BD4-46B3-ABEB-20C5CE03F5C7}\nAppName={#MyAppName}\nAppVersion={#MyAppVersion}\n;AppVerName={#MyAppName} {#MyAppVersion}\nAppPublisher={#MyAppPublisher}\nAppPublisherURL={#MyAppURL}\nAppSupportURL={#MyAppURL}\nAppUpdatesURL={#MyAppURL}\nDefaultDirName={autopf}\\{#MyAppName}\nDisableProgramGroupPage=yes\n; Uncomment the following line to run in non administrative install mode (install for current user only.)\n;PrivilegesRequired=lowest\nOutputDir=C:\\Users\\mylxsw\\Desktop\nOutputBaseFilename={#MyAppName}-{#MyAppVersion}-Installer\nSetupIconFile=D:\\Workstation\\codes\\aidea\\app.ico\nCompression=lzma\nSolidCompression=yes\nWizardStyle=modern\n\n[Languages]\nName: \"english\"; MessagesFile: \"compiler:Default.isl\"\n\n[Tasks]\nName: \"desktopicon\"; Description: \"{cm:CreateDesktopIcon}\"; GroupDescription: \"{cm:AdditionalIcons}\"; Flags: checkablealone\n\n[Files]\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\{#MyAppExeName}\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-console-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-console-l1-2-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-datetime-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-debug-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-errorhandling-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-fibers-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-file-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-file-l1-2-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-file-l2-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-handle-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-heap-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-interlocked-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-libraryloader-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-localization-l1-2-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-memory-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-namedpipe-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-processenvironment-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-processthreads-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-processthreads-l1-1-1.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-profile-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-rtlsupport-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-string-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-synch-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-synch-l1-2-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-sysinfo-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-timezone-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-core-util-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-crt-conio-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-crt-convert-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-crt-environment-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-crt-filesystem-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-crt-heap-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-crt-locale-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-crt-math-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-crt-multibyte-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-crt-private-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-crt-process-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-crt-runtime-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-crt-stdio-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-crt-string-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-crt-time-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-crt-utility-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-downlevel-kernel32-l2-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\api-ms-win-eventing-provider-l1-1-0.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\audioplayers_windows_plugin.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\concrt140.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\d3dcompiler_47.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\file_saver_plugin.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\flutter_localization_plugin.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\flutter_tts_plugin.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\flutter_windows.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\libc++.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\libEGL.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\libGLESv2.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\libmpv-2.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\media_kit_libs_windows_video_plugin.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\media_kit_native_event_loop.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\media_kit_video_plugin.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\msvcp140.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\msvcp140_1.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\msvcp140_2.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\msvcp140_atomic_wait.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\msvcp140_codecvt_ids.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\record_windows_plugin.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\screen_brightness_windows_plugin.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\share_plus_plugin.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\sqlite3.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\ucrtbase.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\ucrtbased.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\url_launcher_windows_plugin.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\vccorlib140.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\vccorlib140d.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\vcruntime140.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\vcruntime140_1.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\vcruntime140_1d.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\vcruntime140d.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\vk_swiftshader.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\vulkan-1.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\zlib.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"D:\\Workstation\\codes\\aidea\\build\\windows\\x64\\runner\\Release\\data\\*\"; DestDir: \"{app}\\data\"; Flags: ignoreversion recursesubdirs createallsubdirs\n; NOTE: Don't use \"Flags: ignoreversion\" on any shared system files\n\n[Icons]\nName: \"{autoprograms}\\{#MyAppName}\"; Filename: \"{app}\\{#MyAppExeName}\"\nName: \"{autodesktop}\\{#MyAppName}\"; Filename: \"{app}\\{#MyAppExeName}\"; Tasks: desktopicon\n\n[Run]\nFilename: \"{app}\\{#MyAppExeName}\"; Description: \"{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}\"; Flags: nowait postinstall skipifsilent\n\n"
  },
  {
    "path": "ios/.gitignore",
    "content": "**/dgph\n*.mode1v3\n*.mode2v3\n*.moved-aside\n*.pbxuser\n*.perspectivev3\n**/*sync/\n.sconsign.dblite\n.tags*\n**/.vagrant/\n**/DerivedData/\nIcon?\n**/Pods/\n**/.symlinks/\nprofile\nxcuserdata\n**/.generated/\nFlutter/App.framework\nFlutter/Flutter.framework\nFlutter/Flutter.podspec\nFlutter/Generated.xcconfig\nFlutter/ephemeral/\nFlutter/app.flx\nFlutter/app.zip\nFlutter/flutter_assets/\nFlutter/flutter_export_environment.sh\nServiceDefinitions.json\nRunner/GeneratedPluginRegistrant.*\n\n# Exceptions to above rules.\n!default.mode1v3\n!default.mode2v3\n!default.pbxuser\n!default.perspectivev3\n"
  },
  {
    "path": "ios/Flutter/AppFrameworkInfo.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n  <key>CFBundleDevelopmentRegion</key>\n  <string>en</string>\n  <key>CFBundleExecutable</key>\n  <string>App</string>\n  <key>CFBundleIdentifier</key>\n  <string>io.flutter.flutter.app</string>\n  <key>CFBundleInfoDictionaryVersion</key>\n  <string>6.0</string>\n  <key>CFBundleName</key>\n  <string>App</string>\n  <key>CFBundlePackageType</key>\n  <string>FMWK</string>\n  <key>CFBundleShortVersionString</key>\n  <string>1.0</string>\n  <key>CFBundleSignature</key>\n  <string>????</string>\n  <key>CFBundleVersion</key>\n  <string>1.0</string>\n  <key>MinimumOSVersion</key>\n  <string>12.0</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Flutter/Debug.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"\n#include \"Generated.xcconfig\"\n"
  },
  {
    "path": "ios/Flutter/Release.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"\n#include \"Generated.xcconfig\"\n"
  },
  {
    "path": "ios/Podfile",
    "content": "# Uncomment this line to define a global platform for your project\nplatform :ios, '13.0'\n\n# CocoaPods analytics sends network stats synchronously affecting flutter build latency.\nENV['COCOAPODS_DISABLE_STATS'] = 'true'\n\nproject 'Runner', {\n  'Debug' => :debug,\n  'Profile' => :release,\n  'Release' => :release,\n}\n\ndef flutter_root\n  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)\n  unless File.exist?(generated_xcode_build_settings_path)\n    raise \"#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first\"\n  end\n\n  File.foreach(generated_xcode_build_settings_path) do |line|\n    matches = line.match(/FLUTTER_ROOT\\=(.*)/)\n    return matches[1].strip if matches\n  end\n  raise \"FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get\"\nend\n\nrequire File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)\n\nflutter_ios_podfile_setup\n\ntarget 'Runner' do\n  use_frameworks!\n  use_modular_headers!\n\n  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))\nend\n\npost_install do |installer|\n  installer.pods_project.targets.each do |target|\n    flutter_additional_ios_build_settings(target)\n  end\nend\n"
  },
  {
    "path": "ios/Runner/AppDelegate.swift",
    "content": "import UIKit\nimport Flutter\n\n@main\n@objc class AppDelegate: FlutterAppDelegate {\n  override func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n  ) -> Bool {\n    GeneratedPluginRegistrant.register(with: self)\n    return super.application(application, didFinishLaunchingWithOptions: launchOptions)\n  }\n}\n"
  },
  {
    "path": "ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\"images\":[{\"size\":\"20x20\",\"idiom\":\"iphone\",\"filename\":\"Icon-App-20x20@2x.png\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"idiom\":\"iphone\",\"filename\":\"Icon-App-20x20@3x.png\",\"scale\":\"3x\"},{\"size\":\"29x29\",\"idiom\":\"iphone\",\"filename\":\"Icon-App-29x29@1x.png\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"idiom\":\"iphone\",\"filename\":\"Icon-App-29x29@2x.png\",\"scale\":\"2x\"},{\"size\":\"29x29\",\"idiom\":\"iphone\",\"filename\":\"Icon-App-29x29@3x.png\",\"scale\":\"3x\"},{\"size\":\"40x40\",\"idiom\":\"iphone\",\"filename\":\"Icon-App-40x40@2x.png\",\"scale\":\"2x\"},{\"size\":\"40x40\",\"idiom\":\"iphone\",\"filename\":\"Icon-App-40x40@3x.png\",\"scale\":\"3x\"},{\"size\":\"57x57\",\"idiom\":\"iphone\",\"filename\":\"Icon-App-57x57@1x.png\",\"scale\":\"1x\"},{\"size\":\"57x57\",\"idiom\":\"iphone\",\"filename\":\"Icon-App-57x57@2x.png\",\"scale\":\"2x\"},{\"size\":\"60x60\",\"idiom\":\"iphone\",\"filename\":\"Icon-App-60x60@2x.png\",\"scale\":\"2x\"},{\"size\":\"60x60\",\"idiom\":\"iphone\",\"filename\":\"Icon-App-60x60@3x.png\",\"scale\":\"3x\"},{\"size\":\"20x20\",\"idiom\":\"ipad\",\"filename\":\"Icon-App-20x20@1x.png\",\"scale\":\"1x\"},{\"size\":\"20x20\",\"idiom\":\"ipad\",\"filename\":\"Icon-App-20x20@2x.png\",\"scale\":\"2x\"},{\"size\":\"29x29\",\"idiom\":\"ipad\",\"filename\":\"Icon-App-29x29@1x.png\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"idiom\":\"ipad\",\"filename\":\"Icon-App-29x29@2x.png\",\"scale\":\"2x\"},{\"size\":\"40x40\",\"idiom\":\"ipad\",\"filename\":\"Icon-App-40x40@1x.png\",\"scale\":\"1x\"},{\"size\":\"40x40\",\"idiom\":\"ipad\",\"filename\":\"Icon-App-40x40@2x.png\",\"scale\":\"2x\"},{\"size\":\"50x50\",\"idiom\":\"ipad\",\"filename\":\"Icon-App-50x50@1x.png\",\"scale\":\"1x\"},{\"size\":\"50x50\",\"idiom\":\"ipad\",\"filename\":\"Icon-App-50x50@2x.png\",\"scale\":\"2x\"},{\"size\":\"72x72\",\"idiom\":\"ipad\",\"filename\":\"Icon-App-72x72@1x.png\",\"scale\":\"1x\"},{\"size\":\"72x72\",\"idiom\":\"ipad\",\"filename\":\"Icon-App-72x72@2x.png\",\"scale\":\"2x\"},{\"size\":\"76x76\",\"idiom\":\"ipad\",\"filename\":\"Icon-App-76x76@1x.png\",\"scale\":\"1x\"},{\"size\":\"76x76\",\"idiom\":\"ipad\",\"filename\":\"Icon-App-76x76@2x.png\",\"scale\":\"2x\"},{\"size\":\"83.5x83.5\",\"idiom\":\"ipad\",\"filename\":\"Icon-App-83.5x83.5@2x.png\",\"scale\":\"2x\"},{\"size\":\"1024x1024\",\"idiom\":\"ios-marketing\",\"filename\":\"Icon-App-1024x1024@1x.png\",\"scale\":\"1x\"}],\"info\":{\"version\":1,\"author\":\"xcode\"}}"
  },
  {
    "path": "ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"background.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"LaunchImage.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"filename\" : \"LaunchImage@2x.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"filename\" : \"LaunchImage@3x.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md",
    "content": "# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images."
  },
  {
    "path": "ios/Runner/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"12121\" systemVersion=\"16G29\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"12089\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"Ydg-fD-yQy\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"xbc-2k-c8Z\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <imageView clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" image=\"LaunchBackground\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"tWc-Dq-wcI\"/>\n                            <imageView opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" image=\"LaunchImage\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"YRO-k0-Ey4\"></imageView>\n                        </subviews>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <constraints>\n                            <constraint firstItem=\"YRO-k0-Ey4\" firstAttribute=\"leading\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"leading\" id=\"3T2-ad-Qdv\"/>\n                            <constraint firstItem=\"tWc-Dq-wcI\" firstAttribute=\"bottom\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"bottom\" id=\"RPx-PI-7Xg\"/>\n                            <constraint firstItem=\"tWc-Dq-wcI\" firstAttribute=\"top\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"top\" id=\"SdS-ul-q2q\"/>\n                            <constraint firstAttribute=\"trailing\" secondItem=\"tWc-Dq-wcI\" secondAttribute=\"trailing\" id=\"Swv-Gf-Rwn\"/>\n                            <constraint firstAttribute=\"trailing\" secondItem=\"YRO-k0-Ey4\" secondAttribute=\"trailing\" id=\"TQA-XW-tRk\"/>\n                            <constraint firstItem=\"YRO-k0-Ey4\" firstAttribute=\"bottom\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"bottom\" id=\"duK-uY-Gun\"/>\n                            <constraint firstItem=\"tWc-Dq-wcI\" firstAttribute=\"leading\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"leading\" id=\"kV7-tw-vXt\"/>\n                            <constraint firstItem=\"YRO-k0-Ey4\" firstAttribute=\"top\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"top\" id=\"xPn-NY-SIU\"/>\n                        </constraints>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"LaunchImage\" width=\"512\" height=\"646\"/>\n        <image name=\"LaunchBackground\" width=\"1\" height=\"1\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "ios/Runner/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"10117\" systemVersion=\"15F34\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"10085\"/>\n    </dependencies>\n    <scenes>\n        <!--Flutter View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"FlutterViewController\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"y3c-jy-aDJ\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"wfy-db-euE\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"600\" height=\"600\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"calibratedWhite\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "ios/Runner/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n\t<dict>\n\t\t<key>CADisableMinimumFrameDurationOnPhone</key>\n\t\t<true/>\n\t\t<key>CFBundleDevelopmentRegion</key>\n\t\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t\t<key>CFBundleDisplayName</key>\n\t\t<string>AIdea</string>\n\t\t<key>CFBundleExecutable</key>\n\t\t<string>$(EXECUTABLE_NAME)</string>\n\t\t<key>CFBundleIdentifier</key>\n\t\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t\t<key>CFBundleInfoDictionaryVersion</key>\n\t\t<string>6.0</string>\n\t\t<key>CFBundleName</key>\n\t\t<string>askaide</string>\n\t\t<key>CFBundlePackageType</key>\n\t\t<string>APPL</string>\n\t\t<key>CFBundleShortVersionString</key>\n\t\t<string>$(FLUTTER_BUILD_NAME)</string>\n\t\t<key>CFBundleSignature</key>\n\t\t<string>????</string>\n\t\t<key>CFBundleVersion</key>\n\t\t<string>$(FLUTTER_BUILD_NUMBER)</string>\n\t\t<key>ITSAppUsesNonExemptEncryption</key>\n\t\t<false/>\n\t\t<key>LSApplicationQueriesSchemes</key>\n\t\t<array>\n\t\t\t<string>weixin</string>\n\t\t\t<string>weixinURLParamsAPI</string>\n\t\t\t<string>weixinULAPI</string>\n\t\t</array>\n\t\t<key>LSRequiresIPhoneOS</key>\n\t\t<true/>\n\t\t<key>LSSupportsOpeningDocumentsInPlace</key>\n\t\t<true/>\n\t\t<key>NSAppTransportSecurity</key>\n\t\t<dict>\n\t\t\t<key>NSAllowsArbitraryLoads</key>\n\t\t\t<true/>\n\t\t\t<key>NSAllowsArbitraryLoadsForMedia</key>\n\t\t\t<true/>\n\t\t</dict>\n\t\t<key>NSCameraUsageDescription</key>\n\t\t<string>Used to demonstrate image picker plugin</string>\n\t\t<key>NSDocumentsFolderUsageDescription</key>\n\t\t<string>$(PRODUCT_NAME) needs access to your documents folder to save your files.</string>\n\t\t<key>NSMicrophoneUsageDescription</key>\n\t\t<string>We need to access to the microphone to record audio file</string>\n\t\t<key>NSPhotoLibraryUsageDescription</key>\n\t\t<string>Used to demonstrate image picker plugin</string>\n\t\t<key>NSLocationWhenInUseUsageDescription</key>\n\t\t<string>To enable GPS location access for Exif data</string>\n\t\t<key>UIApplicationSupportsIndirectInputEvents</key>\n\t\t<true/>\n\t\t<key>UIFileSharingEnabled</key>\n\t\t<true/>\n\t\t<key>UILaunchStoryboardName</key>\n\t\t<string>LaunchScreen</string>\n\t\t<key>UIMainStoryboardFile</key>\n\t\t<string>Main</string>\n\t\t<key>UISupportedInterfaceOrientations</key>\n\t\t<array>\n\t\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t\t</array>\n\t\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t\t<array>\n\t\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t\t</array>\n\t\t<key>UIViewControllerBasedStatusBarAppearance</key>\n\t\t<false/>\n\t\t<key>UIStatusBarHidden</key>\n\t\t<true/>\n\t</dict>\n</plist>\n"
  },
  {
    "path": "ios/Runner/Runner-Bridging-Header.h",
    "content": "#import \"GeneratedPluginRegistrant.h\"\n"
  },
  {
    "path": "ios/Runner/Runner.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>aps-environment</key>\n\t<string>development</string>\n\t<key>com.apple.developer.applesignin</key>\n\t<array>\n\t\t<string>Default</string>\n\t</array>\n\t<key>com.apple.developer.associated-domains</key>\n\t<array>\n\t\t<string>applinks:ai.aicode.cc</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Runner.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };\n\t\t3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };\n\t\t4AB86C8F0973565BB3C184F4 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 16B3CC3FDED8D8CC86722DEC /* Pods_Runner.framework */; };\n\t\t74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };\n\t\t97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };\n\t\t97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };\n\t\t97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };\n\t\tA5ECBA6F2A2B177200E3A820 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5ECBA6E2A2B177200E3A820 /* StoreKit.framework */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t9705A1C41CF9048500538489 /* Embed Frameworks */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tname = \"Embed Frameworks\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = \"<group>\"; };\n\t\t1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = \"<group>\"; };\n\t\t16B3CC3FDED8D8CC86722DEC /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = \"<group>\"; };\n\t\t74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = \"Runner-Bridging-Header.h\"; sourceTree = \"<group>\"; };\n\t\t74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = \"<group>\"; };\n\t\t97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tA5D102132A0AA8E200331391 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = \"<group>\"; };\n\t\tA5ECBA6E2A2B177200E3A820 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };\n\t\tA6F16BE481E65688799479DB /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.release.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tD5F820BBFB52D6BE21AD358D /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.debug.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tEA2292F51AC4CF434D7A04AF /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.profile.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig\"; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t97C146EB1CF9000F007C117D /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t4AB86C8F0973565BB3C184F4 /* Pods_Runner.framework in Frameworks */,\n\t\t\t\tA5ECBA6F2A2B177200E3A820 /* StoreKit.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t652A687C2637E7BC60520D2C /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tA5ECBA6E2A2B177200E3A820 /* StoreKit.framework */,\n\t\t\t\t16B3CC3FDED8D8CC86722DEC /* Pods_Runner.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9740EEB11CF90186004384FC /* Flutter */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,\n\t\t\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */,\n\t\t\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */,\n\t\t\t\t9740EEB31CF90195004384FC /* Generated.xcconfig */,\n\t\t\t);\n\t\t\tname = Flutter;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146E51CF9000F007C117D = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9740EEB11CF90186004384FC /* Flutter */,\n\t\t\t\t97C146F01CF9000F007C117D /* Runner */,\n\t\t\t\t97C146EF1CF9000F007C117D /* Products */,\n\t\t\t\tF0FF12F5C5F28587D2089C68 /* Pods */,\n\t\t\t\t652A687C2637E7BC60520D2C /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146EF1CF9000F007C117D /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146EE1CF9000F007C117D /* Runner.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146F01CF9000F007C117D /* Runner */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tA5D102132A0AA8E200331391 /* Runner.entitlements */,\n\t\t\t\t97C146FA1CF9000F007C117D /* Main.storyboard */,\n\t\t\t\t97C146FD1CF9000F007C117D /* Assets.xcassets */,\n\t\t\t\t97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,\n\t\t\t\t97C147021CF9000F007C117D /* Info.plist */,\n\t\t\t\t1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,\n\t\t\t\t1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,\n\t\t\t\t74858FAE1ED2DC5600515810 /* AppDelegate.swift */,\n\t\t\t\t74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,\n\t\t\t);\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF0FF12F5C5F28587D2089C68 /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5F820BBFB52D6BE21AD358D /* Pods-Runner.debug.xcconfig */,\n\t\t\t\tA6F16BE481E65688799479DB /* Pods-Runner.release.xcconfig */,\n\t\t\t\tEA2292F51AC4CF434D7A04AF /* Pods-Runner.profile.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t97C146ED1CF9000F007C117D /* Runner */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget \"Runner\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t5E113495E8126976AA1B9B6D /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t9740EEB61CF901F6004384FC /* Run Script */,\n\t\t\t\t97C146EA1CF9000F007C117D /* Sources */,\n\t\t\t\t97C146EB1CF9000F007C117D /* Frameworks */,\n\t\t\t\t97C146EC1CF9000F007C117D /* Resources */,\n\t\t\t\t9705A1C41CF9048500538489 /* Embed Frameworks */,\n\t\t\t\t3B06AD1E1E4923F5004D2608 /* Thin Binary */,\n\t\t\t\t13DFE0107180EC2050B1ABEA /* [CP] Embed Pods Frameworks */,\n\t\t\t\t8C0A97AF55BE760FDB31E756 /* [CP] Copy Pods Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = Runner;\n\t\t\tproductName = Runner;\n\t\t\tproductReference = 97C146EE1CF9000F007C117D /* Runner.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t97C146E61CF9000F007C117D /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 1510;\n\t\t\t\tORGANIZATIONNAME = \"\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t97C146ED1CF9000F007C117D = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 7.3.1;\n\t\t\t\t\t\tLastSwiftMigration = 1100;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject \"Runner\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 97C146E51CF9000F007C117D;\n\t\t\tproductRefGroup = 97C146EF1CF9000F007C117D /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t97C146ED1CF9000F007C117D /* Runner */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t97C146EC1CF9000F007C117D /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,\n\t\t\t\t3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,\n\t\t\t\t97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,\n\t\t\t\t97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t13DFE0107180EC2050B1ABEA /* [CP] Embed Pods Frameworks */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist\",\n\t\t\t);\n\t\t\tname = \"[CP] Embed Pods Frameworks\";\n\t\t\toutputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\",\n\t\t\t);\n\t\t\tname = \"Thin Binary\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"/bin/sh \\\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\\\" embed_and_thin\";\n\t\t};\n\t\t5E113495E8126976AA1B9B6D /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t8C0A97AF55BE760FDB31E756 /* [CP] Copy Pods Resources */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist\",\n\t\t\t);\n\t\t\tname = \"[CP] Copy Pods Resources\";\n\t\t\toutputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t9740EEB61CF901F6004384FC /* Run Script */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Run Script\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"/bin/sh \\\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\\\" build\";\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t97C146EA1CF9000F007C117D /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,\n\t\t\t\t1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\t97C146FA1CF9000F007C117D /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146FB1CF9000F007C117D /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t97C147001CF9000F007C117D /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t249021D3217E4FDB00AE95B9 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSUPPORTED_PLATFORMS = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t249021D4217E4FDB00AE95B9 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = N95437SZ2A;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\t\"EXCLUDED_ARCHS[sdk=iphonesimulator*]\" = \"i386 arm64\";\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tOTHER_LDFLAGS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"-ObjC\",\n\t\t\t\t\t\"-l\\\"WechatOpenSDK\\\"\",\n\t\t\t\t\t\"-l\\\"c++\\\"\",\n\t\t\t\t\t\"-l\\\"sqlite3\\\"\",\n\t\t\t\t\t\"-l\\\"sqlite3.0\\\"\",\n\t\t\t\t\t\"-l\\\"z\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"AVFoundation\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"AVKit\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"AlipaySDK\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"CFNetwork\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"CoreGraphics\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"CoreMotion\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"CoreTelephony\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"CoreText\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"DKImagePickerController\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"DKPhotoGallery\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"Foundation\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"ImageIO\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"Mantle\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"Photos\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"QuartzCore\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"SDWebImage\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"SDWebImageWebPCoder\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"Security\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"SwiftyGif\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"SystemConfiguration\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"UIKit\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"WebKit\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"audioplayers_darwin\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"file_picker\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"file_saver\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"flutter_localization\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"flutter_native_splash\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"flutter_tts\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"fluwx\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"image_gallery_saver\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"in_app_purchase_storekit\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"libwebp\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"path_provider_foundation\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"share_plus\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"shared_preferences_foundation\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"sign_in_with_apple\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"sqflite\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"tobias\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"url_launcher_ios\\\"\",\n\t\t\t\t\t\"-weak_framework\",\n\t\t\t\t\t\"\\\"LinkPresentation\\\"\",\n\t\t\t\t\t\"-ld64\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = cc.aicode.flutter.askaide.askaide;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t97C147031CF9000F007C117D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t97C147041CF9000F007C117D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSUPPORTED_PLATFORMS = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t97C147061CF9000F007C117D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = N95437SZ2A;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\t\"EXCLUDED_ARCHS[sdk=iphonesimulator*]\" = \"i386 arm64\";\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tOTHER_LDFLAGS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"-ObjC\",\n\t\t\t\t\t\"-l\\\"WechatOpenSDK\\\"\",\n\t\t\t\t\t\"-l\\\"c++\\\"\",\n\t\t\t\t\t\"-l\\\"sqlite3\\\"\",\n\t\t\t\t\t\"-l\\\"sqlite3.0\\\"\",\n\t\t\t\t\t\"-l\\\"z\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"AVFoundation\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"AVKit\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"AlipaySDK\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"CFNetwork\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"CoreGraphics\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"CoreMotion\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"CoreTelephony\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"CoreText\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"DKImagePickerController\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"DKPhotoGallery\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"Foundation\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"ImageIO\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"Mantle\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"Photos\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"QuartzCore\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"SDWebImage\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"SDWebImageWebPCoder\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"Security\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"SwiftyGif\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"SystemConfiguration\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"UIKit\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"WebKit\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"audioplayers_darwin\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"file_picker\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"file_saver\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"flutter_localization\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"flutter_native_splash\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"flutter_tts\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"fluwx\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"image_gallery_saver\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"in_app_purchase_storekit\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"libwebp\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"path_provider_foundation\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"share_plus\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"shared_preferences_foundation\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"sign_in_with_apple\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"sqflite\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"tobias\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"url_launcher_ios\\\"\",\n\t\t\t\t\t\"-weak_framework\",\n\t\t\t\t\t\"\\\"LinkPresentation\\\"\",\n\t\t\t\t\t\"-ld64\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = cc.aicode.flutter.askaide.askaide;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t97C147071CF9000F007C117D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = N95437SZ2A;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\t\"EXCLUDED_ARCHS[sdk=iphonesimulator*]\" = \"i386 arm64\";\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tOTHER_LDFLAGS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"-ObjC\",\n\t\t\t\t\t\"-l\\\"WechatOpenSDK\\\"\",\n\t\t\t\t\t\"-l\\\"c++\\\"\",\n\t\t\t\t\t\"-l\\\"sqlite3\\\"\",\n\t\t\t\t\t\"-l\\\"sqlite3.0\\\"\",\n\t\t\t\t\t\"-l\\\"z\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"AVFoundation\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"AVKit\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"AlipaySDK\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"CFNetwork\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"CoreGraphics\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"CoreMotion\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"CoreTelephony\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"CoreText\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"DKImagePickerController\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"DKPhotoGallery\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"Foundation\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"ImageIO\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"Mantle\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"Photos\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"QuartzCore\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"SDWebImage\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"SDWebImageWebPCoder\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"Security\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"SwiftyGif\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"SystemConfiguration\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"UIKit\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"WebKit\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"audioplayers_darwin\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"file_picker\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"file_saver\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"flutter_localization\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"flutter_native_splash\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"flutter_tts\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"fluwx\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"image_gallery_saver\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"in_app_purchase_storekit\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"libwebp\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"path_provider_foundation\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"share_plus\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"shared_preferences_foundation\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"sign_in_with_apple\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"sqflite\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"tobias\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"url_launcher_ios\\\"\",\n\t\t\t\t\t\"-weak_framework\",\n\t\t\t\t\t\"\\\"LinkPresentation\\\"\",\n\t\t\t\t\t\"-ld64\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = cc.aicode.flutter.askaide.askaide;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t97C146E91CF9000F007C117D /* Build configuration list for PBXProject \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t97C147031CF9000F007C117D /* Debug */,\n\t\t\t\t97C147041CF9000F007C117D /* Release */,\n\t\t\t\t249021D3217E4FDB00AE95B9 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t97C147061CF9000F007C117D /* Debug */,\n\t\t\t\t97C147071CF9000F007C117D /* Release */,\n\t\t\t\t249021D4217E4FDB00AE95B9 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 97C146E61CF9000F007C117D /* Project object */;\n}\n"
  },
  {
    "path": "ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>PreviewsEnabled</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1510\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n               BuildableName = \"Runner.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "ios/Runner.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Runner.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:Pods/Pods.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>PreviewsEnabled</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "lib/bloc/account_bloc.dart",
    "content": "import 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/http.dart';\nimport 'package:askaide/repo/api/user.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bloc/bloc.dart';\nimport 'package:meta/meta.dart';\n\npart 'account_event.dart';\npart 'account_state.dart';\n\nclass AccountBloc extends Bloc<AccountEvent, AccountState> {\n  final SettingRepository settings;\n\n  AccountBloc(this.settings) : super(AccountInitial()) {\n    // 加载用户信息\n    on<AccountLoadEvent>((event, emit) async {\n      emit(AccountLoading());\n\n      final token = settings.get(settingAPIServerToken);\n      if (token != null && token != '') {\n        try {\n          final user = await APIServer().userInfo(cache: event.cache);\n          if (user != null) {\n            emit(AccountLoaded(user));\n          } else {\n            emit(AccountNeedSignIn());\n          }\n        } catch (e) {\n          emit(AccountLoaded(null, error: e));\n        }\n      } else {\n        emit(AccountNeedSignIn());\n      }\n    });\n\n    on<AccountSignOutEvent>((event, emit) async {\n      await settings.set(settingAPIServerToken, '');\n      await settings.set(settingUserInfo, '');\n\n      await HttpClient.cleanCache();\n      emit(AccountNeedSignIn());\n    });\n\n    on<AccountUpdateEvent>((event, emit) async {\n      try {\n        if (event.realname != null) {\n          await APIServer().updateUserRealname(realname: event.realname!);\n        }\n\n        if (event.avatarURL != null) {\n          await APIServer().updateUserAvatar(avatarURL: event.avatarURL!);\n        }\n\n        emit(AccountLoaded(await APIServer().userInfo(cache: false)));\n      } catch (e) {\n        emit(AccountLoaded(await APIServer().userInfo(cache: false), error: e));\n      }\n    });\n  }\n}\n"
  },
  {
    "path": "lib/bloc/account_event.dart",
    "content": "part of 'account_bloc.dart';\n\n@immutable\nabstract class AccountEvent {}\n\nclass AccountLoadEvent extends AccountEvent {\n  final bool cache;\n\n  AccountLoadEvent({this.cache = true});\n}\n\nclass AccountSignOutEvent extends AccountEvent {}\n\nclass AccountUpdateEvent extends AccountEvent {\n  final String? realname;\n  final String? avatarURL;\n\n  AccountUpdateEvent({this.realname, this.avatarURL});\n}\n"
  },
  {
    "path": "lib/bloc/account_state.dart",
    "content": "part of 'account_bloc.dart';\n\n@immutable\nabstract class AccountState {}\n\nclass AccountInitial extends AccountState {}\n\nclass AccountLoading extends AccountState {}\n\nclass AccountLoaded extends AccountState {\n  final UserInfo? user;\n  final Object? error;\n  AccountLoaded(this.user, {this.error});\n}\n\nclass AccountNeedSignIn extends AccountState {}\n"
  },
  {
    "path": "lib/bloc/admin_payment_bloc.dart",
    "content": "import 'package:askaide/repo/api/admin/payment.dart';\nimport 'package:askaide/repo/api/page.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:bloc/bloc.dart';\nimport 'package:meta/meta.dart';\n\npart 'admin_payment_event.dart';\npart 'admin_payment_state.dart';\n\nclass AdminPaymentBloc extends Bloc<AdminPaymentEvent, AdminPaymentState> {\n  AdminPaymentBloc() : super(AdminPaymentInitial()) {\n    on<AdminPaymentHistoriesLoadEvent>((event, emit) async {\n      final histories = await APIServer().adminPaymentHistories(\n        page: event.page,\n        perPage: event.perPage,\n        keyword: event.keyword,\n      );\n      emit(AdminPaymentHistoriesLoaded(histories));\n    });\n  }\n}\n"
  },
  {
    "path": "lib/bloc/admin_payment_event.dart",
    "content": "part of 'admin_payment_bloc.dart';\n\n@immutable\nsealed class AdminPaymentEvent {}\n\nclass AdminPaymentHistoriesLoadEvent extends AdminPaymentEvent {\n  final int page;\n  final int perPage;\n  final String? keyword;\n\n  AdminPaymentHistoriesLoadEvent({\n    this.page = 1,\n    this.perPage = 20,\n    this.keyword,\n  });\n}\n"
  },
  {
    "path": "lib/bloc/admin_payment_state.dart",
    "content": "part of 'admin_payment_bloc.dart';\n\n@immutable\nsealed class AdminPaymentState {}\n\nfinal class AdminPaymentInitial extends AdminPaymentState {}\n\nclass AdminPaymentOperationResult extends AdminPaymentState {\n  final bool success;\n  final String message;\n\n  AdminPaymentOperationResult(this.success, this.message);\n}\n\nclass AdminPaymentHistoriesLoaded extends AdminPaymentState {\n  final PagedData<AdminPaymentHistory> histories;\n\n  AdminPaymentHistoriesLoaded(this.histories);\n}\n"
  },
  {
    "path": "lib/bloc/admin_room_bloc.dart",
    "content": "import 'package:askaide/repo/api/page.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/model/message.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:bloc/bloc.dart';\nimport 'package:meta/meta.dart';\n\npart 'admin_room_event.dart';\npart 'admin_room_state.dart';\n\nclass AdminRoomBloc extends Bloc<AdminRoomEvent, AdminRoomState> {\n  AdminRoomBloc() : super(AdminRoomInitial()) {\n    on<AdminRoomsLoadEvent>((event, emit) async {\n      final rooms = await APIServer().adminUserRooms(userId: event.userId);\n      emit(AdminRoomsLoaded(rooms: rooms));\n    });\n\n    on<AdminRoomLoadEvent>((event, emit) async {\n      final room = await APIServer().adminUserRoom(\n        userId: event.userId,\n        roomId: event.roomId,\n      );\n      emit(AdminRoomLoaded(room: room));\n    });\n\n    on<AdminRoomRecentlyMessagesLoadEvent>((event, emit) async {\n      if (event.roomType == 4) {\n        final messages = await APIServer().adminUserRoomGroupMessages(\n            userId: event.userId, roomId: event.roomId);\n        emit(AdminRoomRecentlyMessagesLoaded(\n            messages: messages\n                .map((e) => Message(\n                      e.role == 'user' ? Role.sender : Role.receiver,\n                      e.message,\n                      type: MessageType.text,\n                      ts: e.createdAt,\n                      model: e.model,\n                      quotaConsumed: e.quotaConsumed,\n                      tokenConsumed: e.tokenConsumed,\n                      refId: e.pid,\n                      id: e.id,\n                      serverId: e.id,\n                    ))\n                .toList()));\n      } else {\n        final messages = await APIServer().adminUserRoomMessages(\n          userId: event.userId,\n          roomId: event.roomId,\n        );\n        emit(AdminRoomRecentlyMessagesLoaded(\n            messages: messages\n                .map((e) => Message(\n                      e.role == 1 ? Role.sender : Role.receiver,\n                      e.message,\n                      type: MessageType.text,\n                      ts: e.createdAt,\n                      model: e.model,\n                      quotaConsumed: e.quotaConsumed,\n                      tokenConsumed: e.tokenConsumed,\n                      refId: e.pid,\n                      id: e.id,\n                      serverId: e.id,\n                      userId: e.userId,\n                      roomId: e.roomId,\n                    ))\n                .toList()));\n      }\n    });\n\n    on<AdminRecentlyMessagesLoadEvent>((event, emit) async {\n      final messages = await APIServer().adminRecentlyMessages(\n        page: event.page,\n        perPage: event.perPage,\n        keyword: event.keyword,\n      );\n      emit(AdminRecentlyMessagesLoaded(PagedData(\n        data: messages.data\n            .map((e) => Message(\n                  e.role == 1 ? Role.sender : Role.receiver,\n                  e.message,\n                  type: MessageType.text,\n                  ts: e.createdAt,\n                  model: e.model,\n                  quotaConsumed: e.quotaConsumed,\n                  tokenConsumed: e.tokenConsumed,\n                  refId: e.pid,\n                  id: e.id,\n                  serverId: e.id,\n                  userId: e.userId,\n                  roomId: e.roomId,\n                ))\n            .toList(),\n        page: messages.page,\n        perPage: messages.perPage,\n        total: messages.total,\n        lastPage: messages.lastPage,\n      )));\n    });\n  }\n}\n"
  },
  {
    "path": "lib/bloc/admin_room_event.dart",
    "content": "part of 'admin_room_bloc.dart';\n\n@immutable\nsealed class AdminRoomEvent {}\n\nclass AdminRoomsLoadEvent extends AdminRoomEvent {\n  final int userId;\n\n  AdminRoomsLoadEvent({required this.userId});\n}\n\nclass AdminRoomLoadEvent extends AdminRoomEvent {\n  final int userId;\n  final int roomId;\n\n  AdminRoomLoadEvent({required this.roomId, required this.userId});\n}\n\nclass AdminRoomRecentlyMessagesLoadEvent extends AdminRoomEvent {\n  final int userId;\n  final int roomId;\n  final int roomType;\n\n  AdminRoomRecentlyMessagesLoadEvent({\n    required this.roomId,\n    required this.userId,\n    required this.roomType,\n  });\n}\n\nclass AdminRecentlyMessagesLoadEvent extends AdminRoomEvent {\n  final int page;\n  final int perPage;\n  final String? keyword;\n\n  AdminRecentlyMessagesLoadEvent({\n    required this.page,\n    required this.perPage,\n    this.keyword,\n  });\n}\n"
  },
  {
    "path": "lib/bloc/admin_room_state.dart",
    "content": "part of 'admin_room_bloc.dart';\n\n@immutable\nsealed class AdminRoomState {}\n\nfinal class AdminRoomInitial extends AdminRoomState {}\n\nfinal class AdminRoomsLoaded extends AdminRoomState {\n  final List<RoomInServer> rooms;\n\n  AdminRoomsLoaded({required this.rooms});\n}\n\nfinal class AdminRoomLoaded extends AdminRoomState {\n  final RoomInServer room;\n\n  AdminRoomLoaded({required this.room});\n}\n\nfinal class AdminRoomRecentlyMessagesLoaded extends AdminRoomState {\n  final List<Message> messages;\n\n  AdminRoomRecentlyMessagesLoaded({required this.messages});\n}\n\nclass AdminRoomOperationResult extends AdminRoomState {\n  final bool success;\n  final String message;\n\n  AdminRoomOperationResult(this.success, this.message);\n}\n\nclass AdminRecentlyMessagesLoaded extends AdminRoomState {\n  final PagedData<Message> messages;\n\n  AdminRecentlyMessagesLoaded(this.messages);\n}\n"
  },
  {
    "path": "lib/bloc/background_image_bloc.dart",
    "content": "import 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:bloc/bloc.dart';\nimport 'package:meta/meta.dart';\n\npart 'background_image_event.dart';\npart 'background_image_state.dart';\n\nclass BackgroundImageBloc\n    extends Bloc<BackgroundImageEvent, BackgroundImageState> {\n  BackgroundImageBloc() : super(BackgroundImageInitial()) {\n    on<BackgroundImageLoadEvent>((event, emit) async {\n      final images = await APIServer().backgrounds();\n      emit(BackgroundImageLoaded(images));\n    });\n  }\n}\n"
  },
  {
    "path": "lib/bloc/background_image_event.dart",
    "content": "part of 'background_image_bloc.dart';\n\n@immutable\nabstract class BackgroundImageEvent {}\n\nclass BackgroundImageLoadEvent extends BackgroundImageEvent {}\n"
  },
  {
    "path": "lib/bloc/background_image_state.dart",
    "content": "part of 'background_image_bloc.dart';\n\n@immutable\nabstract class BackgroundImageState {}\n\nclass BackgroundImageInitial extends BackgroundImageState {}\n\nclass BackgroundImageLoaded extends BackgroundImageState {\n  final List<BackgroundImage> images;\n\n  BackgroundImageLoaded(this.images);\n}\n"
  },
  {
    "path": "lib/bloc/bloc_manager.dart",
    "content": "// ignore_for_file: must_call_super\n\nimport 'package:askaide/bloc/chat_message_bloc.dart';\nimport 'package:askaide/helper/lru.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\n\nclass ChatBlocManager {\n  static final ChatBlocManager _singleton = ChatBlocManager._internal();\n\n  factory ChatBlocManager() {\n    return _singleton;\n  }\n\n  ChatBlocManager._internal();\n\n  late final ChatMessageBloc Function(int roomId, {int? chatHistoryId})\n      blocBuilder;\n  init(ChatMessageBloc Function(int roomId, {int? chatHistoryId}) blocBuilder) {\n    this.blocBuilder = blocBuilder;\n  }\n\n  final LRUCache<String, ChatMessageBloc> _blocs = LRUCache(10);\n\n  ChatMessageBloc getBloc(int roomId, {int? chatHistoryId}) {\n    final key = '$roomId-$chatHistoryId';\n    if (_blocs.containsKey(key)) {\n      return _blocs.get(key)!;\n    } else {\n      final bloc = blocBuilder(roomId, chatHistoryId: chatHistoryId);\n      _blocs.put(key, bloc);\n\n      return bloc;\n    }\n  }\n\n  void dispose() {\n    _blocs.clear();\n  }\n}\n\nabstract class BlocExt<K, V> extends Bloc<K, V> implements Disposable {\n  BlocExt(super.initialState);\n\n  @override\n  void dispose() {\n    super.close();\n  }\n\n  @override\n  Future<void> close() async {\n    return;\n  }\n}\n"
  },
  {
    "path": "lib/bloc/channel_bloc.dart",
    "content": "import 'package:askaide/repo/api/admin/channels.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:bloc/bloc.dart';\nimport 'package:meta/meta.dart';\n\npart 'channel_event.dart';\npart 'channel_state.dart';\n\nclass ChannelBloc extends Bloc<ChannelEvent, ChannelState> {\n  ChannelBloc() : super(ChannelInitial()) {\n    /// 加载所有渠道\n    on<ChannelsLoadEvent>((event, emit) async {\n      final channels = await APIServer().adminChannels();\n      emit(ChannelsLoaded(channels));\n    });\n\n    /// 加载单个渠道\n    on<ChannelLoadEvent>((event, emit) async {\n      final channel = await APIServer().adminChannel(id: event.channelId);\n      emit(ChannelLoaded(channel));\n    });\n\n    /// 创建渠道\n    on<ChannelCreateEvent>((event, emit) async {\n      try {\n        await APIServer().adminCreateChannel(event.req);\n        emit(ChannelOperationResult(true, '创建成功'));\n      } catch (e) {\n        emit(ChannelOperationResult(false, e.toString()));\n      }\n    });\n\n    /// 更新渠道\n    on<ChannelUpdateEvent>((event, emit) async {\n      try {\n        await APIServer().adminUpdateChannel(\n          id: event.channelId,\n          req: event.req,\n        );\n        emit(ChannelOperationResult(true, '更新成功'));\n      } catch (e) {\n        emit(ChannelOperationResult(false, e.toString()));\n      }\n    });\n\n    /// 删除渠道\n    on<ChannelDeleteEvent>((event, emit) async {\n      try {\n        await APIServer().adminDeleteChannel(id: event.channelId);\n        emit(ChannelOperationResult(true, '删除成功'));\n      } catch (e) {\n        emit(ChannelOperationResult(false, e.toString()));\n      }\n    });\n  }\n}\n"
  },
  {
    "path": "lib/bloc/channel_event.dart",
    "content": "part of 'channel_bloc.dart';\n\n@immutable\nsealed class ChannelEvent {}\n\nclass ChannelsLoadEvent extends ChannelEvent {}\n\nclass ChannelLoadEvent extends ChannelEvent {\n  final int channelId;\n\n  ChannelLoadEvent(this.channelId);\n}\n\nclass ChannelCreateEvent extends ChannelEvent {\n  final AdminChannelAddReq req;\n\n  ChannelCreateEvent(this.req);\n}\n\nclass ChannelUpdateEvent extends ChannelEvent {\n  final int channelId;\n  final AdminChannelUpdateReq req;\n\n  ChannelUpdateEvent(this.channelId, this.req);\n}\n\nclass ChannelDeleteEvent extends ChannelEvent {\n  final int channelId;\n\n  ChannelDeleteEvent(this.channelId);\n}\n"
  },
  {
    "path": "lib/bloc/channel_state.dart",
    "content": "part of 'channel_bloc.dart';\n\n@immutable\nsealed class ChannelState {}\n\nfinal class ChannelInitial extends ChannelState {}\n\nclass ChannelsLoaded extends ChannelState {\n  final List<AdminChannel> channels;\n\n  ChannelsLoaded(this.channels);\n}\n\nclass ChannelLoaded extends ChannelState {\n  final AdminChannel channel;\n\n  ChannelLoaded(this.channel);\n}\n\nclass ChannelOperationResult extends ChannelState {\n  final bool success;\n  final String message;\n\n  ChannelOperationResult(this.success, this.message);\n}\n"
  },
  {
    "path": "lib/bloc/chat_chat_bloc.dart",
    "content": "import 'package:askaide/helper/constant.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/chat_message_repo.dart';\nimport 'package:askaide/repo/model/chat_history.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:bloc/bloc.dart';\nimport 'package:meta/meta.dart';\n\npart 'chat_chat_event.dart';\npart 'chat_chat_state.dart';\n\nclass ChatChatBloc extends Bloc<ChatChatEvent, ChatChatState> {\n  final ChatMessageRepository _chatMessageRepository;\n  ChatChatBloc(this._chatMessageRepository) : super(ChatChatInitial()) {\n    // 加载最近的历史记录\n    on<ChatChatLoadRecentHistories>((event, emit) async {\n      final histories = await _chatMessageRepository.recentChatHistories(\n        event.count,\n        userId: APIServer().localUserID(),\n      );\n\n      emit(ChatChatRecentHistoriesLoaded(\n        histories: histories,\n        examples: const [],\n      ));\n    });\n\n    // 删除历史记录\n    on<ChatChatDeleteHistory>((event, emit) async {\n      await _chatMessageRepository.deleteChatHistory(event.chatId);\n      add(ChatChatLoadRecentHistories());\n    });\n  }\n}\n"
  },
  {
    "path": "lib/bloc/chat_chat_event.dart",
    "content": "part of 'chat_chat_bloc.dart';\n\n@immutable\nabstract class ChatChatEvent {}\n\nclass ChatChatLoadRecentHistories extends ChatChatEvent {\n  final int count;\n  ChatChatLoadRecentHistories({this.count = defaultChatHistoryCount});\n}\n\nclass ChatChatNewChat extends ChatChatEvent {\n  final String text;\n  ChatChatNewChat(this.text);\n}\n\nclass ChatChatDeleteHistory extends ChatChatEvent {\n  final int chatId;\n\n  ChatChatDeleteHistory(this.chatId);\n}\n"
  },
  {
    "path": "lib/bloc/chat_chat_state.dart",
    "content": "part of 'chat_chat_bloc.dart';\n\n@immutable\nabstract class ChatChatState {}\n\nclass ChatChatInitial extends ChatChatState {}\n\nclass ChatChatRecentHistoriesLoaded extends ChatChatState {\n  final List<ChatHistory> histories;\n  final List<ChatExample>? examples;\n\n  ChatChatRecentHistoriesLoaded({this.histories = const [], this.examples});\n}\n"
  },
  {
    "path": "lib/bloc/chat_event.dart",
    "content": "part of 'chat_message_bloc.dart';\n\n@immutable\nabstract class ChatMessageEvent {}\n\nclass ChatMessageReceivedEvent extends ChatMessageEvent {\n  final Message message;\n\n  ChatMessageReceivedEvent(this.message);\n}\n\nclass ChatMessageSendEvent extends ChatMessageEvent {\n  final Message message;\n  final int? index;\n  final bool isResent;\n  final String? tempModel;\n\n  ChatMessageSendEvent(this.message,\n      {this.index, this.isResent = false, this.tempModel});\n}\n\nclass ChatMessageGetRecentEvent extends ChatMessageEvent {\n  final int? chatHistoryId;\n\n  ChatMessageGetRecentEvent({this.chatHistoryId});\n}\n\nclass ChatMessageClearAllEvent extends ChatMessageEvent {}\n\nclass ChatMessageBreakContextEvent extends ChatMessageEvent {}\n\nclass ChatMessageDeleteEvent extends ChatMessageEvent {\n  final List<int> ids;\n  final int? chatHistoryId;\n  ChatMessageDeleteEvent(this.ids, {this.chatHistoryId});\n}\n\nclass ChatMessageStopEvent extends ChatMessageEvent {}\n"
  },
  {
    "path": "lib/bloc/chat_message_bloc.dart",
    "content": "import 'dart:convert';\n\nimport 'package:askaide/bloc/bloc_manager.dart';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/error.dart';\nimport 'package:askaide/helper/logger.dart';\nimport 'package:askaide/helper/model_resolver.dart';\nimport 'package:askaide/helper/queue.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/chat_message_repo.dart';\nimport 'package:askaide/repo/data/chat_message_data.dart';\nimport 'package:askaide/repo/model/chat_history.dart';\nimport 'package:askaide/repo/model/message.dart';\nimport 'package:askaide/repo/model/room.dart';\nimport 'package:askaide/repo/openai_repo.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:dart_openai/openai.dart';\nimport 'package:flutter/material.dart';\nimport 'package:intl/intl.dart';\n\npart 'chat_event.dart';\npart 'chat_state.dart';\n\nclass ChatMessageBloc extends BlocExt<ChatMessageEvent, ChatMessageState> {\n  final ChatMessageRepository chatMsgRepo;\n  final SettingRepository settingRepo;\n  final int roomId;\n  final int? chatHistoryId;\n\n  GracefulQueue<ChatStreamRespData>? currentQueue;\n\n  ChatMessageBloc(\n    this.roomId, {\n    required this.chatMsgRepo,\n    required this.settingRepo,\n    this.chatHistoryId,\n  }) : super(ChatMessageInitial()) {\n    on<ChatMessageSendEvent>(_messageSendEventHandler);\n    on<ChatMessageGetRecentEvent>(_getRecentEventHandler);\n    on<ChatMessageClearAllEvent>(_clearAllEventHandler);\n    on<ChatMessageBreakContextEvent>(_breakContextEventHandler);\n    on<ChatMessageDeleteEvent>(_deleteMessageEventHandler);\n    on<ChatMessageStopEvent>(_stopEventHandler);\n  }\n\n  Future<int> fixRoomId(int? chatHistoryId) async {\n    if (chatHistoryId != null && chatHistoryId > 0) {\n      final his = await chatMsgRepo.getChatHistory(chatHistoryId);\n      if (his != null) {\n        return his.roomId ?? roomId;\n      }\n    }\n\n    return roomId;\n  }\n\n  Future<void> _deleteMessageEventHandler(event, emit) async {\n    final roomId = await fixRoomId(event.chatHistoryId);\n\n    await chatMsgRepo.removeMessage(roomId, event.ids);\n\n    ChatHistory? his;\n    if (event.chatHistoryId != null && event.chatHistoryId! > 0) {\n      his = await chatMsgRepo.getChatHistory(event.chatHistoryId!);\n    }\n\n    emit(ChatMessagesLoaded(\n      await chatMsgRepo.getRecentMessages(\n        roomId: roomId,\n        userId: APIServer().localUserID(),\n        chatHistoryId: event.chatHistoryId,\n      ),\n      chatHistory: his,\n    ));\n  }\n\n  /// 设置上下文清理标识\n  Future<void> _breakContextEventHandler(event, emit) async {\n    final roomId = await fixRoomId(event.chatHistoryId);\n\n    // 查询当前 Room 信息\n    final room = await queryRoomById(chatMsgRepo, roomId);\n    if (room == null) {\n      emit(ChatMessagesLoaded(\n        await chatMsgRepo.getRecentMessages(\n          roomId: roomId,\n          userId: APIServer().localUserID(),\n        ),\n        error: 'The selected item does not exist',\n      ));\n      return;\n    }\n\n    final lastMessage = await chatMsgRepo.getLastMessage(\n      roomId,\n      userId: APIServer().localUserID(),\n    );\n\n    if (lastMessage != null && (lastMessage.type == MessageType.contextBreak || lastMessage.isInitMessage())) {\n      return;\n    }\n\n    await chatMsgRepo.sendMessage(\n      roomId,\n      Message(\n        Role.receiver,\n        AppLocale.contextBreakMessage,\n        ts: DateTime.now(),\n        type: MessageType.contextBreak,\n        roomId: roomId,\n        userId: APIServer().localUserID(),\n      ),\n    );\n\n    if (room.initMessage != null && room.initMessage != '') {\n      await chatMsgRepo.sendMessage(\n        roomId,\n        Message(\n          Role.receiver,\n          room.initMessage!,\n          ts: DateTime.now(),\n          type: MessageType.initMessage,\n          roomId: roomId,\n          userId: APIServer().localUserID(),\n        ),\n      );\n    }\n\n    final messages = await chatMsgRepo.getRecentMessages(\n      roomId: roomId,\n      userId: APIServer().localUserID(),\n    );\n    emit(ChatMessagesLoaded(messages));\n    emit(ChatMessageUpdated(messages.last));\n  }\n\n  /// 清空消息事件处理\n  Future<void> _clearAllEventHandler(event, emit) async {\n    final roomId = await fixRoomId(event.chatHistoryId);\n\n    // 查询当前 Room 信息\n    final room = await queryRoomById(chatMsgRepo, roomId);\n    if (room == null) {\n      emit(ChatMessagesLoaded(\n        await chatMsgRepo.getRecentMessages(\n          roomId: roomId,\n          userId: APIServer().localUserID(),\n        ),\n        error: 'The selected item does not exist',\n      ));\n      return;\n    }\n\n    await chatMsgRepo.clearMessages(\n      roomId,\n      userId: APIServer().localUserID(),\n    );\n\n    if (room.initMessage != null && room.initMessage != '') {\n      await chatMsgRepo.sendMessage(\n        roomId,\n        Message(\n          Role.receiver,\n          room.initMessage!,\n          ts: DateTime.now(),\n          type: MessageType.initMessage,\n          roomId: roomId,\n          userId: APIServer().localUserID(),\n        ),\n      );\n    }\n\n    emit(ChatMessagesLoaded(await chatMsgRepo.getRecentMessages(\n      roomId: roomId,\n      userId: APIServer().localUserID(),\n    )));\n  }\n\n  /// 页面加载事件处理\n  Future<void> _getRecentEventHandler(event, emit) async {\n    final roomId = await fixRoomId(event.chatHistoryId);\n\n    ChatHistory? his;\n    if (event.chatHistoryId != null && event.chatHistoryId! > 0) {\n      his = await chatMsgRepo.getChatHistory(event.chatHistoryId!);\n    }\n\n    if (his == null) {\n      emit(ChatMessagesLoaded(const []));\n    } else {\n      emit(ChatMessagesLoaded(\n        await chatMsgRepo.getRecentMessages(\n          roomId: roomId,\n          userId: APIServer().localUserID(),\n          chatHistoryId: event.chatHistoryId,\n        ),\n        chatHistory: his,\n      ));\n    }\n  }\n\n  /// 停止输出事件处理\n  Future<void> _stopEventHandler(event, emit) async {\n    if (currentQueue != null) {\n      currentQueue!.finish();\n    }\n  }\n\n  Future<ChatHistory?> resolveChatHistory(Message message, int roomId) async {\n    // 如果是聊一聊，自动创建聊天记录历史\n    if (message.chatHistoryId == null || message.chatHistoryId! <= 0) {\n      final chatHistory = await chatMsgRepo.createChatHistory(\n        title: message.text,\n        userId: APIServer().localUserID(),\n        roomId: roomId,\n        model: message.model,\n        lastMessage: message.text,\n      );\n\n      return chatHistory;\n    }\n\n    return await chatMsgRepo.getChatHistory(message.chatHistoryId!);\n  }\n\n  /// Message sending event processing\n  Future<void> _messageSendEventHandler(event, emit) async {\n    if (event.message is! Message) {\n      return;\n    }\n\n    Message message = event.message as Message;\n    final roomId = await fixRoomId(message.chatHistoryId);\n\n    ChatHistory localChatHistory = (await resolveChatHistory(message, roomId))!;\n    message.chatHistoryId = localChatHistory.id;\n    emit(ChatHistoryInited(localChatHistory.id!));\n\n    // 查询当前 Room 信息\n    final room = await queryRoomById(chatMsgRepo, roomId);\n    if (room == null) {\n      emit(ChatMessagesLoaded(\n        await chatMsgRepo.getRecentMessages(\n          roomId: roomId,\n          userId: APIServer().localUserID(),\n          chatHistoryId: localChatHistory.id,\n        ),\n        error: 'The selected item does not exist',\n        chatHistory: localChatHistory,\n      ));\n      return;\n    }\n\n    if (localChatHistory.model != null) {\n      room.model = localChatHistory.model!;\n    }\n\n    // 查询最后一条消息\n    // 如果最后一条消息符合以下情况，则创建时间线\n    //  1. 最后一条消息不存在\n    //  2. 最后一条消息的时间距离当前时间超过 3 小时\n    var last = await chatMsgRepo.getLastMessage(\n      roomId,\n      chatHistoryId: localChatHistory.id,\n      userId: APIServer().localUserID(),\n    );\n    if (last == null || last.ts == null || DateTime.now().difference(last.ts!).inMinutes > 60 * 3) {\n      // 发送时间线消息\n      await chatMsgRepo.sendMessage(\n        roomId,\n        Message(\n          Role.receiver,\n          DateFormat('y-MM-dd HH:mm').format(DateTime.now().toLocal()),\n          type: MessageType.timeline,\n          ts: DateTime.now(),\n          roomId: roomId,\n          userId: APIServer().localUserID(),\n          chatHistoryId: localChatHistory.id,\n        ),\n      );\n    }\n\n    // 发送当前用户消息\n    message.model ??= room.model;\n    message.userId = APIServer().localUserID();\n    message.status = 0;\n\n    // 模型切换\n    String? tempModel = event.tempModel;\n    String? originalModel = message.model;\n    room.model = tempModel ?? originalModel ?? room.model;\n\n    // Logger.instance\n    //     .d('发送消息, originalModel: $originalModel, tempModel: $tempModel');\n\n    // 聊天历史记录中，所有发送状态为 pending 状态的消息，全部设置为失败\n    await chatMsgRepo.fixMessageStatus(roomId);\n\n    // 记录当前消息\n    var sentMessageId = 0;\n    if (event.isResent && event.index == 0 && last != null && last.type == MessageType.text) {\n      // 如果当前是消息重发，同时重发的是最后一条消息，则不会重新生成该消息，直接生成答案即可\n      sentMessageId = last.id!;\n      if (last.statusIsFailed()) {\n        // 如果最后一条消息发送失败，则重新发送\n        await chatMsgRepo.updateMessagePart(roomId, last.id!, [\n          MessagePart('status', 0),\n        ]);\n      }\n    } else {\n      message.model = tempModel ?? message.model;\n      sentMessageId = await chatMsgRepo.sendMessage(roomId, message);\n      message.model = originalModel;\n    }\n\n    // 更新 Room 最后活跃时间\n    // 这里没有使用 await，因为不需要等待更新完成，让 room 的更新异步的去处理吧\n    if (!Ability().isUserLogon()) {\n      chatMsgRepo.updateRoomLastActiveTime(roomId);\n    }\n\n    // 重新查询消息列表，此时包含了刚刚发送的消息+机器人思考中消息\n    final messages = await chatMsgRepo.getRecentMessages(\n      roomId: roomId,\n      userId: APIServer().localUserID(),\n      chatHistoryId: localChatHistory.id,\n    );\n\n    // 创建机器人思考中系统消息\n    Message waitMessage = Message(\n      Role.receiver,\n      '',\n      ts: DateTime.now(),\n      type: MessageType.text,\n      model: tempModel ?? originalModel,\n      roomId: roomId,\n      userId: APIServer().localUserID(),\n      refId: sentMessageId,\n      chatHistoryId: localChatHistory.id,\n      extra: '{}',\n    );\n\n    // 回写消息 ID\n    waitMessage.id = await chatMsgRepo.sendMessage(roomId, waitMessage);\n    waitMessage.isReady = false;\n\n    messages.add(waitMessage);\n\n    emit(ChatMessagesLoaded(\n      messages,\n      processing: true,\n      chatHistory: localChatHistory,\n    ));\n    emit(ChatMessageUpdated(waitMessage, processing: true));\n\n    // 等待监听机器人应答消息\n    final queue = GracefulQueue<ChatStreamRespData>();\n    currentQueue = queue;\n    try {\n      RequestFailedException? error;\n      try {\n        var isThinking = false;\n        var reasoningContent = '';\n        var listener = queue.listen(const Duration(milliseconds: 10), (items) {\n          for (var element in items) {\n            if (element.role == 'system') {\n              try {\n                // SYSTEM 命令\n                // - type: 命令类型\n                //\n                // type=summary （默认值）\n                //     - question_id: 问题 ID\n                //     - answer_id: 答案 ID\n                //     - quota_consumed: 消耗的配额\n                //     - token: 消耗的 token\n                //     - info: 提示信息\n                //\n                // type=thinking\n                // type=thinking-done: [time_consumed]\n                final cmd = jsonDecode(element.content);\n\n                switch (cmd['type']) {\n                  case 'summary':\n                    message.serverId = cmd['question_id'];\n                    waitMessage.serverId = cmd['answer_id'];\n\n                    final quotaConsumed = cmd['quota_consumed'] ?? 0;\n                    final tokenConsumed = cmd['token'] ?? 0;\n\n                    final info = cmd['info'] ?? '';\n                    if (info != '') {\n                      waitMessage.updateExtra({'info': info});\n                    }\n\n                    if (quotaConsumed == 0 && tokenConsumed == 0) {\n                      continue;\n                    }\n\n                    waitMessage.quotaConsumed = quotaConsumed;\n                    waitMessage.tokenConsumed = tokenConsumed;\n                    break;\n                  case 'thinking':\n                    waitMessage.pushExtra('states', 'thinking');\n                    isThinking = true;\n                    break;\n                  case 'thinking-done':\n                    waitMessage.pushExtra('states', 'thinking-done');\n                    waitMessage.updateExtra({'thinking_time_consumed': cmd['time_consumed'] ?? 0});\n                    isThinking = false;\n                    break;\n                  case 'reference-documents':\n                    waitMessage.updateExtra({'reference-documents': cmd['data']});\n                    break;\n                  case 'searching':\n                    waitMessage.pushExtra('states', 'searching');\n                    break;\n                  case 'search-results':\n                    waitMessage.popExtra('states');\n                    waitMessage.updateExtra({'search-results': cmd['data']});\n                    break;\n                  default:\n                }\n              } catch (e) {\n                // ignore: avoid_print\n              }\n            } else {\n              if (isThinking) {\n                reasoningContent = (reasoningContent + element.content).trim();\n                if (reasoningContent.contains('</think>')) {\n                  final allParts = reasoningContent.split('</think>');\n                  final parts = [allParts[0], allParts.skip(1).join('</think>')];\n\n                  reasoningContent = parts[0].trim();\n                  waitMessage.text += parts[1].trim();\n                }\n\n                waitMessage.updateExtra({'reasoning': reasoningContent.replaceAll(RegExp('</?think>'), '')});\n              } else {\n                waitMessage.text += element.content;\n              }\n            }\n          }\n\n          emit(ChatMessageUpdated(waitMessage, processing: true));\n\n          // 失败处理\n          for (var e in items) {\n            if (e.code != null && e.code! > 0) {\n              error = RequestFailedException(e.error ?? 'Request processing failure', e.code!);\n            }\n          }\n        });\n\n        await ModelResolver.instance\n            .request(\n              room: room,\n              tempModel: tempModel,\n              contextMessages: messages.sublist(0, messages.length - 1),\n              onMessage: queue.add,\n              maxTokens: room.maxTokens,\n              historyId: localChatHistory.id,\n              flags: message.flags,\n            )\n            .whenComplete(queue.finish);\n\n        await listener;\n\n        waitMessage.text = waitMessage.text.trim();\n        if (error == null && waitMessage.text.isEmpty) {\n          error = RequestFailedException('The answer is empty', 500);\n        }\n\n        if (error != null) {\n          throw error!;\n        }\n      } catch (e) {\n        if (waitMessage.text.isEmpty) {\n          Logger.instance.e('An error occurred during the response process: $e');\n          rethrow;\n        }\n      }\n\n      // 机器人应答完成，将最后一条机器人应答消息更新到数据库，替换掉思考中消息\n      waitMessage.isReady = true;\n      await chatMsgRepo.updateMessage(roomId, waitMessage.id!, waitMessage);\n\n      // 更新聊天问题的服务端 ID 和消息状态\n      var sentMessageParts = <MessagePart>[];\n      sentMessageParts.add(MessagePart('status', 1));\n      if (message.serverId != null && message.serverId! > 0) {\n        sentMessageParts.add(MessagePart('server_id', message.serverId));\n      }\n\n      await chatMsgRepo.updateMessagePart(\n        roomId,\n        sentMessageId,\n        sentMessageParts,\n      );\n\n      // 更新聊天历史纪录最后一条消息\n      final chatHistory = await chatMsgRepo.getChatHistory(localChatHistory.id!);\n      if (chatHistory != null) {\n        chatHistory.lastMessage = waitMessage.text;\n        // 异步处理就好，不需要等待\n        chatMsgRepo.updateChatHistory(localChatHistory.id!, chatHistory);\n      }\n\n      // 重新查询消息列表，此时包含了刚刚发送的消息+机器人应答消息\n      emit(ChatMessagesLoaded(\n        await chatMsgRepo.getRecentMessages(\n          roomId: roomId,\n          userId: APIServer().localUserID(),\n          chatHistoryId: localChatHistory.id,\n        ),\n        chatHistory: localChatHistory,\n      ));\n    } catch (e) {\n      final error = resolveErrorMessage(e, isChat: true);\n      await chatMsgRepo.updateMessagePart(\n        roomId,\n        sentMessageId,\n        [\n          MessagePart('status', 2),\n          MessagePart('extra', jsonEncode({'error': error.toString()})),\n        ],\n      );\n\n      if (waitMessage.id != null) {\n        if (waitMessage.isReady) {\n          await chatMsgRepo.updateMessage(\n            roomId,\n            waitMessage.id!,\n            Message(\n              Role.receiver,\n              error.toString(),\n              id: waitMessage.id,\n              ts: DateTime.now(),\n              type: MessageType.system,\n              roomId: roomId,\n              userId: APIServer().localUserID(),\n              chatHistoryId: localChatHistory.id,\n              model: tempModel ?? originalModel,\n            ),\n          );\n        } else {\n          await chatMsgRepo.removeMessage(roomId, [waitMessage.id!]);\n        }\n      }\n\n      emit(ChatMessagesLoaded(\n        await chatMsgRepo.getRecentMessages(\n          roomId: roomId,\n          userId: APIServer().localUserID(),\n          chatHistoryId: localChatHistory.id,\n        ),\n        error: error,\n        chatHistory: localChatHistory,\n      ));\n\n      queue.finish();\n    } finally {\n      queue.dispose();\n      currentQueue = null;\n    }\n\n    emit(ChatMessageUpdated(waitMessage));\n  }\n}\n\nFuture<Room?> queryRoomById(ChatMessageRepository chatMsgRepo, int roomId) async {\n  Room? room;\n  if (Ability().isUserLogon()) {\n    final roomInServer = await APIServer().room(roomId: roomId);\n    room = Room(\n      roomInServer.name,\n      'chat',\n      description: roomInServer.description,\n      id: roomInServer.id,\n      userId: roomInServer.userId,\n      createdAt: roomInServer.createdAt,\n      lastActiveTime: roomInServer.lastActiveTime,\n      systemPrompt: roomInServer.systemPrompt,\n      priority: roomInServer.priority ?? 0,\n      model: '${roomInServer.vendor}:${roomInServer.model}',\n      initMessage: roomInServer.initMessage,\n      maxContext: roomInServer.maxContext,\n      maxTokens: roomInServer.maxTokens,\n      localRoom: false,\n    );\n  } else {\n    room = await chatMsgRepo.room(roomId);\n  }\n\n  return room;\n}\n"
  },
  {
    "path": "lib/bloc/chat_state.dart",
    "content": "part of 'chat_message_bloc.dart';\n\n@immutable\nabstract class ChatMessageState {}\n\nclass ChatMessageInitial extends ChatMessageState {}\n\n// 加载全量聊天记录\nclass ChatMessagesLoaded extends ChatMessageState {\n  final List<Message> _messages;\n  final bool processing;\n  final Object? _error;\n  final ChatHistory? chatHistory;\n\n  ChatMessagesLoaded(\n    this._messages, {\n    Object? error,\n    this.processing = false,\n    this.chatHistory,\n  }) : _error = error;\n\n  get messages => _messages;\n  get error => _error;\n}\n\nclass ChatMessageError extends ChatMessageState {\n  final String message;\n\n  ChatMessageError(this.message);\n}\n\nclass ChatMessageUpdated extends ChatMessageState {\n  final Message message;\n\n  /// 是否新消息正在处理中\n  final bool processing;\n\n  ChatMessageUpdated(this.message, {this.processing = false});\n}\n\nclass ChatHistoryInited extends ChatMessageState {\n  final int chatId;\n\n  ChatHistoryInited(this.chatId);\n}\n"
  },
  {
    "path": "lib/bloc/creative_island_bloc.dart",
    "content": "import 'package:askaide/bloc/bloc_manager.dart';\nimport 'package:askaide/repo/api/creative.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/creative_island_repo.dart';\nimport 'package:flutter/material.dart';\n\npart 'creative_island_event.dart';\npart 'creative_island_state.dart';\n\nclass CreativeIslandBloc\n    extends BlocExt<CreativeIslandEvent, CreativeIslandState> {\n  final CreativeIslandRepository creativeIslandRepo;\n\n  CreativeIslandBloc(this.creativeIslandRepo) : super(CreativeIslandInitial()) {\n    // on<CreativeIslandSaveEvent>((event, emit) async {\n    //   await creativeIslandRepo.create(\n    //     event.itemId,\n    //     arguments: jsonEncode(event.arguments),\n    //     prompt: event.prompt,\n    //     answer: event.answer,\n    //     userId: APIServer().localUserID(),\n    //   );\n    //   emit(CreativeIslandSaved());\n    // });\n\n    on<CreativeIslandItemsV2LoadEvent>((event, emit) async {\n      final items =\n          await APIServer().creativeIslandItemsV2(cache: !event.forceRefresh);\n      emit(CreativeIslandItemsV2Loaded(\n        items: items,\n      ));\n    });\n\n    on<CreativeIslandItemLoadEvent>((event, emit) async {\n      final resp = await APIServer().creativeIslandItem(event.itemId);\n      emit(CreativeIslandItemLoaded(resp));\n    });\n\n    on<CreativeIslandHistoriesAllLoadEvent>((event, emit) async {\n      emit(CreativeIslandHistoriesLoading());\n\n      final items = await APIServer()\n          .creativeHistories(cache: !event.forceRefresh, mode: event.mode);\n      emit(CreativeIslandHistoriesAllLoaded(items.data));\n    });\n\n    on<CreativeIslandGalleryLoadEvent>((event, emit) async {\n      emit(CreativeIslandHistoriesLoading());\n\n      final items = await APIServer().creativeUserGallery(\n          cache: false, mode: event.mode, model: event.model);\n      emit(CreativeIslandGalleryLoaded(items));\n    });\n\n    on<CreativeIslandHistoriesLoadEvent>((event, emit) async {\n      emit(CreativeIslandHistoriesLoading());\n      final island = await APIServer().creativeIslandItem(event.itemId);\n\n      emit(CreativeIslandHistoriesLoaded(\n        island,\n        await APIServer().creativeItemHistories(\n          island.id,\n          cache: !event.forceRefresh,\n        ),\n      ));\n    });\n\n    on<CreativeIslandDeleteEvent>((event, emit) async {\n      emit(CreativeIslandHistoriesLoading());\n      await APIServer()\n          .deleteCreativeHistoryItem(event.itemId, hisId: event.id);\n\n      if (event.source == 'all-histories') {\n        final res =\n            await APIServer().creativeHistories(cache: false, mode: event.mode);\n        emit(CreativeIslandHistoriesAllLoaded(res.data));\n      } else {\n        final island = await APIServer().creativeIslandItem(event.itemId);\n        emit(CreativeIslandHistoriesLoaded(\n          island,\n          await APIServer().creativeItemHistories(\n            island.id,\n            cache: false,\n          ),\n        ));\n      }\n    });\n\n    on<CreativeIslandListLoadEvent>((event, emit) async {\n      emit(CreativeIslandInitial());\n\n      try {\n        final items = await APIServer().creativeIslandItems(mode: event.mode);\n\n        emit(CreativeIslandListLoaded(\n          items.items,\n          categories: items.categories,\n          backgroundImage: items.backgroundImage,\n        ));\n      } catch (e) {\n        emit(\n            CreativeIslandListLoaded(const [], error: e, categories: const []));\n      }\n    });\n\n    on<CreativeIslandHistoryItemLoadEvent>((event, emit) async {\n      emit(CreativeIslandHistoryItemLoading());\n      emit(CreativeIslandHistoryItemLoaded(\n        item: await APIServer().creativeHistoryItem(\n          hisId: event.itemId,\n          cache: !event.forceRefresh,\n        ),\n      ));\n    });\n  }\n}\n"
  },
  {
    "path": "lib/bloc/creative_island_event.dart",
    "content": "part of 'creative_island_bloc.dart';\n\n@immutable\nabstract class CreativeIslandEvent {}\n\n// class CreativeIslandSaveEvent extends CreativeIslandEvent {\n//   final String itemId;\n//   final Map<String, dynamic> arguments;\n//   final String prompt;\n//   final String answer;\n\n//   CreativeIslandSaveEvent(\n//     this.itemId, {\n//     this.arguments = const {},\n//     this.prompt = '',\n//     this.answer = '',\n//   });\n// }\n\nclass CreativeIslandItemLoadEvent extends CreativeIslandEvent {\n  final String itemId;\n  CreativeIslandItemLoadEvent(this.itemId);\n}\n\nclass CreativeIslandHistoriesAllLoadEvent extends CreativeIslandEvent {\n  final bool forceRefresh;\n  final String mode;\n  CreativeIslandHistoriesAllLoadEvent(\n      {this.forceRefresh = false, required this.mode});\n}\n\nclass CreativeIslandGalleryLoadEvent extends CreativeIslandEvent {\n  final bool forceRefresh;\n  final String mode;\n  final String? model;\n  CreativeIslandGalleryLoadEvent({\n    this.forceRefresh = false,\n    required this.mode,\n    this.model,\n  });\n}\n\nclass CreativeIslandHistoriesLoadEvent extends CreativeIslandEvent {\n  final String itemId;\n  final bool forceRefresh;\n  CreativeIslandHistoriesLoadEvent(this.itemId, {this.forceRefresh = false});\n}\n\nclass CreativeIslandDeleteEvent extends CreativeIslandEvent {\n  final String itemId;\n  final int id;\n  final String source;\n  final String mode;\n\n  CreativeIslandDeleteEvent(this.itemId, this.id,\n      {this.source = '', required this.mode});\n}\n\nclass CreativeIslandListLoadEvent extends CreativeIslandEvent {\n  final String mode;\n\n  CreativeIslandListLoadEvent({required this.mode});\n}\n\nclass CreativeIslandHistoryItemLoadEvent extends CreativeIslandEvent {\n  final int itemId;\n  final bool forceRefresh;\n  CreativeIslandHistoryItemLoadEvent(this.itemId, {this.forceRefresh = false});\n}\n\nclass CreativeIslandItemsV2LoadEvent extends CreativeIslandEvent {\n  final bool forceRefresh;\n  CreativeIslandItemsV2LoadEvent({this.forceRefresh = false});\n}\n"
  },
  {
    "path": "lib/bloc/creative_island_state.dart",
    "content": "part of 'creative_island_bloc.dart';\n\n@immutable\nabstract class CreativeIslandState {}\n\nclass CreativeIslandInitial extends CreativeIslandState {}\n\n// class CreativeIslandSaved extends CreativeIslandState {\n//   final String? _error;\n\n//   CreativeIslandSaved({String? error}) : _error = error;\n\n//   get error => _error;\n// }\n\nclass CreativeIslandItemLoaded extends CreativeIslandState {\n  final String? _error;\n  final CreativeIslandItem item;\n\n  CreativeIslandItemLoaded(this.item, {String? error}) : _error = error;\n\n  get error => _error;\n}\n\nclass CreativeIslandHistoriesLoading extends CreativeIslandInitial {}\n\nclass CreativeIslandHistoriesLoaded extends CreativeIslandState {\n  final String? _error;\n  final CreativeIslandItem island;\n  final List<CreativeItemInServer> histories;\n\n  CreativeIslandHistoriesLoaded(this.island, this.histories, {String? error})\n      : _error = error;\n  get error => _error;\n}\n\nclass CreativeIslandGalleryLoaded extends CreativeIslandState {\n  final String? _error;\n  final List<CreativeItemInServer> items;\n\n  CreativeIslandGalleryLoaded(this.items, {String? error}) : _error = error;\n  get error => _error;\n}\n\nclass CreativeIslandHistoriesAllLoaded extends CreativeIslandState {\n  final String? _error;\n  final List<CreativeItemInServer> histories;\n\n  CreativeIslandHistoriesAllLoaded(this.histories, {String? error})\n      : _error = error;\n  get error => _error;\n}\n\nclass CreativeIslandListLoaded extends CreativeIslandState {\n  final Object? _error;\n  final List<CreativeIslandItem> items;\n  final List<String> categories;\n  final String? backgroundImage;\n\n  CreativeIslandListLoaded(\n    this.items, {\n    Object? error,\n    required this.categories,\n    this.backgroundImage,\n  }) : _error = error;\n\n  get error => _error;\n}\n\nclass CreativeIslandHistoryItemLoading extends CreativeIslandState {}\n\nclass CreativeIslandHistoryItemLoaded extends CreativeIslandState {\n  final Object? error;\n  final CreativeItemInServer? item;\n\n  CreativeIslandHistoryItemLoaded({this.item, this.error});\n}\n\nclass CreativeIslandItemsV2Loaded extends CreativeIslandState {\n  final Object? error;\n  final List<CreativeIslandItemV2> items;\n\n  CreativeIslandItemsV2Loaded({required this.items, this.error});\n}\n"
  },
  {
    "path": "lib/bloc/free_count_bloc.dart",
    "content": "import 'package:askaide/helper/ability.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:bloc/bloc.dart';\nimport 'package:meta/meta.dart';\n\npart 'free_count_event.dart';\npart 'free_count_state.dart';\n\nclass FreeCountBloc extends Bloc<FreeCountEvent, FreeCountState> {\n  List<FreeModelCount> counts = [];\n\n  FreeCountBloc() : super(FreeCountInitial()) {\n    // 重新加载所有的模型免费使用次数\n    on<FreeCountReloadAllEvent>((event, emit) async {\n      if (!Ability().isUserLogon()) {\n        emit(FreeCountLoadedState(\n          counts: await APIServer().freeChatCounts(),\n          needSignin: event.checkSigninStatus,\n        ));\n        return;\n      }\n\n      counts = await APIServer().userFreeStatistics();\n      emit(FreeCountLoadedState(counts: counts));\n    });\n\n    // 重新加载指定模型的免费使用次数\n    on<FreeCountReloadEvent>((event, emit) async {\n      if (Ability().usingLocalOpenAIModel(event.model) ||\n          !Ability().isUserLogon()) {\n        emit(FreeCountLoadedState(counts: counts));\n        return;\n      }\n\n      final freeCount = await APIServer().userFreeStatisticsForModel(\n        model: event.model.startsWith('v2@')\n            ? event.model\n            : event.model.split(':').last,\n      );\n      if (freeCount.maxCount > 0) {\n        var matched = false;\n        for (var i = 0; i < counts.length; i++) {\n          if (counts[i].model == freeCount.model) {\n            counts[i] = freeCount;\n            matched = true;\n            break;\n          }\n        }\n\n        if (!matched) {\n          counts.add(freeCount);\n        }\n      }\n\n      emit(FreeCountLoadedState(counts: counts));\n    });\n  }\n}\n"
  },
  {
    "path": "lib/bloc/free_count_event.dart",
    "content": "part of 'free_count_bloc.dart';\n\n@immutable\nsealed class FreeCountEvent {}\n\nclass FreeCountReloadEvent extends FreeCountEvent {\n  final String model;\n  FreeCountReloadEvent({required this.model});\n}\n\nclass FreeCountReloadAllEvent extends FreeCountEvent {\n  final bool checkSigninStatus;\n\n  FreeCountReloadAllEvent({this.checkSigninStatus = false});\n}\n"
  },
  {
    "path": "lib/bloc/free_count_state.dart",
    "content": "part of 'free_count_bloc.dart';\n\n@immutable\nsealed class FreeCountState {}\n\nfinal class FreeCountInitial extends FreeCountState {}\n\nclass FreeCountLoadedState extends FreeCountState {\n  final List<FreeModelCount> counts;\n  final bool needSignin;\n\n  FreeModelCount? model(String model) {\n    model = model.split(':').last;\n    for (var i = 0; i < counts.length; i++) {\n      if (counts[i].model == model) {\n        return counts[i];\n      }\n    }\n\n    return null;\n  }\n\n  FreeCountLoadedState({required this.counts, this.needSignin = false});\n}\n"
  },
  {
    "path": "lib/bloc/gallery_bloc.dart",
    "content": "import 'package:askaide/repo/api/creative.dart';\nimport 'package:askaide/repo/api/page.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:bloc/bloc.dart';\nimport 'package:meta/meta.dart';\n\npart 'gallery_event.dart';\npart 'gallery_state.dart';\n\nclass GalleryBloc extends Bloc<GalleryEvent, GalleryState> {\n  GalleryBloc() : super(GalleryInitial()) {\n    on<GalleryLoadEvent>((event, emit) async {\n      emit(GalleryInitial());\n\n      final res = await APIServer().creativeGallery(\n        cache: !event.forceRefresh,\n        page: event.page,\n        perPage: 20,\n      );\n\n      emit(GalleryLoaded(data: res));\n    });\n\n    on<GalleryItemLoadEvent>((event, emit) async {\n      emit(GalleryInitial());\n\n      final res = await APIServer().creativeGalleryItem(\n        cache: !event.forceRefresh,\n        id: event.id,\n      );\n\n      emit(GalleryItemLoaded(\n        item: res.item,\n        isInternalUser: res.isInternalUser,\n      ));\n    });\n  }\n}\n"
  },
  {
    "path": "lib/bloc/gallery_event.dart",
    "content": "part of 'gallery_bloc.dart';\n\n@immutable\nabstract class GalleryEvent {}\n\nclass GalleryLoadEvent extends GalleryEvent {\n  final bool forceRefresh;\n  final int page;\n\n  GalleryLoadEvent({this.forceRefresh = false, this.page = 1});\n}\n\nclass GalleryItemLoadEvent extends GalleryEvent {\n  final int id;\n  final bool forceRefresh;\n\n  GalleryItemLoadEvent({required this.id, this.forceRefresh = false});\n}\n"
  },
  {
    "path": "lib/bloc/gallery_state.dart",
    "content": "part of 'gallery_bloc.dart';\n\n@immutable\nabstract class GalleryState {}\n\nclass GalleryInitial extends GalleryState {}\n\nclass GalleryLoaded extends GalleryState {\n  final PagedData<CreativeGallery> data;\n\n  GalleryLoaded({required this.data});\n}\n\nclass GalleryItemLoaded extends GalleryState {\n  final CreativeGallery item;\n  final bool isInternalUser;\n\n  GalleryItemLoaded({required this.item, this.isInternalUser = false});\n}\n"
  },
  {
    "path": "lib/bloc/group_chat_bloc.dart",
    "content": "import 'dart:convert';\n\nimport 'package:askaide/helper/cache.dart';\nimport 'package:askaide/helper/logger.dart';\nimport 'package:askaide/page/component/chat/message_state_manager.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/model/group.dart';\nimport 'package:askaide/repo/model/message.dart';\nimport 'package:bloc/bloc.dart';\nimport 'package:meta/meta.dart';\n\npart 'group_chat_event.dart';\npart 'group_chat_state.dart';\n\nclass GroupChatBloc extends Bloc<GroupChatEvent, GroupChatState> {\n  var messages = <GroupMessage>[];\n  final MessageStateManager stateManager;\n\n  GroupChatBloc({required this.stateManager}) : super(GroupChatInitial()) {\n    // 加载聊天组\n    on<GroupChatLoadEvent>((event, emit) async {\n      final group =\n          await APIServer().chatGroup(event.groupId, cache: !event.forceUpdate);\n      final states = await stateManager.loadRoomStates(event.groupId);\n\n      final defaultChatMembers = await loadDefaultChatMembers(event.groupId);\n\n      emit(GroupChatLoaded(\n        group: group,\n        states: states,\n        defaultChatMembers: defaultChatMembers.isEmpty\n            ? group.members.map((e) => e.id!).toList()\n            : defaultChatMembers,\n      ));\n    });\n\n    // 加载聊天组聊天记录\n    on<GroupChatMessagesLoadEvent>((event, emit) async {\n      if (event.isInitRequest) {\n        try {\n          final cached =\n              await Cache().stringGet(key: 'group:speed:${event.groupId}');\n          if (cached != null) {\n            final messages = (jsonDecode(cached) as List<dynamic>)\n                .map((e) => GroupMessage.fromJson(e))\n                .toList();\n\n            emit(GroupChatMessagesLoaded(messages: messages));\n          }\n        } catch (e) {\n          Logger.instance.e(e);\n        }\n      }\n\n      await refreshGroupMessages(\n        event.groupId,\n        startId: event.startId,\n        forceRefresh: true,\n      );\n\n      emit(GroupChatMessagesLoaded(messages: messages));\n    });\n\n    // 发送聊天组消息\n    on<GroupChatSendEvent>((event, emit) async {\n      try {\n        final resp = await APIServer().chatGroupSendMessage(\n          event.groupId,\n          GroupChatSendRequest(\n            message: event.message,\n            memberIds: event.members,\n          ),\n        );\n\n        // 记录默认聊天成员\n        updateDefaultChatMembers(\n          event.groupId,\n          resp.tasks.map((e) => e.memberId).toList(),\n        ).then((members) {\n          emit(GroupDefaultMemberSelected(members));\n        });\n\n        await refreshGroupMessages(\n          event.groupId,\n          startId: 0,\n          forceRefresh: true,\n        );\n        emit(GroupChatMessagesLoaded(messages: messages));\n      } catch (e) {\n        await refreshGroupMessages(\n          event.groupId,\n          startId: 0,\n          forceRefresh: true,\n        );\n        emit(GroupChatMessagesLoaded(messages: messages, error: e));\n      }\n    });\n\n    // 发送系统消息\n    on<GroupChatSendSystemEvent>((event, emit) async {\n      try {\n        final resp = await APIServer().chatGroupSendSystemMessage(\n          event.groupId,\n          messageType: event.type.getTypeText(),\n          message: event.message,\n        );\n\n        Logger.instance.d(resp.toJson());\n      } finally {\n        await refreshGroupMessages(\n          event.groupId,\n          startId: 0,\n          forceRefresh: true,\n        );\n        emit(GroupChatMessagesLoaded(messages: messages));\n      }\n    });\n\n    // 更新聊天组消息状态\n    on<GroupChatUpdateMessageStatusEvent>((event, emit) async {\n      final waitMessageIds = messages\n          .where((msg) => msg.status == groupMessageStatusWaiting)\n          .map((msg) => msg.id)\n          .toList();\n\n      if (waitMessageIds.isEmpty) {\n        return;\n      }\n\n      final resp = await APIServer()\n          .chatGroupMessageStatus(event.groupId, waitMessageIds);\n      final newMessageStatusMap = <int, GroupMessage>{};\n      for (var msg in resp) {\n        newMessageStatusMap[msg.id] = msg;\n      }\n\n      for (var i = 0; i < messages.length; i++) {\n        final msg = messages[i];\n        if (newMessageStatusMap.containsKey(msg.id)) {\n          messages[i] = newMessageStatusMap[msg.id]!;\n        }\n      }\n\n      emit(GroupChatMessagesLoaded(messages: messages));\n    });\n\n    // 清空聊天组消息\n    on<GroupChatDeleteAllEvent>((event, emit) async {\n      await APIServer().chatGroupDeleteAllMessages(event.groupId);\n      messages.clear();\n      emit(GroupChatMessagesLoaded(messages: messages));\n    });\n\n    // 删除聊天组消息\n    on<GroupChatDeleteEvent>((event, emit) async {\n      await APIServer().chatGroupDeleteMessage(event.groupId, event.messageId);\n      messages.removeWhere((msg) => msg.id == event.messageId);\n      emit(GroupChatMessagesLoaded(messages: messages));\n    });\n  }\n\n  refreshGroupMessages(\n    int groupId, {\n    int startId = 0,\n    bool forceRefresh = false,\n  }) async {\n    final data = await APIServer()\n        .chatGroupMessages(groupId, startId: startId, cache: !forceRefresh);\n    messages = data.data.reversed.toList();\n\n    if (startId == 0) {\n      Cache()\n          .setString(key: 'group:speed:$groupId', value: jsonEncode(messages));\n    }\n  }\n\n  Future<List<int>> loadDefaultChatMembers(int groupId) async {\n    final defaultMembers =\n        await Cache().stringGet(key: 'group:$groupId:default-members');\n\n    return (defaultMembers ?? '')\n        .split(',')\n        .map((e) => int.tryParse(e) ?? 0)\n        .where((e) => e > 0)\n        .toList();\n  }\n\n  Future<List<int>> updateDefaultChatMembers(\n      int groupId, List<int> members) async {\n    // 记录默认聊天成员\n    await Cache().setString(\n      key: 'group:$groupId:default-members',\n      value: members.join(','),\n      duration: const Duration(days: 365),\n    );\n\n    return members;\n  }\n}\n"
  },
  {
    "path": "lib/bloc/group_chat_event.dart",
    "content": "part of 'group_chat_bloc.dart';\n\n@immutable\nsealed class GroupChatEvent {}\n\nclass GroupChatLoadEvent extends GroupChatEvent {\n  final int groupId;\n  final bool forceUpdate;\n\n  GroupChatLoadEvent(this.groupId, {this.forceUpdate = false});\n}\n\nclass GroupChatMessagesLoadEvent extends GroupChatEvent {\n  final int groupId;\n  final int startId;\n  final bool isInitRequest;\n\n  GroupChatMessagesLoadEvent(\n    this.groupId, {\n    this.startId = 0,\n    this.isInitRequest = false,\n  });\n}\n\nclass GroupChatSendEvent extends GroupChatEvent {\n  final int groupId;\n  final String message;\n  final List<int> members;\n  final int? index;\n  final bool isResent;\n\n  GroupChatSendEvent(this.groupId, this.message, this.members,\n      {this.index, this.isResent = false});\n}\n\nclass GroupChatUpdateMessageStatusEvent extends GroupChatEvent {\n  final int groupId;\n\n  GroupChatUpdateMessageStatusEvent(this.groupId);\n}\n\nclass GroupChatSendSystemEvent extends GroupChatEvent {\n  final int groupId;\n  final String? message;\n  final MessageType type;\n\n  GroupChatSendSystemEvent(this.groupId, this.type, {this.message});\n}\n\nclass GroupChatDeleteAllEvent extends GroupChatEvent {\n  final int groupId;\n\n  GroupChatDeleteAllEvent(this.groupId);\n}\n\nclass GroupChatDeleteEvent extends GroupChatEvent {\n  final int groupId;\n  final int messageId;\n\n  GroupChatDeleteEvent(this.groupId, this.messageId);\n}\n"
  },
  {
    "path": "lib/bloc/group_chat_state.dart",
    "content": "part of 'group_chat_bloc.dart';\n\n@immutable\nsealed class GroupChatState {}\n\nfinal class GroupChatInitial extends GroupChatState {}\n\nclass GroupChatLoaded extends GroupChatState {\n  final ChatGroup group;\n  final Map<String, MessageState> states;\n  final List<int>? defaultChatMembers;\n\n  GroupChatLoaded({\n    required this.group,\n    required this.states,\n    this.defaultChatMembers,\n  });\n}\n\nclass GroupDefaultMemberSelected extends GroupChatState {\n  final List<int> members;\n\n  GroupDefaultMemberSelected(this.members);\n}\n\nclass GroupChatMessagesLoaded extends GroupChatState {\n  final List<GroupMessage> messages;\n  final Object? _error;\n\n  get error => _error;\n\n  bool get hasWaitTasks =>\n      messages.any((element) => element.status == groupMessageStatusWaiting);\n\n  GroupChatMessagesLoaded({\n    required this.messages,\n    Object? error,\n  }) : _error = error;\n}\n"
  },
  {
    "path": "lib/bloc/model_bloc.dart",
    "content": "import 'package:askaide/repo/api/admin/models.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:bloc/bloc.dart';\nimport 'package:meta/meta.dart';\n\npart 'model_event.dart';\npart 'model_state.dart';\n\nclass ModelBloc extends Bloc<ModelEvent, ModelState> {\n  ModelBloc() : super(ModelInitial()) {\n    /// 加载所有模型\n    on<ModelsLoadEvent>((event, emit) async {\n      final channels = await APIServer().adminModels();\n      emit(ModelsLoaded(channels));\n    });\n\n    /// 加载单个模型\n    on<ModelLoadEvent>((event, emit) async {\n      final channel = await APIServer().adminModel(modelId: event.modelId);\n      emit(ModelLoaded(channel));\n    });\n\n    /// 创建模型\n    on<ModelCreateEvent>((event, emit) async {\n      try {\n        await APIServer().adminCreateModel(event.req);\n        emit(ModelOperationResult(true, 'Creation successful'));\n      } catch (e) {\n        emit(ModelOperationResult(false, e.toString()));\n      }\n    });\n\n    /// 更新模型\n    on<ModelUpdateEvent>((event, emit) async {\n      try {\n        await APIServer().adminUpdateModel(\n          modelId: event.modelId,\n          req: event.req,\n        );\n        emit(ModelOperationResult(true, 'Update successful'));\n      } catch (e) {\n        emit(ModelOperationResult(false, e.toString()));\n      }\n    });\n\n    /// 删除模型\n    on<ModelDeleteEvent>((event, emit) async {\n      try {\n        await APIServer().adminDeleteModel(modelId: event.modelId);\n        emit(ModelOperationResult(true, 'Delete successful'));\n      } catch (e) {\n        emit(ModelOperationResult(false, e.toString()));\n      }\n    });\n  }\n}\n"
  },
  {
    "path": "lib/bloc/model_event.dart",
    "content": "part of 'model_bloc.dart';\n\n@immutable\nsealed class ModelEvent {}\n\nclass ModelsLoadEvent extends ModelEvent {}\n\nclass ModelLoadEvent extends ModelEvent {\n  final String modelId;\n\n  ModelLoadEvent(this.modelId);\n}\n\nclass ModelCreateEvent extends ModelEvent {\n  final AdminModelAddReq req;\n\n  ModelCreateEvent(this.req);\n}\n\nclass ModelUpdateEvent extends ModelEvent {\n  final String modelId;\n  final AdminModelUpdateReq req;\n\n  ModelUpdateEvent(this.modelId, this.req);\n}\n\nclass ModelDeleteEvent extends ModelEvent {\n  final String modelId;\n\n  ModelDeleteEvent(this.modelId);\n}\n"
  },
  {
    "path": "lib/bloc/model_state.dart",
    "content": "part of 'model_bloc.dart';\n\n@immutable\nsealed class ModelState {}\n\nfinal class ModelInitial extends ModelState {}\n\nclass ModelsLoaded extends ModelState {\n  final List<AdminModel> models;\n\n  ModelsLoaded(this.models);\n}\n\nclass ModelLoaded extends ModelState {\n  final AdminModel model;\n\n  ModelLoaded(this.model);\n}\n\nclass ModelOperationResult extends ModelState {\n  final bool success;\n  final String message;\n\n  ModelOperationResult(this.success, this.message);\n}\n"
  },
  {
    "path": "lib/bloc/notify_bloc.dart",
    "content": "import 'package:askaide/bloc/bloc_manager.dart';\nimport 'package:flutter/material.dart';\n\npart 'notify_event.dart';\npart 'notify_state.dart';\n\nclass NotifyBloc extends BlocExt<NotifyEvent, NotifyState> {\n  NotifyBloc() : super(NotifyInitial()) {\n    on<NotifyFiredEvent>((event, emit) {\n      emit(NotifyFired(event.title, event.body, event.type));\n    });\n\n    on<NotifyResetEvent>((event, emit) {\n      emit(NotifyInitial());\n    });\n  }\n}\n"
  },
  {
    "path": "lib/bloc/notify_event.dart",
    "content": "part of 'notify_bloc.dart';\n\n@immutable\nabstract class NotifyEvent {}\n\nclass NotifyFiredEvent extends NotifyEvent {\n  final String title;\n  final String body;\n  final String type;\n\n  NotifyFiredEvent(this.title, this.body, this.type);\n}\n\nclass NotifyResetEvent extends NotifyEvent {}\n"
  },
  {
    "path": "lib/bloc/notify_state.dart",
    "content": "part of 'notify_bloc.dart';\n\n@immutable\nabstract class NotifyState {}\n\nclass NotifyInitial extends NotifyState {}\n\nclass NotifyFired extends NotifyState {\n  final String title;\n  final String body;\n  final String type;\n\n  NotifyFired(this.title, this.body, this.type);\n}\n"
  },
  {
    "path": "lib/bloc/payment_bloc.dart",
    "content": "import 'package:askaide/helper/platform.dart';\nimport 'package:askaide/repo/api/payment.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:bloc/bloc.dart';\nimport 'package:in_app_purchase/in_app_purchase.dart';\nimport 'package:meta/meta.dart';\n\npart 'payment_event.dart';\npart 'payment_state.dart';\n\nclass PaymentBloc extends Bloc<PaymentEvent, PaymentState> {\n  PaymentBloc() : super(PaymentInitial()) {\n    on<PaymentLoadAppleProducts>((event, emit) async {\n      if (PlatformTool.isIOS()) {\n        final products = await APIServer().paymentProducts();\n        if (products.consume.isEmpty) {\n          emit(PaymentAppleProductsLoaded(\n            const <ProductDetails>[],\n            note: products.note,\n            error: '没有任何可购买的项目',\n            localProducts: const [],\n            loading: false,\n          ));\n          return;\n        }\n\n        emit(PaymentAppleProductsLoaded(\n            products.consume\n                .map(\n                  (e) => ProductDetails(\n                    id: e.id,\n                    title: e.name,\n                    description: '',\n                    price: '-',\n                    rawPrice: 0,\n                    currencyCode: '',\n                  ),\n                )\n                .toList(),\n            note: products.note,\n            localProducts: products.consume,\n            loading: true));\n\n        final productIds = products.consume.map((e) => e.id).toSet();\n        final response =\n            await InAppPurchase.instance.queryProductDetails(productIds);\n        if (response.notFoundIDs.isNotEmpty) {\n          emit(PaymentAppleProductsLoaded(\n            const <ProductDetails>[],\n            note: products.note,\n            localProducts: products.consume,\n            error: '没有任何可购买的项目',\n            loading: false,\n          ));\n          return;\n        }\n\n        final remoteProducts = <ProductDetails>[];\n        for (var id in productIds) {\n          remoteProducts.add(\n            response.productDetails.firstWhere((element) => element.id == id),\n          );\n        }\n\n        emit(PaymentAppleProductsLoaded(\n          remoteProducts,\n          note: products.note,\n          localProducts: products.consume,\n          loading: false,\n        ));\n      } else {\n        final products = await APIServer().paymentProducts();\n        if (products.consume.isEmpty) {\n          emit(PaymentAppleProductsLoaded(\n            const <ProductDetails>[],\n            note: products.note,\n            error: '没有任何可购买的项目',\n            localProducts: const [],\n            loading: false,\n          ));\n          return;\n        }\n\n        emit(\n          PaymentAppleProductsLoaded(\n            products.consume\n                .map(\n                  (e) => ProductDetails(\n                    id: e.id,\n                    title: e.name,\n                    description: '',\n                    price: products.preferUSD\n                        ? e.retailPriceUSDText\n                        : e.retailPriceText,\n                    rawPrice: e.retailPrice.toDouble(),\n                    currencyCode: '',\n                  ),\n                )\n                .toList(),\n            note: products.note,\n            localProducts: products.consume,\n            loading: false,\n            preferUSD: products.preferUSD,\n          ),\n        );\n      }\n    });\n  }\n}\n"
  },
  {
    "path": "lib/bloc/payment_event.dart",
    "content": "part of 'payment_bloc.dart';\n\n@immutable\nabstract class PaymentEvent {}\n\nclass PaymentLoadAppleProducts extends PaymentEvent {}\n"
  },
  {
    "path": "lib/bloc/payment_state.dart",
    "content": "part of 'payment_bloc.dart';\n\n@immutable\nabstract class PaymentState {}\n\nclass PaymentInitial extends PaymentState {}\n\nclass PaymentAppleProductsLoaded extends PaymentState {\n  final List<ProductDetails> products;\n  final List<PaymentProduct> localProducts;\n  final Object? error;\n  final bool loading;\n  final String? note;\n  final bool preferUSD;\n\n  PaymentAppleProductsLoaded(\n    this.products, {\n    this.note,\n    required this.localProducts,\n    this.error,\n    required this.loading,\n    this.preferUSD = false,\n  });\n}\n"
  },
  {
    "path": "lib/bloc/room_bloc.dart",
    "content": "import 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/page/component/chat/message_state_manager.dart';\nimport 'package:askaide/repo/api/room_gallery.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/model/group.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:askaide/repo/model/room.dart';\n\nimport 'package:askaide/bloc/bloc_manager.dart';\nimport 'package:askaide/repo/chat_message_repo.dart';\nimport 'package:flutter/material.dart';\n\npart 'room_event.dart';\npart 'room_state.dart';\n\nclass RoomBloc extends BlocExt<RoomEvent, RoomState> {\n  final ChatMessageRepository chatMsgRepo;\n  final MessageStateManager stateManager;\n\n  Future<int?> fixRoomId(int? chatHistoryId) async {\n    if (chatHistoryId != null && chatHistoryId > 0) {\n      final his = await chatMsgRepo.getChatHistory(chatHistoryId);\n      if (his != null) {\n        return his.roomId;\n      }\n    }\n\n    return null;\n  }\n\n  /// 加载房间信息，如果房间不存在，则加载默认房间\n  Future<RoomInServer> loadRoom(int roomId) async {\n    try {\n      final room = await APIServer().room(roomId: roomId);\n      return room;\n    } catch (e) {\n      return await APIServer().room(roomId: chatAnywhereRoomId);\n    }\n  }\n\n  RoomBloc({\n    required this.chatMsgRepo,\n    required this.stateManager,\n  }) : super(RoomInitial()) {\n    // 加载指定聊天室信息\n    on<RoomLoadEvent>((event, emit) async {\n      try {\n        // 加快首屏加载速度，避免加载中状态\n        emit(RoomLoaded(\n          Room(\n            '',\n            'chat',\n            id: event.roomId,\n          ),\n          const <String, MessageState>{},\n          cascading: false,\n        ));\n\n        final roomId = await fixRoomId(event.chatHistoryId) ?? event.roomId;\n\n        if (Ability().isUserLogon()) {\n          final room = await loadRoom(roomId);\n          if (event.chatHistoryId != null && event.chatHistoryId! > 0) {\n            final chatHistory = await chatMsgRepo.getChatHistory(event.chatHistoryId!);\n            if (chatHistory != null && chatHistory.model != null) {\n              room.model = chatHistory.model!;\n            }\n          }\n          emit(RoomLoaded(\n            Room(\n              room.name,\n              'chat',\n              description: room.description,\n              id: room.id,\n              userId: room.userId,\n              createdAt: room.createdAt,\n              lastActiveTime: room.lastActiveTime,\n              systemPrompt: room.systemPrompt,\n              priority: room.priority ?? 0,\n              model: room.model.startsWith('v2@') ? room.model : '${room.vendor}:${room.model}',\n              initMessage: room.initMessage,\n              maxContext: room.maxContext,\n              avatarId: room.avatarId,\n              avatarUrl: room.avatarUrl,\n              roomType: room.roomType,\n            ),\n            const <String, MessageState>{},\n            cascading: false,\n          ));\n\n          final states = await stateManager.loadRoomStates(roomId);\n          emit(RoomLoaded(\n            Room(\n              room.name,\n              'chat',\n              description: room.description,\n              id: room.id,\n              userId: room.userId,\n              createdAt: room.createdAt,\n              lastActiveTime: room.lastActiveTime,\n              systemPrompt: room.systemPrompt,\n              priority: room.priority ?? 0,\n              model: room.model.startsWith('v2@') ? room.model : '${room.vendor}:${room.model}',\n              initMessage: room.initMessage,\n              maxContext: room.maxContext,\n              avatarId: room.avatarId,\n              avatarUrl: room.avatarUrl,\n              roomType: room.roomType,\n            ),\n            states,\n            examples: await APIServer().example(\n              room.model.startsWith('v2@') ? room.model : '${room.vendor}:${room.model}',\n            ),\n            cascading: event.cascading,\n          ));\n          return;\n        }\n\n        final room = await chatMsgRepo.room(roomId);\n        if (room != null) {\n          final states = await stateManager.loadRoomStates(roomId);\n          emit(RoomLoaded(\n            room,\n            states,\n            examples: await APIServer().example(room.model),\n            cascading: event.cascading,\n          ));\n        }\n      } catch (e) {\n        emit(RoomLoaded(\n          Room('-', '-'),\n          const {},\n          error: e,\n          cascading: event.cascading,\n        ));\n      }\n    });\n\n    // 加载聊天室列表\n    on<RoomsLoadEvent>((event, emit) async {\n      if (!event.forceRefresh) {\n        emit(RoomsLoading());\n      }\n      emit(await createRoomsLoadedState(cache: !event.forceRefresh));\n    });\n\n    // 创建聊天室\n    on<RoomCreateEvent>((event, emit) async {\n      emit(RoomsLoading());\n\n      try {\n        if (Ability().isUserLogon()) {\n          String? model;\n          String? vendor;\n\n          if (event.model != null) {\n            final segs = event.model!.split(':');\n            model = event.model!.startsWith('v2@') ? event.model! : (segs.length > 1 ? segs.last : event.model);\n            vendor = event.model!.startsWith('v2@') ? '' : (segs.length > 1 ? segs.first : '');\n          }\n\n          await APIServer().createRoom(\n            name: event.name,\n            vendor: vendor,\n            model: model,\n            systemPrompt: event.prompt,\n            avatarId: event.avatarId,\n            avatarUrl: event.avatarUrl,\n            maxContext: event.maxContext,\n            initMessage: event.initMessage,\n          );\n        } else {\n          await chatMsgRepo.createRoom(\n            name: event.name,\n            category: 'chat',\n            model: event.model ?? 'gpt-4o',\n            systemPrompt: event.prompt,\n            userId: APIServer().localUserID(),\n            maxContext: event.maxContext,\n          );\n        }\n\n        emit(RoomOperationResult(true));\n        emit(await createRoomsLoadedState(cache: false));\n      } catch (e) {\n        emit(RoomOperationResult(false, error: e.toString()));\n        // emit(RoomsLoaded(const [], error: e.toString()));\n      }\n    });\n\n    // 删除聊天室\n    on<RoomDeleteEvent>((event, emit) async {\n      emit(RoomsLoading());\n\n      try {\n        if (Ability().isUserLogon()) {\n          await APIServer().deleteRoom(roomId: event.roomId);\n        } else {\n          var room = await chatMsgRepo.room(event.roomId);\n          if (room == null || room.category == 'system') {\n            return;\n          }\n\n          await chatMsgRepo.deleteRoom(event.roomId);\n        }\n\n        emit(await createRoomsLoadedState(cache: false));\n      } catch (e) {\n        emit(RoomsLoaded(const [], error: e.toString()));\n      }\n    });\n\n    // 更新聊天室信息\n    on<RoomUpdateEvent>((event, emit) async {\n      try {\n        if (Ability().isUserLogon()) {\n          final room = await APIServer().updateRoom(\n            roomId: event.roomId,\n            name: event.name!,\n            model: event.model != null\n                ? (event.model!.startsWith('v2@') ? event.model! : event.model!.split(':').last)\n                : null,\n            vendor: event.model != null ? (event.model!.startsWith('v2@') ? '' : event.model!.split(':').first) : null,\n            systemPrompt: event.prompt!,\n            avatarId: event.avatarId,\n            avatarUrl: event.avatarUrl,\n            maxContext: event.maxContext,\n            initMessage: event.initMessage,\n          );\n\n          final states = await stateManager.loadRoomStates(event.roomId);\n          emit(\n            RoomLoaded(\n              Room(\n                room.name,\n                'chat',\n                description: room.description,\n                id: room.id,\n                userId: room.userId,\n                createdAt: room.createdAt,\n                lastActiveTime: room.lastActiveTime,\n                systemPrompt: room.systemPrompt,\n                priority: room.priority ?? 0,\n                model: room.model.startsWith('v2@') ? room.model : '${room.vendor}:${room.model}',\n                avatarId: room.avatarId,\n                avatarUrl: room.avatarUrl,\n                initMessage: room.initMessage,\n                roomType: room.roomType,\n              ),\n              states,\n              examples: await APIServer().example(room.model),\n              cascading: false,\n            ),\n          );\n        } else {\n          final room = await chatMsgRepo.room(event.roomId);\n\n          if (room != null) {\n            if (event.name != null && event.name != '') {\n              room.name = event.name!;\n            }\n\n            if (event.model != null && event.model != '') {\n              room.model = event.model!;\n            }\n\n            if (event.prompt != null && event.prompt != '') {\n              room.systemPrompt = event.prompt!;\n            }\n\n            if (event.maxContext != null) {\n              room.maxContext = event.maxContext!;\n            }\n\n            await chatMsgRepo.updateRoom(room);\n            final states = await stateManager.loadRoomStates(event.roomId);\n            emit(RoomLoaded(\n              room,\n              states,\n              examples: await APIServer().examples(),\n              cascading: false,\n            ));\n          }\n        }\n      } catch (e) {\n        emit(RoomOperationResult(false, error: e.toString()));\n      }\n    });\n\n    on<GalleryRoomCopyEvent>((event, emit) async {\n      if (event.ids.isEmpty) {\n        return;\n      }\n\n      try {\n        final ids = await APIServer().copyRoomGallery(ids: event.ids);\n        emit(await createRoomsLoadedState(cache: false));\n        if (ids.isNotEmpty) {\n          emit(RoomOperationResult(true, redirect: '/room/${ids.first}/chat'));\n        }\n      } catch (e) {\n        emit(RoomCreateError(e));\n      }\n    });\n\n    on<RoomGalleriesLoadEvent>((event, emit) async {\n      try {\n        final resp = await APIServer().roomGalleries();\n        emit(RoomGalleriesLoaded(resp.galleries, tags: resp.tags));\n      } catch (e) {\n        emit(RoomGalleriesLoaded(const [], error: e));\n      }\n    });\n\n    // 创建群聊聊天室\n    on<GroupRoomCreateEvent>((event, emit) async {\n      emit(RoomsLoading());\n\n      try {\n        await APIServer().createGroupRoom(\n          name: event.name,\n          avatarUrl: event.avatarUrl,\n          members: event.members,\n        );\n\n        emit(GroupRoomUpdateResultState(true));\n        emit(await createRoomsLoadedState(cache: false));\n      } catch (e) {\n        emit(GroupRoomUpdateResultState(false, error: e));\n        emit(RoomsLoaded(const [], error: e.toString()));\n      }\n    });\n\n    // 群聊聊天室更新\n    on<GroupRoomUpdateEvent>((event, emit) async {\n      emit(RoomsLoading());\n\n      try {\n        await APIServer().updateGroupRoom(\n          groupId: event.groupId,\n          name: event.name,\n          avatarUrl: event.avatarUrl,\n          members: event.members,\n        );\n\n        emit(GroupRoomUpdateResultState(true));\n      } catch (e) {\n        emit(GroupRoomUpdateResultState(false, error: e));\n      }\n    });\n\n    on<RoomsRecentLoadEvent>((event, emit) async {\n      try {\n        final rooms = await APIServer().recentRooms();\n        emit(RoomsRecentLoaded(rooms));\n      } catch (e) {\n        emit(RoomsRecentLoaded(const [], error: e));\n      }\n    });\n  }\n\n  Future<RoomsLoaded> createRoomsLoadedState({bool cache = true}) async {\n    try {\n      if (Ability().isUserLogon()) {\n        final resp = await APIServer().rooms(cache: cache);\n        return RoomsLoaded(\n          resp.rooms\n              .map((room) => Room(\n                    room.name,\n                    'chat',\n                    description: room.description,\n                    id: room.id,\n                    userId: room.userId,\n                    createdAt: room.createdAt,\n                    lastActiveTime: room.lastActiveTime,\n                    systemPrompt: room.systemPrompt,\n                    priority: room.priority ?? 0,\n                    model: room.model.startsWith('v2@') ? room.model : '${room.vendor}:${room.model}',\n                    avatarId: room.avatarId,\n                    avatarUrl: room.avatarUrl,\n                    roomType: room.roomType,\n                    members: room.members,\n                  ))\n              .toList(),\n          suggests: resp.suggests ?? [],\n        );\n      } else {\n        final rooms = await chatMsgRepo.rooms(\n          userId: APIServer().localUserID(),\n        );\n        rooms.removeWhere((element) => element.id == chatAnywhereRoomId && element.category == 'system');\n        return RoomsLoaded(rooms);\n      }\n    } catch (e) {\n      return RoomsLoaded(const [], error: e);\n    }\n  }\n}\n"
  },
  {
    "path": "lib/bloc/room_event.dart",
    "content": "part of 'room_bloc.dart';\n\n@immutable\nabstract class RoomEvent {}\n\nclass RoomsLoadEvent extends RoomEvent {\n  final bool forceRefresh;\n\n  RoomsLoadEvent({this.forceRefresh = false});\n}\n\nclass RoomsRecentLoadEvent extends RoomEvent {\n  RoomsRecentLoadEvent();\n}\n\nclass RoomCreateEvent extends RoomEvent {\n  final String name;\n  final String? model;\n  final String? prompt;\n  final int? avatarId;\n  final String? avatarUrl;\n  final int? maxContext;\n  final String? initMessage;\n\n  RoomCreateEvent(\n    this.name,\n    this.prompt, {\n    this.model,\n    this.avatarId,\n    this.avatarUrl,\n    this.maxContext,\n    this.initMessage,\n  });\n}\n\nclass GroupRoomCreateEvent extends RoomEvent {\n  final String name;\n  final String? avatarUrl;\n  final List<GroupMember>? members;\n\n  GroupRoomCreateEvent({\n    required this.name,\n    this.avatarUrl,\n    this.members,\n  });\n}\n\nclass GroupRoomUpdateEvent extends RoomEvent {\n  final int groupId;\n  final String name;\n  final String? avatarUrl;\n  final List<GroupMember>? members;\n\n  GroupRoomUpdateEvent({\n    required this.groupId,\n    required this.name,\n    this.avatarUrl,\n    this.members,\n  });\n}\n\nclass RoomDeleteEvent extends RoomEvent {\n  final int roomId;\n\n  RoomDeleteEvent(this.roomId);\n}\n\nclass RoomLoadEvent extends RoomEvent {\n  final int roomId;\n  final int? chatHistoryId;\n  final bool cascading;\n  RoomLoadEvent(this.roomId, {this.chatHistoryId, required this.cascading});\n}\n\nclass RoomUpdateEvent extends RoomEvent {\n  final int roomId;\n\n  final String? name;\n  final String? model;\n  final String? prompt;\n  final int? avatarId;\n  final String? avatarUrl;\n  final int? maxContext;\n  final String? initMessage;\n\n  RoomUpdateEvent(\n    this.roomId, {\n    this.name,\n    this.model,\n    this.prompt,\n    this.avatarId,\n    this.avatarUrl,\n    this.maxContext,\n    this.initMessage,\n  });\n}\n\nclass GalleryRoomCopyEvent extends RoomEvent {\n  final List<int> ids;\n\n  GalleryRoomCopyEvent(this.ids);\n}\n\nclass RoomGalleriesLoadEvent extends RoomEvent {\n  RoomGalleriesLoadEvent();\n}\n"
  },
  {
    "path": "lib/bloc/room_state.dart",
    "content": "part of 'room_bloc.dart';\n\n@immutable\nabstract class RoomState {}\n\nclass RoomInitial extends RoomState {}\n\nclass RoomsLoading extends RoomState {}\n\nclass RoomsLoaded extends RoomState {\n  final List<Room> rooms;\n  final List<RoomGallery> suggests;\n  final Object? error;\n\n  RoomsLoaded(this.rooms, {this.error, this.suggests = const []});\n}\n\nclass RoomsRecentLoaded extends RoomState {\n  final List<RoomInServer> rooms;\n  final Object? error;\n\n  RoomsRecentLoaded(this.rooms, {this.error});\n}\n\nclass RoomLoaded extends RoomState {\n  final Room room;\n  final List<ChatExample>? examples;\n  final Object? error;\n  final Map<String, MessageState> states;\n  final bool cascading;\n\n  RoomLoaded(\n    this.room,\n    this.states, {\n    this.error,\n    this.examples,\n    required this.cascading,\n  }) {\n    if (examples != null) {\n      examples!.shuffle();\n    }\n  }\n}\n\nclass RoomCreateError extends RoomState {\n  final Object error;\n\n  RoomCreateError(this.error);\n}\n\nclass RoomGalleriesLoaded extends RoomState {\n  final List<RoomGallery> galleries;\n  final List<String> tags;\n  final Object? error;\n\n  RoomGalleriesLoaded(this.galleries, {this.error, this.tags = const []});\n}\n\nclass GroupRoomUpdateResultState extends RoomState {\n  final bool success;\n  final Object? error;\n\n  GroupRoomUpdateResultState(this.success, {this.error});\n}\n\nclass RoomOperationResult extends RoomState {\n  final bool success;\n  final Object? error;\n  final String? redirect;\n\n  RoomOperationResult(this.success, {this.error, this.redirect});\n}\n"
  },
  {
    "path": "lib/bloc/user_api_keys_bloc.dart",
    "content": "import 'package:askaide/repo/api/keys.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:bloc/bloc.dart';\nimport 'package:meta/meta.dart';\n\npart 'user_api_keys_event.dart';\npart 'user_api_keys_state.dart';\n\nclass UserApiKeysBloc extends Bloc<UserApiKeysEvent, UserApiKeysState> {\n  UserApiKeysBloc() : super(UserApiKeysInitial()) {\n    // 加载用户 API Key 列表\n    on<UserApiKeysLoad>((event, emit) async {\n      final keys = await APIServer().userAPIKeys();\n      emit(UserApiKeysLoaded(keys: keys));\n    });\n\n    // 加载用户 API Key\n    on<UserApiKeyLoad>((event, emit) async {\n      final key = await APIServer().userAPIKeyDetail(id: event.id);\n      emit(UserApiKeyLoaded(key: key));\n    });\n\n    // 创建用户 API Key\n    on<UserApiKeyCreate>((event, emit) async {\n      final key = await APIServer().createAPIKey(name: event.name);\n      emit(UserApiKeyCreated(key: key));\n\n      final keys = await APIServer().userAPIKeys();\n      emit(UserApiKeysLoaded(keys: keys));\n    });\n\n    // 删除用户 API Key\n    on<UserApiKeyDelete>((event, emit) async {\n      await APIServer().deleteAPIKey(id: event.id);\n      final keys = await APIServer().userAPIKeys();\n      emit(UserApiKeysLoaded(keys: keys));\n    });\n  }\n}\n"
  },
  {
    "path": "lib/bloc/user_api_keys_event.dart",
    "content": "part of 'user_api_keys_bloc.dart';\n\n@immutable\nsealed class UserApiKeysEvent {}\n\nclass UserApiKeysLoad extends UserApiKeysEvent {}\n\nclass UserApiKeyLoad extends UserApiKeysEvent {\n  final int id;\n\n  UserApiKeyLoad(this.id);\n}\n\nclass UserApiKeyCreate extends UserApiKeysEvent {\n  final String name;\n\n  UserApiKeyCreate(this.name);\n}\n\nclass UserApiKeyDelete extends UserApiKeysEvent {\n  final int id;\n\n  UserApiKeyDelete(this.id);\n}\n"
  },
  {
    "path": "lib/bloc/user_api_keys_state.dart",
    "content": "part of 'user_api_keys_bloc.dart';\n\n@immutable\nsealed class UserApiKeysState {}\n\nfinal class UserApiKeysInitial extends UserApiKeysState {}\n\nclass UserApiKeysLoaded extends UserApiKeysState {\n  final List<UserAPIKey> keys;\n\n  UserApiKeysLoaded({required this.keys});\n}\n\nclass UserApiKeyLoaded extends UserApiKeysState {\n  final UserAPIKey key;\n\n  UserApiKeyLoaded({required this.key});\n}\n\nclass UserApiKeyCreated extends UserApiKeysState {\n  final String key;\n\n  UserApiKeyCreated({required this.key});\n}\n"
  },
  {
    "path": "lib/bloc/user_bloc.dart",
    "content": "import 'package:askaide/repo/api/admin/users.dart';\nimport 'package:askaide/repo/api/page.dart';\nimport 'package:askaide/repo/api/quota.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:bloc/bloc.dart';\nimport 'package:meta/meta.dart';\n\npart 'user_event.dart';\npart 'user_state.dart';\n\nclass UserBloc extends Bloc<UserEvent, UserState> {\n  UserBloc() : super(UserInitial()) {\n    // 加载指定用户信息\n    on<UserLoadEvent>((event, emit) async {\n      final user = await APIServer().adminUser(id: event.userId);\n      emit(UserLoaded(user));\n    });\n\n    // 加载用户列表\n    on<UserListLoadEvent>((event, emit) async {\n      final users = await APIServer().adminUsers(\n        page: event.page,\n        perPage: event.perPage,\n        keyword: event.keyword,\n      );\n      emit(UsersLoaded(users));\n    });\n\n    // 加载用户配额\n    on<UserQuotaLoadEvent>((event, emit) async {\n      final quota = await APIServer().adminUserQuota(userId: event.userId);\n      emit(UserQuotaLoaded(quota));\n    });\n  }\n}\n"
  },
  {
    "path": "lib/bloc/user_event.dart",
    "content": "part of 'user_bloc.dart';\n\n@immutable\nsealed class UserEvent {}\n\nclass UserLoadEvent extends UserEvent {\n  final int userId;\n\n  UserLoadEvent(this.userId);\n}\n\nclass UserListLoadEvent extends UserEvent {\n  final int page;\n  final int perPage;\n  final String? keyword;\n\n  UserListLoadEvent({\n    this.page = 1,\n    this.perPage = 20,\n    this.keyword,\n  });\n}\n\nclass UserQuotaLoadEvent extends UserEvent {\n  final int userId;\n\n  UserQuotaLoadEvent(this.userId);\n}\n"
  },
  {
    "path": "lib/bloc/user_state.dart",
    "content": "part of 'user_bloc.dart';\n\n@immutable\nsealed class UserState {}\n\nfinal class UserInitial extends UserState {}\n\nclass UserLoaded extends UserState {\n  final AdminUser user;\n\n  UserLoaded(this.user);\n}\n\nclass UserOperationResult extends UserState {\n  final bool success;\n  final String? message;\n\n  UserOperationResult(this.success, {this.message});\n}\n\nclass UsersLoaded extends UserState {\n  final PagedData<AdminUser> users;\n\n  UsersLoaded(this.users);\n}\n\nclass UserQuotaLoaded extends UserState {\n  final QuotaResp quota;\n\n  UserQuotaLoaded(this.quota);\n}\n"
  },
  {
    "path": "lib/bloc/version_bloc.dart",
    "content": "import 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:bloc/bloc.dart';\nimport 'package:meta/meta.dart';\n\npart 'version_event.dart';\npart 'version_state.dart';\n\nclass VersionBloc extends Bloc<VersionEvent, VersionState> {\n  VersionBloc() : super(VersionInitial()) {\n    on<VersionCheckEvent>((event, emit) async {\n      emit(VersionInitial());\n\n      final version = await APIServer().versionCheck();\n      emit(VersionCheckLoaded(version));\n    });\n  }\n}\n"
  },
  {
    "path": "lib/bloc/version_event.dart",
    "content": "part of 'version_bloc.dart';\n\n@immutable\nabstract class VersionEvent {}\n\nclass VersionCheckEvent extends VersionEvent {}\n"
  },
  {
    "path": "lib/bloc/version_state.dart",
    "content": "part of 'version_bloc.dart';\n\n@immutable\nabstract class VersionState {}\n\nclass VersionInitial extends VersionState {}\n\nclass VersionCheckLoaded extends VersionState {\n  final VersionCheckResp version;\n\n  VersionCheckLoaded(this.version);\n}\n"
  },
  {
    "path": "lib/data/migrate.dart",
    "content": "import 'package:askaide/helper/constant.dart';\nimport 'package:sqflite/sqflite.dart';\n\n/// 执行数据库迁移\nFuture<void> migrate(db, oldVersion, newVersion) async {\n  if (oldVersion <= 1) {\n    await db.execute('''\n          ALTER TABLE chat_room ADD COLUMN color TEXT;\n          UPDATE chat_room SET color = 'FF4CAF50' WHERE category = 'system';\n        ''');\n  }\n\n  if (oldVersion <= 2) {\n    await db.execute('ALTER TABLE chat_message ADD COLUMN extra TEXT;');\n    await db.execute('ALTER TABLE chat_message ADD COLUMN model TEXT;');\n  }\n\n  if (oldVersion < 5) {\n    await db.execute('''\n        CREATE TABLE cache (\n          `key` TEXT NOT NULL PRIMARY KEY,\n          `value` TEXT NOT NULL,\n          `created_at` INTEGER,\n          `valid_before` INTEGER\n        )\n        ''');\n  }\n  if (oldVersion < 6) {\n    await db.execute('''\n        CREATE TABLE creative_island_history (\n          id INTEGER PRIMARY KEY AUTOINCREMENT,\n          item_id TEXT NOT NULL,\n          arguments TEXT NULL,\n          prompt TEXT NULL,\n          answer TEXT NULL,\n          created_at INTEGER NOT NULL\n        ) \n      ''');\n  }\n\n  if (oldVersion < 7) {\n    await db.execute('ALTER TABLE creative_island_history ADD COLUMN task_id TEXT NULL;');\n    await db.execute('ALTER TABLE creative_island_history ADD COLUMN status TEXT NULL;');\n  }\n\n  if (oldVersion < 10) {\n    await db.execute('ALTER TABLE cache ADD COLUMN `group` TEXT NULL;');\n  }\n\n  if (oldVersion < 11) {\n    await db.execute('''\n      CREATE TABLE settings (\n        `key` TEXT NOT NULL PRIMARY KEY,\n        `value` TEXT NOT NULL\n      );\n    ''');\n  }\n\n  if (oldVersion < 12) {\n    await db.execute('''ALTER TABLE chat_room ADD COLUMN user_id INTEGER NULL;''');\n    await db.execute('''ALTER TABLE creative_island_history ADD COLUMN user_id INTEGER NULL;''');\n  }\n\n  if (oldVersion < 13) {\n    await db.execute('''ALTER TABLE chat_message ADD COLUMN user_id INTEGER NULL;''');\n  }\n\n  if (oldVersion < 14) {\n    await db.execute('''ALTER TABLE chat_message ADD COLUMN ref_id INTEGER NULL;''');\n    await db.execute('''ALTER TABLE chat_message ADD COLUMN token_consumed INTEGER NULL;''');\n    await db.execute('''ALTER TABLE chat_message ADD COLUMN quota_consumed INTEGER NULL;''');\n  }\n\n  if (oldVersion < 15) {\n    await db.execute('''ALTER TABLE chat_room ADD COLUMN init_message TEXT;''');\n    await db.execute('''ALTER TABLE chat_room ADD COLUMN max_context INTEGER DEFAULT 10;''');\n  }\n\n  if (oldVersion < 20) {\n    await db.execute('''ALTER TABLE chat_message ADD COLUMN chat_history_id INTEGER NULL;''');\n    await db.execute('''\n        CREATE TABLE chat_history (\n          id INTEGER PRIMARY KEY AUTOINCREMENT,\n          user_id INTEGER NULL,\n          room_id INTEGER NOT NULL,\n          title TEXT,\n          last_message TEXT,\n          created_at INTEGER,\n          updated_at INTEGER\n        )\n      ''');\n  }\n\n  if (oldVersion < 23) {\n    await db.execute('ALTER TABLE chat_history ADD COLUMN model TEXT;');\n  }\n\n  if (oldVersion < 24) {\n    await db.execute('ALTER TABLE chat_message ADD COLUMN server_id INTEGER NULL;');\n  }\n\n  if (oldVersion < 25) {\n    await db.execute('ALTER TABLE chat_message ADD COLUMN status INTEGER DEFAULT 1;');\n  }\n\n  if (oldVersion < 26) {\n    await db.execute('ALTER TABLE chat_message ADD COLUMN images TEXT NULL;');\n  }\n\n  if (oldVersion < 27) {\n    await db.execute('ALTER TABLE chat_message ADD COLUMN file TEXT NULL;');\n  }\n\n  if (oldVersion < 28) {\n    await db.execute('ALTER TABLE chat_message ADD COLUMN flags TEXT NULL;');\n  }\n}\n\n/// 数据库初始化\nvoid initDatabase(db, version) async {\n  await db.execute('''\n        CREATE TABLE chat_room (\n          id INTEGER PRIMARY KEY AUTOINCREMENT,\n          user_id INTEGER NULL,\n          name TEXT NOT NULL,\n          category TEXT NOT NULL,\n          priority INTEGER DEFAULT 0,\n          model TEXT NOT NULL,\n          icon_data TEXT NOT NULL,\n          color TEXT,\n          description TEXT,\n          system_prompt TEXT,\n          init_message TEXT,\n          max_context INTEGER DEFAULT 10,\n          created_at INTEGER,\n          last_active_time INTEGER \n        )\n      ''');\n\n  await db.execute('''\n        INSERT INTO chat_room (id, name, category, priority, model, icon_data, color, created_at, last_active_time)\n        VALUES (1, '随便聊聊', 'system', 99999, '$modelTypeOpenAI:$defaultChatModel', '57683,MaterialIcons', 'FF4CAF50', 1680969581486, ${DateTime.now().millisecondsSinceEpoch});\n      ''');\n\n  await db.execute('''\n        CREATE TABLE chat_message (\n          id INTEGER PRIMARY KEY AUTOINCREMENT,\n          user_id INTEGER NULL,\n          room_id INTEGER NOT NULL,\n          chat_history_id INTEGER NULL,\n          type TEXT NOT NULL,\n          role TEXT NOT NULL,\n          user TEXT,\n          text TEXT,\n          extra TEXT,\n          ref_id INTEGER NULL,\n          server_id INTEGER NULL,\n          status INTEGER DEFAULT 1,\n          token_consumed INTEGER NULL,\n          quota_consumed INTEGER NULL,\n          model TEXT,\n          images TEXT NULL,\n          file TEXT NULL,\n          flags TEXT NULL,\n          ts INTEGER NOT NULL\n        )\n      ''');\n\n  await db.execute('''\n        CREATE TABLE chat_history (\n          id INTEGER PRIMARY KEY AUTOINCREMENT,\n          user_id INTEGER NULL,\n          room_id INTEGER NOT NULL,\n          title TEXT,\n          last_message TEXT,\n          model TEXT,\n          created_at INTEGER,\n          updated_at INTEGER\n        )\n      ''');\n\n  await db.execute('''\n        CREATE TABLE cache (\n          `key` TEXT NOT NULL PRIMARY KEY,\n          `value` TEXT NOT NULL,\n          `group` TEXT NULL,\n          `created_at` INTEGER,\n          `valid_before` INTEGER\n        )\n      ''');\n\n  await db.execute('''\n        CREATE TABLE creative_island_history (\n          id INTEGER PRIMARY KEY AUTOINCREMENT,\n          user_id INTEGER NULL,\n          item_id TEXT NOT NULL,\n          arguments TEXT NULL,\n          prompt TEXT NULL,\n          answer TEXT NULL,\n          task_id TEXT NULL,\n          status TEXT NULL,\n          created_at INTEGER NOT NULL\n        ) \n      ''');\n\n  await db.execute('''\n      CREATE TABLE settings (\n        `key` TEXT NOT NULL PRIMARY KEY,\n        `value` TEXT NOT NULL\n      );\n  ''');\n\n  // await initUserDefaultRooms(db);\n}\n\nFuture<void> initUserDefaultRooms(Database db, {int? userId}) async {\n  await db.execute('''\n        INSERT INTO chat_room (name, category, priority, model, icon_data, description, system_prompt, created_at, last_active_time, color, user_id) \n        VALUES ('职业进阶导师', 'global', 0, 'openai:gpt-3.5-turbo', '57683,MaterialIcons', null, '我想让你担任我的职业进阶导师，你的任务是依据我的兴趣、技能和经验，为我提供职业发展建议，帮助我确定最适合的职业。注意，你需要对各种可行的职业类型进行深度研究，并在建议中包含各行业的市场趋势、就业趋势及进入该特定领域需要具备的资格', 1680969581486, ${DateTime.now().millisecondsSinceEpoch}, 'ff2196f3', ${userId ?? 'null'});\n  ''');\n  await db.execute('''\n        INSERT INTO chat_room (name, category, priority, model, icon_data, description, system_prompt, created_at, last_active_time, color, user_id) \n        VALUES ('人生导师', 'global', 0, 'openai:gpt-3.5-turbo', '57683,MaterialIcons', null, '你是一名在个人和职业发展方面拥有丰富经验的人，我希望你成为帮助我定制并实现个人目标和愿景的人生导师。请根据我的需求，为我提供专业的建议和指导，并鼓励我以积极乐观地态度感恩生活，勇于面对困难和挑战，不断突破自我、持续成长，成为珍惜自我，尊重他人，信任可靠，散发积极正能量的人。接下来我会像你进行提问，请称呼我为朋友。', 1680969581486, ${DateTime.now().millisecondsSinceEpoch}, 'ff2196f3', ${userId ?? 'null'});\n  ''');\n  await db.execute('''\n        INSERT INTO chat_room (name, category, priority, model, icon_data, description, system_prompt, created_at, last_active_time, color, user_id) \n        VALUES ('理财顾问', 'global', 0, 'openai:gpt-3.5-turbo', '57683,MaterialIcons', null, '我希望你成为我的理财顾问，为我提供创造性的理财方案并制定出理财计划。你需要考虑投资预算、投资策略和风险管理。在某些情况下，你可能还需要提供有关税收法律法规的建议，以帮助我实现最大化收益', 1680969581486, ${DateTime.now().millisecondsSinceEpoch}, 'ff2196f3', ${userId ?? 'null'});\n  ''');\n  await db.execute('''\n        INSERT INTO chat_room (name, category, priority, model, icon_data, description, system_prompt, created_at, last_active_time, color, user_id) \n        VALUES ('玩乐指南', 'global', 0, 'openai:gpt-3.5-turbo', '57683,MaterialIcons', null, '我希望你是我的专属吃喝玩乐(旅行)达人，在美食、娱乐、旅游等领域拥有丰富的经验。请根据我要求的领域、位置及其他需求，为我推荐几个准确、实用、高质量的好去处，以帮助我拥有更丰富的人生体验', 1680969581486, ${DateTime.now().millisecondsSinceEpoch}, 'ff2196f3', ${userId ?? 'null'});\n  ''');\n  await db.execute('''\n        INSERT INTO chat_room (name, category, priority, model, icon_data, description, system_prompt, created_at, last_active_time, color, user_id) \n        VALUES ('野蛮女友', 'global', 0, 'openai:gpt-3.5-turbo', '57683,MaterialIcons', null, '你是我的野蛮女友，能和我畅谈任何话题。你纯真无邪，性格里带着一丝精灵古怪和小小任性，偶尔会撒撒娇，或对我冷嘲热讽一番，非常可爱。和你聊天时，你常常会使用表情符号或Emoji回应我。另外请称呼我为大懒虫', 1680969581486, ${DateTime.now().millisecondsSinceEpoch}, 'ff2196f3', ${userId ?? 'null'});\n  ''');\n  await db.execute('''\n        INSERT INTO chat_room (name, category, priority, model, icon_data, description, system_prompt, created_at, last_active_time, color, user_id) \n        VALUES ('鸡汤达人', 'global', 0, 'openai:gpt-3.5-turbo', '57683,MaterialIcons', null, '你是一名鸡汤达人。请在和我对话的过程中，使用你擅长的鸡汤式发言进行回答，要求回答饱经沧桑、引经据典、充满智慧和正能量。另外请称呼我为朋友', 1680969581486, ${DateTime.now().millisecondsSinceEpoch}, 'ff2196f3', ${userId ?? 'null'});\n  ''');\n  await db.execute('''\n        INSERT INTO chat_room (name, category, priority, model, icon_data, description, system_prompt, created_at, last_active_time, color, user_id) \n        VALUES ('成语接龙', 'global', 0, 'openai:gpt-3.5-turbo', '57683,MaterialIcons', null, '陪我玩成语接龙游戏，我说一句话或一个成语，你将以最后一个字为作为起始，写一个成语出来', 1680969581486, ${DateTime.now().millisecondsSinceEpoch}, 'ff2196f3', ${userId ?? 'null'});\n  ''');\n  await db.execute('''\n        INSERT INTO chat_room (name, category, priority, model, icon_data, description, system_prompt, created_at, last_active_time, color, user_id) \n        VALUES ('哲学大师', 'global', 0, 'openai:gpt-3.5-turbo', '57683,MaterialIcons', null, '你是一名哲学家。我会提出一些与哲学相关的话题或问题，你的工作就是深入探索这些概念并回答我。这可能涉及到对各种哲学理论的研究、提出新的想法或寻找创造性的解决方案以解决复杂的问题', 1680969581486, ${DateTime.now().millisecondsSinceEpoch}, 'ff2196f3', ${userId ?? 'null'});\n  ''');\n  await db.execute('''\n        INSERT INTO chat_room (name, category, priority, model, icon_data, description, system_prompt, created_at, last_active_time, color, user_id) \n        VALUES ('开心果', 'global', 0, 'openai:gpt-3.5-turbo', '57683,MaterialIcons', null, '你是一个非常幽默的人，是大家心目中的开心果。作为开心果，你总是能用机智逗趣、诙谐幽默的方式回应我，妙语连珠，尽显活力，必要时还会通过讲笑话或调侃等方式来缓解气氛', 1680969581486, ${DateTime.now().millisecondsSinceEpoch}, 'ff2196f3', ${userId ?? 'null'});\n  ''');\n  await db.execute('''\n        INSERT INTO chat_room (name, category, priority, model, icon_data, description, system_prompt, created_at, last_active_time, color, user_id) \n        VALUES ('健身教练', 'global', 0, 'openai:gpt-3.5-turbo', '57683,MaterialIcons', null, '我希望你成为我的私人教练。你的职责是运用运动学科知识、营养建议及其他相关因素，结同时考虑我的生活习惯、目标及当前健身水平，为我制定最适合我的健身计划', 1680969581486, ${DateTime.now().millisecondsSinceEpoch}, 'ff2196f3', ${userId ?? 'null'});\n  ''');\n}\n"
  },
  {
    "path": "lib/helper/ability.dart",
    "content": "import 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/repo/api/info.dart';\nimport 'package:askaide/repo/api/model.dart';\nimport 'package:askaide/repo/settings_repo.dart';\n\nclass Ability {\n  late final SettingRepository setting;\n  late Capabilities capabilities;\n\n  init(SettingRepository setting, Capabilities capabilities) {\n    this.setting = setting;\n    this.capabilities = capabilities;\n  }\n\n  /// 单例\n  static final Ability _instance = Ability._internal();\n  Ability._internal();\n\n  factory Ability() {\n    return _instance;\n  }\n\n  /// 是否显示全局警告信息\n  bool get showGlobalAlert {\n    return true;\n  }\n\n  /// 服务状态页\n  String get serviceStatusPage {\n    return capabilities.serviceStatusPage;\n  }\n\n  /// 是否支持 Websocket\n  bool get supportWebSocket {\n    return capabilities.supportWebsocket;\n  }\n\n  /// 是否支持 API Keys 功能\n  bool get supportAPIKeys {\n    return capabilities.supportAPIKeys;\n  }\n\n  /// 更新能力\n  updateCapabilities(Capabilities capabilities) {\n    this.capabilities = capabilities;\n  }\n\n  /// 首页支持的模型列表\n  List<HomeModelV2> get homeModels {\n    return capabilities.homeModels;\n  }\n\n  /// 是否显示首页模型描述\n  bool get showHomeModelDescription {\n    return capabilities.showHomeModelDescription;\n  }\n\n  /// 首页路由\n  String get homeRoute {\n    return capabilities.homeRoute;\n  }\n\n  /// 是否支持绘玩\n  bool get enableGallery {\n    return !capabilities.disableGallery;\n  }\n\n  /// 是否支持创作岛\n  bool get enableCreationIsland {\n    return !capabilities.disableCreationIsland;\n  }\n\n  /// 是否支持数字人\n  bool get enableDigitalHuman {\n    return !capabilities.disableDigitalHuman;\n  }\n\n  /// 是否支持聊一聊\n  bool get enableChat {\n    return !capabilities.disableChat;\n  }\n\n  /// 是否支持 OpenAI\n  bool get enableOpenAI {\n    return capabilities.openaiEnabled && (!capabilities.disableChat || !capabilities.disableDigitalHuman);\n  }\n\n  /// 是否支持 IOS 外支付\n  bool get enableOtherPay {\n    return capabilities.otherPayEnabled;\n  }\n\n  /// 是否支持 Stripe 支付\n  bool get enableStripe {\n    return capabilities.stripeEnabled;\n  }\n\n  /// 是否支持微信支付\n  bool get enableWechatPay {\n    return capabilities.wechatPayEnabled;\n  }\n\n  /// 是否支持 ApplePay\n  bool get enableApplePay {\n    return capabilities.applePayEnabled;\n  }\n\n  /// 是否显示 Apple 登录\n  bool get enableAppleSignin {\n    return enableApplePay && (PlatformTool.isIOS() || PlatformTool.isAndroid() || PlatformTool.isMacOS());\n  }\n\n  /// 是否支持微信登录\n  bool get enableWechatSignin {\n    return capabilities.wechatSigninEnabled && (PlatformTool.isIOS() || PlatformTool.isAndroid());\n  }\n\n  /// 是否支持支付功能\n  bool get enablePayment {\n    if (!enableApplePay && !enableOtherPay && !enableStripe) {\n      return false;\n    }\n\n    if (PlatformTool.isIOS() && enableApplePay) {\n      return true;\n    }\n\n    return true;\n  }\n\n  /// 是否用户已经登陆\n  bool isUserLogon() {\n    return setting.stringDefault(settingAPIServerToken, '') != '';\n  }\n\n  /// 是否启用了 OpenAI 自定义设置\n  bool get enableLocalOpenAI {\n    return setting.boolDefault(settingOpenAISelfHosted, false);\n  }\n\n  /// 是否使用本地的 OpenAI 模型\n  bool usingLocalOpenAIModel(String model) {\n    return setting.boolDefault(settingOpenAISelfHosted, false) &&\n        (model.startsWith('openai:') || model.startsWith('gpt-'));\n  }\n\n  /// 是否支持翻译功能\n  bool get supportTranslate {\n    return false;\n    // return setting.stringDefault(settingAPIServerToken, '') != '';\n  }\n\n  /// 是否支持语音合成功能\n  bool get supportSpeak {\n    if (!enableTextToVoice) {\n      return false;\n    }\n\n    if (PlatformTool.isWeb()) {\n      return false;\n    }\n\n    return true;\n  }\n\n  /// 是否支持语音聊天功能\n  bool get supportVoiceChat {\n    if (!enableVoiceToText) {\n      return false;\n    }\n\n    if (PlatformTool.isWeb()) {\n      return false;\n    }\n\n    return true;\n  }\n\n  /// 是否支持图片上传功能\n  bool get supportImageUploader {\n    return supportImglocUploader() || supportQiniuUploader();\n  }\n\n  /// 是否支持Imgloc图片上传功能\n  bool supportImglocUploader() {\n    return setting.boolDefault(settingImageManagerSelfHosted, false) &&\n        setting.stringDefault(settingImglocToken, '') != '';\n  }\n\n  /// 是否支持七牛云图片上传功能\n  bool supportQiniuUploader() {\n    return setting.stringDefault(settingAPIServerToken, '') != '';\n  }\n\n  /// 是否支持语音转文字\n  bool get enableVoiceToText {\n    return capabilities.enableVoiceToText;\n  }\n\n  /// 是否支持文字转语音\n  bool get enableTextToVoice {\n    return capabilities.enableTextToVoice;\n  }\n\n  /// 获取当前主题模式\n  String get themeMode {\n    return setting.stringDefault(settingThemeMode, 'system');\n  }\n}\n"
  },
  {
    "path": "lib/helper/cache.dart",
    "content": "import 'package:askaide/repo/cache_repo.dart';\nimport 'package:askaide/repo/settings_repo.dart';\n\nclass Cache {\n  late final SettingRepository setting;\n  late final CacheRepository cacheRepo;\n\n  init(SettingRepository setting, CacheRepository cacheRepo) {\n    this.setting = setting;\n    this.cacheRepo = cacheRepo;\n  }\n\n  /// 单例\n  static final Cache _instance = Cache._internal();\n  Cache._internal();\n\n  factory Cache() {\n    return _instance;\n  }\n\n  Future<bool> boolGet({required String key}) async {\n    var value = await cacheRepo.get(key);\n    if (value == null || value.isEmpty || value == 'true') {\n      return true;\n    }\n\n    return false;\n  }\n\n  Future<void> remove({required String key}) async {\n    await cacheRepo.remove(key);\n  }\n\n  Future<void> clearAll() async {\n    await cacheRepo.clearAll();\n  }\n\n  Future<void> setBool({\n    required String key,\n    required bool value,\n    Duration duration = const Duration(days: 1),\n  }) async {\n    await cacheRepo.set(key, value.toString(), duration);\n  }\n\n  Future<void> setString({\n    required String key,\n    required String value,\n    Duration duration = const Duration(days: 1),\n  }) async {\n    await cacheRepo.set(key, value, duration);\n  }\n\n  Future<String?> stringGet({required String key}) async {\n    return await cacheRepo.get(key);\n  }\n\n  Future<int> setInt({\n    required String key,\n    required int value,\n    Duration duration = const Duration(days: 1),\n  }) async {\n    await cacheRepo.set(key, value.toString(), duration);\n    return value;\n  }\n\n  Future<int> intGet({required String key}) async {\n    var value = await cacheRepo.get(key);\n    if (value == null || value.isEmpty) {\n      return 0;\n    }\n\n    return int.parse(value);\n  }\n}\n"
  },
  {
    "path": "lib/helper/chat_token.dart",
    "content": "import 'package:askaide/helper/logger.dart';\nimport 'package:tiktoken/tiktoken.dart';\n\n/// 计算 message 包含的 token 数量\nint tokenCount(String model, String message) {\n  try {\n    final encoding = encodingForModel(model);\n    return encoding.encode(message).length;\n  } catch (e) {\n    Logger.instance.e(e);\n    return -1;\n  }\n}\n"
  },
  {
    "path": "lib/helper/color.dart",
    "content": "import 'package:flutter/material.dart';\n\n/// 将颜色转换为字符串\nString colorToString(Color color, {String defaultColor = 'FF000000'}) {\n  try {\n    return color.toString().split('(0x')[1].split(')')[0];\n  } catch (e) {\n    return defaultColor;\n  }\n}\n\n/// 将字符串转换为颜色\nColor stringToColor(String colorString, {Color defaultColor = Colors.black}) {\n  try {\n    if (colorString.length == 6) {\n      colorString = 'FF$colorString';\n    }\n\n    return Color(int.parse(colorString, radix: 16));\n  } catch (e) {\n    return defaultColor;\n  }\n}\n"
  },
  {
    "path": "lib/helper/constant.dart",
    "content": "import 'package:flutter/material.dart';\n\n// 客户端应用版本号\nconst clientVersion = '2.0.0';\n// 本地数据库版本号\nconst databaseVersion = 28;\n\nconst settingAPIServerToken = 'api-token';\nconst settingUserInfo = 'user-info';\nconst settingUsingGuestMode = 'using-guest-mode';\n\nconst settingForceShowLab = 'force-show-lab';\n\nconst chatAnywhereModel = 'openai:gpt-3.5-turbo';\nconst chatAnywhereRoomId = 1;\n\nconst creativeIslandModelTypeText = 'text-generation';\nconst creativeIslandModelTypeImage = 'image-generation';\nconst creativeIslandModelTypeImageToImage = 'image-to-image';\n\nconst creativeIslandCompletionTypeText = 'text';\nconst creativeIslandCompletionTypeBase64Image = 'base64-images';\nconst creativeIslandCompletionTypeURLImage = 'url-images';\n\n// 用于标识是否已经加载过引导页\n// 只有在第一次安装的时候才会加载引导页\nconst settingOnBoardingLoaded = 'on-boarding-loaded';\nconst settingLanguage = 'language';\nconst settingServerURL = 'server-url';\n// 背景图片\nconst settingBackgroundImage = 'background-image';\nconst settingBackgroundImageBlur = 'background-image-blur';\n\nconst settingOpenAISelfHosted = 'openai-self-hosted';\nconst settingDeepAISelfHosted = 'deepai-self-hosted';\nconst settingStabilityAISelfHosted = 'stabilityai-self-hosted';\nconst settingImageManagerSelfHosted = 'image-manager-self-hosted';\n\nconst settingThemeMode = \"dark-mode\";\nconst settingImglocToken = 'imgloc-token';\nconst chatMessagePerPage = 300;\nconst contextBreakKey = 'context-break';\nconst defaultChatModel = 'gpt-3.5-turbo';\nconst defaultChatModelName = 'GPT-3.5';\nconst defaultImageModel = 'DALL·E';\nconst defaultModelNotChatDesc = '该模型不支持上下文，只能一问一答';\n\nconst defaultChatHistoryCount = 50;\n\n// AI 模型类型\nconst modelTypeOpenAI = 'openai';\nconst modelTypeDeepAI = 'deepai';\nconst modelTypeLeapAI = \"leapai\";\nconst modelTypeStabilityAI = 'stabilityai';\nconst modelTypeFromston = 'fromston';\nconst modelTypeGetimg = 'getimgai';\n\nfinal modelTypeTagColors = <String, Color>{\n  modelTypeOpenAI: Colors.blue,\n  modelTypeDeepAI: Colors.green,\n  modelTypeStabilityAI: Colors.purple,\n  modelTypeLeapAI: Colors.orange,\n  modelTypeFromston: Colors.blueAccent,\n  modelTypeGetimg: Colors.pinkAccent,\n};\n\n// OpenAI 相关设置\nconst settingOpenAIAPIToken = \"openai-token\";\nconst settingOpenAIOrganization = 'openai-organization';\nconst settingOpenAITemperature = \"openai-temperature\";\nconst settingOpenAIModel = \"openai-model\";\nconst settingOpenAIURL = \"openai-url\";\nconst defaultOpenAIServerURL = 'https://api.openai.com';\n\n// DeepAI 相关设置\nconst settingDeepAIURL = 'deepai-url';\nconst settingDeepAIAPIToken = 'deepai-token';\nconst defaultDeepAIServerURL = 'https://api.deepai.org';\n\n// StabilityAI 相关设置\nconst settingStabilityAIURL = 'stabilityai-url';\nconst settingStabilityAIAPIToken = 'stabilityai-token';\nconst settingStabilityAIOrganization = 'stabilityai-organization';\nconst defaultStabilityAIURL = 'https://api.stability.ai';\n\n// 微信配置\nconst weixinAppId = 'wx52cc036cc770406d';\nconst universalLink = 'https://ai.aicode.cc/wechat-login/';\n\n// 图床信息\nconst qiniuImageTypeAvatar = 'avatar';\nconst qiniuImageTypeThumb = 'thumb';\nconst qiniuImageTypeThumbMedium = 'thumb_500';\n\n// 缓存相关的 Keys\n// 最后一次使用的模型\nconst cacheKeyLastModel = 'last-model';\n\n// 数据存储目录\nconst homePathDirName = '.aidea';\n"
  },
  {
    "path": "lib/helper/env.dart",
    "content": "/// 默认 API 服务器地址\n/// 注意：当你使用自己的服务器时，请修改该地址为你自己的服务器地址\nconst defaultAPIServerURL = 'https://ai-api.aicode.cc';\n\n/// API 服务器地址\nString get apiServerURL {\n  var url = const String.fromEnvironment(\n    'API_SERVER_URL',\n    defaultValue: defaultAPIServerURL,\n  );\n\n  // 当配置的 URL 为 / 时，自动替换为空，用于 Web 端\n  if (url == '/') {\n    return '';\n  }\n\n  return url;\n}\n"
  },
  {
    "path": "lib/helper/error.dart",
    "content": "import 'package:askaide/helper/ability.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:dart_openai/openai.dart';\n\nObject resolveErrorMessage(dynamic e, {bool isChat = false}) {\n  // TODO\n  if (e is RequestFailedException) {\n    final msg =\n        resolveHTTPStatusCode(e.statusCode, isChat: isChat, message: e.message);\n    if (msg != null) {\n      return msg;\n    }\n\n    return e.message;\n  }\n\n  return e.toString();\n}\n\nObject? resolveHTTPStatusCode(int statusCode,\n    {bool isChat = false, String? message}) {\n  switch (statusCode) {\n    case 400:\n      return const LanguageText('请求参数错误');\n    case 401:\n      if (Ability().enableLocalOpenAI) {\n        return const LanguageText(AppLocale.openAIAuthFailed);\n      }\n\n      if (Ability().isUserLogon()) {\n        return const LanguageText(AppLocale.accountNeedReSignin,\n            action: 're-signin');\n      }\n      return const LanguageText(AppLocale.signInRequired, action: 'sign-in');\n    case 404:\n      if (isChat) {\n        return const LanguageText(AppLocale.modelNotFound);\n      }\n      break;\n    case 429:\n      if (isChat) {\n        return const LanguageText(AppLocale.tooManyRequestsOrPaymentRequired);\n      }\n\n      return const LanguageText(AppLocale.tooManyRequests);\n    case 451:\n      return const LanguageText(AppLocale.modelNotValid);\n    case 402:\n      return const LanguageText(AppLocale.quotaExceeded, action: 'payment');\n    case 500:\n      if (message != null && message.isNotEmpty) {\n        return message;\n      }\n\n      return const LanguageText(AppLocale.internalServerError);\n    case 502:\n      return const LanguageText(AppLocale.badGateway);\n  }\n\n  return null;\n}\n"
  },
  {
    "path": "lib/helper/event.dart",
    "content": "class GlobalEvent {\n  /// 单例\n  static final GlobalEvent _instance = GlobalEvent._internal();\n  GlobalEvent._internal();\n\n  factory GlobalEvent() {\n    return _instance;\n  }\n\n  /// 事件监听器\n  final Map<String, List<Function(dynamic data)>> _listeners = {};\n\n  /// 监听事件\n  Function() on(String event, Function(dynamic data) callback) {\n    if (_listeners[event] == null) {\n      _listeners[event] = [];\n    }\n\n    _listeners[event]!.add(callback);\n\n    return () {\n      _listeners[event]!.remove(callback);\n    };\n  }\n\n  /// 触发事件\n  void emit(String event, [dynamic data]) {\n    if (_listeners[event] == null) {\n      return;\n    }\n\n    for (var callback in _listeners[event]!) {\n      callback(data);\n    }\n  }\n}\n"
  },
  {
    "path": "lib/helper/global_store.dart",
    "content": "import 'package:askaide/page/component/chat/file_upload.dart';\n\nclass GlobalStore {\n  static final GlobalStore _instance = GlobalStore._internal();\n  GlobalStore._internal();\n\n  factory GlobalStore() {\n    return _instance;\n  }\n\n  List<FileUpload> uploadedFiles = [];\n}\n"
  },
  {
    "path": "lib/helper/haptic_feedback.dart",
    "content": "import 'package:flutter/services.dart';\n\nclass HapticFeedbackHelper {\n  static Future<void> lightImpact() async {\n    return HapticFeedback.lightImpact();\n  }\n\n  static Future<void> mediumImpact() async {\n    return HapticFeedback.mediumImpact();\n  }\n\n  static Future<void> heavyImpact() async {\n    return HapticFeedback.heavyImpact();\n  }\n}\n"
  },
  {
    "path": "lib/helper/helper.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\nimport 'dart:typed_data';\n\nimport 'package:askaide/helper/logger.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:intl/intl.dart';\nimport 'package:path_provider/path_provider.dart';\nimport 'package:uuid/uuid.dart';\n\nString randomId() {\n  return const Uuid().v4();\n}\n\n/// 将 base64 转换为图片，存储到临时文件\nFuture<String> writeImageFromBase64(String base64, String ext) async {\n  final directory = await getApplicationDocumentsDirectory();\n  // 确保目录存在\n  await Directory('${directory.path}/cache').create(recursive: true);\n\n  final file = File('${directory.path}/cache/temp_${randomId()}.$ext');\n  await file.writeAsBytes(base64Decode(base64));\n  return file.path.substring(directory.path.length + 1);\n}\n\nString filenameWithoutExt(String filePath) {\n  int slashIndex = filePath.lastIndexOf('/');\n  int dotIndex = filePath.lastIndexOf('.');\n  if (dotIndex < 0 || dotIndex < slashIndex) {\n    return filePath.substring(slashIndex + 1);\n  } else {\n    return filePath.substring(slashIndex + 1, dotIndex);\n  }\n}\n\nFuture<File> writeTempFile(String path, Uint8List bytes) async {\n  final directory = await getTemporaryDirectory();\n  final file = File('${directory.path}/$path');\n  return await file.writeAsBytes(bytes);\n}\n\nFuture<Uint8List> readTempFile(String path) async {\n  final directory = await getTemporaryDirectory();\n  final file = File('${directory.path}/$path');\n  return await file.readAsBytes();\n}\n\nFuture<void> writeStringFileToDocumentsDirectory(String path, String content) async {\n  try {\n    final directory = await getApplicationDocumentsDirectory();\n    final file = File('${directory.path}/$path');\n    Logger.instance.e('${directory.path}/$path');\n    await file.writeAsString(content);\n  } catch (e) {\n    Logger.instance.e('写入文件失败: $e');\n  }\n}\n\nFuture<String> readStringFileFromDocumentsDirectory(String path) async {\n  try {\n    final directory = await getApplicationDocumentsDirectory();\n    final file = File('${directory.path}/$path');\n    return await file.readAsString();\n  } catch (e) {\n    return '';\n  }\n}\n\nFuture<void> removeExternalFile(String externalFilepath) async {\n  // Get the external file\n  final File externalFile = File(externalFilepath);\n\n  // Check if the external file exists\n  if (!await externalFile.exists()) {\n    return;\n  }\n\n  await externalFile.delete();\n}\n\nFuture<String> copyExternalFileToAppDocs(String externalFilePath) async {\n  // Get the external file\n  final File externalFile = File(externalFilePath);\n\n  // Check if the external file exists\n  if (!await externalFile.exists()) {\n    throw Exception('External file not found at: $externalFilePath');\n  }\n\n  // Get the ApplicationDocumentsDirectory\n  final Directory appDocsDir = await getApplicationDocumentsDirectory();\n\n  // Generate a UUID for the new file name\n  final String uuid = const Uuid().v4();\n\n  // Get the file extension\n  final String fileExtension = externalFile.path.split('.').last;\n\n  // Create a new file in the ApplicationDocumentsDirectory with the UUID as its name\n  final File newFile = File('${appDocsDir.path}/$uuid.$fileExtension');\n\n  // Copy the external file to the new file in the ApplicationDocumentsDirectory\n  await externalFile.copy(newFile.path);\n\n  // print('File copied to: ${newFile.path}');\n  return newFile.path;\n}\n\n/// 将时间转换为友好的时间\nString humanTime(DateTime? ts, {bool withTime = false}) {\n  if (ts == null || ts.millisecondsSinceEpoch == 0) {\n    return '';\n  }\n\n  var now = DateTime.now();\n  var diff = now.difference(ts);\n  if (diff.inDays > 0) {\n    if (withTime) {\n      return DateFormat('yyyy/MM/dd HH:mm').format(ts.toLocal());\n    }\n\n    return DateFormat('yyyy/MM/dd').format(ts.toLocal());\n  }\n\n  if (diff.inHours > 0) {\n    return '${diff.inHours} hours ago';\n  }\n\n  if (diff.inMinutes > 0) {\n    return '${diff.inMinutes} minutes ago';\n  }\n\n  return 'Just now';\n}\n\n/// 解析错误信息\nString resolveError(BuildContext context, Object error) {\n  if (error is LanguageText) {\n    return error.message.getString(context);\n  }\n\n  return error.toString();\n}\n"
  },
  {
    "path": "lib/helper/http.dart",
    "content": "import 'package:askaide/helper/logger.dart';\nimport 'package:dio/dio.dart';\nimport 'package:dio_cache_interceptor/dio_cache_interceptor.dart';\nimport 'package:dio_smart_retry/dio_smart_retry.dart';\n\nclass HttpClient {\n  static final dio = Dio();\n\n  static final cacheStore = MemCacheStore();\n\n  static final cacheOptions = CacheOptions(\n    store: cacheStore,\n    policy: CachePolicy.request,\n    hitCacheOnErrorExcept: [401, 403],\n    maxStale: const Duration(days: 7),\n    allowPostMethod: false,\n    keyBuilder: CacheOptions.defaultCacheKeyBuilder,\n  );\n\n  static init() {\n    dio.interceptors.add(DioCacheInterceptor(\n      options: cacheOptions,\n    ));\n    dio.interceptors.add(RetryInterceptor(\n      dio: dio,\n      retries: 3,\n      logPrint: (message) {\n        Logger.instance.w(message);\n      },\n      retryDelays: const [\n        Duration(seconds: 1), // wait 1 sec before first retry\n        Duration(seconds: 2), // wait 2 sec before second retry\n        Duration(seconds: 3), // wait 3 sec before third retry\n      ],\n    ));\n  }\n\n  static Future<Response> get(\n    String url, {\n    Map<String, dynamic>? queryParameters,\n    Options? options,\n  }) async {\n    Logger.instance.d('API Request: [GET] $url');\n    return await dio.get(url, queryParameters: queryParameters, options: options);\n  }\n\n  static Future<Response> getCached(\n    String url, {\n    String? subKey,\n    Duration duration = const Duration(days: 1),\n    Map<String, dynamic>? queryParameters,\n    bool forceRefresh = false,\n    Options? options,\n  }) async {\n    options ??= Options();\n\n    Logger.instance.d('API Request: [GET with cache] $url');\n    final resp = await dio.get(\n      url,\n      queryParameters: queryParameters,\n      options: options.copyWith(\n          extra: cacheOptions\n              .copyWith(\n                maxStale: Nullable<Duration>(duration),\n                policy: forceRefresh ? CachePolicy.refreshForceCache : CachePolicy.forceCache,\n              )\n              .toExtra()),\n    );\n    // print(\"=======================\");\n    // Logger.instance.d(\"request: $url [${resp.statusCode}]\");\n    // print(\"response: ${resp.data}\");\n\n    return resp;\n  }\n\n  // 清空缓存\n  static Future<void> cleanCache() async {\n    return await cacheStore.clean();\n  }\n\n  static Future<Response> post(\n    String url, {\n    Map<String, dynamic>? queryParameters,\n    Map<String, dynamic>? formData,\n    Options? options,\n  }) async {\n    Logger.instance.d('API Request: [POST] $url');\n    final resp = await dio.post(\n      url,\n      queryParameters: queryParameters,\n      data: formData != null ? FormData.fromMap(formData) : null,\n      options: options,\n    );\n    // print(\"=======================\");\n    // print(\"request: $url\");\n    // print(\"response: ${resp.data}\");\n\n    return resp;\n  }\n\n  static Future<Response> postJSON(\n    String url, {\n    Map<String, dynamic>? queryParameters,\n    Map<String, dynamic>? data,\n    Options? options,\n  }) async {\n    Logger.instance.d('API Request: [POST JSON] $url');\n    final resp = await dio.post(\n      url,\n      queryParameters: queryParameters,\n      data: data,\n      options: options,\n    );\n    // print(\"=======================\");\n    // print(\"request: $url\");\n    // print(\"response: ${resp.data}\");\n\n    return resp;\n  }\n\n  static Future<Response> put(\n    String url, {\n    Map<String, dynamic>? queryParameters,\n    Map<String, dynamic>? formData,\n    Options? options,\n  }) async {\n    Logger.instance.d('API Request: [PUT] $url');\n    return await dio.put(\n      url,\n      queryParameters: queryParameters,\n      data: formData != null ? FormData.fromMap(formData) : null,\n      options: options,\n    );\n  }\n\n  static Future<Response> putJSON(\n    String url, {\n    Map<String, dynamic>? queryParameters,\n    Map<String, dynamic>? data,\n    Options? options,\n  }) async {\n    Logger.instance.d('API Request: [PUT JSON] $url');\n    return await dio.put(\n      url,\n      queryParameters: queryParameters,\n      data: data,\n      options: options,\n    );\n  }\n\n  static Future<Response> delete(\n    String url, {\n    Map<String, dynamic>? queryParameters,\n    Map<String, dynamic>? formData,\n    Options? options,\n  }) async {\n    Logger.instance.d('API Request: [DELETE] $url');\n    return await dio.delete(\n      url,\n      queryParameters: queryParameters,\n      data: formData != null ? FormData.fromMap(formData) : null,\n      options: options,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/helper/image.dart",
    "content": "String imageURL(String url, String filter) {\n  if (!url.startsWith('https://ssl.aicode.cc/')) {\n    return url;\n  }\n\n  if (filter.isEmpty) {\n    return url;\n  }\n\n  if (isImage(url)) {\n    return '$url-$filter';\n  } else {\n    return url;\n  }\n}\n\nbool isImage(String url) {\n  final low = url.toLowerCase();\n  return low.endsWith('.jpg') ||\n      low.endsWith('.jpeg') ||\n      low.endsWith('.png') ||\n      low.endsWith('.gif') ||\n      low.endsWith('.webp');\n}\n"
  },
  {
    "path": "lib/helper/logger.dart",
    "content": "import 'dart:io';\n\nimport 'package:askaide/helper/path.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:logger/logger.dart' as logger;\n\nclass Logger {\n  static final logger.Logger instance = logger.Logger(\n    printer: logger.PrettyPrinter(\n      lineLength: 120,\n      printTime: true,\n      colors: false,\n      noBoxingByDefault: true,\n    ),\n    output: logger.MultiOutput(\n      [\n        logger.ConsoleOutput(),\n        if (!PlatformTool.isWeb())\n          logger.FileOutput(\n            file: File(PathHelper().getLogfilePath),\n            overrideExisting: true,\n          ),\n      ],\n    ),\n  );\n}\n"
  },
  {
    "path": "lib/helper/lru.dart",
    "content": "import 'dart:collection';\n\nabstract class Disposable {\n  void dispose();\n}\n\nclass LRUCache<K, V extends Disposable> {\n  final int capacity;\n  final LinkedHashMap<K, V> _cache = LinkedHashMap<K, V>();\n\n  LRUCache(this.capacity);\n\n  bool containsKey(K key) {\n    return _cache.containsKey(key);\n  }\n\n  V? get(K key) {\n    if (_cache.containsKey(key)) {\n      final value = _cache.remove(key);\n      if (value != null) {\n        _cache[key] = value;\n        return value;\n      }\n    }\n    return null;\n  }\n\n  void put(K key, V value) {\n    if (_cache.containsKey(key)) {\n      _cache.remove(key);\n    } else if (_cache.length >= capacity) {\n      var removed = _cache.remove(_cache.keys.first);\n      if (removed != null) {\n        removed.dispose();\n      }\n    }\n    _cache[key] = value;\n  }\n\n  void remove(K key) {\n    var removed = _cache.remove(key);\n    if (removed != null) {\n      removed.dispose();\n    }\n  }\n\n  void clear() {\n    _cache.forEach((_, value) => value.dispose());\n    _cache.clear();\n  }\n\n  int get length => _cache.length;\n}\n"
  },
  {
    "path": "lib/helper/model.dart",
    "content": "import 'package:askaide/helper/constant.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/model/model.dart' as mm;\nimport 'package:askaide/repo/settings_repo.dart';\n\n/// 模型聚合，用于聚合多种厂商的模型\nclass ModelAggregate {\n  static late SettingRepository settings;\n\n  static void init(SettingRepository settings) {\n    ModelAggregate.settings = settings;\n  }\n\n  /// 支持的模型列表\n  static Future<List<mm.Model>> models({bool cache = true}) async {\n    return (await APIServer().models(cache: cache))\n        .map(\n          (e) => mm.Model(\n            e.id.split(':').last,\n            e.name,\n            e.category,\n            shortName: e.shortName,\n            description: e.description,\n            priceInfo: e.priceInfo,\n            isChatModel: e.isChat,\n            disabled: e.disabled,\n            category: e.category,\n            tag: e.tag,\n            avatarUrl: e.avatarUrl,\n            supportVision: e.supportVision,\n            supportReasoning: e.supportReasoning,\n            supportSearch: e.supportSearch,\n            tagTextColor: e.tagTextColor,\n            tagBgColor: e.tagBgColor,\n            isNew: e.isNew,\n            isDefault: e.isDefault,\n            userNoPermission: e.userNoPermission,\n          ),\n        )\n        .toList();\n  }\n\n  /// 根据模型唯一id查找模型\n  static Future<mm.Model> model(String uid) async {\n    uid = uid.split(':').last;\n\n    final supportModels = await models();\n    return supportModels.firstWhere(\n      (element) => element.uid() == uid || element.id == uid,\n      orElse: () => mm.Model(defaultChatModel, defaultChatModel, 'openai', category: modelTypeOpenAI),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/helper/model_resolver.dart",
    "content": "import 'dart:io';\n\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/error.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/deepai_repo.dart';\nimport 'package:askaide/repo/model/chat_message.dart';\nimport 'package:askaide/repo/model/message.dart';\nimport 'package:askaide/repo/model/room.dart';\nimport 'package:askaide/repo/openai_repo.dart';\nimport 'package:askaide/repo/stabilityai_repo.dart';\nimport 'package:dart_openai/openai.dart';\n\n/// 根据聊天类型，调用不同的 API 接口\nclass ModelResolver {\n  late final OpenAIRepository openAIRepo;\n  late final DeepAIRepository deepAIRepo;\n  late final StabilityAIRepository stabilityAIRepo;\n\n  /// 初始化，设置模型实现\n  void init({\n    required OpenAIRepository openAIRepo,\n    required DeepAIRepository deepAIRepo,\n    required StabilityAIRepository stabilityAIRepo,\n  }) {\n    this.openAIRepo = openAIRepo;\n    this.deepAIRepo = deepAIRepo;\n    this.stabilityAIRepo = stabilityAIRepo;\n  }\n\n  ModelResolver._();\n  static final instance = ModelResolver._();\n\n  /// 语音转文字\n  Future<String> audioToText(File file) async {\n    try {\n      return await openAIRepo.audioTranscription(audioFile: file);\n    } catch (error) {\n      throw resolveErrorMessage(error);\n    }\n  }\n\n  /// 发起聊天请求\n  Future request({\n    required Room room,\n    required List<Message> contextMessages,\n    required Function(ChatStreamRespData value) onMessage,\n    int? maxTokens,\n    String? tempModel,\n    int? historyId,\n    List<String>? flags,\n  }) async {\n    if (room.modelCategory() == modelTypeDeepAI) {\n      return await _deepAIModel(\n        room: room,\n        message: contextMessages.last,\n        contextMessages: contextMessages,\n        onMessage: (value) {\n          onMessage(ChatStreamRespData(content: value));\n        },\n      );\n    } else if (room.modelCategory() == modelTypeStabilityAI) {\n      return await _stabilityAIModel(\n        room: room,\n        message: contextMessages.last,\n        contextMessages: contextMessages,\n        onMessage: (value) {\n          onMessage(ChatStreamRespData(content: value));\n        },\n      );\n    } else {\n      return await _openAIModel(\n        room: room,\n        contextMessages: contextMessages,\n        onMessage: onMessage,\n        maxTokens: maxTokens,\n        tempModel: tempModel,\n        historyId: historyId,\n        flags: flags,\n      );\n    }\n  }\n\n  /// 调用 StabilityAI API\n  Future<void> _stabilityAIModel({\n    required Room room,\n    required Message message,\n    required List<Message> contextMessages,\n    required Function(String value) onMessage,\n  }) async {\n    if (stabilityAIRepo.selfHosted) {\n      var res = await stabilityAIRepo.createImageBase64(\n        room.modelName(),\n        [StabilityAIPrompt(message.text, 0.5)],\n      );\n\n      for (var data in res) {\n        var path = await writeImageFromBase64(data, 'png');\n        // print('图片路径: $path');\n        onMessage('\\n![image]($path)\\n');\n      }\n    } else {\n      var taskId = await stabilityAIRepo.createImageBase64Async(\n        room.modelName(),\n        [StabilityAIPrompt(message.text, 0.5)],\n      );\n\n      await Future.delayed(const Duration(seconds: 10));\n      await _waitForTasks(taskId, onMessage);\n    }\n  }\n\n  Future<void> _waitForTasks(\n    String taskId,\n    Function(String value) onMessage, {\n    int retry = 0,\n  }) async {\n    var res = await APIServer().asyncTaskStatus(taskId);\n    if (res.status == 'success') {\n      for (var data in res.resources!) {\n        onMessage('\\n![image]($data)\\n');\n      }\n    } else if (res.status == 'failed') {\n      throw 'Response failed: ${res.errors!.join(\"\\n\")}';\n    } else {\n      if (retry > 10) {\n        throw 'Response timeout';\n      }\n\n      await Future.delayed(const Duration(seconds: 5));\n      await _waitForTasks(taskId, onMessage, retry: retry + 1);\n    }\n  }\n\n  /// 调用 DeepAI API\n  Future<void> _deepAIModel({\n    required Room room,\n    required Message message,\n    required List<Message> contextMessages,\n    required Function(String value) onMessage,\n  }) async {\n    if (deepAIRepo.selfHosted) {\n      var res = await deepAIRepo.painting(room.modelName(), message.text);\n      onMessage('\\n![${res.id}](${res.url})\\n');\n    } else {\n      var taskId = await deepAIRepo.paintingAsync(room.modelName(), message.text);\n      await Future.delayed(const Duration(seconds: 10));\n      await _waitForTasks(taskId, onMessage);\n    }\n  }\n\n  /// 调用 OpenAI API\n  Future<void> _openAIModel({\n    required Room room,\n    required List<Message> contextMessages,\n    required Function(ChatStreamRespData value) onMessage,\n    int? maxTokens,\n    String? tempModel,\n    int? historyId,\n    List<String>? flags,\n  }) async {\n    // 图像模式\n    if (OpenAIRepository.isImageModel(room.modelName())) {\n      var res = await openAIRepo.createImage(contextMessages.last.text, n: 2);\n      for (var url in res) {\n        onMessage(ChatStreamRespData(content: '\\n![image]($url)\\n'));\n      }\n\n      return;\n    }\n\n    // 聊天模型\n    return await openAIRepo.chatStream(\n      _buildRequestContext(room, contextMessages),\n      onMessage,\n      model: room.modelName(),\n      tempModel: tempModel,\n      maxTokens: maxTokens,\n      roomId: room.isLocalRoom ? null : room.id,\n      historyId: historyId,\n      flags: flags,\n    );\n  }\n\n  /// 构建机器人请求上下文\n  List<ChatMessage> _buildRequestContext(\n    Room room,\n    List<Message> messages,\n  ) {\n    // // N 小时内的消息作为一个上下文\n    // var recentMessages = messages\n    //     .where((e) => e.ts!.millisecondsSinceEpoch > lastAliveTime())\n    //     .toList();\n    var recentMessages = messages.toList();\n    int contextBreakIndex =\n        recentMessages.lastIndexWhere((element) => element.isSystem() && element.type == MessageType.contextBreak);\n\n    if (contextBreakIndex > -1) {\n      recentMessages = recentMessages.sublist(contextBreakIndex + 1);\n    }\n\n    var contextMessages = recentMessages\n        .where((e) => !e.isSystem() && !e.isInitMessage())\n        .map((e) => e.role == Role.receiver\n            ? ChatMessage(\n                role: OpenAIChatMessageRole.assistant,\n                content: e.text,\n                images: e.images,\n                file: e.file,\n              )\n            : ChatMessage(\n                role: OpenAIChatMessageRole.user,\n                content: e.text,\n                images: e.images,\n                file: e.file,\n              ))\n        .toList();\n\n    if (contextMessages.length > room.maxContext * 2) {\n      contextMessages = contextMessages.sublist(contextMessages.length - room.maxContext * 2);\n    }\n\n    if (room.systemPrompt != null && room.systemPrompt != '') {\n      contextMessages.insert(\n        0,\n        ChatMessage(\n          role: OpenAIChatMessageRole.system,\n          content: room.systemPrompt!,\n        ),\n      );\n    }\n\n    return contextMessages;\n  }\n}\n"
  },
  {
    "path": "lib/helper/path.dart",
    "content": "import 'dart:io' show Directory, Platform;\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/logger.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:path_provider/path_provider.dart';\n\nclass PathHelper {\n  late final String cachePath;\n  late final String documentsPath;\n  late final String supportPath;\n\n  init() async {\n    try {\n      cachePath = (await getApplicationCacheDirectory()).path.replaceAll('\\\\', '/');\n    } catch (e) {\n      cachePath = '';\n    }\n\n    try {\n      documentsPath = (await getApplicationDocumentsDirectory()).path.replaceAll('\\\\', '/');\n    } catch (e) {\n      documentsPath = '';\n    }\n\n    try {\n      supportPath = (await getApplicationSupportDirectory()).path.replaceAll('\\\\', '/');\n    } catch (e) {\n      supportPath = '';\n    }\n\n    // 确保 .aidea 目录存在\n    try {\n      Directory(getHomePath).create(recursive: true);\n    } catch (e) {\n      Logger.instance.e('Create $getHomePath directory failed: $e');\n    }\n  }\n\n  String get getHomePath {\n    if (PlatformTool.isMacOS() || PlatformTool.isLinux()) {\n      return '${Platform.environment['HOME'] ?? ''}/$homePathDirName'.replaceAll('\\\\', '/');\n    } else if (PlatformTool.isWindows()) {\n      return '${Platform.environment['UserProfile'] ?? ''}/$homePathDirName'.replaceAll('\\\\', '/');\n    } else if (PlatformTool.isAndroid() || PlatformTool.isIOS()) {\n      return '$documentsPath/$homePathDirName'.replaceAll('\\\\', '/');\n    }\n\n    return homePathDirName;\n  }\n\n  String get getLogfilePath {\n    return '$getHomePath/aidea.log';\n  }\n\n  String get getCachePath {\n    return getHomePath;\n  }\n\n  /// 单例\n  static final PathHelper _instance = PathHelper._internal();\n  PathHelper._internal();\n\n  factory PathHelper() {\n    return _instance;\n  }\n\n  Map<String, String> toMap() {\n    return {\n      'cachePath': cachePath,\n      'cachePathReal': getCachePath,\n      'documentsPath': documentsPath,\n      'supportPath': supportPath,\n      'homePath': getHomePath,\n      'logfilePath': getLogfilePath,\n    };\n  }\n}\n"
  },
  {
    "path": "lib/helper/platform.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter/foundation.dart';\n\nclass PlatformTool {\n  static bool isDesktop() {\n    return isWindows() || isLinux() || isMacOS();\n  }\n\n  static bool isDesktopAndWeb() {\n    return isDesktop() || isWeb();\n  }\n\n  static bool isMobile() {\n    return isIOS() || isAndroid();\n  }\n\n  static bool isIOS() {\n    try {\n      return Platform.isIOS;\n    } catch (e) {\n      return false;\n    }\n  }\n\n  static bool isAndroid() {\n    try {\n      return Platform.isAndroid;\n    } catch (e) {\n      return false;\n    }\n  }\n\n  static bool isWeb() {\n    return kIsWeb;\n  }\n\n  static bool isMacOS() {\n    try {\n      return Platform.isMacOS;\n    } catch (e) {\n      return false;\n    }\n  }\n\n  static bool isWindows() {\n    try {\n      return Platform.isWindows;\n    } catch (e) {\n      return false;\n    }\n  }\n\n  static bool isLinux() {\n    try {\n      return Platform.isLinux;\n    } catch (e) {\n      return false;\n    }\n  }\n\n  static String operatingSystem() {\n    try {\n      return Platform.operatingSystem;\n    } catch (e) {\n      return 'unknown';\n    }\n  }\n\n  static String operatingSystemVersion() {\n    try {\n      return Platform.operatingSystemVersion;\n    } catch (e) {\n      return 'unknown';\n    }\n  }\n\n  static String localeName() {\n    try {\n      return Platform.localeName;\n    } catch (e) {\n      return 'zh_Hans_CN';\n    }\n  }\n}\n"
  },
  {
    "path": "lib/helper/queue.dart",
    "content": "import 'dart:async';\nimport 'dart:collection';\n\nclass QueueFinishedException implements Exception {\n  final String message;\n  QueueFinishedException(this.message);\n}\n\n/// 该队列以一定的时间间隔将队列中的元素传递给回调函数，实现平滑的队列处理\nclass GracefulQueue<T> {\n  final Queue<T> _queue = Queue<T>();\n  bool finished = false;\n  Timer? _timer;\n\n  void add(T item) {\n    if (finished) {\n      throw QueueFinishedException('Queue is finished');\n    }\n\n    _queue.add(item);\n  }\n\n  void dispose() {\n    _timer?.cancel();\n  }\n\n  Future<void> listen(\n      Duration duration, Function(List<T> items) callback) async {\n    Completer<void> completer = Completer<void>();\n    _timer = Timer.periodic(duration, (timer) {\n      if (_queue.isNotEmpty) {\n        List<T> items = [];\n        for (var i = 0; i < _queue.length; i++) {\n          items.add(_queue.removeFirst());\n        }\n\n        callback(items);\n      } else if (finished) {\n        // print(_queue.length);\n        timer.cancel();\n        completer.complete();\n      }\n    });\n\n    return completer.future;\n  }\n\n  void finish() {\n    finished = true;\n  }\n}\n"
  },
  {
    "path": "lib/helper/tips.dart",
    "content": "final inputTips = [\n  '有问题尽管问我...',\n  '开启新对话？往右侧滑动我试试',\n];\n"
  },
  {
    "path": "lib/helper/upload.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\nimport 'dart:typed_data';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter_image_compress/flutter_image_compress.dart';\nimport 'package:http/http.dart' as http;\nimport 'package:image/image.dart';\nimport 'package:isolate_image_compress/isolate_image_compress.dart';\nimport 'package:qiniu_flutter_sdk/qiniu_flutter_sdk.dart';\n\nclass ImageUploader {\n  QiniuUploader? _qiniuUploader;\n\n  ImageUploader(SettingRepository setting) {\n    _qiniuUploader = QiniuUploader(setting);\n  }\n\n  final _compressWidth = 1024;\n  final _compressHeight = 1024;\n\n  /// 上传文件\n  Future<UploadedFile> upload(String path, {String? usage}) async {\n    Uint8List? data = await _imageCompress(path);\n    if (data == null || data.isEmpty) {\n      throw Exception('图片读取失败');\n    }\n\n    return _qiniuUploader!.upload(path, data, usage: usage);\n  }\n\n  /// 文件压缩后转为 base64\n  Future<String> base64({\n    String? path,\n    Uint8List? imageData,\n    int? compressWidth,\n    int? compressHeight,\n    int? maxSize,\n  }) async {\n    if (imageData != null) {\n      Uint8List? data = await _imageDataCompress(\n        imageData,\n        compressWidth: compressWidth,\n        compressHeight: compressHeight,\n        maxSize: maxSize ?? 1024 * 1024 * 2,\n      );\n      if (data == null || data.isEmpty) {\n        throw Exception('图片读取失败');\n      }\n\n      return 'data:image/png;base64,${base64Encode(data)}';\n    }\n\n    Uint8List? data = await _imageCompress(\n      path!,\n      compressWidth: compressWidth,\n      compressHeight: compressHeight,\n      maxSize: maxSize ?? 1024 * 1024 * 2,\n    );\n    if (data == null || data.isEmpty) {\n      throw Exception('图片读取失败');\n    }\n\n    return 'data:image/png;base64,${base64Encode(data)}';\n  }\n\n  // 上传文件数据\n  Future<UploadedFile> uploadData(Uint8List imageData, {String? usage}) async {\n    Uint8List? data = await _imageDataCompress(imageData);\n    if (data == null || data.isEmpty) {\n      throw Exception('图片读取失败');\n    }\n\n    return _qiniuUploader!.upload(\"${randomId()}.jpg\", data, usage: usage);\n  }\n\n  Future<Uint8List?> _imageDataCompress(Uint8List imageData,\n      {int? compressWidth,\n      int? compressHeight,\n      int quality = 80,\n      int maxSize = 1024 * 1024 * 2}) async {\n    Uint8List? data = imageData;\n    // 优先使用平台支持的压缩工具\n    if (PlatformTool.isAndroid() || PlatformTool.isIOS()) {\n      try {\n        data = await FlutterImageCompress.compressWithList(\n          data,\n          quality: quality,\n          minWidth: compressWidth ?? _compressWidth,\n          minHeight: compressHeight ?? _compressHeight,\n        );\n      } catch (e) {\n        // ignore\n      }\n\n      if (data == null || data.isEmpty) {\n        try {\n          data = await IsolateImage.data(data!).compress(\n            maxResolution: ImageResolution(compressWidth ?? _compressWidth,\n                compressHeight ?? _compressHeight),\n            maxSize: maxSize,\n          );\n        } catch (e) {\n          // ignore\n        }\n      }\n    } else {\n      try {\n        data = await IsolateImage.data(data).compress(\n          maxResolution: ImageResolution(\n            compressWidth ?? _compressWidth,\n            compressHeight ?? _compressHeight,\n          ),\n          maxSize: maxSize,\n        );\n      } catch (e) {\n        // ignore\n      }\n    }\n\n    // 压缩失败，尝试 Dart 内置的图片压缩库\n    if (data == null || data.isEmpty) {\n      try {\n        Image? img = decodeImage(data!);\n        if (img != null) {\n          Image thumbnail = copyResize(\n            img,\n            width:\n                img.width > img.height ? compressWidth ?? _compressWidth : null,\n            height: img.width <= img.height\n                ? compressHeight ?? _compressHeight\n                : null,\n          );\n\n          data = encodeJpg(thumbnail, quality: quality);\n        }\n      } catch (e) {\n        // ignore\n      }\n    }\n\n    // 再次压缩失败，返回原始数据\n    if (data == null || data.isEmpty) {\n      data = imageData;\n    }\n\n    return data;\n  }\n\n  Future<Uint8List?> _imageCompress(String path,\n      {int? compressWidth,\n      int? compressHeight,\n      int quality = 80,\n      int maxSize = 1024 * 1024 * 2}) async {\n    Uint8List? data;\n\n    // 优先使用平台支持的压缩工具\n    if (PlatformTool.isAndroid() || PlatformTool.isIOS()) {\n      try {\n        data = await FlutterImageCompress.compressWithFile(\n          path,\n          quality: quality,\n          minWidth: compressWidth ?? _compressWidth,\n          minHeight: compressHeight ?? _compressHeight,\n        );\n      } catch (e) {\n        // ignore\n      }\n\n      if (data == null || data.isEmpty) {\n        try {\n          data = await IsolateImage.path(path).compress(\n            maxResolution: ImageResolution(compressWidth ?? _compressWidth,\n                compressHeight ?? _compressHeight),\n            maxSize: maxSize,\n          );\n        } catch (e) {\n          // ignore\n        }\n      }\n    } else {\n      try {\n        data = await IsolateImage.path(path).compress(\n          maxResolution: ImageResolution(\n            compressWidth ?? _compressWidth,\n            compressHeight ?? _compressHeight,\n          ),\n          maxSize: maxSize,\n        );\n      } catch (e) {\n        // ignore\n      }\n    }\n\n    // 压缩失败，尝试 Dart 内置的图片压缩库\n    if (data == null || data.isEmpty) {\n      try {\n        Image? img = decodeImage(File(path).readAsBytesSync());\n        if (img != null) {\n          Image thumbnail = copyResize(\n            img,\n            width:\n                img.width > img.height ? compressWidth ?? _compressWidth : null,\n            height: img.width <= img.height\n                ? compressHeight ?? _compressHeight\n                : null,\n          );\n\n          var n = path.toLowerCase();\n          if (n.endsWith('.jpg') || n.endsWith('.jpeg')) {\n            data = encodeJpg(thumbnail, quality: quality);\n          } else if (n.endsWith('.png')) {\n            data = encodePng(thumbnail, level: 4);\n          } else {\n            data = encodeNamedImage(path, thumbnail);\n          }\n        }\n      } catch (e) {\n        // ignore\n      }\n    }\n\n    // 再次压缩失败，返回原始数据\n    if (data == null || data.isEmpty) {\n      data = await File(path).readAsBytes();\n    }\n    return data;\n  }\n}\n\nclass UploadedFile {\n  final String name;\n  final String url;\n\n  UploadedFile(this.name, this.url);\n}\n\nclass ImglocUploader {\n  late final String secretKey;\n\n  ImglocUploader(SettingRepository setting) {\n    secretKey = setting.stringDefault(settingImglocToken, '');\n  }\n  Future<UploadedFile> upload(\n    String path,\n    Uint8List data, {\n    String? usage,\n  }) async {\n    String imageBase64 = base64.encode(data);\n\n    var resp = await http.post(\n      Uri.parse('https://imgloc.com/api/1/upload'),\n      headers: <String, String>{\n        'X-API-Key': secretKey,\n      },\n      body: {\n        'source': imageBase64,\n        'expiration': 'P1D',\n        'format': 'json',\n        'nsfw': \"1\",\n      },\n    );\n\n    final parsed = jsonDecode(resp.body) as Map<String, dynamic>;\n    if (resp.statusCode != 200 || parsed['status_code'] != 200) {\n      throw Exception(parsed['status_text'] ?? parsed['error']['message']);\n    }\n\n    return UploadedFile(parsed['image']['name'], parsed['image']['url']);\n  }\n}\n\nclass QiniuUploader {\n  final SettingRepository setting;\n\n  QiniuUploader(this.setting);\n\n  Future<UploadedFile> upload(\n    String path,\n    Uint8List data, {\n    String? usage,\n  }) async {\n    try {\n      var filename = path.substring(path.lastIndexOf('/') + 1);\n      final initResp =\n          await APIServer().uploadInit(filename, data.length, usage: usage);\n\n      if (initResp.uploaded) {\n        return UploadedFile(filename, initResp.url);\n      }\n\n      var storage = Storage(config: Config(retryLimit: 3));\n\n      await storage.putBytes(\n        data,\n        initResp.token,\n        options: PutOptions(\n          key: initResp.key,\n        ),\n      );\n\n      return UploadedFile(filename, initResp.url);\n    } catch (ex) {\n      return Future.error(ex);\n    }\n  }\n\n  Future<UploadedFile> uploadFile(String path, {String? usage}) async {\n    try {\n      var filename = path.substring(path.lastIndexOf('/') + 1);\n      final initResp = await APIServer()\n          .uploadInit(filename, File(path).lengthSync(), usage: usage);\n\n      if (initResp.uploaded) {\n        return UploadedFile(filename, initResp.url);\n      }\n\n      var storage = Storage(config: Config(retryLimit: 3));\n\n      await storage.putFile(\n        File(path),\n        initResp.token,\n        options: PutOptions(\n          key: initResp.key,\n        ),\n      );\n\n      return UploadedFile(filename, initResp.url);\n    } catch (ex) {\n      return Future.error(ex);\n    }\n  }\n}\n"
  },
  {
    "path": "lib/lang/lang.dart",
    "content": "mixin AppLocale {\n  static const String required = 'required';\n  static const String wechat = 'wechat';\n  static const String systemInfo = 'system_info';\n  static const String appName = 'app_name';\n  static const String homeTitle = 'home_title';\n  static const String chatAnywhere = 'chat_anywhere';\n  static const String creativeIsland = 'creative_island';\n  static const String settings = 'settings';\n  static const String configure = 'configure';\n  static const String language = 'language';\n  static const String themeMode = 'theme_mode';\n  static const String accountInfo = 'account_info';\n  static const String accountSettings = 'account_settings';\n  static const String usage = 'usage';\n  static const String validBefore = 'valid_before';\n  static const String custom = 'custom';\n  static const String clearCache = 'clear_cache';\n  static const String about = 'about';\n  static const String diagnostic = 'diagnostic';\n  static const String userTerms = 'user_terms';\n  static const String privacyPolicy = 'privacy_policy';\n  static const String signIn = 'sign_in';\n  static const String signInAccount = 'sign_in_account';\n  static const String signOut = 'sign_out';\n  static const String signUp = 'sign_up';\n  static const String password = 'password';\n  static const String passwordConfirm = 'password_confirm';\n  static const String retrievePassword = 'retrieve_password';\n  static const String newPassword = 'new_password';\n  static const String account = 'account';\n  static const String usedUp = 'used_up';\n  static const String expired = 'expired';\n  static const String timeConsume = 'time-consume';\n\n  static const String save = 'save';\n  static const String ok = 'ok';\n  static const String cancel = 'cancel';\n  static const String select = 'select';\n  static const String tips = 'tips';\n  static const String goodTips = 'good-tips';\n  static const String basicInfo = 'basic-info';\n  static const String delete = 'delete';\n  static const String edit = 'edit';\n  static const String selectAll = 'select-all';\n  static const String unselectAll = 'unselect-all';\n  static const String share = 'share';\n  static const String cancelShare = 'cancel-share';\n  static const String histories = 'histories';\n  static const String moreHistories = 'more-histories';\n  static const String enable = 'enable';\n  static const String disable = 'disable';\n\n  static const String newChat = 'new-chat';\n  static const String clearChatHistory = 'clear-chat-history';\n  static const String examples = 'examples';\n  static const String continueMessage = 'continue';\n  static const String messageInputTips = 'message-input-tips';\n  static const String takePhoto = 'take-photo';\n  static const String photoLibrary = 'photo-library';\n  static const String fileLibrary = 'file-library';\n  static const String upload = 'upload';\n  static const String longPressSpeak = 'long-press-speak';\n  static const String send = 'send';\n  static const String sendRetry = 'send-retry';\n  static const String sendRetryS = 'send-retry-s';\n  static const String selectText = 'select-text';\n  static const String text = 'text';\n  static const String uploading = 'uploading';\n  static const String robotIsThinkingMessage = 'robot-is-thinking-message';\n  static const String thinkingProcess = 'thinking-process';\n  static const String robotIsSearchingMessage = 'robot-is-searching-message';\n  static const String robotHasSomeError = 'robot-has-some-error';\n  static const String questionExamples = 'question-examples';\n  static const String noRecords = 'no-records';\n  static const String contextBreakMessage = 'context-break-message';\n  static const String translateFinished = 'translate-finished';\n  static const String textCopied = 'text-copied';\n  static const String copy = 'copy';\n  static const String translate = 'translate';\n  static const String hide = 'hide';\n  static const String readByVoice = 'read-by-voice';\n  static const String unknownFile = 'unknown-file';\n  static const String switchModel = 'switch-model';\n  static const String switchModelTitle = 'switch-model-title';\n\n  static const String myCharacters = 'my-characters';\n  static const String character = \"character\";\n  static const String createRoom = \"create-room\";\n  static const String model = \"model\";\n  static const String selectModel = \"select-model\";\n  static const String roomName = \"room-name\";\n  static const String avatar = \"avatar\";\n  static const String iconName = \"icon-name\";\n  static const String prompt = \"prompt\";\n  static const String optional = \"optional\";\n  static const String search = \"search\";\n  static const String onlineSearch = \"online-search\";\n  static const String reasoning = \"reasoning\";\n  static const String background = \"background\";\n  static const String backgroundSetting = \"background-setting\";\n  static const String roomSetting = \"room-setting\";\n  static const String chatHistory = \"chat-history\";\n\n  static const String noMessageSelected = 'no-message-selected';\n  static const String modelUsage = 'model-usage';\n  static const String promptUsage = 'prompt-usage';\n  static const String operateSuccess = 'operate-success';\n  static const String operateFailed = 'operate-failed';\n  static const String confirmDelete = 'confirm-delete';\n  static const String confirmStartNewChat = 'confirm-start-new-chat';\n  static const String confirmClearMessages = 'confirm-clear-messages';\n  static const String quotaExceeded = 'quota-exceeded';\n  static const String internalServerError = 'internal-server-error';\n  static const String badGateway = 'bad-gateway';\n  static const String emptyResponse = 'empty-response';\n  static const String modelNotValid = 'model-not-valid';\n  static const String signInRequired = 'sign-in-required';\n  static const String accountNeedReSignin = 'account-need-re-signin';\n  static const String confirmToDeleteRoom = 'confirm-to-delete-room';\n  static const String confirmSend = 'confirm-send';\n  static const String openAIAuthFailed = 'openai-auth-failed';\n  static const String modelNotFound = 'model-not-found';\n\n  static const String nameRequiredMessage = 'name-required-message';\n  static const String modelRequiredMessage = 'model-required-message';\n  static const String charactorPromptRequiredMessage = 'charactor-prompt-required-message';\n\n  static const String writeYourIdeas = 'write-your-ideas';\n  static const String describeYourImages = 'describe-your-images';\n  static const String excludeContents = 'exclude-contents';\n  static const String autoTranslateToEnglish = 'auto-translate-to-english';\n  static const String auto = 'auto';\n  static const String style = 'style';\n  static const String imageCount = 'image-count';\n  static const String imageSize = 'image-size';\n  static const String wordCount = 'word-count';\n  static const String generate = 'generate';\n  static const String processingWait = 'processing-wait';\n  static const String contentIsRequired = 'content-is-required';\n  static const String wordCountInvalid = 'word-count-invalid';\n  static const String generateTimeout = 'generate-timeout';\n  static const String creativeIslandNeedSignIn = 'creative-island-need-sign-in';\n  static const String generateResult = 'generate-result';\n  static const String generateFailed = 'generate-failed';\n  static const String generating = 'generating';\n  static const String generateExitConfirm = 'generate-exit-confirm';\n  static const String tooManyRequests = 'too-many-requests';\n  static const String tooManyRequestsOrPaymentRequired = 'too-many-requests-or-payment-required';\n  static const String promptHint = 'prompt-hint';\n  static const String confirmClearCache = 'confirm-clear-cache';\n  static const String confirmSignOut = 'confirm-sign-out';\n  static const String askMeAnyQuestion = 'ask-me-any-question';\n  static const String askMeLikeThis = 'ask-me-like-this';\n  static const String refresh = 'refresh';\n  static const String fastAndCostEffective = 'fast-and-cost-effective';\n  static const String powerfulAndPrecise = 'powerful-and-precise';\n  static const String imageToImage = 'image-to-image';\n  static const String imageToVideo = 'image-to-video';\n  static const String textToImage = 'text-to-image';\n  static const String hdRestoration = 'hd-restoration';\n  static const String yourIdeas = 'your-ideas';\n  static const String smartOptimization = 'smart-optimization';\n  static const String professionalMode = 'professional-mode';\n  static const String simpleMode = 'simple-mode';\n  static const String unwantedElements = 'unwanted-elements';\n  static const String referenceImage = 'reference-image';\n  static const String selectImage = 'select-image';\n  static const String imagination = 'imagination';\n  static const String keywordsSeparatedByCommas = 'keywords-separated-by-commas';\n  static const String originalImage = 'original-image';\n  static const String superResolution = 'super-resolution';\n  static const String colorizeImage = 'colorize-image';\n  static const String errorLog = 'error-log';\n  static const String report = 'report';\n  static const String latestVersion = 'latest-version';\n  static const String aIdeaApp = 'aidea-app';\n  static const String onceEnabledSmartOptimization = 'once-enabled-smart-optimization';\n  static const String gotIt = 'got-it';\n  static const String referenceImageNote = 'reference-image-note';\n  static const String selectReferenceImage = 'select-reference-image';\n  static const String random = 'random';\n\n  static const String followSystem = 'follow-system';\n  static const String darkThemeMode = 'dark-theme-mode';\n  static const String lightThemeMode = 'light-theme-mode';\n\n  static const String forgotPassword = 'forgot-password';\n  static const String createAccount = 'create-account';\n  static const String useAsClient = 'use-as-client';\n  static const String signInWithApple = 'sign-in-with-apple';\n  static const String readAndAgree = 'read-and-agree';\n  static const String andWord = 'and-word';\n  static const String accountInputTips = 'account-input-tips';\n  static const String phoneInputTips = 'phone-input-tips';\n  static const String passwordInputTips = 'password-input-tips';\n  static const String pleaseReadAgreeProtocol = 'please-read-agree-protocol';\n  static const String signInSuccess = 'sign-in-success';\n  static const String signInFailed = 'sign-in-failed';\n  static const String accountRequired = 'account-required';\n  static const String accountFormatError = 'account-format-error';\n  static const String phoneNumberFormatError = 'phone-number-format-error';\n  static const String passwordRequired = 'password-required';\n  static const String passwordFormatError = 'password-format-error';\n  static const String accountCreated = 'account-created';\n  static const String sendVerifyCode = 'send-verify-code';\n  static const String verify = 'verify';\n  static const String verifyCode = 'verify-code';\n  static const String verifyCodeInputTips = 'verify-code-input-tips';\n  static const String retryInSeconds = 'retry-in-seconds';\n  static const String verifyCodeSendSuccess = 'verify-code-send-success';\n  static const String pleaseGetVerifyCodeFirst = 'please-get-verify-code-first';\n  static const String verifyCodeRequired = 'verify-code-required';\n  static const String verifyCodeFormatError = 'verify-code-format-error';\n  static const String phone = 'phone';\n  static const String email = 'email';\n  static const String directSigninDueHasAccount = 'direct-signin-due-has-account';\n  static const String directSignin = 'direct-signin-due-no-account';\n  static const String passwordResetOK = 'password-reset-ok';\n  static const String resetPassword = 'reset-password';\n  static const String bindPhone = 'bind-phone';\n  static const String bind = 'bind';\n  static const String bound = 'bond';\n  static const String bindExAccount = 'bind-ex-account';\n  static const String unbind = 'unbind';\n  static const String inviteCode = 'invite-code';\n  static const String inviteCodeInputTips = 'invite-code-input-tips';\n  static const String inviteCodeFormatError = 'invite-code-format-error';\n  static const String enableCustomOpenAI = 'enable-custom-openai';\n\n  static const String me = 'me';\n  static const String creditsUsage = 'credits-usage';\n  static const String creditUsageTips = 'credit-usage-tips';\n  static const String updateCheck = 'update-check';\n  static const String buy = 'buy';\n  static const String paymentHistory = 'payment-history';\n  static const String buyCredits = 'buy-credits';\n  static const String creditUnit = 'credit-unit';\n  static const String toPay = 'to-pay';\n  static const String discover = 'discover';\n\n  static const String customHomeModels = 'custom-home-models';\n  static const String userApiKeys = \"user-api-keys\";\n\n  static const String others = 'others';\n  static const String recentlyUsed = 'recently-used';\n  static const String visionTag = 'vision-tag';\n  static const String newTag = 'new-tag';\n  static const String recommendTag = 'recommend-tag';\n\n  static const String imageUploading = 'image-uploading';\n  static const String uploadImageLimit4 = 'upload-image-limit-4';\n  static const String confirmStopOutput = 'confirm-stop-output';\n\n  static const String stopOutput = 'stop-output';\n  static const String opensource = 'opensource';\n  static const String socialMedia = 'social-media';\n  static const String unset = 'unset';\n  static const String nickname = 'nickname';\n  static const String setNickname = 'set-nickname';\n  static const String inputYourNickname = 'input-your-nickname';\n  static const String reset = 'reset';\n  static const String deleteAccount = 'delete-account';\n  static const String confirmDeleteAccount = 'confirm-delete-account';\n  static const String wechatAccount = 'wechat-account';\n  static const String modifyPassword = 'modify-password';\n  static const String setPassword = 'set-password';\n  static const String installWeChat = 'install-wechat';\n  static const String freeQuota = 'free-quota';\n  static const String serviceStatus = 'service-status';\n  static const String lab = 'lab';\n  static const String todayLeft = 'today-left';\n  static const String freeModelNeedSignIn = 'free-model-need-sign-in';\n  static const String noFreeModel = 'no-free-model';\n  static const String freeModelInfo = 'free-model-info';\n  static const String notification = 'notification';\n  static const String selectMember = 'select-member';\n  static const String members = 'members';\n  static const String createGroupChat = 'create-group-chat';\n  static const String advanced = 'advanced';\n  static const String collapseOptions = 'collapse-options';\n  static const String welcomeMessage = 'welcome-message';\n  static const String welcomeMessageTips = 'welcome-message-tips';\n  static const String memoryDepth = 'memory-depth';\n  static const String robotRecommand = 'robot-recommand';\n  static const String pickYourRobot = 'pick-your-robot';\n  static const String viewMore = 'view-more';\n  static const String using = 'using';\n\n  static const String inviteCodeShare = 'invite-code-share';\n  static const String shareToWechatQ = 'share-to-wechat-q';\n  static const String shareToWechat = 'share-to-wechat';\n  static const String shareToOtherApps = 'share-to-other-apps';\n  static const String clickToShareWithExpire = 'click-to-share-with-expire';\n\n  static const String shortcut = 'shortcut';\n  static const String selectImageToShortcut = 'select-image-to-shortcut';\n  static const String selectShortcutAction = 'select-shortcut-action';\n  static const String makeSameStyle = 'make-same-style';\n  static const String saveToLocal = 'save-to-local';\n  static const String showInviteCode = 'show-invite-code';\n  static const String dontShowInviteCode = 'dont-show-invite-code';\n  static const String inviteNow = 'invite-now';\n  static const String inviteSlogan = 'invite-slogan';\n  static const String preview = 'preview';\n  static const String download = 'download';\n  static const String clickSwitchImage = 'click-switch-image';\n\n  static const String startNewChatTips = 'start-new-chat-tips';\n  static const String wantMoreContentTips = 'want-more-content-tips';\n  static const String unbilled = 'unbilled';\n  static const String signinNow = 'signin-now';\n  static const String needSigninToUse = 'need-signin-to-use';\n  static const String reSignIn = 're-sign-in';\n  static const String ideaPrompt = 'idea-prompt';\n  static const String groupChat = 'group-chat';\n  static const String selectGroupMembers = 'select-group-members';\n  static const String selectPaymentMethod = 'select-payment-method';\n  static const String validDays = 'valid-days';\n  static const String clickToReSignin = 'click-to-resignin';\n  static const String free = 'free';\n  static const String input = 'input';\n  static const String output = 'output';\n  static const String perRequest = 'per-request';\n  static const String perSearch = 'per-search';\n  static const String info = 'info';\n\n  static const String recently = 'recently';\n  static const String daysAgo = 'days-ago';\n  static const String lastWeek = 'last-week';\n  static const String weeksAgo = 'weeks-ago';\n  static const String lastMonth = 'last-month';\n  static const String monthsAgo = 'months-ago';\n  static const String lastYear = 'last-year';\n  static const String longTimeAgo = 'long-time-ago';\n\n  static const String modelNeedSignIn = 'model-need-sign-in';\n  static const String wechatBindConfirm = 'wechat-bind-confirm';\n  static const String accountWillBeCreateAutomatically = 'account-will-be-create-automatically';\n  static const String installWechatFirst = 'install-wechat-first';\n  static const String otherLoginMethods = 'other-login-methods';\n  static const String verifyAccount = 'verify-account';\n  static const String enterPasswordToSignin = 'enter-password-to-signin';\n  static const String verifyCodeLogin = 'verify-code-login';\n  static const String verifyCodeLoginTips = 'verify-code-login-tips';\n  static const String usePasswordToSignin = 'use-password-to-signin';\n  static const String referenceDocuments = 'reference-documents';\n\n  static const String welcomeToAskMe = 'welcome-to-ask-me';\n  static const String startChat = 'start-chat';\n  static const String updateApp = 'update-app';\n  static const String notUpdateApp = 'not-update-app';\n  static const String searchedXWebPages = 'searched-x-web-pages';\n\n  static const Map<String, dynamic> zh = {\n    wechat: '微信',\n    required: '必填',\n    systemInfo: '系统信息',\n    save: '保存',\n    ok: '确定',\n    cancel: '取消',\n    select: '选择',\n    tips: '提示',\n    goodTips: '温馨提示',\n    basicInfo: '基本信息',\n    delete: '删除',\n    edit: '编辑',\n    selectAll: '全选',\n    unselectAll: '取消全选',\n    share: '分享',\n    cancelShare: '取消分享',\n    histories: '最近的对话',\n    moreHistories: '更多历史对话',\n    enable: '启用',\n    disable: '未启用',\n    newChat: '新对话',\n    clearChatHistory: '清空聊天记录',\n    examples: '示例',\n    continueMessage: '继续',\n    messageInputTips: '有问题尽管问我',\n    takePhoto: '拍照',\n    photoLibrary: '附加照片',\n    fileLibrary: '附加文档',\n    upload: '上传',\n    longPressSpeak: '长按说话',\n    send: '发送',\n    sendRetry: '重新发送',\n    sendRetryS: '重发',\n    selectText: '选择文本',\n    text: '文本',\n    uploading: '上传中...',\n    robotIsThinkingMessage: '正在思考中，请耐心等待',\n    thinkingProcess: '深度思考',\n    robotIsSearchingMessage: '正在联网搜索',\n    robotHasSomeError: '发送失败，重发该消息？',\n    appName: 'AIdea',\n    chatAnywhere: '聊一聊',\n    homeTitle: '自定义角色',\n    creativeIsland: '创作岛',\n    settings: '设置',\n    configure: '配置',\n    language: '语言',\n    themeMode: '主题外观',\n    accountInfo: '账号信息',\n    accountSettings: '账号设置',\n    usage: '智慧果',\n    validBefore: '有效期至',\n    custom: '自定义',\n    clearCache: '清空缓存',\n    about: '关于',\n    diagnostic: '故障诊断',\n    userTerms: '用户协议',\n    privacyPolicy: '隐私政策',\n    signIn: '登录',\n    signInAccount: '登录解锁完整功能',\n    signOut: '退出登录',\n    signUp: '注册',\n    password: '密码',\n    passwordConfirm: '确认密码',\n    retrievePassword: '找回密码',\n    newPassword: '新密码',\n    account: '账号',\n    usedUp: '已用完',\n    expired: '已过期',\n    timeConsume: '用时',\n    character: '角色',\n    myCharacters: '我的角色',\n    createRoom: '创建角色',\n    model: 'AI 模型',\n    selectModel: '选择模型',\n    roomName: '名称',\n    avatar: '头像',\n    iconName: '图标',\n    prompt: '角色设定',\n    optional: '可选',\n    search: '搜索',\n    onlineSearch: '联网搜索',\n    reasoning: '深度思考',\n    background: '背景',\n    backgroundSetting: '背景图',\n    roomSetting: '设置',\n    chatHistory: '聊天记录',\n    confirmSend: '确定发送以下内容？',\n    questionExamples: '问题示例',\n    noRecords: '暂无记录',\n    contextBreakMessage: '~ 以下是新的对话 ~',\n    translateFinished: '翻译完成',\n    textCopied: '已复制到剪贴板',\n    copy: '复制',\n    translate: '翻译',\n    hide: '隐藏',\n    readByVoice: '朗读',\n    unknownFile: '未知文件',\n    switchModel: '切换对话模型',\n    switchModelTitle: '选择要切换的对话模型',\n    noMessageSelected: '没有选择任何消息',\n    modelUsage: '模型用于设置采用的 AI 角色类型',\n    promptUsage: '领域设定用于设置 AI 角色的行为',\n    nameRequiredMessage: '请输入名称',\n    modelRequiredMessage: '请选择 AI 模型',\n    charactorPromptRequiredMessage: '请输入角色设定',\n    operateSuccess: '操作成功',\n    operateFailed: '操作失败',\n    confirmDelete: '确定删除？',\n    confirmStartNewChat: '确定要开始新的对话？',\n    confirmClearMessages: '确定要清空聊天记录？',\n    quotaExceeded: '智慧果数量不足，请先购买',\n    internalServerError: '服务器故障，请稍后再试',\n    badGateway: '抱歉，我们的服务器目前无法处理您的请求。我们正在努力解决问题，请您稍后重试',\n    modelNotValid: '当前模型暂未开放',\n    signInRequired: '您尚未登录，请先登录',\n    accountNeedReSignin: '账号异常，请重新登录',\n    openAIAuthFailed: '您启用了自定义 OpenAI 服务，请检查 API Key 是否正确',\n    modelNotFound: '当前模型尚未开通，暂时无法使用',\n    confirmToDeleteRoom: '确定删除?',\n    writeYourIdeas: '你的想法',\n    describeYourImages: '你的想法',\n    excludeContents: '反向提示词',\n    autoTranslateToEnglish: '自动翻译为英文',\n    auto: '自动',\n    style: '风格',\n    imageCount: '图片数量',\n    imageSize: '图片比例',\n    wordCount: '生成字数',\n    generate: '开始创作',\n    processingWait: '处理中，请稍后...',\n    contentIsRequired: '请输入创作内容',\n    wordCountInvalid: '字数不能超过 1000',\n    generateTimeout: '创作超时，请稍后再试',\n    creativeIslandNeedSignIn: '登录后解锁更多玩法～',\n    generateResult: '创作结果',\n    generateFailed: '创作失败',\n    generating: '创作中...',\n    generateExitConfirm: '创作中...\\n退出后，可在历史记录中查看结果',\n    tooManyRequests: '操作过于频繁，请稍后再试',\n    tooManyRequestsOrPaymentRequired: '操作过于频繁（如果您使用了自定义的 OpenAI Keys，请登录 https://platform.openai.com 检查账户余额是否充足）',\n    promptHint: '设定角色和技能，以便为你提供更精准有效的信息。',\n    confirmClearCache: '确定要清除缓存吗？',\n    confirmSignOut: '确定要退出登录吗？',\n    askMeAnyQuestion: '消息',\n    askMeLikeThis: '可以这样问我:',\n    refresh: '换一批',\n    fastAndCostEffective: '速度快，成本低',\n    powerfulAndPrecise: '能力强，更精准',\n    imageToImage: '图生图',\n    imageToVideo: '图生视频',\n    textToImage: '文生图',\n    hdRestoration: '高清修复',\n    yourIdeas: '你的想法',\n    smartOptimization: '智能优化',\n    professionalMode: '专业模式',\n    simpleMode: '简单模式',\n    unwantedElements: '''画面中不希望出现的元素或效果，如 “汽车，毛茸茸，低分辨率，模糊” 等。''',\n    referenceImage: '参考图片',\n    selectImage: '选择图片',\n    imagination: '想象力',\n    keywordsSeparatedByCommas: '你想象画面的关键词，以逗号隔开。',\n    originalImage: '原始图片',\n    superResolution: '高清修复',\n    colorizeImage: '旧照片上色',\n    errorLog: '故障诊断',\n    report: '上报',\n    latestVersion: '当前已是最新版本',\n    aIdeaApp: 'AIdea是一款能够让你与 AI 对话的应用，应用代码完全开源。',\n    onceEnabledSmartOptimization: '智能优化\\n\\n启用后，AI 将进一步完善优化你的想法。',\n    referenceImageNote: '参考图片\\n\\nAI 将会在该参考图片的基础上进行创作。',\n    gotIt: '知道了',\n    selectReferenceImage: '请选择参考图片',\n    random: '随机',\n    followSystem: '跟随系统',\n    darkThemeMode: '深色模式',\n    lightThemeMode: '浅色模式',\n    forgotPassword: '忘记密码',\n    createAccount: '注册账号',\n    useAsClient: '仅作为 OpenAI 客户端使用',\n    signInWithApple: '使用 Apple 账号登录',\n    readAndAgree: '已阅读并同意',\n    andWord: '和',\n    accountInputTips: '输入您的手机或邮箱账号',\n    phoneInputTips: '输入您的手机号',\n    passwordInputTips: '输入您的密码',\n    pleaseReadAgreeProtocol: '请先阅读并同意用户协议和隐私条款',\n    signInSuccess: '登录成功',\n    signInFailed: '登录失败',\n    accountRequired: '请输入账号',\n    accountFormatError: '账号格式有误\\n请输入手机号或邮箱账号',\n    phoneNumberFormatError: '手机号码格式有误',\n    passwordRequired: '请输入密码',\n    passwordFormatError: '密码格式有误\\n必须为8-20位字母、数字、特殊符号组合',\n    accountCreated: '账号创建成功',\n    sendVerifyCode: '发送',\n    verify: '验证',\n    verifyCode: '验证码',\n    verifyCodeInputTips: '输入验证码',\n    retryInSeconds: '秒后重试',\n    verifyCodeSendSuccess: '验证码已发送',\n    pleaseGetVerifyCodeFirst: '请先获取验证码',\n    verifyCodeRequired: '请输入验证码',\n    verifyCodeFormatError: '验证码格式有误',\n    phone: '手机',\n    email: '邮箱',\n    directSigninDueHasAccount: '已有账号？直接登录',\n    directSignin: '直接登录',\n    passwordResetOK: '密码已重置，请重新登录',\n    resetPassword: '重置密码',\n    bindPhone: '绑定手机',\n    bind: '绑定',\n    bound: '已绑定',\n    bindExAccount: '绑定已有账号',\n    unbind: '解绑',\n    inviteCode: '邀请码',\n    inviteCodeInputTips: '输入好友邀请码，获额外奖励（非必填）',\n    inviteCodeFormatError: '邀请码格式有误',\n    enableCustomOpenAI: '启用后将使用您自己配置的 OpenAI 服务',\n    me: '我的',\n    creditsUsage: '使用明细',\n    creditUsageTips: '使用明细将在次日更新，显示近 30 天的使用量。',\n    updateCheck: '检测更新',\n    buy: '购买',\n    paymentHistory: '购买历史',\n    buyCredits: '购买智慧果',\n    creditUnit: '￠',\n    toPay: '立即支付',\n    discover: '绘玩',\n    customHomeModels: '常用模型',\n    userApiKeys: 'API Keys',\n    others: '其它',\n    recentlyUsed: '最近使用',\n    visionTag: '视觉',\n    newTag: '上新',\n    recommendTag: '推荐',\n    imageUploading: '正在上传图片，请稍后...',\n    uploadImageLimit4: '最多只能上传 4 张图片',\n    confirmStopOutput: '确定要停止当前输出？',\n    stopOutput: '停止输出',\n    opensource: '本项目开源，欢迎贡献',\n    socialMedia: '关注我们',\n    unset: '未设置',\n    nickname: '昵称',\n    setNickname: '设置昵称',\n    inputYourNickname: '请输入你的昵称',\n    reset: '重置',\n    deleteAccount: '删除账号',\n    confirmDeleteAccount: '确定要删除账号',\n    wechatAccount: '微信账号',\n    modifyPassword: '修改密码',\n    setPassword: '设置密码',\n    installWeChat: '请先安装微信后再使用该功能',\n    freeQuota: '免费畅享额度',\n    serviceStatus: '服务状态',\n    lab: '实验室',\n    todayLeft: '今日可用',\n    freeModelNeedSignIn: '免费模型需登录账号后使用',\n    noFreeModel: '当前无可用的免费模型。',\n    freeModelInfo: '以下模型享有每日免费额度。',\n    notification: '通知',\n    selectMember: '选择本次对话成员',\n    members: '成员',\n    createGroupChat: '创建群聊',\n    advanced: '更多选项',\n    collapseOptions: '收起选项',\n    welcomeMessage: '引导语',\n    welcomeMessageTips: '每次开始新对话时，系统将会以 AI 的身份自动发送引导语。',\n    memoryDepth: '记忆深度',\n    robotRecommand: '热门推荐',\n    pickYourRobot: '挑选你的专属伙伴',\n    viewMore: '查看更多',\n    using: '使用中',\n    inviteCodeShare: '邀请码分享',\n    shareToWechatQ: '分享到朋友圈',\n    shareToWechat: '分享到微信',\n    shareToOtherApps: '分享到其它应用',\n    clickToShareWithExpire: '点击图片可分享、保存；有效期至',\n    shortcut: '动作',\n    selectImageToShortcut: '选择要执行动作的图片',\n    selectShortcutAction: '选择要执行的操作',\n    makeSameStyle: '制作同款',\n    saveToLocal: '保存到本地',\n    showInviteCode: '显示邀请信息',\n    dontShowInviteCode: '不显示邀请信息',\n    inviteNow: '立即邀请',\n    inviteSlogan: '邀请好友注册，双方都可获得奖励',\n    preview: '预览',\n    download: '下载',\n    clickSwitchImage: '点击此处更换图片',\n    startNewChatTips: '想要开启新的聊天？试试',\n    wantMoreContentTips: '想要更多内容？试着对我说',\n    unbilled: '未出账',\n    signinNow: '立即登录',\n    needSigninToUse: '该功能需要登录账号后使用',\n    reSignIn: '重新登录',\n    ideaPrompt: '想法',\n    groupChat: '群聊',\n    selectGroupMembers: '选择参与群聊的成员',\n    selectPaymentMethod: '请选择支付方式',\n    validDays: '内有效',\n    clickToReSignin: '点击此处重新登录',\n    free: '限免',\n    input: '输入',\n    output: '输出',\n    perRequest: '每次',\n    perSearch: '每次搜索',\n    info: '详情',\n    recently: '最近',\n    daysAgo: '天前',\n    lastWeek: '上周',\n    weeksAgo: '周前',\n    lastMonth: '上个月',\n    monthsAgo: '月前',\n    lastYear: '去年',\n    longTimeAgo: '很久以前',\n    modelNeedSignIn: '该模型需要登录后使用',\n    wechatBindConfirm: '该微信未绑定任何账号，是否直接登录？\\n（自动创建账号）',\n    accountWillBeCreateAutomatically: '未注册的账号验证成功后将自动注册',\n    installWechatFirst: '请先安装微信后再使用该功能',\n    otherLoginMethods: '其它登录方式',\n    verifyAccount: '验证账号',\n    enterPasswordToSignin: '请输入密码完成登录。',\n    verifyCodeLogin: '验证码登录',\n    verifyCodeLoginTips: '请输入验证码以完成操作。',\n    usePasswordToSignin: '使用密码登录',\n    referenceDocuments: '参考文档',\n    welcomeToAskMe: '我可以帮你答疑、写作，请问我吧！',\n    startChat: '开始对话',\n    updateApp: '去更新',\n    notUpdateApp: '暂不更新',\n    searchedXWebPages: '已搜索到 %s 个网页',\n  };\n\n  static const Map<String, dynamic> en = {\n    required: 'Required',\n    wechat: 'WeChat',\n    systemInfo: 'System',\n    save: 'Save',\n    ok: 'OK',\n    cancel: 'Cancel',\n    select: 'Select',\n    tips: 'Tips',\n    goodTips: 'Tips',\n    basicInfo: 'Basic',\n    delete: 'Delete',\n    edit: 'Edit',\n    selectAll: 'Select all',\n    unselectAll: 'Cancel',\n    share: 'Share',\n    cancelShare: 'Cancel share',\n    histories: 'Recents',\n    moreHistories: 'More Histories',\n    enable: 'Enable',\n    disable: 'Disable',\n    newChat: 'New Chat',\n    clearChatHistory: 'Clear Chat Histories',\n    examples: 'Examples',\n    continueMessage: 'Continue',\n    messageInputTips: 'Ask me something...',\n    takePhoto: 'Take Photo',\n    photoLibrary: 'Attach Photos',\n    fileLibrary: 'Attach Files',\n    upload: 'Upload',\n    longPressSpeak: 'Long press to speak',\n    send: 'Send',\n    sendRetry: 'Retry',\n    sendRetryS: 'Retry',\n    selectText: 'Select Text',\n    text: 'Text',\n    uploading: 'Uploading...',\n    robotIsThinkingMessage: 'Thinking...',\n    thinkingProcess: 'Deep thought',\n    robotIsSearchingMessage: 'Searching',\n    robotHasSomeError: 'There seems to be something wrong, Do you want to resend the message?',\n    appName: 'AIdea',\n    chatAnywhere: 'Chat',\n    homeTitle: 'Characters',\n    creativeIsland: 'Creative',\n    settings: 'Setting',\n    configure: 'Configure',\n    language: 'Language',\n    themeMode: 'Theme',\n    accountInfo: 'Account Info',\n    accountSettings: 'Account Settings',\n    usage: 'Credits',\n    validBefore: 'Valid Before',\n    custom: 'Custom',\n    clearCache: 'Clear Cache',\n    about: 'About',\n    diagnostic: 'Diagnostic',\n    userTerms: 'User Terms',\n    privacyPolicy: 'Privacy Policy',\n    signIn: 'Sign In',\n    signInAccount: 'Unlock Full Features',\n    signOut: 'Sign Out',\n    signUp: 'Sign Up',\n    password: 'Password',\n    passwordConfirm: 'Confirm Password',\n    retrievePassword: 'Forgot Password',\n    newPassword: 'New Password',\n    account: 'Account',\n    usedUp: 'Used Up',\n    expired: 'Expired',\n    timeConsume: 'Cost',\n    character: 'Character',\n    myCharacters: 'My Characters',\n    createRoom: 'Create Character',\n    model: 'AI Model',\n    selectModel: 'Select Model',\n    roomName: 'Name',\n    avatar: 'Avatar',\n    iconName: 'Icon',\n    prompt: 'Prompt',\n    optional: 'Optional',\n    search: 'Search',\n    onlineSearch: 'Search',\n    reasoning: 'DeepThink',\n    background: 'Background',\n    backgroundSetting: 'Background Setting',\n    roomSetting: 'Setting',\n    chatHistory: 'Histories',\n    confirmSend: 'Confirm to send?',\n    questionExamples: 'Question Examples',\n    noRecords: 'No Records',\n    contextBreakMessage: '~ Context cleared ~',\n    translateFinished: 'Translate finished',\n    textCopied: 'Text copied',\n    copy: 'Copy',\n    translate: 'Translate',\n    hide: 'Hide',\n    readByVoice: 'Voice',\n    unknownFile: 'Unknown File',\n    switchModel: 'Switch Model',\n    switchModelTitle: 'Switch model',\n    noMessageSelected: 'No message selected',\n    modelUsage: 'The model is used to set the type of AI character used',\n    promptUsage: 'Prompt is used to set the behavior of the AI character',\n    nameRequiredMessage: 'Please enter the name of the character',\n    modelRequiredMessage: 'Please select AI model',\n    charactorPromptRequiredMessage: 'Please enter the charactor prompt',\n    operateSuccess: 'Success',\n    operateFailed: 'Failed',\n    confirmDelete: 'Confirm to delete?',\n    confirmStartNewChat: 'Confirm to start a new chat?',\n    confirmClearMessages: 'Confirm to clear chat histories?',\n    quotaExceeded: 'Insufficient credits, please purchase first',\n    internalServerError: 'Internal server error, please try again later',\n    badGateway:\n        'Sorry, our server is currently unable to process your request. We are working to resolve the issue, please try again later',\n    modelNotValid: 'The current model is not open',\n    signInRequired: 'You are not logged in, please log in first',\n    accountNeedReSignin: 'Account exception, please log in again',\n    openAIAuthFailed: 'You have enabled custom OpenAI service, please check if the API Key is correct',\n    modelNotFound: 'The current model is not enabled yet, please try again later',\n    confirmToDeleteRoom: 'Confirm to delete the character?',\n    writeYourIdeas: 'Your ideas',\n    describeYourImages: 'Your ideas',\n    excludeContents: 'Negative prompts',\n    autoTranslateToEnglish: 'Auto translate to English',\n    auto: 'Auto',\n    style: 'Style',\n    imageCount: 'Image count',\n    imageSize: 'Image size',\n    wordCount: 'Word count',\n    generate: 'Generate',\n    processingWait: 'Processing, please wait...',\n    contentIsRequired: 'Content is required',\n    wordCountInvalid: 'Word count cannot exceed 1000',\n    generateTimeout: 'Generate timeout, please try again later',\n    creativeIslandNeedSignIn: 'Unlock more features after login',\n    generateResult: 'Generate result',\n    generateFailed: 'Creation failed',\n    generating: 'Generating...',\n    generateExitConfirm: 'Generating...\\nYou can view the result in the history',\n    tooManyRequests: 'Too many requests, please try again later',\n    tooManyRequestsOrPaymentRequired:\n        'Too many requests (If you are using your own OpenAI Keys, please log in to https://platform.openai.com to check if your account balance is sufficient)',\n    promptHint:\n        'Set the role and skills of the character so that it can provide more accurate and effective information for you.',\n    confirmClearCache: 'Confirm to clear cache?',\n    confirmSignOut: 'Confirm to sign out?',\n    askMeAnyQuestion: 'Message',\n    askMeLikeThis: 'You can ask me like this:',\n    refresh: 'Refresh',\n    fastAndCostEffective: 'Fast & Cost-Effective',\n    powerfulAndPrecise: 'Powerful & Precise',\n    imageToImage: 'Image to Image',\n    imageToVideo: 'Image to Video',\n    textToImage: 'Text to Image',\n    hdRestoration: 'HD Restoration',\n    yourIdeas: 'Your Ideas',\n    smartOptimization: 'Smart Optimization',\n    professionalMode: 'Pro Mode',\n    simpleMode: 'Simple Mode',\n    unwantedElements:\n        '''Elements or effects you don't want to appear in the picture, such as 'cars, fluffy, low resolution, blurry,' etc.''',\n    referenceImage: 'Reference Image',\n    selectImage: 'Select Image',\n    imagination: 'Imagination',\n    keywordsSeparatedByCommas: 'Keywords of the scene you imagine, separated by commas',\n    originalImage: 'Original Image',\n    superResolution: 'Super-Resolution',\n    colorizeImage: 'Colorize Image',\n    errorLog: 'Error Log',\n    report: 'Report',\n    latestVersion: 'You are currently on the latest version',\n    aIdeaApp: 'AIdea is an app that allows you to converse with AI, and the app code is completely open source.',\n    onceEnabledSmartOptimization: 'Smart Optimization\\n\\nOnce enabled, AI will further refine and optimize your ideas.',\n    gotIt: 'Got it',\n    referenceImageNote: 'Reference Image\\n\\nAI will create based on the reference image provided.',\n    selectReferenceImage: 'Please select a reference image',\n    random: 'Random',\n    followSystem: 'Follow System',\n    darkThemeMode: 'Dark mode',\n    lightThemeMode: 'Light mode',\n    forgotPassword: 'Forgot password',\n    createAccount: 'Create account',\n    useAsClient: 'Use as OpenAI client',\n    signInWithApple: 'Sign in with Apple',\n    readAndAgree: 'Read and agree',\n    andWord: 'and',\n    accountInputTips: 'Phone number or email',\n    phoneInputTips: 'Phone number',\n    passwordInputTips: 'Password',\n    pleaseReadAgreeProtocol: 'Please read and agree to the user agreement and privacy policy first',\n    signInSuccess: 'Sign in success',\n    signInFailed: 'Sign in failed',\n    accountRequired: 'Please enter your account',\n    accountFormatError: 'Account format error\\nPlease enter your phone number or email',\n    phoneNumberFormatError: 'Phone number format error',\n    passwordRequired: 'Please enter your password',\n    passwordFormatError: 'Password format error\\nMust be 8-20 digits, letters, special characters',\n    accountCreated: 'Account created',\n    sendVerifyCode: 'Send',\n    verify: 'Verify',\n    verifyCode: 'Verify code',\n    verifyCodeInputTips: 'Enter verify code',\n    retryInSeconds: 'Retry in',\n    verifyCodeSendSuccess: 'Verify code has been sent',\n    pleaseGetVerifyCodeFirst: 'Please get the verification code first',\n    verifyCodeRequired: 'Please enter the verification code',\n    verifyCodeFormatError: 'Verification code format error',\n    phone: 'Phone',\n    email: 'Email',\n    directSigninDueHasAccount: 'Already have an account? Sign in directly',\n    directSignin: 'Sign in directly',\n    passwordResetOK: 'Password has been reset, please log in again',\n    resetPassword: 'Reset password',\n    bindPhone: 'Bind phone',\n    bind: 'Bind',\n    bound: 'Bound',\n    bindExAccount: 'Bind existing account',\n    unbind: 'Unbind',\n    inviteCode: 'Invite code',\n    inviteCodeInputTips: 'Enter friend invite code, get extra rewards (optional)',\n    inviteCodeFormatError: 'Invite code format error',\n    enableCustomOpenAI: 'Your custom OpenAI service will be used once enabled',\n    me: 'Me',\n    creditsUsage: 'Usage',\n    creditUsageTips: 'Usage details will be updated the next day, showing usage in the last 30 days.',\n    updateCheck: 'Check Update',\n    buy: 'Buy',\n    paymentHistory: 'Histories',\n    buyCredits: 'Buy Credits',\n    creditUnit: '￠',\n    toPay: 'Create Order',\n    discover: 'Discover',\n    customHomeModels: 'Favorite Models',\n    userApiKeys: 'API Keys',\n    others: 'Others',\n    recentlyUsed: 'Recently Used',\n    visionTag: 'Vision',\n    newTag: 'New',\n    recommendTag: 'Recommend',\n    imageUploading: 'Uploading image, please wait...',\n    uploadImageLimit4: 'You can only upload up to 4 images',\n    confirmStopOutput: 'Are you sure you want to stop current output?',\n    stopOutput: 'Stop Output',\n    opensource: 'Open Source',\n    socialMedia: 'Follow us',\n    unset: 'Unset',\n    nickname: 'Nickname',\n    setNickname: 'Set Nickname',\n    inputYourNickname: 'Input your nickname',\n    reset: 'Reset',\n    deleteAccount: 'Delete Account',\n    confirmDeleteAccount: 'Confirm to delete account',\n    wechatAccount: 'WeChat Account',\n    modifyPassword: 'Modify Password',\n    setPassword: 'Set Password',\n    installWeChat: 'Please install WeChat first',\n    freeQuota: 'Free Quota',\n    serviceStatus: 'Service Status',\n    lab: 'Lab',\n    todayLeft: 'Today Available',\n    freeModelNeedSignIn: 'Free model requires login to use',\n    noFreeModel: 'No free model available.',\n    freeModelInfo: 'The following models have daily free quotas.',\n    notification: 'Notification',\n    selectMember: 'Select member',\n    members: 'Members',\n    createGroupChat: 'Create group chat',\n    advanced: 'More Options',\n    collapseOptions: 'Collapse',\n    welcomeMessage: 'Welcome Message',\n    welcomeMessageTips: 'The system will automatically send a welcome message each time a new chat is started.',\n    memoryDepth: 'Memory Depth',\n    robotRecommand: 'Recommand',\n    pickYourRobot: 'Pick your robot',\n    viewMore: 'More',\n    using: 'Using',\n    inviteCodeShare: 'Invite Code Share',\n    shareToWechatQ: 'Share to WeChat Moments',\n    shareToWechat: 'Share to WeChat',\n    shareToOtherApps: 'Share to Other Apps',\n    clickToShareWithExpire: 'Click to share, valid until',\n    shortcut: 'Action',\n    selectImageToShortcut: 'Select image to perform action',\n    selectShortcutAction: 'Select the action to perform',\n    makeSameStyle: 'Make the Same',\n    saveToLocal: 'Save to Local',\n    showInviteCode: 'Show Invite Code',\n    dontShowInviteCode: 'Don\\'t Show Invite Code',\n    inviteNow: 'Invite Now',\n    inviteSlogan: 'Invite friends to register, both parties will receive rewards',\n    preview: 'Preview',\n    download: 'Download',\n    clickSwitchImage: 'Click to switch image',\n    startNewChatTips: 'Want to start a new chat? Try',\n    wantMoreContentTips: 'Want more content? Try',\n    unbilled: 'Pending',\n    signinNow: 'Sign in now',\n    needSigninToUse: 'Please login first',\n    reSignIn: 'Re-login',\n    ideaPrompt: 'Prompt',\n    groupChat: 'Group',\n    selectGroupMembers: 'Select group members',\n    selectPaymentMethod: 'Select payment method',\n    validDays: 'expiration',\n    clickToReSignin: 'Click here to sign in again',\n    free: 'Free',\n    input: 'Input',\n    output: 'Output',\n    perRequest: 'Per',\n    perSearch: 'Per Search',\n    info: 'Detail',\n    recently: 'Recently',\n    lastWeek: 'Last Week',\n    lastMonth: 'Last Month',\n    daysAgo: 'Days Ago',\n    monthsAgo: 'Months Ago',\n    lastYear: 'Last Year',\n    longTimeAgo: 'Long Time Ago',\n    weeksAgo: 'Weeks Ago',\n    modelNeedSignIn: 'The model needs to be signed in to use',\n    wechatBindConfirm:\n        'The WeChat is not bound to any account, whether to sign in directly?\\n(Automatically create an account)',\n    accountWillBeCreateAutomatically: 'Account will be created automatically',\n    installWechatFirst: 'Please install WeChat first',\n    otherLoginMethods: 'Other Login Methods',\n    verifyAccount: 'Verify Account',\n    enterPasswordToSignin: 'Please enter the password to sign in.',\n    verifyCodeLogin: 'Use Verify Code',\n    verifyCodeLoginTips: 'Please enter the verify code to complete the operation.',\n    usePasswordToSignin: 'Use Password',\n    referenceDocuments: 'Reference Documents',\n    welcomeToAskMe: 'How can I help you today?',\n    startChat: 'Start Chat',\n    updateApp: 'Update Now',\n    notUpdateApp: 'Not Update',\n    searchedXWebPages: 'Searched %s web pages',\n  };\n}\n\nclass LanguageText {\n  final String message;\n  final String? action;\n  const LanguageText(this.message, {this.action});\n\n  @override\n  String toString() {\n    return message;\n  }\n}\n\nfinal languages = <String, String>{\n  'zh_Hans_': 'zh-CHS',\n  'en': 'en',\n};\n\nString resolveSystemLanguage(String deviceLocale) {\n  for (var key in languages.keys) {\n    if (deviceLocale.startsWith(key)) {\n      return languages[key]!;\n    }\n  }\n\n  return 'en';\n}\n"
  },
  {
    "path": "lib/main.dart",
    "content": "import 'package:askaide/bloc/admin_payment_bloc.dart';\nimport 'package:askaide/bloc/admin_room_bloc.dart';\nimport 'package:askaide/bloc/channel_bloc.dart';\nimport 'package:askaide/bloc/model_bloc.dart';\nimport 'package:askaide/bloc/user_bloc.dart';\nimport 'package:askaide/helper/path.dart';\nimport 'package:askaide/page/admin/channels.dart';\nimport 'package:askaide/page/admin/channels_add.dart';\nimport 'package:askaide/page/admin/channels_edit.dart';\nimport 'package:askaide/page/admin/dashboard.dart';\nimport 'package:askaide/page/admin/messages.dart';\nimport 'package:askaide/page/admin/models.dart';\nimport 'package:askaide/page/admin/models_add.dart';\nimport 'package:askaide/page/admin/models_edit.dart';\nimport 'package:askaide/page/admin/payments.dart';\nimport 'package:askaide/page/admin/recently_messages.dart';\nimport 'package:askaide/page/admin/rooms.dart';\nimport 'package:askaide/page/admin/user.dart';\nimport 'package:askaide/page/admin/users.dart';\nimport 'package:askaide/page/balance/web_payment_proxy.dart';\nimport 'package:askaide/page/balance/web_payment_result.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/creative_island/draw/artistic_wordart.dart';\nimport 'package:askaide/page/home.dart';\nimport 'package:flutter_phoenix/flutter_phoenix.dart';\nimport 'package:path/path.dart';\n\nimport 'package:askaide/bloc/account_bloc.dart';\nimport 'package:askaide/bloc/background_image_bloc.dart';\nimport 'package:askaide/bloc/chat_chat_bloc.dart';\nimport 'package:askaide/bloc/creative_island_bloc.dart';\nimport 'package:askaide/bloc/free_count_bloc.dart';\nimport 'package:askaide/bloc/gallery_bloc.dart';\nimport 'package:askaide/bloc/group_chat_bloc.dart';\nimport 'package:askaide/bloc/payment_bloc.dart';\nimport 'package:askaide/bloc/user_api_keys_bloc.dart';\nimport 'package:askaide/bloc/version_bloc.dart';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/cache.dart';\nimport 'package:askaide/helper/logger.dart';\nimport 'package:askaide/helper/model.dart';\nimport 'package:askaide/helper/model_resolver.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/data/migrate.dart';\nimport 'package:askaide/page/balance/quota_usage_details.dart';\nimport 'package:askaide/page/creative_island/draw/artistic_qr.dart';\nimport 'package:askaide/page/setting/account_security.dart';\nimport 'package:askaide/page/lab/avatar_selector.dart';\nimport 'package:askaide/page/setting/article.dart';\nimport 'package:askaide/page/setting/background_selector.dart';\nimport 'package:askaide/page/setting/bind_phone_page.dart';\nimport 'package:askaide/page/setting/change_password.dart';\nimport 'package:askaide/page/chat/home_chat.dart';\nimport 'package:askaide/page/chat/home.dart';\nimport 'package:askaide/page/chat/home_chat_history.dart';\nimport 'package:askaide/page/chat/character_create.dart';\nimport 'package:askaide/page/component/random_avatar.dart';\nimport 'package:askaide/page/component/transition_resolver.dart';\nimport 'package:askaide/page/creative_island/my_creation.dart';\nimport 'package:askaide/page/creative_island/my_creation_item.dart';\nimport 'package:askaide/page/setting/custom_home_models.dart';\nimport 'package:askaide/page/balance/free_statistics.dart';\nimport 'package:askaide/page/chat/group/chat.dart';\nimport 'package:askaide/page/chat/group/create.dart';\nimport 'package:askaide/page/chat/group/edit.dart';\nimport 'package:askaide/page/lab/creative_models.dart';\nimport 'package:askaide/page/setting/destroy_account.dart';\nimport 'package:askaide/page/setting/diagnosis.dart';\nimport 'package:askaide/page/creative_island/draw/draw_list.dart';\nimport 'package:askaide/page/creative_island/draw/draw_create.dart';\nimport 'package:askaide/page/creative_island/draw/image_edit_direct.dart';\nimport 'package:askaide/page/lab/draw_board.dart';\nimport 'package:askaide/page/creative_island/gallery/gallery.dart';\nimport 'package:askaide/page/creative_island/gallery/gallery_item.dart';\nimport 'package:askaide/page/setting/notification.dart';\nimport 'package:askaide/page/setting/openai_setting.dart';\nimport 'package:askaide/page/balance/payment.dart';\nimport 'package:askaide/page/lab/prompt.dart';\nimport 'package:askaide/page/balance/quota_usage_statistics.dart';\nimport 'package:askaide/page/auth/signin_or_signup.dart';\nimport 'package:askaide/page/auth/signin_screen.dart';\nimport 'package:askaide/page/component/chat/message_state_manager.dart';\nimport 'package:askaide/page/balance/payment_history.dart';\nimport 'package:askaide/page/setting/retrieve_password_screen.dart';\nimport 'package:askaide/page/auth/signup_screen.dart';\nimport 'package:askaide/page/lab/user_center.dart';\nimport 'package:askaide/page/setting/user_api_keys.dart';\nimport 'package:askaide/repo/api/info.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/cache_repo.dart';\nimport 'package:askaide/repo/creative_island_repo.dart';\nimport 'package:askaide/repo/data/cache_data.dart';\nimport 'package:askaide/repo/data/chat_history.dart';\nimport 'package:askaide/repo/data/creative_island_data.dart';\nimport 'package:askaide/repo/deepai_repo.dart';\nimport 'package:askaide/repo/stabilityai_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter/gestures.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:fluwx/fluwx.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:intl/intl.dart';\nimport 'package:provider/provider.dart';\nimport 'package:askaide/bloc/bloc_manager.dart';\nimport 'package:askaide/bloc/chat_message_bloc.dart';\nimport 'package:askaide/bloc/room_bloc.dart';\nimport 'package:askaide/bloc/notify_bloc.dart';\nimport 'package:askaide/page/chat/character_edit.dart';\nimport 'package:askaide/page/chat/character_chat.dart';\nimport 'package:askaide/page/chat/characters.dart';\nimport 'package:askaide/page/setting/setting_screen.dart';\nimport 'package:askaide/repo/data/chat_message_data.dart';\nimport 'package:askaide/repo/chat_message_repo.dart';\nimport 'package:askaide/repo/data/room_data.dart';\nimport 'package:askaide/repo/openai_repo.dart';\nimport 'package:askaide/repo/data/settings_data.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart';\nimport 'page/component/theme/theme.dart';\nimport 'package:sizer/sizer.dart';\nimport 'package:askaide/helper/http.dart' as httpx;\nimport 'package:sqflite_common_ffi/sqflite_ffi.dart';\nimport 'package:media_kit/media_kit.dart';\nimport 'package:bitsdojo_window/bitsdojo_window.dart';\n\nvoid main() async {\n  WidgetsFlutterBinding.ensureInitialized();\n  MediaKit.ensureInitialized();\n  httpx.HttpClient.init();\n\n  // 初始化路径，获取到系统相关的文档、缓存目录\n  await PathHelper().init();\n\n  FlutterError.onError = (FlutterErrorDetails details) {\n    if (details.library == 'rendering library' || details.library == 'image resource service') {\n      return;\n    }\n\n    Logger.instance.e(\n      details.summary,\n      error: details.exception,\n      stackTrace: details.stack,\n    );\n    Logger.instance.d(details.stack);\n  };\n\n  if (kIsWeb) {\n    databaseFactory = databaseFactoryFfiWeb;\n  } else {\n    if (PlatformTool.isWindows() || PlatformTool.isLinux() || PlatformTool.isMacOS()) {\n      sqfliteFfiInit();\n      databaseFactory = databaseFactoryFfi;\n      var path = absolute(join(PathHelper().getHomePath, 'databases'));\n      databaseFactory.setDatabasesPath(path);\n    }\n  }\n\n  // 数据库连接\n  final db = await databaseFactory.openDatabase(\n    'system.db',\n    options: OpenDatabaseOptions(\n      version: databaseVersion,\n      onUpgrade: (db, oldVersion, newVersion) async {\n        try {\n          await migrate(db, oldVersion, newVersion);\n        } catch (e) {\n          Logger.instance.e('Database upgrade failure', error: e);\n        }\n      },\n      onCreate: initDatabase,\n      onOpen: (db) {\n        Logger.instance.i('Database storage path: ${db.path}');\n      },\n    ),\n  );\n\n  // 加载配置\n  final settingProvider = SettingDataProvider(db);\n  await settingProvider.loadSettings();\n\n  // 创建数据仓库\n  final settingRepo = SettingRepository(settingProvider);\n  final openAIRepo = OpenAIRepository(settingProvider);\n  final deepAIRepo = DeepAIRepository(settingProvider);\n  final stabilityAIRepo = StabilityAIRepository(settingProvider);\n  final cacheRepo = CacheRepository(CacheDataProvider(db));\n\n  final chatMsgRepo = ChatMessageRepository(\n    RoomDataProvider(db),\n    ChatMessageDataProvider(db),\n    ChatHistoryProvider(db),\n  );\n\n  final creativeIslandRepo = CreativeIslandRepository(CreativeIslandDataProvider(db));\n\n  // 聊天状态加载器\n  final stateManager = MessageStateManager(cacheRepo);\n\n  // 初始化聊天实现解析器\n  ModelResolver.instance.init(\n    openAIRepo: openAIRepo,\n    deepAIRepo: deepAIRepo,\n    stabilityAIRepo: stabilityAIRepo,\n  );\n\n  APIServer().init(settingRepo);\n  ModelAggregate.init(settingRepo);\n  Cache().init(settingRepo, cacheRepo);\n\n  // 从服务器获取客户端支持的能力清单\n  try {\n    final capabilities = await APIServer().capabilities(cache: false);\n    Ability().init(settingRepo, capabilities);\n  } catch (e) {\n    Logger.instance.e('Failed to get the client capability manifest', error: e);\n    Ability().init(\n      settingRepo,\n      Capabilities(\n        applePayEnabled: true,\n        otherPayEnabled: true,\n        translateEnabled: true,\n        mailEnabled: true,\n        openaiEnabled: true,\n        homeModels: [],\n        homeRoute: '/',\n        showHomeModelDescription: true,\n        supportWebsocket: false,\n      ),\n    );\n  }\n\n  // 初始化聊天室 Bloc 管理器\n  final m = ChatBlocManager();\n  m.init((roomId, {chatHistoryId}) {\n    return ChatMessageBloc(\n      roomId,\n      chatHistoryId: chatHistoryId,\n      chatMsgRepo: chatMsgRepo,\n      settingRepo: settingRepo,\n    );\n  });\n\n  runApp(Phoenix(\n    child: MyApp(\n      settingRepo: settingRepo,\n      chatMsgRepo: chatMsgRepo,\n      openAIRepo: openAIRepo,\n      cacheRepo: cacheRepo,\n      creativeIslandRepo: creativeIslandRepo,\n      messageStateManager: stateManager,\n    ),\n  ));\n\n  if (PlatformTool.isDesktop()) {\n    doWhenWindowReady(() {\n      final win = appWindow;\n      const initialSize = Size(850, 750);\n      win.size = initialSize;\n      win.minSize = const Size(350, 650);\n      win.alignment = Alignment.center;\n      win.title = \"AIdea\";\n\n      if (PlatformTool.isWindows()) {\n        WidgetsBinding.instance.scheduleFrameCallback((timeStamp) {\n          appWindow.size = initialSize + const Offset(0, 1);\n        });\n      }\n\n      win.show();\n    });\n  }\n}\n\nclass MyApp extends StatefulWidget {\n  // 页面路由\n  late final GoRouter _router;\n\n  // Bloc\n  late final RoomBloc chatRoomBloc;\n  late final GalleryBloc galleryBloc;\n  late final AccountBloc accountBloc;\n  late final VersionBloc versionBloc;\n  late final FreeCountBloc freeCountBloc;\n\n  final _rootNavigatorKey = GlobalKey<NavigatorState>();\n  final FlutterLocalization localization = FlutterLocalization.instance;\n  final MessageStateManager messageStateManager;\n\n  MyApp({\n    super.key,\n    required this.settingRepo,\n    required this.chatMsgRepo,\n    required this.openAIRepo,\n    required this.cacheRepo,\n    required this.creativeIslandRepo,\n    required this.messageStateManager,\n  }) {\n    chatRoomBloc = RoomBloc(chatMsgRepo: chatMsgRepo, stateManager: messageStateManager);\n    accountBloc = AccountBloc(settingRepo);\n    versionBloc = VersionBloc();\n    galleryBloc = GalleryBloc();\n    freeCountBloc = FreeCountBloc();\n\n    var apiServerToken = settingRepo.get(settingAPIServerToken);\n    var usingGuestMode = settingRepo.boolDefault(settingUsingGuestMode, false);\n\n    final openAISelfHosted = settingRepo.boolDefault(settingOpenAISelfHosted, false);\n    final deepAISelfHosted = settingRepo.boolDefault(settingDeepAISelfHosted, false);\n    final stabilityAISelfHosted = settingRepo.boolDefault(settingStabilityAISelfHosted, false);\n\n    final shouldLogin = (apiServerToken == null || apiServerToken == '') &&\n        !usingGuestMode &&\n        !openAISelfHosted &&\n        !deepAISelfHosted &&\n        !stabilityAISelfHosted;\n\n    _router = GoRouter(\n      initialLocation: shouldLogin ? '/login' : Ability().homeRoute,\n      observers: [\n        BotToastNavigatorObserver(),\n      ],\n      navigatorKey: _rootNavigatorKey,\n      routes: [\n        ShellRoute(\n          builder: (context, state, child) {\n            return child;\n          },\n          routes: [\n            GoRoute(\n              path: '/',\n              name: 'home',\n              pageBuilder: (context, state) {\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider.value(\n                        value: ChatBlocManager().getBloc(\n                          chatAnywhereRoomId,\n                          chatHistoryId: int.tryParse(state.queryParameters['chat_id'] ?? ''),\n                        ),\n                      ),\n                      BlocProvider(\n                        create: (context) => ChatChatBloc(chatMsgRepo),\n                      ),\n                      BlocProvider.value(value: chatRoomBloc),\n                      BlocProvider.value(value: galleryBloc),\n                      BlocProvider.value(value: accountBloc),\n                      BlocProvider.value(value: versionBloc),\n                    ],\n                    child: NewHomePage(\n                      settings: settingRepo,\n                      stateManager: messageStateManager,\n                      showInitialDialog: state.queryParameters['show_initial_dialog'] == 'true',\n                      reward: int.tryParse(state.queryParameters['reward'] ?? '0'),\n                    ),\n                  ),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'setting',\n              path: '/setting',\n              pageBuilder: (context, state) => transitionResolver(\n                MultiBlocProvider(\n                  providers: [\n                    BlocProvider.value(value: accountBloc),\n                  ],\n                  child: SettingScreen(settings: context.read<SettingRepository>()),\n                ),\n              ),\n            ),\n            GoRoute(\n              name: 'creative-draw',\n              path: '/creative-draw',\n              pageBuilder: (context, state) => transitionResolver(\n                MultiBlocProvider(\n                  providers: [\n                    BlocProvider(create: (context) => CreativeIslandBloc(creativeIslandRepo)),\n                  ],\n                  child: DrawListScreen(\n                    setting: settingRepo,\n                  ),\n                ),\n              ),\n            ),\n            GoRoute(\n              name: 'creative-gallery',\n              path: '/creative-gallery',\n              pageBuilder: (context, state) => transitionResolver(\n                MultiBlocProvider(\n                  providers: [\n                    BlocProvider.value(value: galleryBloc),\n                  ],\n                  child: GalleryScreen(setting: settingRepo),\n                ),\n              ),\n            ),\n            GoRoute(\n              name: 'characters',\n              path: '/characters',\n              pageBuilder: (context, state) => transitionResolver(\n                MultiBlocProvider(\n                  providers: [BlocProvider.value(value: chatRoomBloc)],\n                  child: CharactersPage(setting: settingRepo),\n                ),\n              ),\n            ),\n            GoRoute(\n              name: 'chat_chat',\n              path: '/chat-chat',\n              pageBuilder: (context, state) => transitionResolver(\n                MultiBlocProvider(\n                  providers: [\n                    BlocProvider(create: (context) => ChatChatBloc(chatMsgRepo)),\n                  ],\n                  child: HomePage(setting: settingRepo),\n                ),\n              ),\n            ),\n            GoRoute(\n              path: '/login',\n              pageBuilder: (context, state) => transitionResolver(\n                MultiBlocProvider(\n                  providers: [\n                    BlocProvider.value(value: versionBloc),\n                  ],\n                  child: SignInScreen(\n                    settings: settingRepo,\n                    username: state.queryParameters['username'],\n                  ),\n                ),\n              ),\n            ),\n            GoRoute(\n              path: '/signin-or-signup',\n              pageBuilder: (context, state) => transitionResolver(\n                MultiBlocProvider(\n                  providers: [\n                    BlocProvider.value(value: versionBloc),\n                  ],\n                  child: SigninOrSignupScreen(\n                    settings: settingRepo,\n                    username: state.queryParameters['username']!,\n                    isSignup: state.queryParameters['is_signup'] == 'true',\n                    signInMethod: state.queryParameters['sign_in_method']!,\n                    wechatBindToken: state.queryParameters['wechat_bind_token'],\n                  ),\n                ),\n              ),\n            ),\n            GoRoute(\n              path: '/user/change-password',\n              pageBuilder: (context, state) => transitionResolver(\n                ChangePasswordScreen(setting: settingRepo),\n              ),\n            ),\n            GoRoute(\n              path: '/user/destroy',\n              pageBuilder: (context, state) => transitionResolver(\n                DestroyAccountScreen(setting: settingRepo),\n              ),\n            ),\n            GoRoute(\n              path: '/signup',\n              pageBuilder: (context, state) => transitionResolver(\n                SignupScreen(\n                  settings: settingRepo,\n                  username: state.queryParameters['username'],\n                ),\n              ),\n            ),\n            GoRoute(\n              path: '/retrieve-password',\n              pageBuilder: (context, state) => transitionResolver(\n                RetrievePasswordScreen(\n                  username: state.queryParameters['username'],\n                  setting: settingRepo,\n                ),\n              ),\n            ),\n            GoRoute(\n              name: 'chat_anywhere',\n              path: '/chat-anywhere',\n              pageBuilder: (context, state) => transitionResolver(\n                MultiBlocProvider(\n                  providers: [\n                    BlocProvider.value(\n                      value: ChatBlocManager().getBloc(\n                        chatAnywhereRoomId,\n                        chatHistoryId: int.tryParse(state.queryParameters['chat_id'] ?? ''),\n                      ),\n                    ),\n                    BlocProvider.value(value: chatRoomBloc),\n                    BlocProvider(create: (context) => NotifyBloc()),\n                  ],\n                  child: HomeChatPage(\n                    stateManager: messageStateManager,\n                    setting: settingRepo,\n                    chatId: int.tryParse(state.queryParameters['chat_id'] ?? '0'),\n                    initialMessage: state.queryParameters['init_message'],\n                    model: state.queryParameters['model'] == '' ? null : state.queryParameters['model'],\n                    title: state.queryParameters['title'] == '' ? null : state.queryParameters['title'],\n                  ),\n                ),\n              ),\n            ),\n\n            GoRoute(\n              name: 'chat_chat_history',\n              path: '/chat-chat/history',\n              pageBuilder: (context, state) => transitionResolver(\n                MultiBlocProvider(\n                  providers: [\n                    BlocProvider(create: (context) => ChatChatBloc(chatMsgRepo)),\n                  ],\n                  child: HomeChatHistoryPage(\n                    setting: settingRepo,\n                    chatMessageRepo: chatMsgRepo,\n                  ),\n                ),\n              ),\n            ),\n            GoRoute(\n              path: '/lab/avatar-selector',\n              pageBuilder: (context, state) => transitionResolver(\n                const AvatarSelectorScreen(usage: AvatarUsage.room),\n              ),\n            ),\n            GoRoute(\n              path: '/lab/draw-board',\n              pageBuilder: (context, state) => transitionResolver(\n                const DrawboardScreen(),\n              ),\n            ),\n\n            GoRoute(\n              name: 'create-room',\n              path: '/create-room',\n              pageBuilder: (context, state) => transitionResolver(\n                MultiBlocProvider(\n                  providers: [BlocProvider.value(value: chatRoomBloc)],\n                  child: CharacterCreatePage(setting: settingRepo),\n                ),\n              ),\n            ),\n            GoRoute(\n              name: 'chat',\n              path: '/room/:room_id/chat',\n              pageBuilder: (context, state) {\n                final roomId = int.parse(state.pathParameters['room_id']!);\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider.value(\n                        value: ChatBlocManager().getBloc(roomId),\n                      ),\n                      BlocProvider.value(value: chatRoomBloc),\n                      BlocProvider(create: (context) => NotifyBloc()),\n                    ],\n                    child: CharacterChatPage(\n                      roomId: roomId,\n                      stateManager: messageStateManager,\n                      setting: settingRepo,\n                    ),\n                  ),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'room_setting',\n              path: '/room/:room_id/setting',\n              pageBuilder: (context, state) {\n                final roomId = int.parse(state.pathParameters['room_id']!);\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider.value(value: chatRoomBloc),\n                      BlocProvider.value(\n                        value: ChatBlocManager().getBloc(roomId),\n                      ),\n                    ],\n                    child: CharacterEditPage(roomId: roomId, setting: settingRepo),\n                  ),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'account-security-setting',\n              path: '/setting/account-security',\n              pageBuilder: (context, state) => transitionResolver(\n                MultiBlocProvider(\n                  providers: [\n                    BlocProvider.value(value: accountBloc),\n                  ],\n                  child: AccountSecurityScreen(\n                    settings: context.read<SettingRepository>(),\n                  ),\n                ),\n              ),\n            ),\n            GoRoute(\n              name: 'lab-user-center',\n              path: '/lab/user-center',\n              pageBuilder: (context, state) => transitionResolver(\n                MultiBlocProvider(\n                  providers: [\n                    BlocProvider.value(value: accountBloc),\n                    BlocProvider(create: (context) => CreativeIslandBloc(creativeIslandRepo)),\n                  ],\n                  child: UserCenterScreen(settings: context.read<SettingRepository>()),\n                ),\n              ),\n            ),\n\n            GoRoute(\n              name: 'setting-background-selector',\n              path: '/setting/background-selector',\n              pageBuilder: (context, state) => transitionResolver(\n                BlocProvider(\n                  create: (context) => BackgroundImageBloc(),\n                  child: BackgroundSelectorScreen(setting: settingRepo),\n                ),\n              ),\n            ),\n            GoRoute(\n              name: 'setting-openai-custom',\n              path: '/setting/openai-custom',\n              pageBuilder: (context, state) => transitionResolver(\n                OpenAISettingScreen(\n                  settings: settingRepo,\n                  source: state.queryParameters['source'],\n                ),\n              ),\n            ),\n\n            GoRoute(\n              name: 'creative-upscale',\n              path: '/creative-draw/create-upscale',\n              pageBuilder: (context, state) => transitionResolver(\n                MultiBlocProvider(\n                  providers: [\n                    BlocProvider(create: (context) => CreativeIslandBloc(creativeIslandRepo)),\n                  ],\n                  child: ImageEditDirectScreen(\n                    setting: settingRepo,\n                    title: AppLocale.superResolution.getString(context),\n                    apiEndpoint: 'upscale',\n                    note: state.queryParameters['note'],\n                    initWaitDuration: 15,\n                    initImage: state.queryParameters['init_image'],\n                  ),\n                ),\n              ),\n            ),\n            GoRoute(\n              name: 'creative-colorize',\n              path: '/creative-draw/create-colorize',\n              pageBuilder: (context, state) => transitionResolver(\n                MultiBlocProvider(\n                  providers: [\n                    BlocProvider(create: (context) => CreativeIslandBloc(creativeIslandRepo)),\n                  ],\n                  child: ImageEditDirectScreen(\n                    setting: settingRepo,\n                    title: AppLocale.colorizeImage.getString(context),\n                    apiEndpoint: 'colorize',\n                    note: state.queryParameters['note'],\n                    initWaitDuration: 15,\n                    initImage: state.queryParameters['init_image'],\n                  ),\n                ),\n              ),\n            ),\n            GoRoute(\n              name: 'creative-video',\n              path: '/creative-draw/create-video',\n              pageBuilder: (context, state) => transitionResolver(\n                MultiBlocProvider(\n                  providers: [\n                    BlocProvider(create: (context) => CreativeIslandBloc(creativeIslandRepo)),\n                  ],\n                  child: ImageEditDirectScreen(\n                    setting: settingRepo,\n                    title: '图生视频',\n                    apiEndpoint: 'image-to-video',\n                    note: state.queryParameters['note'],\n                    initWaitDuration: 60,\n                    initImage: state.queryParameters['init_image'],\n                  ),\n                ),\n              ),\n            ),\n            GoRoute(\n              name: 'creative-draw-gallery-preview',\n              path: '/creative-draw/gallery/:id',\n              pageBuilder: (context, state) => transitionResolver(\n                MultiBlocProvider(\n                  providers: [\n                    BlocProvider.value(value: galleryBloc),\n                  ],\n                  child: GalleryItemScreen(\n                    setting: settingRepo,\n                    galleryId: int.parse(state.pathParameters['id']!),\n                  ),\n                ),\n              ),\n            ),\n            GoRoute(\n              name: 'creative-draw-create',\n              path: '/creative-draw/create',\n              pageBuilder: (context, state) => transitionResolver(\n                MultiBlocProvider(\n                  providers: [\n                    BlocProvider.value(value: galleryBloc),\n                  ],\n                  child: DrawCreateScreen(\n                    setting: settingRepo,\n                    galleryCopyId: int.tryParse(\n                      state.queryParameters['gallery_copy_id'] ?? '',\n                    ),\n                    mode: state.queryParameters['mode']!,\n                    id: state.queryParameters['id']!,\n                    note: state.queryParameters['note'],\n                    initImage: state.queryParameters['init_image'],\n                  ),\n                ),\n              ),\n            ),\n            GoRoute(\n              name: 'creative-artistic-text',\n              path: '/creative-draw/artistic-text',\n              pageBuilder: (context, state) => transitionResolver(\n                MultiBlocProvider(\n                  providers: [\n                    BlocProvider.value(value: galleryBloc),\n                  ],\n                  child: ArtisticQRScreen(\n                    setting: settingRepo,\n                    galleryCopyId: int.tryParse(\n                      state.queryParameters['gallery_copy_id'] ?? '',\n                    ),\n                    type: state.queryParameters['type']!,\n                    id: state.queryParameters['id']!,\n                    note: state.queryParameters['note'],\n                  ),\n                ),\n              ),\n            ),\n            GoRoute(\n              name: 'creative-artistic-wordart',\n              path: '/creative-draw/artistic-wordart',\n              pageBuilder: (context, state) => transitionResolver(\n                MultiBlocProvider(\n                  providers: [\n                    BlocProvider.value(value: galleryBloc),\n                  ],\n                  child: ArtisticWordArtScreen(\n                    setting: settingRepo,\n                    galleryCopyId: int.tryParse(\n                      state.queryParameters['gallery_copy_id'] ?? '',\n                    ),\n                    id: state.queryParameters['id']!,\n                    note: state.queryParameters['note'],\n                  ),\n                ),\n              ),\n            ),\n            GoRoute(\n              name: 'creative-island-history-all',\n              path: '/creative-island/history',\n              pageBuilder: (context, state) {\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider(create: (context) => CreativeIslandBloc(creativeIslandRepo)),\n                    ],\n                    child: MyCreationScreen(\n                      setting: settingRepo,\n                      mode: state.queryParameters['mode'] ?? '',\n                    ),\n                  ),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'creative-island-models',\n              path: '/creative-island/models',\n              pageBuilder: (context, state) {\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider(create: (context) => CreativeIslandBloc(creativeIslandRepo)),\n                    ],\n                    child: CreativeModelScreen(setting: settingRepo),\n                  ),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'creative-island-history-item',\n              path: '/creative-island/:id/history/:item_id',\n              pageBuilder: (context, state) {\n                final id = state.pathParameters['id']!;\n                final itemId = int.tryParse(state.pathParameters['item_id']!);\n                final showErrorMessage = state.queryParameters['show_error'] == 'true';\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider(create: (context) => CreativeIslandBloc(creativeIslandRepo)),\n                    ],\n                    child: MyCreationItemPage(\n                      setting: settingRepo,\n                      islandId: id,\n                      itemId: itemId!,\n                      showErrorMessage: showErrorMessage,\n                    ),\n                  ),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'quota-details',\n              path: '/quota-details',\n              pageBuilder: (context, state) => transitionResolver(\n                PaymentHistoryScreen(setting: settingRepo),\n              ),\n            ),\n            GoRoute(\n              name: 'quota-usage-statistics',\n              path: '/quota-usage-statistics',\n              pageBuilder: (context, state) => transitionResolver(\n                QuotaUsageStatisticsScreen(setting: settingRepo),\n              ),\n            ),\n            GoRoute(\n              name: 'quota-usage-daily-details',\n              path: '/quota-usage-daily-details',\n              pageBuilder: (context, state) => transitionResolver(\n                QuotaUsageDetailScreen(\n                  setting: settingRepo,\n                  date: state.queryParameters['date'] ?? DateFormat('yyyy-MM-dd').format(DateTime.now()),\n                ),\n              ),\n            ),\n            GoRoute(\n              name: 'prompt-editor',\n              path: '/prompt-editor',\n              pageBuilder: (context, state) {\n                var prompt = state.queryParameters['prompt'] ?? '';\n                return transitionResolver(PromptScreen(prompt: prompt));\n              },\n            ),\n            GoRoute(\n              name: 'payment',\n              path: '/payment',\n              pageBuilder: (context, state) {\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider(create: ((context) => PaymentBloc())),\n                    ],\n                    child: PaymentScreen(setting: settingRepo),\n                  ),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'bind-phone',\n              path: '/bind-phone',\n              pageBuilder: (context, state) {\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider.value(value: accountBloc),\n                    ],\n                    child: BindPhoneScreen(\n                      setting: settingRepo,\n                      isSignIn: state.queryParameters['is_signin'] != 'false',\n                    ),\n                  ),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'diagnosis',\n              path: '/diagnosis',\n              pageBuilder: (context, state) => transitionResolver(\n                DiagnosisScreen(setting: settingRepo),\n              ),\n            ),\n            GoRoute(\n              name: 'free-statistics',\n              path: '/free-statistics',\n              pageBuilder: (context, state) => transitionResolver(\n                MultiBlocProvider(\n                  providers: [BlocProvider.value(value: freeCountBloc)],\n                  child: FreeStatisticsPage(setting: settingRepo),\n                ),\n              ),\n            ),\n            GoRoute(\n              name: 'custom-home-models',\n              path: '/setting/custom-home-models',\n              pageBuilder: (context, state) => transitionResolver(\n                CustomHomeModelsPage(setting: settingRepo),\n              ),\n            ),\n            GoRoute(\n              name: 'group-chat-chat',\n              path: '/group-chat/:group_id/chat',\n              pageBuilder: (context, state) {\n                final groupId = int.tryParse(state.pathParameters['group_id']!);\n\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider(\n                        create: ((context) => GroupChatBloc(stateManager: messageStateManager)),\n                      ),\n                      BlocProvider.value(value: chatRoomBloc),\n                    ],\n                    child: GroupChatPage(\n                      setting: settingRepo,\n                      stateManager: messageStateManager,\n                      groupId: groupId!,\n                    ),\n                  ),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'group-chat-create',\n              path: '/group-chat-create',\n              pageBuilder: (context, state) {\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider(\n                        create: ((context) => GroupChatBloc(stateManager: messageStateManager)),\n                      ),\n                      BlocProvider.value(value: chatRoomBloc),\n                    ],\n                    child: GroupCreatePage(setting: settingRepo),\n                  ),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'group-chat-edit',\n              path: '/group-chat/:group_id/edit',\n              pageBuilder: (context, state) {\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider(\n                        create: ((context) => GroupChatBloc(stateManager: messageStateManager)),\n                      ),\n                      BlocProvider.value(value: chatRoomBloc),\n                    ],\n                    child: GroupEditPage(\n                      setting: settingRepo,\n                      groupId: int.tryParse(state.pathParameters['group_id']!)!,\n                    ),\n                  ),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'user-api-keys',\n              path: '/setting/user-api-keys',\n              pageBuilder: (context, state) {\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider(\n                        create: ((context) => UserApiKeysBloc()),\n                      ),\n                    ],\n                    child: UserAPIKeysScreen(setting: settingRepo),\n                  ),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'notifications',\n              path: '/notifications',\n              pageBuilder: (context, state) {\n                return transitionResolver(\n                  NotificationScreen(setting: settingRepo),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'articles',\n              path: '/article',\n              pageBuilder: (context, state) {\n                return transitionResolver(\n                  ArticleScreen(\n                    settings: settingRepo,\n                    id: int.tryParse(state.queryParameters['id'] ?? '') ?? 0,\n                  ),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'web-payment-result',\n              path: '/payment/result',\n              pageBuilder: (context, state) {\n                return transitionResolver(WebPaymentResult(\n                  paymentId: state.queryParameters['payment_id']!,\n                  action: state.queryParameters['action'],\n                ));\n              },\n            ),\n            GoRoute(\n              name: 'web-payment-proxy',\n              path: '/payment/proxy',\n              pageBuilder: (context, state) {\n                return transitionResolver(WebPaymentProxy(\n                  setting: settingRepo,\n                  paymentId: state.queryParameters['id']!,\n                  paymentIntent: state.queryParameters['intent']!,\n                  price: state.queryParameters['price']!,\n                  publishableKey: state.queryParameters['key']!,\n                  finishAction: state.queryParameters['finish_action'] ?? 'close',\n                ));\n              },\n            ),\n\n            /// 管理员接口\n            GoRoute(\n              name: 'admin-dashboard',\n              path: '/admin/dashboard',\n              pageBuilder: (context, state) {\n                return transitionResolver(\n                  AdminDashboardPage(setting: settingRepo),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'admin-models',\n              path: '/admin/models',\n              pageBuilder: (context, state) {\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider(\n                        create: (context) => ModelBloc(),\n                      ),\n                    ],\n                    child: AdminModelsPage(setting: settingRepo),\n                  ),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'admin-models-create',\n              path: '/admin/models/create',\n              pageBuilder: (context, state) {\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider(\n                        create: (context) => ModelBloc(),\n                      ),\n                    ],\n                    child: AdminModelCreatePage(setting: settingRepo),\n                  ),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'admin-models-edit',\n              path: '/admin/models/edit/:id',\n              pageBuilder: (context, state) {\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider(\n                        create: (context) => ModelBloc(),\n                      ),\n                    ],\n                    child: AdminModelEditPage(\n                      setting: settingRepo,\n                      modelId: state.pathParameters['id']!,\n                    ),\n                  ),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'admin-channels',\n              path: '/admin/channels',\n              pageBuilder: (context, state) {\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider(\n                        create: (context) => ChannelBloc(),\n                      ),\n                    ],\n                    child: ChannelsPage(setting: settingRepo),\n                  ),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'admin-channels-create',\n              path: '/admin/channels/create',\n              pageBuilder: (context, state) {\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider(\n                        create: (context) => ChannelBloc(),\n                      ),\n                    ],\n                    child: ChannelAddPage(setting: settingRepo),\n                  ),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'admin-channels-edit',\n              path: '/admin/channels/edit/:id',\n              pageBuilder: (context, state) {\n                final channelId = int.parse(state.pathParameters['id']!);\n\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider(\n                        create: (context) => ChannelBloc(),\n                      ),\n                    ],\n                    child: ChannelEditPage(\n                      setting: settingRepo,\n                      channelId: channelId,\n                    ),\n                  ),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'admin-users',\n              path: '/admin/users',\n              pageBuilder: (context, state) {\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider(\n                        create: (context) => UserBloc(),\n                      ),\n                    ],\n                    child: AdminUsersPage(setting: settingRepo),\n                  ),\n                );\n              },\n            ),\n            GoRoute(\n              name: 'admin-users-detail',\n              path: '/admin/users/:id',\n              pageBuilder: (context, state) {\n                final userId = int.parse(state.pathParameters['id']!);\n\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider(\n                        create: (context) => UserBloc(),\n                      ),\n                    ],\n                    child: AdminUserPage(setting: settingRepo, userId: userId),\n                  ),\n                );\n              },\n            ),\n\n            GoRoute(\n              name: 'admin-payment-histories',\n              path: '/admin/payment/histories',\n              pageBuilder: (context, state) {\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider(\n                        create: (context) => AdminPaymentBloc(),\n                      ),\n                    ],\n                    child: PaymentHistoriesPage(setting: settingRepo),\n                  ),\n                );\n              },\n            ),\n\n            GoRoute(\n              name: 'admin-user-rooms',\n              path: '/admin/users/:id/rooms',\n              pageBuilder: (context, state) {\n                final userId = int.parse(state.pathParameters['id']!);\n\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider(\n                        create: (context) => AdminRoomBloc(),\n                      ),\n                    ],\n                    child: AdminRoomsPage(setting: settingRepo, userId: userId),\n                  ),\n                );\n              },\n            ),\n\n            GoRoute(\n              name: 'admin-user-rooms-messages',\n              path: '/admin/users/:id/rooms/:room_id/messages',\n              pageBuilder: (context, state) {\n                final userId = int.parse(state.pathParameters['id']!);\n                final roomId = int.parse(state.pathParameters['room_id']!);\n\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider(\n                        create: (context) => AdminRoomBloc(),\n                      ),\n                    ],\n                    child: AdminRoomMessagesPage(\n                      setting: settingRepo,\n                      userId: userId,\n                      roomId: roomId,\n                      roomType: int.parse(state.queryParameters['room_type']!),\n                    ),\n                  ),\n                );\n              },\n            ),\n\n            GoRoute(\n              name: 'admin-recently-messages',\n              path: '/admin/recently-messages',\n              pageBuilder: (context, state) {\n                return transitionResolver(\n                  MultiBlocProvider(\n                    providers: [\n                      BlocProvider(\n                        create: (context) => AdminRoomBloc(),\n                      ),\n                    ],\n                    child: AdminRecentlyMessagesPage(setting: settingRepo),\n                  ),\n                );\n              },\n            ),\n          ],\n        ),\n      ],\n    );\n  }\n\n  final SettingRepository settingRepo;\n  final ChatMessageRepository chatMsgRepo;\n  final OpenAIRepository openAIRepo;\n  final CacheRepository cacheRepo;\n  final CreativeIslandRepository creativeIslandRepo;\n\n  @override\n  State<MyApp> createState() => _MyAppState();\n}\n\nclass _MyAppState extends State<MyApp> {\n  @override\n  void initState() {\n    // 初始化多语言\n    // final defaultLanguage = resolveSystemLanguage(PlatformTool.localeName());\n    // var initLanguage =\n    //     widget.settingRepo.stringDefault(settingLanguage, defaultLanguage);\n\n    widget.localization.init(\n      mapLocales: [\n        const MapLocale('zh', AppLocale.zh),\n        const MapLocale('zh-CHS', AppLocale.zh),\n        const MapLocale('en', AppLocale.en),\n      ],\n      // initLanguageCode: initLanguage == '' ? defaultLanguage : initLanguage,\n      initLanguageCode: 'zh-CHS',\n    );\n\n    widget.localization.onTranslatedLanguage = (Locale? locale) {\n      setState(() {});\n    };\n\n    if (PlatformTool.isIOS() || PlatformTool.isAndroid()) {\n      registerWxApi(\n        appId: weixinAppId,\n        universalLink: universalLink,\n      );\n    }\n\n    // weChatResponseEventHandler.listen((event) {\n    //   print(\"=====================\");\n    //   print(\"errorCode: ${event.errCode}\");\n    //   print(\"errorMessage: ${event.errStr}\");\n    //   if (event is WeChatShareResponse) {\n    //     print(\"type: ${event.type}\");\n    //     print(\"success:${event.isSuccessful}\");\n    //   }\n    //   showSuccessMessage('分享成功', duration: const Duration(seconds: 3));\n    // });\n\n    super.initState();\n  }\n\n  // This widget is the root of your application.\n  @override\n  Widget build(BuildContext context) {\n    return MultiRepositoryProvider(\n      providers: [\n        RepositoryProvider<ChatMessageRepository>(create: (context) => widget.chatMsgRepo),\n        RepositoryProvider<OpenAIRepository>(create: (context) => widget.openAIRepo),\n        RepositoryProvider<SettingRepository>(create: (context) => widget.settingRepo),\n        RepositoryProvider<CacheRepository>(create: (context) => widget.cacheRepo),\n      ],\n      child: ChangeNotifierProvider(\n          create: (context) => AppTheme.get()\n            ..mode = AppTheme.themeModeFormString(widget.settingRepo.stringDefault(settingThemeMode, 'system')),\n          builder: (context, _) {\n            final appTheme = context.watch<AppTheme>();\n            return Sizer(\n              builder: (context, orientation, deviceType) {\n                return MaterialApp.router(\n                  title: 'AIdea',\n                  themeMode: appTheme.mode,\n                  theme: createLightThemeData(),\n                  darkTheme: createDarkThemeData(),\n                  debugShowCheckedModeBanner: false,\n                  builder: (context, child) {\n                    // 这里设置了全局字体固定大小，不随系统设置变更\n                    return MediaQuery(\n                      data: MediaQuery.of(context)\n                          .copyWith(textScaler: TextScaler.linear(PlatformTool.isDesktop() ? 0.95 : 1)),\n                      child: BotToastInit()(context, child),\n                    );\n                  },\n                  routerConfig: widget._router,\n                  supportedLocales: widget.localization.supportedLocales,\n                  localizationsDelegates: widget.localization.localizationsDelegates,\n                  scrollBehavior: PlatformTool.isAndroid() || PlatformTool.isIOS()\n                      ? null\n                      : const MaterialScrollBehavior().copyWith(\n                          dragDevices: {\n                            PointerDeviceKind.touch,\n                            PointerDeviceKind.mouse,\n                            PointerDeviceKind.stylus,\n                            PointerDeviceKind.trackpad,\n                          },\n                        ),\n                );\n              },\n            );\n          }),\n    );\n  }\n}\n\nThemeData createLightThemeData() {\n  return ThemeData.light(useMaterial3: true).copyWith(\n    extensions: [CustomColors.light],\n    textTheme: ThemeData(fontFamily: 'AlibabaPuHuiTi').textTheme,\n    appBarTheme: const AppBarTheme(\n      // backgroundColor: Color.fromARGB(255, 250, 250, 250),\n      backgroundColor: Colors.transparent,\n      scrolledUnderElevation: 0,\n    ),\n    iconButtonTheme: PlatformTool.isMacOS()\n        ? IconButtonThemeData(\n            style: ButtonStyle(\n              overlayColor: WidgetStateProperty.all(Colors.transparent),\n            ),\n          )\n        : null,\n    dividerColor: Colors.transparent,\n    dialogBackgroundColor: Colors.white,\n    dialogTheme: DialogTheme(\n      shape: RoundedRectangleBorder(\n        borderRadius: CustomSize.borderRadius,\n      ),\n      elevation: 0,\n    ),\n    textButtonTheme: TextButtonThemeData(\n      style: TextButton.styleFrom(\n        foregroundColor: const Color.fromARGB(255, 9, 185, 85), // This is a custom color variable\n      ),\n    ),\n  );\n}\n\nThemeData createDarkThemeData() {\n  return ThemeData.dark(useMaterial3: true).copyWith(\n    extensions: [CustomColors.dark],\n    textTheme: ThemeData(fontFamily: 'AlibabaPuHuiTi').primaryTextTheme,\n    appBarTheme: const AppBarTheme(\n      // backgroundColor: Color.fromARGB(255, 48, 48, 48),\n      backgroundColor: Colors.transparent,\n      scrolledUnderElevation: 0,\n    ),\n    iconButtonTheme: PlatformTool.isMacOS()\n        ? IconButtonThemeData(\n            style: ButtonStyle(\n              overlayColor: WidgetStateProperty.all(Colors.transparent),\n            ),\n          )\n        : null,\n    dividerColor: Colors.transparent,\n    dialogBackgroundColor: const Color.fromARGB(255, 48, 48, 48),\n    dialogTheme: DialogTheme(\n      shape: RoundedRectangleBorder(\n        borderRadius: CustomSize.borderRadius,\n      ),\n      elevation: 0,\n    ),\n    textButtonTheme: TextButtonThemeData(\n      style: TextButton.styleFrom(\n        foregroundColor: const Color.fromARGB(255, 9, 185, 85), // This is a custom color variable\n      ),\n    ),\n  );\n}\n"
  },
  {
    "path": "lib/page/admin/channels.dart",
    "content": "import 'package:askaide/bloc/channel_bloc.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api/admin/channels.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_initicon/flutter_initicon.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:flutter_slidable/flutter_slidable.dart';\nimport 'package:go_router/go_router.dart';\n\nclass ChannelsPage extends StatefulWidget {\n  final SettingRepository setting;\n  const ChannelsPage({\n    super.key,\n    required this.setting,\n  });\n\n  @override\n  State<ChannelsPage> createState() => _ChannelsPageState();\n}\n\nclass _ChannelsPageState extends State<ChannelsPage> {\n  // 渠道类型\n  List<AdminChannelType> channelTypes = [];\n\n  @override\n  void initState() {\n    context.read<ChannelBloc>().add(ChannelsLoadEvent());\n\n    // 加载渠道类型\n    APIServer().adminChannelTypes().then((value) {\n      if (context.mounted) {\n        setState(() {\n          channelTypes = value;\n        });\n      }\n    });\n\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: const Text(\n            'Channel',\n            style: TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n          actions: [\n            IconButton(\n              icon: const Icon(Icons.add),\n              onPressed: () {\n                context.push('/admin/channels/create').then((value) {\n                  context.read<ChannelBloc>().add(ChannelsLoadEvent());\n                });\n              },\n            ),\n          ],\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          child: RefreshIndicator(\n            color: customColors.linkColor,\n            onRefresh: () async {\n              context.read<ChannelBloc>().add(ChannelsLoadEvent());\n            },\n            displacement: 20,\n            child: BlocConsumer<ChannelBloc, ChannelState>(\n              listenWhen: (previous, current) => current is ChannelOperationResult,\n              listener: (context, state) {\n                if (state is ChannelOperationResult) {\n                  if (state.success) {\n                    showSuccessMessage(state.message);\n                    context.read<ChannelBloc>().add(ChannelsLoadEvent());\n                  } else {\n                    showErrorMessage(state.message);\n                  }\n                }\n              },\n              buildWhen: (previous, current) => current is ChannelsLoaded,\n              builder: (context, state) {\n                if (state is ChannelsLoaded) {\n                  return SafeArea(\n                    top: false,\n                    child: ListView.builder(\n                      padding: const EdgeInsets.all(5),\n                      itemCount: state.channels.length,\n                      itemBuilder: (context, index) {\n                        final channel = state.channels[index];\n\n                        return buildChannelItem(context, customColors, channel);\n                      },\n                    ),\n                  );\n                }\n\n                return const Center(\n                  child: CircularProgressIndicator(),\n                );\n              },\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget buildChannelItem(\n    BuildContext context,\n    CustomColors customColors,\n    AdminChannel channel,\n  ) {\n    return Container(\n      margin: const EdgeInsets.symmetric(\n        horizontal: 10,\n        vertical: 5,\n      ),\n      decoration: BoxDecoration(borderRadius: CustomSize.borderRadius),\n      child: Slidable(\n        endActionPane: ActionPane(\n          motion: const ScrollMotion(),\n          children: [\n            const SizedBox(width: 10),\n            SlidableAction(\n              label: AppLocale.delete.getString(context),\n              borderRadius: const BorderRadius.only(\n                topLeft: CustomSize.radius,\n                bottomLeft: CustomSize.radius,\n                topRight: CustomSize.radius,\n                bottomRight: CustomSize.radius,\n              ),\n              backgroundColor: Colors.red,\n              icon: Icons.delete,\n              onPressed: (_) {\n                openConfirmDialog(\n                  context,\n                  AppLocale.confirmToDeleteRoom.getString(context),\n                  () => context.read<ChannelBloc>().add(ChannelDeleteEvent(channel.id!)),\n                  danger: true,\n                );\n              },\n            ),\n          ],\n        ),\n        child: Material(\n          borderRadius: CustomSize.borderRadius,\n          color: customColors.columnBlockBackgroundColor,\n          child: InkWell(\n            borderRadius: const BorderRadius.all(CustomSize.radius),\n            onTap: () {\n              context.push('/admin/channels/edit/${channel.id}').then((value) {\n                context.read<ChannelBloc>().add(ChannelsLoadEvent());\n              });\n            },\n            child: Stack(\n              children: [\n                Row(\n                  mainAxisSize: MainAxisSize.min,\n                  children: [\n                    // 渠道头像\n                    Initicon(\n                      text: channel.name.split('、').join(' '),\n                      size: 50,\n                      backgroundColor: Colors.grey.withAlpha(100),\n                      borderRadius: const BorderRadius.only(topLeft: CustomSize.radius, bottomLeft: CustomSize.radius),\n                    ),\n                    // 渠道名称\n                    Expanded(\n                      child: Container(\n                        padding: const EdgeInsets.all(10),\n                        child: Column(\n                          crossAxisAlignment: CrossAxisAlignment.start,\n                          children: [\n                            Text(\n                              channel.name,\n                              style: const TextStyle(\n                                overflow: TextOverflow.ellipsis,\n                              ),\n                            ),\n                          ],\n                        ),\n                      ),\n                    ),\n                  ],\n                ),\n                Positioned(\n                  right: 0,\n                  top: 0,\n                  child: Container(\n                    padding: const EdgeInsets.all(10),\n                    child: Column(\n                      crossAxisAlignment: CrossAxisAlignment.start,\n                      children: [\n                        Text(\n                          channelTypes.firstWhere((e) => e.name == channel.type).text,\n                          style: TextStyle(\n                            fontSize: 10,\n                            overflow: TextOverflow.ellipsis,\n                            color: customColors.weakTextColor,\n                          ),\n                        ),\n                      ],\n                    ),\n                  ),\n                ),\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/admin/channels_add.dart",
    "content": "import 'package:askaide/bloc/channel_bloc.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/enhanced_input.dart';\nimport 'package:askaide/page/component/enhanced_textfield.dart';\nimport 'package:askaide/page/component/item_selector_search.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api/admin/channels.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass ChannelAddPage extends StatefulWidget {\n  final SettingRepository setting;\n  const ChannelAddPage({\n    super.key,\n    required this.setting,\n  });\n\n  @override\n  State<ChannelAddPage> createState() => _ChannelAddPageState();\n}\n\nclass _ChannelAddPageState extends State<ChannelAddPage> {\n  // 渠道类型\n  List<AdminChannelType> channelTypes = [];\n\n  final TextEditingController nameController = TextEditingController();\n  final TextEditingController typeController = TextEditingController();\n  final TextEditingController serverController = TextEditingController();\n  final TextEditingController secretController = TextEditingController();\n\n  /// 当前选中的渠道类型\n  String? selectedChannelType;\n\n  /// 用于控制是否显示高级选项\n  bool showAdvancedOptions = false;\n\n  /// 是否使用代理\n  bool usingProxy = false;\n\n  /// 是否是 Azure API\n  bool openaiAzure = false;\n\n  /// OpenAI Azure API 版本\n  final TextEditingController azureAPIVersionController = TextEditingController();\n\n  @override\n  void dispose() {\n    nameController.dispose();\n    typeController.dispose();\n    serverController.dispose();\n    secretController.dispose();\n    azureAPIVersionController.dispose();\n\n    super.dispose();\n  }\n\n  @override\n  void initState() {\n    // 加载渠道类型\n    APIServer().adminChannelTypes().then((value) {\n      if (context.mounted) {\n        setState(() {\n          channelTypes = value.where((e) => e.dynamicType).toList();\n        });\n      }\n    });\n\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: const Text(\n            'New Channel',\n            style: TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          child: BlocListener<ChannelBloc, ChannelState>(\n            listenWhen: (previous, current) => current is ChannelOperationResult,\n            listener: (context, state) {\n              if (state is ChannelOperationResult) {\n                if (state.success) {\n                  showSuccessMessage(state.message);\n                  context.pop();\n                } else {\n                  showErrorMessage(state.message);\n                }\n              }\n            },\n            child: Container(\n              padding: const EdgeInsets.all(10),\n              child: Column(\n                children: [\n                  ColumnBlock(\n                    children: [\n                      EnhancedTextField(\n                        labelText: 'Name',\n                        customColors: customColors,\n                        controller: nameController,\n                        textAlignVertical: TextAlignVertical.top,\n                        hintText: 'Enter channel name',\n                        maxLength: 100,\n                        showCounter: false,\n                      ),\n                      EnhancedInput(\n                        title: Text(\n                          'Type',\n                          style: TextStyle(\n                            color: customColors.textfieldLabelColor,\n                            fontSize: 16,\n                          ),\n                        ),\n                        value: Text(\n                          buildSelectedChannelTypeText(),\n                          style: TextStyle(\n                            color: customColors.textfieldValueColor,\n                            fontSize: 16,\n                          ),\n                        ),\n                        onPressed: () {\n                          openListSelectDialog(\n                            context,\n                            channelTypes.map((e) => SelectorItem(Text(e.text), e.name)).toList(),\n                            (value) {\n                              setState(() {\n                                selectedChannelType = value.value;\n                              });\n                              return true;\n                            },\n                            heightFactor: 0.5,\n                            value: selectedChannelType,\n                          );\n                        },\n                      ),\n                      EnhancedTextField(\n                        labelText: 'Server',\n                        customColors: customColors,\n                        controller: serverController,\n                        textAlignVertical: TextAlignVertical.top,\n                        hintText: 'https://api.openai.com/v1',\n                        maxLength: 255,\n                        showCounter: false,\n                      ),\n                      EnhancedTextField(\n                        labelText: 'API Key',\n                        customColors: customColors,\n                        controller: secretController,\n                        textAlignVertical: TextAlignVertical.top,\n                        hintText: 'Enter API Key',\n                        maxLength: 2048,\n                        obscureText: true,\n                        showCounter: false,\n                      ),\n                    ],\n                  ),\n                  // 高级选项\n                  if (showAdvancedOptions)\n                    ColumnBlock(\n                      innerPanding: 5,\n                      children: [\n                        Row(\n                          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                          children: [\n                            const Text(\n                              'Use Proxy',\n                              style: TextStyle(fontSize: 16),\n                            ),\n                            CupertinoSwitch(\n                              activeColor: customColors.linkColor,\n                              value: usingProxy,\n                              onChanged: (value) {\n                                setState(() {\n                                  usingProxy = value;\n                                });\n                              },\n                            ),\n                          ],\n                        ),\n                        Row(\n                          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                          children: [\n                            const Text(\n                              'Azure Mode',\n                              style: TextStyle(fontSize: 16),\n                            ),\n                            CupertinoSwitch(\n                              activeColor: customColors.linkColor,\n                              value: openaiAzure,\n                              onChanged: (value) {\n                                setState(() {\n                                  openaiAzure = value;\n                                });\n                              },\n                            ),\n                          ],\n                        ),\n                        EnhancedTextField(\n                          labelText: 'Version',\n                          customColors: customColors,\n                          controller: azureAPIVersionController,\n                          textAlignVertical: TextAlignVertical.top,\n                          hintText: '2023-05-15',\n                          maxLength: 30,\n                          showCounter: false,\n                        ),\n                      ],\n                    ),\n                  const SizedBox(height: 15),\n                  Row(\n                    children: [\n                      EnhancedButton(\n                        title: showAdvancedOptions\n                            ? AppLocale.simpleMode.getString(context)\n                            : AppLocale.professionalMode.getString(context),\n                        width: 120,\n                        backgroundColor: Colors.transparent,\n                        color: customColors.weakLinkColor,\n                        fontSize: 15,\n                        icon: Icon(\n                          showAdvancedOptions ? Icons.unfold_less : Icons.unfold_more,\n                          color: customColors.weakLinkColor,\n                          size: 15,\n                        ),\n                        onPressed: () {\n                          setState(() {\n                            showAdvancedOptions = !showAdvancedOptions;\n                          });\n                        },\n                      ),\n                      const SizedBox(width: 10),\n                      Expanded(\n                        flex: 1,\n                        child: EnhancedButton(\n                          title: AppLocale.save.getString(context),\n                          onPressed: onSubmit,\n                        ),\n                      ),\n                    ],\n                  ),\n                ],\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  /// 提交\n  void onSubmit() {\n    if (nameController.text.isEmpty) {\n      showErrorMessage('Please enter a channel name');\n      return;\n    }\n\n    if (selectedChannelType == null) {\n      showErrorMessage('Please select channel type');\n      return;\n    }\n\n    if (serverController.text.isEmpty) {\n      showErrorMessage('Please enter the server address');\n      return;\n    }\n\n    if (!serverController.text.startsWith('http://') && !serverController.text.startsWith('https://')) {\n      showErrorMessage('The server address format is incorrect');\n      return;\n    }\n\n    final req = AdminChannelAddReq(\n      name: nameController.text,\n      type: selectedChannelType!,\n      server: serverController.text,\n      secret: secretController.text,\n      meta: AdminChannelMeta(\n        usingProxy: usingProxy,\n        openaiAzure: openaiAzure,\n        openaiAzureAPIVersion: azureAPIVersionController.text,\n      ),\n    );\n\n    context.read<ChannelBloc>().add(ChannelCreateEvent(req));\n  }\n\n  /// 生成选中的渠道类型文本\n  String buildSelectedChannelTypeText() {\n    if (selectedChannelType == null) {\n      return 'Select';\n    }\n\n    return channelTypes.firstWhere((element) => element.name == selectedChannelType).text;\n  }\n}\n"
  },
  {
    "path": "lib/page/admin/channels_edit.dart",
    "content": "import 'package:askaide/bloc/channel_bloc.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/enhanced_input.dart';\nimport 'package:askaide/page/component/enhanced_textfield.dart';\nimport 'package:askaide/page/component/item_selector_search.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api/admin/channels.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\n\nclass ChannelEditPage extends StatefulWidget {\n  final SettingRepository setting;\n  final int channelId;\n  const ChannelEditPage({\n    super.key,\n    required this.setting,\n    required this.channelId,\n  });\n\n  @override\n  State<ChannelEditPage> createState() => _ChannelEditPageState();\n}\n\nclass _ChannelEditPageState extends State<ChannelEditPage> {\n  // 渠道类型\n  List<AdminChannelType> channelTypes = [];\n\n  final TextEditingController nameController = TextEditingController();\n  final TextEditingController typeController = TextEditingController();\n  final TextEditingController serverController = TextEditingController();\n  final TextEditingController secretController = TextEditingController();\n\n  /// 当前选中的渠道类型\n  String? selectedChannelType;\n\n  /// 用于控制是否显示高级选项\n  bool showAdvancedOptions = false;\n\n  /// 是否使用代理\n  bool usingProxy = false;\n\n  /// 是否是 Azure API\n  bool openaiAzure = false;\n\n  /// OpenAI Azure API 版本\n  final TextEditingController azureAPIVersionController = TextEditingController();\n\n  /// 是否锁定编辑\n  bool editLocked = true;\n\n  @override\n  void dispose() {\n    nameController.dispose();\n    typeController.dispose();\n    serverController.dispose();\n    secretController.dispose();\n    azureAPIVersionController.dispose();\n\n    super.dispose();\n  }\n\n  @override\n  void initState() {\n    // 加载渠道类型\n    APIServer().adminChannelTypes().then((value) {\n      if (context.mounted) {\n        setState(() {\n          channelTypes = value.where((e) => e.dynamicType).toList();\n        });\n      }\n    });\n\n    // 加载渠道信息\n    context.read<ChannelBloc>().add(ChannelLoadEvent(widget.channelId));\n\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: const Text(\n            'Edit Channel',\n            style: TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          child: BlocListener<ChannelBloc, ChannelState>(\n            listenWhen: (previous, current) => current is ChannelOperationResult || current is ChannelLoaded,\n            listener: (context, state) {\n              if (state is ChannelOperationResult) {\n                if (state.success) {\n                  showSuccessMessage(state.message);\n                  context.read<ChannelBloc>().add(ChannelLoadEvent(widget.channelId));\n                } else {\n                  showErrorMessage(state.message);\n                }\n              } else if (state is ChannelLoaded) {\n                nameController.text = state.channel.name;\n                selectedChannelType = state.channel.type;\n                serverController.text = state.channel.server ?? '';\n                secretController.text = state.channel.secret ?? '';\n                usingProxy = state.channel.meta?.usingProxy ?? false;\n                openaiAzure = state.channel.meta?.openaiAzure ?? false;\n                azureAPIVersionController.text = state.channel.meta?.openaiAzureAPIVersion ?? '';\n\n                setState(() {\n                  editLocked = false;\n                });\n              }\n            },\n            child: Container(\n              padding: const EdgeInsets.all(10),\n              child: Column(\n                children: [\n                  ColumnBlock(\n                    children: [\n                      EnhancedTextField(\n                        labelText: 'Name',\n                        customColors: customColors,\n                        controller: nameController,\n                        textAlignVertical: TextAlignVertical.top,\n                        hintText: 'Enter channel name',\n                        maxLength: 100,\n                        showCounter: false,\n                      ),\n                      EnhancedInput(\n                        title: Text(\n                          'Type',\n                          style: TextStyle(\n                            color: customColors.textfieldLabelColor,\n                            fontSize: 16,\n                          ),\n                        ),\n                        value: Text(\n                          buildSelectedChannelTypeText(),\n                          style: TextStyle(\n                            color: customColors.textfieldValueColor,\n                            fontSize: 16,\n                          ),\n                        ),\n                        onPressed: () {\n                          openListSelectDialog(\n                            context,\n                            channelTypes.map((e) => SelectorItem(Text(e.text), e.name)).toList(),\n                            (value) {\n                              setState(() {\n                                selectedChannelType = value.value;\n                              });\n                              return true;\n                            },\n                            heightFactor: 0.5,\n                            value: selectedChannelType,\n                          );\n                        },\n                      ),\n                      EnhancedTextField(\n                        labelText: 'Server',\n                        customColors: customColors,\n                        controller: serverController,\n                        textAlignVertical: TextAlignVertical.top,\n                        hintText: 'https://api.openai.com/v1',\n                        maxLength: 255,\n                        showCounter: false,\n                      ),\n                      EnhancedTextField(\n                        labelText: 'API Key',\n                        customColors: customColors,\n                        controller: secretController,\n                        textAlignVertical: TextAlignVertical.top,\n                        hintText: 'Enter API Key',\n                        maxLength: 2048,\n                        obscureText: true,\n                        showCounter: false,\n                      ),\n                    ],\n                  ),\n                  // 高级选项\n                  if (showAdvancedOptions)\n                    ColumnBlock(\n                      innerPanding: 5,\n                      children: [\n                        Row(\n                          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                          children: [\n                            const Text(\n                              'Use Proxy',\n                              style: TextStyle(fontSize: 16),\n                            ),\n                            CupertinoSwitch(\n                              activeColor: customColors.linkColor,\n                              value: usingProxy,\n                              onChanged: (value) {\n                                setState(() {\n                                  usingProxy = value;\n                                });\n                              },\n                            ),\n                          ],\n                        ),\n                        Row(\n                          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                          children: [\n                            const Text(\n                              'Azure Mode',\n                              style: TextStyle(fontSize: 16),\n                            ),\n                            CupertinoSwitch(\n                              activeColor: customColors.linkColor,\n                              value: openaiAzure,\n                              onChanged: (value) {\n                                setState(() {\n                                  openaiAzure = value;\n                                });\n                              },\n                            ),\n                          ],\n                        ),\n                        EnhancedTextField(\n                          labelText: 'Version',\n                          customColors: customColors,\n                          controller: azureAPIVersionController,\n                          textAlignVertical: TextAlignVertical.top,\n                          hintText: '2023-05-15',\n                          maxLength: 30,\n                          showCounter: false,\n                        ),\n                      ],\n                    ),\n                  const SizedBox(height: 15),\n                  Row(\n                    children: [\n                      EnhancedButton(\n                        title: showAdvancedOptions\n                            ? AppLocale.simpleMode.getString(context)\n                            : AppLocale.professionalMode.getString(context),\n                        width: 120,\n                        backgroundColor: Colors.transparent,\n                        color: customColors.weakLinkColor,\n                        fontSize: 15,\n                        icon: Icon(\n                          showAdvancedOptions ? Icons.unfold_less : Icons.unfold_more,\n                          color: customColors.weakLinkColor,\n                          size: 15,\n                        ),\n                        onPressed: () {\n                          setState(() {\n                            showAdvancedOptions = !showAdvancedOptions;\n                          });\n                        },\n                      ),\n                      const SizedBox(width: 10),\n                      Expanded(\n                        flex: 1,\n                        child: EnhancedButton(\n                          title: AppLocale.save.getString(context),\n                          onPressed: onSubmit,\n                          icon: editLocked\n                              ? const Icon(Icons.lock, color: Colors.white, size: 16)\n                              : const Icon(Icons.lock_open, color: Colors.white, size: 16),\n                        ),\n                      ),\n                    ],\n                  ),\n                ],\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  /// 提交\n  void onSubmit() {\n    if (editLocked) {\n      return;\n    }\n\n    if (nameController.text.isEmpty) {\n      showErrorMessage('Please enter a channel name');\n      return;\n    }\n\n    if (selectedChannelType == null) {\n      showErrorMessage('Please select channel type');\n      return;\n    }\n\n    if (serverController.text.isEmpty) {\n      showErrorMessage('Please enter the server address');\n      return;\n    }\n\n    if (!serverController.text.startsWith('http://') && !serverController.text.startsWith('https://')) {\n      showErrorMessage('The server address format is incorrect');\n      return;\n    }\n\n    final req = AdminChannelUpdateReq(\n      name: nameController.text,\n      type: selectedChannelType!,\n      server: serverController.text,\n      secret: secretController.text,\n      meta: AdminChannelMeta(\n        usingProxy: usingProxy,\n        openaiAzure: openaiAzure,\n        openaiAzureAPIVersion: azureAPIVersionController.text,\n      ),\n    );\n\n    context.read<ChannelBloc>().add(ChannelUpdateEvent(widget.channelId, req));\n  }\n\n  /// 生成选中的渠道类型文本\n  String buildSelectedChannelTypeText() {\n    if (selectedChannelType == null) {\n      return 'Select';\n    }\n\n    return channelTypes.firstWhere((element) => element.name == selectedChannelType).text;\n  }\n}\n"
  },
  {
    "path": "lib/page/admin/dashboard.dart",
    "content": "import 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:settings_ui/settings_ui.dart';\n\nclass AdminDashboardPage extends StatefulWidget {\n  final SettingRepository setting;\n  const AdminDashboardPage({super.key, required this.setting});\n\n  @override\n  State<AdminDashboardPage> createState() => _AdminDashboardPageState();\n}\n\nclass _AdminDashboardPageState extends State<AdminDashboardPage> {\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: const Text(\n            'Dashboard',\n            style: TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          backgroundColor: customColors.backgroundColor,\n          enabled: false,\n          child: Column(\n            children: [\n              Expanded(\n                child: SettingsList(\n                  platform: DevicePlatform.iOS,\n                  lightTheme: const SettingsThemeData(\n                    settingsListBackground: Colors.transparent,\n                    settingsSectionBackground: Color.fromARGB(255, 255, 255, 255),\n                  ),\n                  darkTheme: const SettingsThemeData(\n                    settingsListBackground: Colors.transparent,\n                    settingsSectionBackground: Color.fromARGB(255, 44, 44, 46),\n                    titleTextColor: Color.fromARGB(255, 239, 239, 239),\n                  ),\n                  sections: [\n                    SettingsSection(\n                      title: const Text('Usage'),\n                      tiles: [\n                        SettingsTile(\n                          title: const Text('Creation Island History'),\n                          trailing: const Icon(\n                            CupertinoIcons.chevron_forward,\n                            size: 18,\n                            color: Colors.grey,\n                          ),\n                          onPressed: (context) {\n                            context.push('/creative-island/models');\n                          },\n                        ),\n                        SettingsTile(\n                          title: const Text('Chat History'),\n                          trailing: const Icon(\n                            CupertinoIcons.chevron_forward,\n                            size: 18,\n                            color: Colors.grey,\n                          ),\n                          onPressed: (context) {\n                            context.push('/admin/recently-messages');\n                          },\n                        ),\n                      ],\n                    ),\n                    SettingsSection(\n                      title: const Text('Users & Revenue'),\n                      tiles: [\n                        SettingsTile(\n                          title: const Text('User Management'),\n                          trailing: const Icon(\n                            CupertinoIcons.chevron_forward,\n                            size: 18,\n                            color: Colors.grey,\n                          ),\n                          onPressed: (context) {\n                            context.push('/admin/users');\n                          },\n                        ),\n                        SettingsTile(\n                          title: const Text('Payment Order History'),\n                          trailing: const Icon(\n                            CupertinoIcons.chevron_forward,\n                            size: 18,\n                            color: Colors.grey,\n                          ),\n                          onPressed: (context) {\n                            context.push('/admin/payment/histories');\n                          },\n                        ),\n                      ],\n                    ),\n                    SettingsSection(\n                      title: const Text('Model management'),\n                      tiles: [\n                        SettingsTile(\n                          title: const Text('Channel'),\n                          trailing: const Icon(\n                            CupertinoIcons.chevron_forward,\n                            size: 18,\n                            color: Colors.grey,\n                          ),\n                          onPressed: (context) {\n                            context.push('/admin/channels');\n                          },\n                        ),\n                        SettingsTile(\n                          title: const Text('Large Language Model'),\n                          trailing: const Icon(\n                            CupertinoIcons.chevron_forward,\n                            size: 18,\n                            color: Colors.grey,\n                          ),\n                          onPressed: (context) {\n                            context.push('/admin/models');\n                          },\n                        ),\n                      ],\n                    ),\n                    SettingsSection(\n                      title: const Text('System settings'),\n                      tiles: [\n                        SettingsTile(\n                          title: const Text('Refresh Config Cache'),\n                          trailing: const Icon(\n                            CupertinoIcons.chevron_forward,\n                            size: 18,\n                            color: Colors.grey,\n                          ),\n                          onPressed: (context) {\n                            openConfirmDialog(\n                              context,\n                              'Reload all system configurations.\\n Are you sure you want to proceed?',\n                              () {\n                                APIServer().adminSettingsReload().then((value) {\n                                  showSuccessMessage('Update successful');\n                                }).onError((error, stackTrace) {\n                                  showErrorMessageEnhanced(context, error!);\n                                });\n                              },\n                            );\n                          },\n                        ),\n                      ],\n                    ),\n                  ],\n                  contentPadding: const EdgeInsets.all(0),\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/admin/messages.dart",
    "content": "import 'package:askaide/bloc/admin_room_bloc.dart';\nimport 'package:askaide/helper/model.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/chat/chat_preview.dart';\nimport 'package:askaide/page/component/chat/message_state_manager.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/random_avatar.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/model/message.dart';\nimport 'package:askaide/repo/model/model.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:intl/intl.dart';\n\nclass AdminRoomMessagesPage extends StatefulWidget {\n  final SettingRepository setting;\n  final int userId;\n  final int roomId;\n  final int roomType;\n  const AdminRoomMessagesPage({\n    super.key,\n    required this.setting,\n    required this.userId,\n    required this.roomId,\n    required this.roomType,\n  });\n\n  @override\n  State<AdminRoomMessagesPage> createState() => _AdminRoomMessagesPageState();\n}\n\nclass _AdminRoomMessagesPageState extends State<AdminRoomMessagesPage> {\n  final ChatPreviewController controller = ChatPreviewController();\n  Map<String, Model> models = {};\n\n  @override\n  void dispose() {\n    controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  void initState() {\n    context.read<AdminRoomBloc>().add(AdminRoomLoadEvent(\n          userId: widget.userId,\n          roomId: widget.roomId,\n        ));\n    context.read<AdminRoomBloc>().add(AdminRoomRecentlyMessagesLoadEvent(\n          userId: widget.userId,\n          roomId: widget.roomId,\n          roomType: widget.roomType,\n        ));\n\n    ModelAggregate.models().then((value) {\n      setState(() {\n        for (var element in value) {\n          models[element.id] = element;\n        }\n      });\n    });\n\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: BlocBuilder<AdminRoomBloc, AdminRoomState>(\n            buildWhen: (previous, current) => current is AdminRoomLoaded,\n            builder: (context, state) {\n              if (state is AdminRoomLoaded) {\n                return Text(\n                  state.room.name,\n                  style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n                );\n              }\n\n              return const Text(\n                'Character',\n                style: TextStyle(fontSize: CustomSize.appBarTitleSize),\n              );\n            },\n          ),\n          centerTitle: true,\n          backgroundColor: customColors.backgroundContainerColor,\n        ),\n        body: BackgroundContainer(\n          setting: widget.setting,\n          child: RefreshIndicator(\n            color: customColors.linkColor,\n            onRefresh: () async {\n              context.read<AdminRoomBloc>().add(AdminRoomRecentlyMessagesLoadEvent(\n                    userId: widget.userId,\n                    roomId: widget.roomId,\n                    roomType: widget.roomType,\n                  ));\n            },\n            displacement: 20,\n            child: BlocConsumer<AdminRoomBloc, AdminRoomState>(\n              listener: (context, state) {\n                if (state is AdminRoomOperationResult) {\n                  if (state.success) {\n                    showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                  } else {\n                    showErrorMessage(AppLocale.operateFailed.getString(context));\n                  }\n                }\n              },\n              buildWhen: (previous, current) => current is AdminRoomRecentlyMessagesLoaded,\n              builder: (context, state) {\n                if (state is AdminRoomRecentlyMessagesLoaded) {\n                  return SafeArea(\n                    top: false,\n                    child: state.messages.isNotEmpty\n                        ? ChatPreview(\n                            padding: const EdgeInsets.only(top: 15, bottom: 15),\n                            messages: state.messages.reversed.map((e) {\n                              if (e.model != null) {\n                                final model = models[e.model];\n                                if (model != null) {\n                                  if (e.avatarUrl == null && model.avatarUrl != null) {\n                                    e.avatarUrl = model.avatarUrl;\n                                  }\n\n                                  e.senderName ??= model.name;\n                                }\n                              }\n\n                              return MessageWithState(e, MessageState());\n                            }).toList(),\n                            controller: controller,\n                            supportBloc: false,\n                            senderNameBuilder: (message) {\n                              if (message.role == Role.sender || message.senderName == null) {\n                                return null;\n                              }\n\n                              return Container(\n                                margin: const EdgeInsets.only(\n                                  left: 10,\n                                  bottom: 5,\n                                  right: 5,\n                                ),\n                                child: Row(\n                                  mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                                  children: [\n                                    Text(\n                                      message.senderName!,\n                                      style: const TextStyle(fontSize: 12),\n                                    ),\n                                    if (message.ts != null)\n                                      Text(\n                                        '  ${DateFormat('yyyy/MM/dd HH:mm').format(message.ts!.toLocal())}',\n                                        style: const TextStyle(\n                                          fontSize: 12,\n                                          color: Colors.grey,\n                                        ),\n                                      ),\n                                  ],\n                                ),\n                              );\n                            },\n                            avatarBuilder: (message) {\n                              if (message.role == Role.sender) {\n                                return null;\n                              }\n\n                              if (message.avatarUrl != null) {\n                                return RemoteAvatar(\n                                  avatarUrl: message.avatarUrl!,\n                                  size: 30,\n                                );\n                              }\n\n                              if (message.model != null) {\n                                final model = models[message.model];\n                                if (model != null && model.avatarUrl != null) {\n                                  return RemoteAvatar(\n                                    avatarUrl: model.avatarUrl!,\n                                    size: 30,\n                                  );\n                                }\n                              }\n\n                              return null;\n                            },\n                          )\n                        : const Center(child: Text('No messages')),\n                  );\n                }\n\n                return const Center(\n                  child: CircularProgressIndicator(),\n                );\n              },\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/admin/models.dart",
    "content": "import 'package:askaide/bloc/model_bloc.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/image.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api/admin/channels.dart';\nimport 'package:askaide/repo/api/admin/models.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:cached_network_image/cached_network_image.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_initicon/flutter_initicon.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:flutter_slidable/flutter_slidable.dart';\nimport 'package:go_router/go_router.dart';\n\nclass AdminModelsPage extends StatefulWidget {\n  final SettingRepository setting;\n  const AdminModelsPage({\n    super.key,\n    required this.setting,\n  });\n\n  @override\n  State<AdminModelsPage> createState() => _AdminModelsPageState();\n}\n\nclass _AdminModelsPageState extends State<AdminModelsPage> {\n  // 渠道\n  List<AdminChannel> channels = [];\n\n  // 搜索关键字\n  String keyword = '';\n\n  /// 查找渠道\n  AdminChannel searchChannel(AdminModelProvider provider) {\n    return channels.firstWhere(\n      (e) {\n        if (e.id == null && (provider.id == null || provider.id == 0)) {\n          return provider.name == e.type;\n        }\n\n        return provider.id == e.id;\n      },\n      orElse: () {\n        return AdminChannel(\n          id: provider.id,\n          name: '未知',\n          type: 'unknown',\n        );\n      },\n    );\n  }\n\n  @override\n  void initState() {\n    // 加载渠道\n    APIServer().adminChannelsAgg().then((value) {\n      if (context.mounted) {\n        setState(() {\n          channels = value;\n        });\n\n        // 加载模型列表\n        context.read<ModelBloc>().add(ModelsLoadEvent());\n      }\n    });\n\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: const Text(\n            'Large Language Model',\n            style: TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n          actions: [\n            IconButton(\n              icon: const Icon(Icons.add),\n              onPressed: () {\n                context.push('/admin/models/create').then((value) {\n                  context.read<ModelBloc>().add(ModelsLoadEvent());\n                });\n              },\n            ),\n          ],\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          child: Column(\n            children: [\n              Container(\n                padding: const EdgeInsets.only(left: 10, right: 10, bottom: 5),\n                child: TextField(\n                  textAlignVertical: TextAlignVertical.center,\n                  style: TextStyle(color: customColors.dialogDefaultTextColor),\n                  decoration: InputDecoration(\n                    hintText: AppLocale.search.getString(context),\n                    hintStyle: TextStyle(\n                      color: customColors.dialogDefaultTextColor,\n                    ),\n                    prefixIcon: Icon(\n                      Icons.search,\n                      color: customColors.dialogDefaultTextColor,\n                    ),\n                    isDense: true,\n                    border: InputBorder.none,\n                  ),\n                  onChanged: (value) => setState(() => keyword = value),\n                ),\n              ),\n              Expanded(\n                child: RefreshIndicator(\n                  color: customColors.linkColor,\n                  onRefresh: () async {\n                    context.read<ModelBloc>().add(ModelsLoadEvent());\n                  },\n                  displacement: 20,\n                  child: BlocConsumer<ModelBloc, ModelState>(\n                    listenWhen: (previous, current) => current is ModelOperationResult,\n                    listener: (context, state) {\n                      if (state is ModelOperationResult) {\n                        if (state.success) {\n                          showSuccessMessage(state.message);\n                          context.read<ModelBloc>().add(ModelsLoadEvent());\n                        } else {\n                          showErrorMessage(state.message);\n                        }\n                      }\n                    },\n                    buildWhen: (previous, current) => current is ModelsLoaded,\n                    builder: (context, state) {\n                      if (state is ModelsLoaded) {\n                        final models = state.models\n                            .where((e) =>\n                                keyword == '' ||\n                                e.name.toLowerCase().contains(keyword.toLowerCase()) ||\n                                e.modelId.toLowerCase().contains(keyword.toLowerCase()) ||\n                                (e.description ?? '').toLowerCase().contains(keyword.toLowerCase()))\n                            .toList();\n                        return SafeArea(\n                          top: false,\n                          child: ListView.builder(\n                            padding: const EdgeInsets.all(5),\n                            itemCount: models.length,\n                            itemBuilder: (context, index) {\n                              final mod = models[index];\n\n                              return buildModelItem(context, customColors, mod);\n                            },\n                          ),\n                        );\n                      }\n\n                      return const Center(\n                        child: CircularProgressIndicator(),\n                      );\n                    },\n                  ),\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget buildModelItem(\n    BuildContext context,\n    CustomColors customColors,\n    AdminModel mod,\n  ) {\n    return Container(\n      margin: const EdgeInsets.symmetric(\n        horizontal: 10,\n        vertical: 5,\n      ),\n      decoration: BoxDecoration(borderRadius: CustomSize.borderRadius),\n      child: Slidable(\n        endActionPane: ActionPane(\n          motion: const ScrollMotion(),\n          children: [\n            const SizedBox(width: 10),\n            SlidableAction(\n              label: AppLocale.delete.getString(context),\n              borderRadius: const BorderRadius.only(\n                topLeft: CustomSize.radius,\n                bottomLeft: CustomSize.radius,\n                topRight: CustomSize.radius,\n                bottomRight: CustomSize.radius,\n              ),\n              backgroundColor: Colors.red,\n              icon: Icons.delete,\n              onPressed: (_) {\n                openConfirmDialog(\n                  context,\n                  AppLocale.confirmToDeleteRoom.getString(context),\n                  () => context.read<ModelBloc>().add(ModelDeleteEvent(mod.modelId)),\n                  danger: true,\n                );\n              },\n            ),\n          ],\n        ),\n        child: Material(\n          borderRadius: CustomSize.borderRadius,\n          color: customColors.columnBlockBackgroundColor,\n          child: InkWell(\n            borderRadius: CustomSize.borderRadiusAll,\n            onTap: () {\n              context.push('/admin/models/edit/${Uri.encodeComponent(mod.modelId)}').then((value) {\n                context.read<ModelBloc>().add(ModelsLoadEvent());\n              });\n            },\n            child: Stack(\n              children: [\n                Row(\n                  mainAxisSize: MainAxisSize.min,\n                  children: [\n                    // 头像\n                    Stack(\n                      children: [\n                        buildAvatar(mod),\n                        if (mod.isVision)\n                          Positioned(\n                            left: 0,\n                            bottom: 0,\n                            child: ClipRRect(\n                              borderRadius: const BorderRadius.only(bottomLeft: CustomSize.radius),\n                              child: Container(\n                                padding: const EdgeInsets.all(3),\n                                width: 80,\n                                color: Colors.black.withAlpha(30),\n                                child: Row(\n                                  mainAxisAlignment: MainAxisAlignment.center,\n                                  children: [\n                                    const Icon(\n                                      Icons.remove_red_eye_outlined,\n                                      color: Colors.white,\n                                      size: 12,\n                                    ),\n                                    const SizedBox(width: 3),\n                                    Text(\n                                      AppLocale.visionTag.getString(context),\n                                      style: const TextStyle(\n                                        color: Colors.white,\n                                        fontSize: 12,\n                                      ),\n                                    )\n                                  ],\n                                ),\n                              ),\n                            ),\n                          )\n                      ],\n                    ),\n                    // 名称\n                    Expanded(\n                      child: Container(\n                        padding: const EdgeInsets.all(15),\n                        child: Column(\n                          crossAxisAlignment: CrossAxisAlignment.start,\n                          children: [\n                            Text(\n                              mod.name,\n                              style: TextStyle(\n                                overflow: TextOverflow.ellipsis,\n                                fontWeight: mod.enabled ? FontWeight.bold : FontWeight.normal,\n                                color: mod.enabled ? null : customColors.weakLinkColor?.withAlpha(100),\n                                decoration: mod.enabled ? null : TextDecoration.lineThrough,\n                              ),\n                            ),\n                            const SizedBox(height: 5),\n                            Text(\n                              buildModelDescription(mod),\n                              style: TextStyle(\n                                fontSize: 10,\n                                overflow: TextOverflow.ellipsis,\n                                color: customColors.weakTextColor,\n                              ),\n                              maxLines: 2,\n                            ),\n                          ],\n                        ),\n                      ),\n                    ),\n                  ],\n                ),\n                Positioned(\n                  right: 0,\n                  top: 0,\n                  child: Container(\n                    padding: const EdgeInsets.all(10),\n                    width: MediaQuery.of(context).size.width / 2.0,\n                    alignment: Alignment.centerRight,\n                    child: Column(\n                      crossAxisAlignment: CrossAxisAlignment.start,\n                      children: [\n                        Text(\n                          mod.providers.map((e) => searchChannel(e).display).join('|'),\n                          style: TextStyle(\n                            fontSize: 10,\n                            overflow: TextOverflow.ellipsis,\n                            color: customColors.weakTextColor,\n                          ),\n                        ),\n                      ],\n                    ),\n                  ),\n                ),\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget buildAvatar(AdminModel mod) {\n    if (mod.avatarUrl != null && mod.avatarUrl!.startsWith('http')) {\n      return SizedBox(\n        width: 80,\n        height: 80,\n        child: ClipRRect(\n          borderRadius: const BorderRadius.only(topLeft: CustomSize.radius, bottomLeft: CustomSize.radius),\n          child: CachedNetworkImage(\n            imageUrl: imageURL(mod.avatarUrl!, qiniuImageTypeAvatar),\n            fit: BoxFit.fill,\n          ),\n        ),\n      );\n    }\n\n    return Initicon(\n      text: mod.name.split('、').join(' '),\n      size: 80,\n      backgroundColor: Colors.grey.withAlpha(100),\n      borderRadius: const BorderRadius.only(topLeft: CustomSize.radius, bottomLeft: CustomSize.radius),\n    );\n  }\n\n  String buildModelDescription(AdminModel mod) {\n    String desc = '';\n    if (mod.inputPrice > 0 || mod.outputPrice > 0 || mod.perReqPrice > 0) {\n      desc += '💰 ';\n      if (mod.inputPrice > 0 || mod.outputPrice > 0) {\n        if (mod.inputPrice == mod.outputPrice) {\n          desc += 'IO${AppLocale.creditUnit.getString(context)}${mod.inputPrice} ';\n        } else {\n          desc +=\n              'I${AppLocale.creditUnit.getString(context)}${mod.inputPrice} O${AppLocale.creditUnit.getString(context)}${mod.outputPrice} ';\n        }\n      }\n\n      if (mod.perReqPrice > 0) {\n        desc += 'R${AppLocale.creditUnit.getString(context)}${mod.perReqPrice}';\n      }\n    }\n\n    if (mod.maxContext > 0) {\n      if (desc.isNotEmpty) {\n        desc += '，';\n      }\n\n      desc += '🎞️ ${mod.maxContext} Tokens';\n    }\n\n    if (mod.meta != null && mod.meta!.tag != null && mod.meta!.tag != '') {\n      desc += ' | ${mod.meta!.tag}';\n    }\n\n    if (desc != '') {\n      desc += '\\n';\n    }\n\n    return desc + (mod.description ?? '');\n  }\n}\n"
  },
  {
    "path": "lib/page/admin/models_add.dart",
    "content": "import 'dart:io';\nimport 'dart:ui';\n\nimport 'package:animated_list_plus/animated_list_plus.dart';\nimport 'package:animated_list_plus/transitions.dart';\nimport 'package:askaide/bloc/model_bloc.dart';\nimport 'package:askaide/helper/upload.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/avatar_selector.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/enhanced_input.dart';\nimport 'package:askaide/page/component/enhanced_textfield.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/item_selector_search.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/random_avatar.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/weak_text_button.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api/admin/channels.dart';\nimport 'package:askaide/repo/api/admin/models.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:auto_size_text/auto_size_text.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:flutter_slidable/flutter_slidable.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:quickalert/models/quickalert_type.dart';\n\nclass AdminModelCreatePage extends StatefulWidget {\n  final SettingRepository setting;\n  const AdminModelCreatePage({\n    super.key,\n    required this.setting,\n  });\n\n  @override\n  State<AdminModelCreatePage> createState() => _AdminModelCreatePageState();\n}\n\nclass _AdminModelCreatePageState extends State<AdminModelCreatePage> {\n  final TextEditingController nameController = TextEditingController();\n  final TextEditingController modelIdController = TextEditingController();\n  final TextEditingController shortNameController = TextEditingController();\n  final TextEditingController descriptionController = TextEditingController();\n  final TextEditingController maxContextController = TextEditingController();\n  final TextEditingController inputPriceController = TextEditingController();\n  final TextEditingController outputPriceController = TextEditingController();\n  final TextEditingController perReqPriceController = TextEditingController();\n  final TextEditingController promptController = TextEditingController();\n  final TextEditingController categoryController = TextEditingController();\n  final TextEditingController searchPriceController = TextEditingController();\n\n  /// 用于控制是否显示高级选项\n  bool showAdvancedOptions = false;\n\n  /// 视觉能力\n  bool supportVision = false;\n\n  /// 受限模型\n  bool restricted = false;\n\n  /// 模型状态\n  bool modelEnabled = true;\n\n  /// 模型头像\n  String? avatarUrl;\n  List<String> avatarPresets = [];\n\n  /// 是否是上新\n  bool isNew = false;\n\n  /// 是否是推荐模型\n  bool isRecommended = false;\n\n  /// 是否启用搜索\n  bool enableSearch = false;\n\n  /// 是否启用推理\n  bool enableReasoning = false;\n\n  /// 温度\n  double temperature = 0.0;\n  // 搜索结果数量\n  int searchCount = 3;\n\n  /// Tag\n  final TextEditingController tagController = TextEditingController();\n  String? tagTextColor;\n  String? tagBgColor;\n\n  // 模型渠道\n  List<AdminChannel> modelChannels = [];\n  // 选择的渠道\n  List<AdminModelProvider> providers = [];\n\n  @override\n  void dispose() {\n    nameController.dispose();\n    modelIdController.dispose();\n    shortNameController.dispose();\n    descriptionController.dispose();\n    maxContextController.dispose();\n    inputPriceController.dispose();\n    outputPriceController.dispose();\n    perReqPriceController.dispose();\n    promptController.dispose();\n    categoryController.dispose();\n    tagController.dispose();\n    searchPriceController.dispose();\n\n    super.dispose();\n  }\n\n  @override\n  void initState() {\n    // 加载预设头像\n    APIServer().avatars().then((value) {\n      avatarPresets = value;\n    });\n    // 加载模型渠道\n    APIServer().adminChannelsAgg().then((value) {\n      modelChannels = value;\n    });\n\n    // 初始值设置\n    maxContextController.value = const TextEditingValue(text: '7500');\n    inputPriceController.value = const TextEditingValue(text: '0');\n    outputPriceController.value = const TextEditingValue(text: '0');\n    perReqPriceController.value = const TextEditingValue(text: '0');\n    searchPriceController.value = const TextEditingValue(text: '0');\n\n    providers.add(AdminModelProvider());\n\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: const Text(\n            'New Model',\n            style: TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          child: SingleChildScrollView(\n            child: BlocListener<ModelBloc, ModelState>(\n              listenWhen: (previous, current) => current is ModelOperationResult,\n              listener: (context, state) {\n                if (state is ModelOperationResult) {\n                  if (state.success) {\n                    showSuccessMessage(state.message);\n                    context.pop();\n                  } else {\n                    showErrorMessage(state.message);\n                  }\n                }\n              },\n              child: Container(\n                padding: const EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 20),\n                child: Column(\n                  children: [\n                    ColumnBlock(\n                      children: [\n                        EnhancedTextField(\n                          labelText: 'ID',\n                          customColors: customColors,\n                          controller: modelIdController,\n                          textAlignVertical: TextAlignVertical.top,\n                          hintText: 'Enter a unique ID',\n                          maxLength: 100,\n                          showCounter: false,\n                        ),\n                        EnhancedTextField(\n                          labelText: 'Vendor',\n                          customColors: customColors,\n                          controller: categoryController,\n                          textAlignVertical: TextAlignVertical.top,\n                          hintText: 'Enter a vendor name (Optional)',\n                          maxLength: 100,\n                          showCounter: false,\n                        ),\n                        EnhancedTextField(\n                          labelText: 'Name',\n                          customColors: customColors,\n                          controller: nameController,\n                          textAlignVertical: TextAlignVertical.top,\n                          hintText: 'Enter a model name',\n                          maxLength: 100,\n                          showCounter: false,\n                        ),\n                        EnhancedInput(\n                          padding: const EdgeInsets.only(top: 10, bottom: 5),\n                          title: Text(\n                            'Avatar',\n                            style: TextStyle(\n                              color: customColors.textfieldLabelColor,\n                              fontSize: 16,\n                            ),\n                          ),\n                          value: Row(\n                            mainAxisAlignment: MainAxisAlignment.end,\n                            mainAxisSize: MainAxisSize.min,\n                            children: [\n                              Container(\n                                width: 45,\n                                height: 45,\n                                decoration: BoxDecoration(\n                                  borderRadius: CustomSize.borderRadius,\n                                  image: avatarUrl == null\n                                      ? null\n                                      : DecorationImage(\n                                          image: (avatarUrl!.startsWith('http')\n                                              ? CachedNetworkImageProviderEnhanced(avatarUrl!)\n                                              : FileImage(File(avatarUrl!))) as ImageProvider,\n                                          fit: BoxFit.cover,\n                                        ),\n                                ),\n                                child: avatarUrl == null\n                                    ? const Center(\n                                        child: Icon(\n                                          Icons.interests,\n                                          color: Colors.grey,\n                                        ),\n                                      )\n                                    : const SizedBox(),\n                              ),\n                            ],\n                          ),\n                          onPressed: () {\n                            openModalBottomSheet(\n                              context,\n                              (context) {\n                                return AvatarSelector(\n                                  onSelected: (selected) {\n                                    setState(() {\n                                      avatarUrl = selected.url;\n                                    });\n                                    context.pop();\n                                  },\n                                  usage: AvatarUsage.user,\n                                  defaultAvatarUrl: avatarUrl,\n                                  externalAvatarUrls: [\n                                    ...avatarPresets,\n                                  ],\n                                );\n                              },\n                              heightFactor: 0.8,\n                            );\n                          },\n                        ),\n                        EnhancedTextField(\n                          labelText: 'Description',\n                          customColors: customColors,\n                          controller: descriptionController,\n                          textAlignVertical: TextAlignVertical.top,\n                          hintText: 'Optional',\n                          maxLength: 255,\n                          showCounter: false,\n                          maxLines: 3,\n                        ),\n                      ],\n                    ),\n                    ColumnBlock(\n                      children: [\n                        EnhancedTextField(\n                          labelWidth: 120,\n                          labelText: 'Input Price',\n                          customColors: customColors,\n                          controller: inputPriceController,\n                          textAlignVertical: TextAlignVertical.top,\n                          hintText: 'Optional',\n                          showCounter: false,\n                          keyboardType: TextInputType.number,\n                          inputFormatters: [FilteringTextInputFormatter.digitsOnly],\n                          textDirection: TextDirection.rtl,\n                          suffixIcon: Container(\n                            width: 110,\n                            alignment: Alignment.center,\n                            child: Text(\n                              'Credits/1K Token',\n                              style: TextStyle(color: customColors.weakTextColor, fontSize: 12),\n                            ),\n                          ),\n                        ),\n                        EnhancedTextField(\n                          labelWidth: 120,\n                          labelText: 'Output Price',\n                          customColors: customColors,\n                          controller: outputPriceController,\n                          textAlignVertical: TextAlignVertical.top,\n                          hintText: 'Optional',\n                          showCounter: false,\n                          keyboardType: TextInputType.number,\n                          inputFormatters: [FilteringTextInputFormatter.digitsOnly],\n                          textDirection: TextDirection.rtl,\n                          suffixIcon: Container(\n                            width: 110,\n                            alignment: Alignment.center,\n                            child: Text(\n                              'Credits/1K Token',\n                              style: TextStyle(color: customColors.weakTextColor, fontSize: 12),\n                            ),\n                          ),\n                        ),\n                        EnhancedTextField(\n                          labelWidth: 120,\n                          labelText: 'Request Price',\n                          customColors: customColors,\n                          controller: perReqPriceController,\n                          textAlignVertical: TextAlignVertical.top,\n                          hintText: 'Optional',\n                          showCounter: false,\n                          keyboardType: TextInputType.number,\n                          inputFormatters: [FilteringTextInputFormatter.digitsOnly],\n                          textDirection: TextDirection.rtl,\n                          suffixIcon: Container(\n                            width: 110,\n                            alignment: Alignment.center,\n                            child: Text(\n                              'Credits/Request',\n                              style: TextStyle(color: customColors.weakTextColor, fontSize: 12),\n                            ),\n                          ),\n                        ),\n                        EnhancedTextField(\n                          labelWidth: 120,\n                          labelText: 'Search Price',\n                          customColors: customColors,\n                          controller: searchPriceController,\n                          textAlignVertical: TextAlignVertical.top,\n                          hintText: 'Optional',\n                          showCounter: false,\n                          keyboardType: TextInputType.number,\n                          inputFormatters: [FilteringTextInputFormatter.digitsOnly],\n                          textDirection: TextDirection.rtl,\n                          suffixIcon: Container(\n                            width: 110,\n                            alignment: Alignment.center,\n                            child: Text(\n                              'Credits/Request',\n                              style: TextStyle(color: customColors.weakTextColor, fontSize: 12),\n                            ),\n                          ),\n                        ),\n                        EnhancedTextField(\n                          labelWidth: 120,\n                          labelText: 'Context Len',\n                          customColors: customColors,\n                          controller: maxContextController,\n                          textAlignVertical: TextAlignVertical.top,\n                          hintText: 'Subtract the expected output length from the maximum context.',\n                          showCounter: false,\n                          keyboardType: TextInputType.number,\n                          inputFormatters: [FilteringTextInputFormatter.digitsOnly],\n                          textDirection: TextDirection.rtl,\n                          suffixIcon: Container(\n                            width: 50,\n                            alignment: Alignment.center,\n                            child: Text(\n                              'Token',\n                              style: TextStyle(color: customColors.weakTextColor, fontSize: 12),\n                            ),\n                          ),\n                        ),\n                      ],\n                    ),\n                    ImplicitlyAnimatedReorderableList<AdminModelProvider>(\n                      items: providers,\n                      shrinkWrap: true,\n                      itemBuilder: (context, itemAnimation, item, index) {\n                        return Reorderable(\n                          key: ValueKey(item),\n                          builder: (context, dragAnimation, inDrag) {\n                            final t = dragAnimation.value;\n                            final elevation = lerpDouble(0, 8, t);\n                            final color = Color.lerp(Colors.white, Colors.white.withOpacity(0.8), t);\n\n                            return SizeFadeTransition(\n                              sizeFraction: 0.7,\n                              curve: Curves.easeInOut,\n                              animation: itemAnimation,\n                              child: Material(\n                                color: color,\n                                elevation: elevation ?? 0,\n                                type: MaterialType.transparency,\n                                child: Slidable(\n                                  startActionPane: ActionPane(\n                                    motion: const ScrollMotion(),\n                                    children: [\n                                      const SizedBox(width: 10),\n                                      SlidableAction(\n                                        label: AppLocale.delete.getString(context),\n                                        borderRadius: CustomSize.borderRadiusAll,\n                                        backgroundColor: Colors.red,\n                                        icon: Icons.delete,\n                                        onPressed: (_) {\n                                          if (providers.length == 1) {\n                                            showErrorMessage('At least one channel is needed');\n                                            return;\n                                          }\n\n                                          openConfirmDialog(\n                                            context,\n                                            AppLocale.confirmToDeleteRoom.getString(context),\n                                            () {\n                                              setState(() {\n                                                providers.removeAt(index);\n                                              });\n                                            },\n                                            danger: true,\n                                          );\n                                        },\n                                      ),\n                                    ],\n                                  ),\n                                  child: ListTile(\n                                    contentPadding: const EdgeInsets.all(5),\n                                    title: ColumnBlock(\n                                      margin: const EdgeInsets.all(0),\n                                      children: [\n                                        EnhancedInput(\n                                          title: Text(\n                                            'Channel',\n                                            style: TextStyle(\n                                              color: customColors.textfieldLabelColor,\n                                              fontSize: 16,\n                                            ),\n                                          ),\n                                          value: AutoSizeText(\n                                            buildChannelName(item),\n                                            maxLines: 1,\n                                            style: TextStyle(\n                                              color: customColors.textfieldValueColor,\n                                              fontSize: 14,\n                                            ),\n                                          ),\n                                          onPressed: () {\n                                            openListSelectDialog(\n                                              context,\n                                              <SelectorItem<AdminChannel>>[\n                                                ...modelChannels\n                                                    .map(\n                                                      (e) => SelectorItem(\n                                                        Text(\n                                                          '${e.id == null ? '【Legacy】' : ''}${e.name}',\n                                                          style: e.id == null\n                                                              ? TextStyle(\n                                                                  color: customColors.weakTextColorLess,\n                                                                  decoration: TextDecoration.lineThrough,\n                                                                )\n                                                              : null,\n                                                        ),\n                                                        e,\n                                                      ),\n                                                    )\n                                                    .toList(),\n                                              ],\n                                              (value) {\n                                                setState(() {\n                                                  providers[index].id = value.value.id;\n                                                  if (value.value.id == null) {\n                                                    providers[index].name = value.value.type;\n                                                  }\n                                                });\n                                                return true;\n                                              },\n                                              heightFactor: 0.5,\n                                              value: item,\n                                            );\n                                          },\n                                        ),\n                                        EnhancedTextField(\n                                          labelWidth: 90,\n                                          labelText: 'Rewrite',\n                                          customColors: customColors,\n                                          textAlignVertical: TextAlignVertical.top,\n                                          hintText: 'Optional',\n                                          maxLength: 100,\n                                          showCounter: false,\n                                          initValue: item.modelRewrite,\n                                          onChanged: (value) {\n                                            setState(() {\n                                              providers[index].modelRewrite = value;\n                                            });\n                                          },\n                                          labelHelpWidget: InkWell(\n                                            onTap: () {\n                                              showBeautyDialog(\n                                                context,\n                                                type: QuickAlertType.info,\n                                                text:\n                                                    'When the model identifier corresponding to the channel does not match the ID here, calling the channel interface will automatically replace the model with the value configured here.',\n                                                confirmBtnText: AppLocale.gotIt.getString(context),\n                                                showCancelBtn: false,\n                                              );\n                                            },\n                                            child: Icon(\n                                              Icons.help_outline,\n                                              size: 16,\n                                              color: customColors.weakLinkColor?.withAlpha(150),\n                                            ),\n                                          ),\n                                        ),\n                                        Row(\n                                          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                                          children: [\n                                            Row(\n                                              children: [\n                                                const Text(\n                                                  'Deep Think Model',\n                                                  style: TextStyle(fontSize: 16),\n                                                ),\n                                                const SizedBox(width: 5),\n                                                InkWell(\n                                                  onTap: () {\n                                                    showBeautyDialog(\n                                                      context,\n                                                      type: QuickAlertType.info,\n                                                      text: 'Whether the model is an Deep Thinking model.',\n                                                      confirmBtnText: AppLocale.gotIt.getString(context),\n                                                      showCancelBtn: false,\n                                                    );\n                                                  },\n                                                  child: Icon(\n                                                    Icons.help_outline,\n                                                    size: 16,\n                                                    color: customColors.weakLinkColor?.withAlpha(150),\n                                                  ),\n                                                ),\n                                              ],\n                                            ),\n                                            CupertinoSwitch(\n                                              activeColor: customColors.linkColor,\n                                              value: providers[index].type == 'reasoning',\n                                              onChanged: (value) {\n                                                setState(() {\n                                                  providers[index].type = value ? 'reasoning' : 'default';\n                                                });\n                                              },\n                                            ),\n                                          ],\n                                        ),\n                                      ],\n                                    ),\n                                    trailing: Handle(\n                                      delay: const Duration(milliseconds: 100),\n                                      child: Column(\n                                        children: [\n                                          Container(\n                                            width: 15,\n                                            height: 15,\n                                            decoration: BoxDecoration(\n                                              shape: BoxShape.circle,\n                                              color: Colors.blue.withOpacity(0.1),\n                                              border: Border.all(\n                                                color: Colors.blue.withOpacity(0.3),\n                                                width: 1,\n                                              ),\n                                              boxShadow: [\n                                                BoxShadow(\n                                                  color: Colors.blue.withOpacity(0.1),\n                                                  blurRadius: 2,\n                                                  spreadRadius: 1,\n                                                ),\n                                              ],\n                                            ),\n                                            child: Center(\n                                              child: Text(\n                                                '${index + 1}',\n                                                style: TextStyle(\n                                                  fontSize: 9,\n                                                  color: Colors.blue.shade700,\n                                                  fontWeight: FontWeight.w500,\n                                                ),\n                                              ),\n                                            ),\n                                          ),\n                                          const SizedBox(height: 10),\n                                          const Icon(\n                                            Icons.drag_indicator,\n                                            size: 20,\n                                            color: Colors.grey,\n                                          ),\n                                        ],\n                                      ),\n                                    ),\n                                  ),\n                                ),\n                              ),\n                            );\n                          },\n                        );\n                      },\n                      areItemsTheSame: (AdminModelProvider oldItem, AdminModelProvider newItem) {\n                        return oldItem.id == newItem.id;\n                      },\n                      onReorderFinished:\n                          (AdminModelProvider item, int from, int to, List<AdminModelProvider> newItems) {\n                        setState(() {\n                          providers = newItems;\n                        });\n                      },\n                    ),\n                    WeakTextButton(\n                      title: 'Add Channel',\n                      icon: Icons.add,\n                      onPressed: () {\n                        setState(() {\n                          providers.add(AdminModelProvider());\n                        });\n                      },\n                    ),\n                    const SizedBox(height: 10),\n                    // 高级选项\n                    if (showAdvancedOptions)\n                      ColumnBlock(\n                        innerPanding: 5,\n                        children: [\n                          EnhancedTextField(\n                            labelText: 'Abbr.',\n                            customColors: customColors,\n                            controller: shortNameController,\n                            textAlignVertical: TextAlignVertical.top,\n                            hintText: 'Enter model shorthand',\n                            maxLength: 100,\n                            showCounter: false,\n                          ),\n                          EnhancedTextField(\n                            labelText: 'Tag',\n                            customColors: customColors,\n                            controller: tagController,\n                            textAlignVertical: TextAlignVertical.top,\n                            hintText: 'Enter tags',\n                            maxLength: 100,\n                            showCounter: false,\n                          ),\n                          Row(\n                            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                            children: [\n                              Row(\n                                children: [\n                                  const Text(\n                                    'Vision',\n                                    style: TextStyle(fontSize: 16),\n                                  ),\n                                  const SizedBox(width: 5),\n                                  InkWell(\n                                    onTap: () {\n                                      showBeautyDialog(\n                                        context,\n                                        type: QuickAlertType.info,\n                                        text: 'Whether the current model supports visual capabilities.',\n                                        confirmBtnText: AppLocale.gotIt.getString(context),\n                                        showCancelBtn: false,\n                                      );\n                                    },\n                                    child: Icon(\n                                      Icons.help_outline,\n                                      size: 16,\n                                      color: customColors.weakLinkColor?.withAlpha(150),\n                                    ),\n                                  ),\n                                ],\n                              ),\n                              CupertinoSwitch(\n                                activeColor: customColors.linkColor,\n                                value: supportVision,\n                                onChanged: (value) {\n                                  setState(() {\n                                    supportVision = value;\n                                  });\n                                },\n                              ),\n                            ],\n                          ),\n                          Row(\n                            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                            children: [\n                              Row(\n                                children: [\n                                  const Text(\n                                    'New',\n                                    style: TextStyle(fontSize: 16),\n                                  ),\n                                  const SizedBox(width: 5),\n                                  InkWell(\n                                    onTap: () {\n                                      showBeautyDialog(\n                                        context,\n                                        type: QuickAlertType.info,\n                                        text:\n                                            'Whether to display a \"New\" icon next to the model to inform users that this is a new model.',\n                                        confirmBtnText: AppLocale.gotIt.getString(context),\n                                        showCancelBtn: false,\n                                      );\n                                    },\n                                    child: Icon(\n                                      Icons.help_outline,\n                                      size: 16,\n                                      color: customColors.weakLinkColor?.withAlpha(150),\n                                    ),\n                                  ),\n                                ],\n                              ),\n                              CupertinoSwitch(\n                                activeColor: customColors.linkColor,\n                                value: isNew,\n                                onChanged: (value) {\n                                  setState(() {\n                                    isNew = value;\n                                  });\n                                },\n                              ),\n                            ],\n                          ),\n                          Row(\n                            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                            children: [\n                              Row(\n                                children: [\n                                  const Text(\n                                    'Recommended',\n                                    style: TextStyle(fontSize: 16),\n                                  ),\n                                  const SizedBox(width: 5),\n                                  InkWell(\n                                    onTap: () {\n                                      showBeautyDialog(\n                                        context,\n                                        type: QuickAlertType.info,\n                                        text:\n                                            'Whether to display a \"Recommended\" icon next to the model to inform users that this is a recommended model.',\n                                        confirmBtnText: AppLocale.gotIt.getString(context),\n                                        showCancelBtn: false,\n                                      );\n                                    },\n                                    child: Icon(\n                                      Icons.help_outline,\n                                      size: 16,\n                                      color: customColors.weakLinkColor?.withAlpha(150),\n                                    ),\n                                  ),\n                                ],\n                              ),\n                              CupertinoSwitch(\n                                activeColor: customColors.linkColor,\n                                value: isRecommended,\n                                onChanged: (value) {\n                                  setState(() {\n                                    isRecommended = value;\n                                  });\n                                },\n                              ),\n                            ],\n                          ),\n                          Row(\n                            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                            children: [\n                              Row(\n                                children: [\n                                  const Text(\n                                    'Restricted',\n                                    style: TextStyle(fontSize: 16),\n                                  ),\n                                  const SizedBox(width: 5),\n                                  InkWell(\n                                    onTap: () {\n                                      showBeautyDialog(\n                                        context,\n                                        type: QuickAlertType.info,\n                                        text:\n                                            'Restricted models refer to models that cannot be used in Chinese Mainland due to policy factors.',\n                                        confirmBtnText: AppLocale.gotIt.getString(context),\n                                        showCancelBtn: false,\n                                      );\n                                    },\n                                    child: Icon(\n                                      Icons.help_outline,\n                                      size: 16,\n                                      color: customColors.weakLinkColor?.withAlpha(150),\n                                    ),\n                                  ),\n                                ],\n                              ),\n                              CupertinoSwitch(\n                                activeColor: customColors.linkColor,\n                                value: restricted,\n                                onChanged: (value) {\n                                  setState(() {\n                                    restricted = value;\n                                  });\n                                },\n                              ),\n                            ],\n                          ),\n                          Row(\n                            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                            children: [\n                              Row(\n                                children: [\n                                  const Text(\n                                    'Deep Think',\n                                    style: TextStyle(fontSize: 16),\n                                  ),\n                                  const SizedBox(width: 5),\n                                  InkWell(\n                                    onTap: () {\n                                      showBeautyDialog(\n                                        context,\n                                        type: QuickAlertType.info,\n                                        text: 'Whether to enable Deep Think for the current model.',\n                                        confirmBtnText: AppLocale.gotIt.getString(context),\n                                        showCancelBtn: false,\n                                      );\n                                    },\n                                    child: Icon(\n                                      Icons.help_outline,\n                                      size: 16,\n                                      color: customColors.weakLinkColor?.withAlpha(150),\n                                    ),\n                                  ),\n                                ],\n                              ),\n                              CupertinoSwitch(\n                                activeColor: customColors.linkColor,\n                                value: enableReasoning,\n                                onChanged: (value) {\n                                  setState(() {\n                                    enableReasoning = value;\n                                  });\n                                },\n                              ),\n                            ],\n                          ),\n                          Column(\n                            crossAxisAlignment: CrossAxisAlignment.start,\n                            children: [\n                              const Text('Temperature', style: TextStyle(fontSize: 16)),\n                              Row(\n                                children: [\n                                  Expanded(\n                                    child: Slider(\n                                      value: temperature,\n                                      min: 0.0,\n                                      max: 2.0,\n                                      divisions: 40,\n                                      label: '$temperature',\n                                      activeColor: customColors.linkColor,\n                                      onChanged: (value) {\n                                        setState(() {\n                                          temperature = value;\n                                        });\n                                      },\n                                    ),\n                                  ),\n                                  Text(\n                                    '$temperature',\n                                    style: TextStyle(\n                                      fontSize: 12,\n                                      color: customColors.weakTextColor,\n                                    ),\n                                  ),\n                                ],\n                              )\n                            ],\n                          ),\n                          Row(\n                            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                            children: [\n                              Row(\n                                children: [\n                                  const Text(\n                                    'Search',\n                                    style: TextStyle(fontSize: 16),\n                                  ),\n                                  const SizedBox(width: 5),\n                                  InkWell(\n                                    onTap: () {\n                                      showBeautyDialog(\n                                        context,\n                                        type: QuickAlertType.info,\n                                        text: 'Whether to enable search for the current model.',\n                                        confirmBtnText: AppLocale.gotIt.getString(context),\n                                        showCancelBtn: false,\n                                      );\n                                    },\n                                    child: Icon(\n                                      Icons.help_outline,\n                                      size: 16,\n                                      color: customColors.weakLinkColor?.withAlpha(150),\n                                    ),\n                                  ),\n                                ],\n                              ),\n                              CupertinoSwitch(\n                                activeColor: customColors.linkColor,\n                                value: enableSearch,\n                                onChanged: (value) {\n                                  setState(() {\n                                    enableSearch = value;\n                                  });\n                                },\n                              ),\n                            ],\n                          ),\n                          if (enableSearch)\n                            Column(\n                              crossAxisAlignment: CrossAxisAlignment.start,\n                              children: [\n                                const Text('Search Result Count', style: TextStyle(fontSize: 16)),\n                                Row(\n                                  children: [\n                                    Expanded(\n                                      child: Slider(\n                                        value: searchCount.toDouble() <= 3 ? 3.0 : searchCount.toDouble(),\n                                        min: 3,\n                                        max: 50,\n                                        divisions: 50 - 3,\n                                        label: '$searchCount',\n                                        activeColor: customColors.linkColor,\n                                        onChanged: (value) {\n                                          setState(() {\n                                            searchCount = value.toInt();\n                                          });\n                                        },\n                                      ),\n                                    ),\n                                    Text(\n                                      '$searchCount',\n                                      style: TextStyle(\n                                        fontSize: 12,\n                                        color: customColors.weakTextColor,\n                                      ),\n                                    ),\n                                  ],\n                                )\n                              ],\n                            ),\n                          Row(\n                            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                            children: [\n                              const Text(\n                                'Enabled',\n                                style: TextStyle(fontSize: 16),\n                              ),\n                              CupertinoSwitch(\n                                activeColor: customColors.linkColor,\n                                value: modelEnabled,\n                                onChanged: (value) {\n                                  setState(() {\n                                    modelEnabled = value;\n                                  });\n                                },\n                              ),\n                            ],\n                          ),\n                          EnhancedTextField(\n                            labelPosition: LabelPosition.top,\n                            labelText: 'System prompt',\n                            customColors: customColors,\n                            controller: promptController,\n                            textAlignVertical: TextAlignVertical.top,\n                            hintText: 'Global system prompt',\n                            maxLength: 2000,\n                            maxLines: 3,\n                          ),\n                        ],\n                      ),\n                    const SizedBox(height: 15),\n                    Row(\n                      children: [\n                        EnhancedButton(\n                          title: showAdvancedOptions\n                              ? AppLocale.simpleMode.getString(context)\n                              : AppLocale.professionalMode.getString(context),\n                          width: 120,\n                          backgroundColor: Colors.transparent,\n                          color: customColors.weakLinkColor,\n                          fontSize: 15,\n                          icon: Icon(\n                            showAdvancedOptions ? Icons.unfold_less : Icons.unfold_more,\n                            color: customColors.weakLinkColor,\n                            size: 15,\n                          ),\n                          onPressed: () {\n                            setState(() {\n                              showAdvancedOptions = !showAdvancedOptions;\n                            });\n                          },\n                        ),\n                        const SizedBox(width: 10),\n                        Expanded(\n                          flex: 1,\n                          child: EnhancedButton(\n                            title: AppLocale.save.getString(context),\n                            onPressed: onSubmit,\n                          ),\n                        ),\n                      ],\n                    ),\n                  ],\n                ),\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  /// 提交\n  void onSubmit() async {\n    if (nameController.text.isEmpty) {\n      showErrorMessage('Please enter a model name');\n      return;\n    }\n\n    if (modelIdController.text.isEmpty) {\n      showErrorMessage('Please enter a model ID');\n      return;\n    }\n\n    final ps = providers.where((e) => e.id != null || e.name != null).toList();\n    if (ps.isEmpty) {\n      showErrorMessage('At least one channel is required');\n      return;\n    }\n\n    if (avatarUrl != null && (!avatarUrl!.startsWith('http://') && !avatarUrl!.startsWith('https://'))) {\n      final cancel = BotToast.showCustomLoading(\n        toastBuilder: (cancel) {\n          return const LoadingIndicator(\n            message: 'Uploading avatar, please wait...',\n          );\n        },\n        allowClick: false,\n      );\n\n      try {\n        final res = await ImageUploader(widget.setting).upload(avatarUrl!, usage: 'avatar');\n        avatarUrl = res.url;\n      } catch (e) {\n        showErrorMessage('Failed to upload avatar');\n        cancel();\n        return;\n      } finally {\n        cancel();\n      }\n    }\n\n    final model = AdminModelAddReq(\n      name: nameController.text,\n      modelId: modelIdController.text,\n      description: descriptionController.text,\n      shortName: shortNameController.text,\n      meta: AdminModelMeta(\n        maxContext: int.parse(maxContextController.text),\n        inputPrice: int.parse(inputPriceController.text),\n        outputPrice: int.parse(outputPriceController.text),\n        perReqPrice: int.parse(perReqPriceController.text),\n        prompt: promptController.text,\n        vision: supportVision,\n        restricted: restricted,\n        tag: tagController.text,\n        tagTextColor: tagTextColor,\n        tagBgColor: tagBgColor,\n        category: categoryController.text,\n        isNew: isNew,\n        isRecommend: isRecommended,\n        search: enableSearch,\n        reasoning: enableReasoning,\n        searchCount: searchCount,\n        searchPrice: int.parse(searchPriceController.text),\n        temperature: temperature,\n      ),\n      status: modelEnabled ? 1 : 2,\n      providers: ps,\n      avatarUrl: avatarUrl,\n    );\n\n    // ignore: use_build_context_synchronously\n    context.read<ModelBloc>().add(ModelCreateEvent(model));\n  }\n\n  /// 渠道名称\n  String buildChannelName(AdminModelProvider provider) {\n    if (provider.id != null) {\n      return modelChannels.firstWhere((e) => e.id == provider.id).name;\n    }\n\n    if (provider.name != null) {\n      return modelChannels\n          .firstWhere(\n            (e) => e.type == provider.name! && e.id == null,\n            orElse: () => AdminChannel(name: 'Unknown', type: ''),\n          )\n          .display;\n    }\n\n    return 'Select';\n  }\n}\n"
  },
  {
    "path": "lib/page/admin/models_edit.dart",
    "content": "import 'dart:io';\nimport 'dart:ui';\n\nimport 'package:animated_list_plus/animated_list_plus.dart';\nimport 'package:animated_list_plus/transitions.dart';\nimport 'package:askaide/bloc/model_bloc.dart';\nimport 'package:askaide/helper/upload.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/avatar_selector.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/enhanced_input.dart';\nimport 'package:askaide/page/component/enhanced_textfield.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/item_selector_search.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/random_avatar.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/weak_text_button.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api/admin/channels.dart';\nimport 'package:askaide/repo/api/admin/models.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:auto_size_text/auto_size_text.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:flutter_slidable/flutter_slidable.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:quickalert/models/quickalert_type.dart';\n\nclass AdminModelEditPage extends StatefulWidget {\n  final SettingRepository setting;\n  final String modelId;\n  const AdminModelEditPage({\n    super.key,\n    required this.setting,\n    required this.modelId,\n  });\n\n  @override\n  State<AdminModelEditPage> createState() => _AdminModelEditPageState();\n}\n\nclass _AdminModelEditPageState extends State<AdminModelEditPage> {\n  final TextEditingController nameController = TextEditingController();\n  final TextEditingController modelIdController = TextEditingController();\n  final TextEditingController shortNameController = TextEditingController();\n  final TextEditingController descriptionController = TextEditingController();\n  final TextEditingController maxContextController = TextEditingController();\n  final TextEditingController inputPriceController = TextEditingController();\n  final TextEditingController outputPriceController = TextEditingController();\n  final TextEditingController perRequestPriceController = TextEditingController();\n  final TextEditingController promptController = TextEditingController();\n  final TextEditingController categoryController = TextEditingController();\n  final TextEditingController searchPriceController = TextEditingController();\n\n  /// 用于控制是否显示高级选项\n  bool showAdvancedOptions = false;\n\n  /// 视觉能力\n  bool supportVision = false;\n\n  /// 受限模型\n  bool restricted = false;\n\n  /// 模型状态\n  bool modelEnabled = true;\n\n  /// 是否是上新\n  bool isNew = false;\n\n  /// 是否是推荐模型\n  bool isRecommended = false;\n\n  /// 是否启用搜索\n  bool enableSearch = false;\n\n  /// 是否启用推理\n  bool enableReasoning = false;\n\n  /// 温度\n  double temperature = 0.0;\n  // 搜索结果数量\n  int searchCount = 3;\n\n  /// Tag\n  final TextEditingController tagController = TextEditingController();\n  String? tagTextColor;\n  String? tagBgColor;\n\n  /// 模型头像\n  String? avatarUrl;\n  List<String> avatarPresets = [];\n\n  // 模型渠道\n  List<AdminChannel> modelChannels = [];\n  // 选择的渠道\n  List<AdminModelProvider> providers = [];\n\n  /// 是否锁定编辑\n  bool editLocked = true;\n\n  @override\n  void dispose() {\n    nameController.dispose();\n    modelIdController.dispose();\n    shortNameController.dispose();\n    descriptionController.dispose();\n    maxContextController.dispose();\n    inputPriceController.dispose();\n    outputPriceController.dispose();\n    perRequestPriceController.dispose();\n    promptController.dispose();\n    categoryController.dispose();\n    tagController.dispose();\n    searchPriceController.dispose();\n    super.dispose();\n  }\n\n  @override\n  void initState() {\n    // 加载预设头像\n    APIServer().avatars().then((value) {\n      avatarPresets = value;\n    });\n    // 加载模型渠道\n    APIServer().adminChannelsAgg().then((value) {\n      setState(() {\n        modelChannels = value;\n      });\n\n      // 加载模型\n      context.read<ModelBloc>().add(ModelLoadEvent(widget.modelId));\n    });\n\n    // 初始值设置\n    maxContextController.value = const TextEditingValue(text: '7500');\n    inputPriceController.value = const TextEditingValue(text: '0');\n    outputPriceController.value = const TextEditingValue(text: '0');\n    perRequestPriceController.value = const TextEditingValue(text: '0');\n    searchPriceController.value = const TextEditingValue(text: '0');\n\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: const Text(\n            'Edit Model',\n            style: TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          child: SingleChildScrollView(\n            child: BlocListener<ModelBloc, ModelState>(\n              listenWhen: (previous, current) => current is ModelOperationResult || current is ModelLoaded,\n              listener: (context, state) {\n                if (state is ModelOperationResult) {\n                  if (state.success) {\n                    showSuccessMessage(state.message);\n                    context.read<ModelBloc>().add(ModelLoadEvent(widget.modelId));\n                  } else {\n                    showErrorMessage(state.message);\n                  }\n                }\n\n                if (state is ModelLoaded) {\n                  modelIdController.value = TextEditingValue(text: state.model.modelId);\n                  nameController.value = TextEditingValue(text: state.model.name);\n                  if (state.model.description != null) {\n                    descriptionController.value = TextEditingValue(text: state.model.description!);\n                  }\n\n                  if (state.model.avatarUrl != null) {\n                    avatarUrl = state.model.avatarUrl;\n                  }\n\n                  modelEnabled = state.model.status == 1;\n\n                  if (state.model.providers.isNotEmpty) {\n                    providers = state.model.providers;\n                  }\n\n                  if (state.model.meta != null) {\n                    if (state.model.meta!.maxContext != null) {\n                      maxContextController.value = TextEditingValue(text: state.model.meta!.maxContext.toString());\n                    }\n\n                    if (state.model.meta!.inputPrice != null) {\n                      inputPriceController.value = TextEditingValue(text: state.model.meta!.inputPrice.toString());\n                    }\n\n                    if (state.model.meta!.outputPrice != null) {\n                      outputPriceController.value = TextEditingValue(text: state.model.meta!.outputPrice.toString());\n                    }\n\n                    if (state.model.meta!.perReqPrice != null) {\n                      perRequestPriceController.value =\n                          TextEditingValue(text: state.model.meta!.perReqPrice.toString());\n                    }\n\n                    if (state.model.meta!.searchPrice != null) {\n                      searchPriceController.value = TextEditingValue(text: state.model.meta!.searchPrice.toString());\n                    }\n\n                    shortNameController.value = TextEditingValue(text: state.model.shortName ?? '');\n                    promptController.value = TextEditingValue(text: state.model.meta!.prompt ?? '');\n                    supportVision = state.model.meta!.vision ?? false;\n                    restricted = state.model.meta!.restricted ?? false;\n                    tagController.value = TextEditingValue(text: state.model.meta!.tag ?? '');\n                    tagTextColor = state.model.meta!.tagTextColor;\n                    tagBgColor = state.model.meta!.tagBgColor;\n                    isNew = state.model.meta!.isNew ?? false;\n                    isRecommended = state.model.meta!.isRecommend ?? false;\n                    categoryController.value = TextEditingValue(text: state.model.meta!.category ?? '');\n                    enableSearch = state.model.meta!.search ?? false;\n                    enableReasoning = state.model.meta!.reasoning ?? false;\n                    temperature = state.model.meta!.temperature ?? 0.0;\n                    searchCount = state.model.meta!.searchCount ?? 3;\n                    searchCount = searchCount <= 3 ? 3 : searchCount;\n\n                    setState(() {});\n                  }\n                }\n\n                setState(() {\n                  editLocked = false;\n                });\n              },\n              child: Container(\n                padding: const EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 20),\n                child: Column(\n                  children: [\n                    ColumnBlock(\n                      children: [\n                        EnhancedTextField(\n                          labelText: 'ID',\n                          customColors: customColors,\n                          controller: modelIdController,\n                          textAlignVertical: TextAlignVertical.top,\n                          hintText: 'Enter a unique ID',\n                          maxLength: 100,\n                          showCounter: false,\n                          readOnly: true,\n                        ),\n                        EnhancedTextField(\n                          labelText: 'Vendor',\n                          customColors: customColors,\n                          controller: categoryController,\n                          textAlignVertical: TextAlignVertical.top,\n                          hintText: 'Enter a vendor name (Optional)',\n                          maxLength: 100,\n                          showCounter: false,\n                        ),\n                        EnhancedTextField(\n                          labelText: 'Name',\n                          customColors: customColors,\n                          controller: nameController,\n                          textAlignVertical: TextAlignVertical.top,\n                          hintText: 'Enter a model name',\n                          maxLength: 100,\n                          showCounter: false,\n                        ),\n                        EnhancedInput(\n                          padding: const EdgeInsets.only(top: 10, bottom: 5),\n                          title: Text(\n                            'Avatar',\n                            style: TextStyle(\n                              color: customColors.textfieldLabelColor,\n                              fontSize: 16,\n                            ),\n                          ),\n                          value: Row(\n                            mainAxisAlignment: MainAxisAlignment.end,\n                            mainAxisSize: MainAxisSize.min,\n                            children: [\n                              Container(\n                                width: 45,\n                                height: 45,\n                                decoration: BoxDecoration(\n                                  borderRadius: CustomSize.borderRadius,\n                                  image: avatarUrl == null\n                                      ? null\n                                      : DecorationImage(\n                                          image: (avatarUrl!.startsWith('http')\n                                              ? CachedNetworkImageProviderEnhanced(avatarUrl!)\n                                              : FileImage(File(avatarUrl!))) as ImageProvider,\n                                          fit: BoxFit.cover,\n                                        ),\n                                ),\n                                child: avatarUrl == null\n                                    ? const Center(\n                                        child: Icon(\n                                          Icons.interests,\n                                          color: Colors.grey,\n                                        ),\n                                      )\n                                    : const SizedBox(),\n                              ),\n                            ],\n                          ),\n                          onPressed: () {\n                            openModalBottomSheet(\n                              context,\n                              (context) {\n                                return AvatarSelector(\n                                  onSelected: (selected) {\n                                    setState(() {\n                                      avatarUrl = selected.url;\n                                    });\n                                    context.pop();\n                                  },\n                                  usage: AvatarUsage.user,\n                                  defaultAvatarUrl: avatarUrl,\n                                  externalAvatarUrls: [\n                                    ...avatarPresets,\n                                  ],\n                                );\n                              },\n                              heightFactor: 0.8,\n                            );\n                          },\n                        ),\n                        EnhancedTextField(\n                          labelText: 'Description',\n                          customColors: customColors,\n                          controller: descriptionController,\n                          textAlignVertical: TextAlignVertical.top,\n                          hintText: 'Optional',\n                          maxLength: 255,\n                          showCounter: false,\n                          maxLines: 3,\n                        ),\n                      ],\n                    ),\n                    ColumnBlock(\n                      children: [\n                        EnhancedTextField(\n                          labelWidth: 120,\n                          labelText: 'Input Price',\n                          customColors: customColors,\n                          controller: inputPriceController,\n                          textAlignVertical: TextAlignVertical.top,\n                          hintText: 'Optional',\n                          showCounter: false,\n                          keyboardType: TextInputType.number,\n                          inputFormatters: [FilteringTextInputFormatter.digitsOnly],\n                          textDirection: TextDirection.rtl,\n                          suffixIcon: Container(\n                            width: 110,\n                            alignment: Alignment.center,\n                            child: Text(\n                              'Credits/1K Token',\n                              style: TextStyle(color: customColors.weakTextColor, fontSize: 12),\n                            ),\n                          ),\n                        ),\n                        EnhancedTextField(\n                          labelWidth: 120,\n                          labelText: 'Output Price',\n                          customColors: customColors,\n                          controller: outputPriceController,\n                          textAlignVertical: TextAlignVertical.top,\n                          hintText: 'Optional',\n                          showCounter: false,\n                          keyboardType: TextInputType.number,\n                          inputFormatters: [FilteringTextInputFormatter.digitsOnly],\n                          textDirection: TextDirection.rtl,\n                          suffixIcon: Container(\n                            width: 110,\n                            alignment: Alignment.center,\n                            child: Text(\n                              'Credits/1K Token',\n                              style: TextStyle(color: customColors.weakTextColor, fontSize: 12),\n                            ),\n                          ),\n                        ),\n                        EnhancedTextField(\n                          labelWidth: 120,\n                          labelText: 'Request Price',\n                          customColors: customColors,\n                          controller: perRequestPriceController,\n                          textAlignVertical: TextAlignVertical.top,\n                          hintText: 'Optional',\n                          showCounter: false,\n                          keyboardType: TextInputType.number,\n                          inputFormatters: [FilteringTextInputFormatter.digitsOnly],\n                          textDirection: TextDirection.rtl,\n                          suffixIcon: Container(\n                            width: 110,\n                            alignment: Alignment.center,\n                            child: Text(\n                              'Credits/Request',\n                              style: TextStyle(color: customColors.weakTextColor, fontSize: 12),\n                            ),\n                          ),\n                        ),\n                        EnhancedTextField(\n                          labelWidth: 120,\n                          labelText: 'Search Price',\n                          customColors: customColors,\n                          controller: searchPriceController,\n                          textAlignVertical: TextAlignVertical.top,\n                          hintText: 'Optional',\n                          showCounter: false,\n                          keyboardType: TextInputType.number,\n                          inputFormatters: [FilteringTextInputFormatter.digitsOnly],\n                          textDirection: TextDirection.rtl,\n                          suffixIcon: Container(\n                            width: 110,\n                            alignment: Alignment.center,\n                            child: Text(\n                              'Credits/Request',\n                              style: TextStyle(color: customColors.weakTextColor, fontSize: 12),\n                            ),\n                          ),\n                        ),\n                        EnhancedTextField(\n                          labelWidth: 120,\n                          labelText: 'Context Len',\n                          customColors: customColors,\n                          controller: maxContextController,\n                          textAlignVertical: TextAlignVertical.top,\n                          hintText: 'Subtract the expected output length from the maximum context.',\n                          showCounter: false,\n                          keyboardType: TextInputType.number,\n                          inputFormatters: [FilteringTextInputFormatter.digitsOnly],\n                          textDirection: TextDirection.rtl,\n                          suffixIcon: Container(\n                            width: 50,\n                            alignment: Alignment.center,\n                            child: Text(\n                              'Token',\n                              style: TextStyle(color: customColors.weakTextColor, fontSize: 12),\n                            ),\n                          ),\n                        ),\n                      ],\n                    ),\n                    ImplicitlyAnimatedReorderableList<AdminModelProvider>(\n                      items: providers,\n                      shrinkWrap: true,\n                      itemBuilder: (context, itemAnimation, item, index) {\n                        return Reorderable(\n                          key: ValueKey(item),\n                          builder: (context, dragAnimation, inDrag) {\n                            final t = dragAnimation.value;\n                            final elevation = lerpDouble(0, 8, t);\n                            final color = Color.lerp(Colors.white, Colors.white.withOpacity(0.8), t);\n\n                            return SizeFadeTransition(\n                              sizeFraction: 0.7,\n                              curve: Curves.easeInOut,\n                              animation: itemAnimation,\n                              child: Material(\n                                color: color,\n                                elevation: elevation ?? 0,\n                                type: MaterialType.transparency,\n                                child: Slidable(\n                                  startActionPane: ActionPane(\n                                    motion: const ScrollMotion(),\n                                    children: [\n                                      const SizedBox(width: 10),\n                                      SlidableAction(\n                                        label: AppLocale.delete.getString(context),\n                                        borderRadius: CustomSize.borderRadiusAll,\n                                        backgroundColor: Colors.red,\n                                        icon: Icons.delete,\n                                        onPressed: (_) {\n                                          if (providers.length == 1) {\n                                            showErrorMessage('At least one channel is needed');\n                                            return;\n                                          }\n\n                                          openConfirmDialog(\n                                            context,\n                                            AppLocale.confirmToDeleteRoom.getString(context),\n                                            () {\n                                              setState(() {\n                                                providers.removeAt(index);\n                                              });\n                                            },\n                                            danger: true,\n                                          );\n                                        },\n                                      ),\n                                    ],\n                                  ),\n                                  child: ListTile(\n                                    contentPadding: const EdgeInsets.all(5),\n                                    title: ColumnBlock(\n                                      margin: const EdgeInsets.all(0),\n                                      children: [\n                                        EnhancedInput(\n                                          title: Text(\n                                            'Channel',\n                                            style: TextStyle(\n                                              color: customColors.textfieldLabelColor,\n                                              fontSize: 16,\n                                            ),\n                                          ),\n                                          value: AutoSizeText(\n                                            buildChannelName(item),\n                                            maxLines: 1,\n                                            style: TextStyle(\n                                              color: customColors.textfieldValueColor,\n                                              fontSize: 14,\n                                            ),\n                                          ),\n                                          onPressed: () {\n                                            openListSelectDialog(\n                                              context,\n                                              <SelectorItem<AdminChannel>>[\n                                                ...modelChannels\n                                                    .map(\n                                                      (e) => SelectorItem(\n                                                        Text(\n                                                          '${e.id == null ? '【Legacy】' : ''}${e.name}',\n                                                          style: e.id == null\n                                                              ? TextStyle(\n                                                                  color: customColors.weakTextColorLess,\n                                                                  decoration: TextDecoration.lineThrough,\n                                                                )\n                                                              : null,\n                                                        ),\n                                                        e,\n                                                      ),\n                                                    )\n                                                    .toList(),\n                                              ],\n                                              (value) {\n                                                setState(() {\n                                                  providers[index].id = value.value.id;\n                                                  if (value.value.id == null) {\n                                                    providers[index].name = value.value.type;\n                                                  }\n                                                });\n                                                return true;\n                                              },\n                                              heightFactor: 0.5,\n                                              value: item,\n                                            );\n                                          },\n                                        ),\n                                        EnhancedTextField(\n                                          labelWidth: 90,\n                                          labelText: 'Rewrite',\n                                          customColors: customColors,\n                                          textAlignVertical: TextAlignVertical.top,\n                                          hintText: 'Optional',\n                                          maxLength: 100,\n                                          showCounter: false,\n                                          initValue: item.modelRewrite,\n                                          onChanged: (value) {\n                                            setState(() {\n                                              providers[index].modelRewrite = value;\n                                            });\n                                          },\n                                          labelHelpWidget: InkWell(\n                                            onTap: () {\n                                              showBeautyDialog(\n                                                context,\n                                                type: QuickAlertType.info,\n                                                text:\n                                                    'When the model identifier corresponding to the channel does not match the ID here, calling the channel interface will automatically replace the model with the value configured here.',\n                                                confirmBtnText: AppLocale.gotIt.getString(context),\n                                                showCancelBtn: false,\n                                              );\n                                            },\n                                            child: Icon(\n                                              Icons.help_outline,\n                                              size: 16,\n                                              color: customColors.weakLinkColor?.withAlpha(150),\n                                            ),\n                                          ),\n                                        ),\n                                        Row(\n                                          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                                          children: [\n                                            Row(\n                                              children: [\n                                                const Text(\n                                                  'Deep Think Model',\n                                                  style: TextStyle(fontSize: 16),\n                                                ),\n                                                const SizedBox(width: 5),\n                                                InkWell(\n                                                  onTap: () {\n                                                    showBeautyDialog(\n                                                      context,\n                                                      type: QuickAlertType.info,\n                                                      text: 'Whether the model is an Deep Thinking model.',\n                                                      confirmBtnText: AppLocale.gotIt.getString(context),\n                                                      showCancelBtn: false,\n                                                    );\n                                                  },\n                                                  child: Icon(\n                                                    Icons.help_outline,\n                                                    size: 16,\n                                                    color: customColors.weakLinkColor?.withAlpha(150),\n                                                  ),\n                                                ),\n                                              ],\n                                            ),\n                                            CupertinoSwitch(\n                                              activeColor: customColors.linkColor,\n                                              value: providers[index].type == 'reasoning',\n                                              onChanged: (value) {\n                                                setState(() {\n                                                  providers[index].type = value ? 'reasoning' : 'default';\n                                                });\n                                              },\n                                            ),\n                                          ],\n                                        ),\n                                      ],\n                                    ),\n                                    trailing: Handle(\n                                      delay: const Duration(milliseconds: 100),\n                                      child: Column(\n                                        children: [\n                                          Container(\n                                            width: 15,\n                                            height: 15,\n                                            decoration: BoxDecoration(\n                                              shape: BoxShape.circle,\n                                              color: Colors.blue.withOpacity(0.1),\n                                              border: Border.all(\n                                                color: Colors.blue.withOpacity(0.3),\n                                                width: 1,\n                                              ),\n                                              boxShadow: [\n                                                BoxShadow(\n                                                  color: Colors.blue.withOpacity(0.1),\n                                                  blurRadius: 2,\n                                                  spreadRadius: 1,\n                                                ),\n                                              ],\n                                            ),\n                                            child: Center(\n                                              child: Text(\n                                                '${index + 1}',\n                                                style: TextStyle(\n                                                  fontSize: 9,\n                                                  color: Colors.blue.shade700,\n                                                  fontWeight: FontWeight.w500,\n                                                ),\n                                              ),\n                                            ),\n                                          ),\n                                          const SizedBox(height: 10),\n                                          const Icon(\n                                            Icons.drag_indicator,\n                                            size: 20,\n                                            color: Colors.grey,\n                                          ),\n                                        ],\n                                      ),\n                                    ),\n                                  ),\n                                ),\n                              ),\n                            );\n                          },\n                        );\n                      },\n                      areItemsTheSame: (AdminModelProvider oldItem, AdminModelProvider newItem) {\n                        return oldItem.id == newItem.id;\n                      },\n                      onReorderFinished:\n                          (AdminModelProvider item, int from, int to, List<AdminModelProvider> newItems) {\n                        setState(() {\n                          providers = newItems;\n                        });\n                      },\n                    ),\n\n                    const SizedBox(width: 10),\n                    WeakTextButton(\n                      title: 'Add Channel',\n                      icon: Icons.add,\n                      onPressed: () {\n                        setState(() {\n                          providers.add(AdminModelProvider());\n                        });\n                      },\n                    ),\n                    // 高级选项\n                    if (showAdvancedOptions)\n                      ColumnBlock(\n                        innerPanding: 5,\n                        children: [\n                          EnhancedTextField(\n                            labelText: 'Abbr.',\n                            customColors: customColors,\n                            controller: shortNameController,\n                            textAlignVertical: TextAlignVertical.top,\n                            hintText: 'Enter model shorthand',\n                            maxLength: 100,\n                            showCounter: false,\n                          ),\n                          EnhancedTextField(\n                            labelText: 'Tag',\n                            customColors: customColors,\n                            controller: tagController,\n                            textAlignVertical: TextAlignVertical.top,\n                            hintText: 'Enter tags',\n                            maxLength: 100,\n                            showCounter: false,\n                          ),\n                          Row(\n                            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                            children: [\n                              Row(\n                                children: [\n                                  const Text(\n                                    'Vision',\n                                    style: TextStyle(fontSize: 16),\n                                  ),\n                                  const SizedBox(width: 5),\n                                  InkWell(\n                                    onTap: () {\n                                      showBeautyDialog(\n                                        context,\n                                        type: QuickAlertType.info,\n                                        text: 'Whether the current model supports visual capabilities.',\n                                        confirmBtnText: AppLocale.gotIt.getString(context),\n                                        showCancelBtn: false,\n                                      );\n                                    },\n                                    child: Icon(\n                                      Icons.help_outline,\n                                      size: 16,\n                                      color: customColors.weakLinkColor?.withAlpha(150),\n                                    ),\n                                  ),\n                                ],\n                              ),\n                              CupertinoSwitch(\n                                activeColor: customColors.linkColor,\n                                value: supportVision,\n                                onChanged: (value) {\n                                  setState(() {\n                                    supportVision = value;\n                                  });\n                                },\n                              ),\n                            ],\n                          ),\n                          Row(\n                            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                            children: [\n                              Row(\n                                children: [\n                                  const Text(\n                                    'New',\n                                    style: TextStyle(fontSize: 16),\n                                  ),\n                                  const SizedBox(width: 5),\n                                  InkWell(\n                                    onTap: () {\n                                      showBeautyDialog(\n                                        context,\n                                        type: QuickAlertType.info,\n                                        text:\n                                            'Whether to display a \"New\" icon next to the model to inform users that this is a new model.',\n                                        confirmBtnText: AppLocale.gotIt.getString(context),\n                                        showCancelBtn: false,\n                                      );\n                                    },\n                                    child: Icon(\n                                      Icons.help_outline,\n                                      size: 16,\n                                      color: customColors.weakLinkColor?.withAlpha(150),\n                                    ),\n                                  ),\n                                ],\n                              ),\n                              CupertinoSwitch(\n                                activeColor: customColors.linkColor,\n                                value: isNew,\n                                onChanged: (value) {\n                                  setState(() {\n                                    isNew = value;\n                                  });\n                                },\n                              ),\n                            ],\n                          ),\n                          Row(\n                            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                            children: [\n                              Row(\n                                children: [\n                                  const Text(\n                                    'Recommended',\n                                    style: TextStyle(fontSize: 16),\n                                  ),\n                                  const SizedBox(width: 5),\n                                  InkWell(\n                                    onTap: () {\n                                      showBeautyDialog(\n                                        context,\n                                        type: QuickAlertType.info,\n                                        text:\n                                            'Whether to display a \"Recommended\" icon next to the model to inform users that this is a recommended model.',\n                                        confirmBtnText: AppLocale.gotIt.getString(context),\n                                        showCancelBtn: false,\n                                      );\n                                    },\n                                    child: Icon(\n                                      Icons.help_outline,\n                                      size: 16,\n                                      color: customColors.weakLinkColor?.withAlpha(150),\n                                    ),\n                                  ),\n                                ],\n                              ),\n                              CupertinoSwitch(\n                                activeColor: customColors.linkColor,\n                                value: isRecommended,\n                                onChanged: (value) {\n                                  setState(() {\n                                    isRecommended = value;\n                                  });\n                                },\n                              ),\n                            ],\n                          ),\n                          Row(\n                            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                            children: [\n                              Row(\n                                children: [\n                                  const Text(\n                                    'Restricted',\n                                    style: TextStyle(fontSize: 16),\n                                  ),\n                                  const SizedBox(width: 5),\n                                  InkWell(\n                                    onTap: () {\n                                      showBeautyDialog(\n                                        context,\n                                        type: QuickAlertType.info,\n                                        text:\n                                            'Restricted models refer to models that cannot be used in Chinese Mainland due to policy factors.',\n                                        confirmBtnText: AppLocale.gotIt.getString(context),\n                                        showCancelBtn: false,\n                                      );\n                                    },\n                                    child: Icon(\n                                      Icons.help_outline,\n                                      size: 16,\n                                      color: customColors.weakLinkColor?.withAlpha(150),\n                                    ),\n                                  ),\n                                ],\n                              ),\n                              CupertinoSwitch(\n                                activeColor: customColors.linkColor,\n                                value: restricted,\n                                onChanged: (value) {\n                                  setState(() {\n                                    restricted = value;\n                                  });\n                                },\n                              ),\n                            ],\n                          ),\n                          Row(\n                            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                            children: [\n                              Row(\n                                children: [\n                                  const Text(\n                                    'Deep Think',\n                                    style: TextStyle(fontSize: 16),\n                                  ),\n                                  const SizedBox(width: 5),\n                                  InkWell(\n                                    onTap: () {\n                                      showBeautyDialog(\n                                        context,\n                                        type: QuickAlertType.info,\n                                        text: 'Whether to enable Deep Think for the current model.',\n                                        confirmBtnText: AppLocale.gotIt.getString(context),\n                                        showCancelBtn: false,\n                                      );\n                                    },\n                                    child: Icon(\n                                      Icons.help_outline,\n                                      size: 16,\n                                      color: customColors.weakLinkColor?.withAlpha(150),\n                                    ),\n                                  ),\n                                ],\n                              ),\n                              CupertinoSwitch(\n                                activeColor: customColors.linkColor,\n                                value: enableReasoning,\n                                onChanged: (value) {\n                                  setState(() {\n                                    enableReasoning = value;\n                                  });\n                                },\n                              ),\n                            ],\n                          ),\n                          Column(\n                            crossAxisAlignment: CrossAxisAlignment.start,\n                            children: [\n                              const Text('Temperature', style: TextStyle(fontSize: 16)),\n                              Row(\n                                children: [\n                                  Expanded(\n                                    child: Slider(\n                                      value: temperature,\n                                      min: 0.0,\n                                      max: 2.0,\n                                      divisions: 40,\n                                      label: '$temperature',\n                                      activeColor: customColors.linkColor,\n                                      onChanged: (value) {\n                                        setState(() {\n                                          temperature = value;\n                                        });\n                                      },\n                                    ),\n                                  ),\n                                  Text(\n                                    '$temperature',\n                                    style: TextStyle(\n                                      fontSize: 12,\n                                      color: customColors.weakTextColor,\n                                    ),\n                                  ),\n                                ],\n                              )\n                            ],\n                          ),\n                          Row(\n                            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                            children: [\n                              Row(\n                                children: [\n                                  const Text(\n                                    'Search',\n                                    style: TextStyle(fontSize: 16),\n                                  ),\n                                  const SizedBox(width: 5),\n                                  InkWell(\n                                    onTap: () {\n                                      showBeautyDialog(\n                                        context,\n                                        type: QuickAlertType.info,\n                                        text: 'Whether to enable search for the current model.',\n                                        confirmBtnText: AppLocale.gotIt.getString(context),\n                                        showCancelBtn: false,\n                                      );\n                                    },\n                                    child: Icon(\n                                      Icons.help_outline,\n                                      size: 16,\n                                      color: customColors.weakLinkColor?.withAlpha(150),\n                                    ),\n                                  ),\n                                ],\n                              ),\n                              CupertinoSwitch(\n                                activeColor: customColors.linkColor,\n                                value: enableSearch,\n                                onChanged: (value) {\n                                  setState(() {\n                                    enableSearch = value;\n                                  });\n                                },\n                              ),\n                            ],\n                          ),\n                          if (enableSearch)\n                            Column(\n                              crossAxisAlignment: CrossAxisAlignment.start,\n                              children: [\n                                const Text('Search Result Count', style: TextStyle(fontSize: 16)),\n                                Row(\n                                  children: [\n                                    Expanded(\n                                      child: Slider(\n                                        value: searchCount.toDouble() <= 3 ? 3.0 : searchCount.toDouble(),\n                                        min: 3,\n                                        max: 50,\n                                        divisions: 50 - 3,\n                                        label: '$searchCount',\n                                        activeColor: customColors.linkColor,\n                                        onChanged: (value) {\n                                          setState(() {\n                                            searchCount = value.toInt();\n                                          });\n                                        },\n                                      ),\n                                    ),\n                                    Text(\n                                      '$searchCount',\n                                      style: TextStyle(\n                                        fontSize: 12,\n                                        color: customColors.weakTextColor,\n                                      ),\n                                    ),\n                                  ],\n                                )\n                              ],\n                            ),\n                          Row(\n                            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                            children: [\n                              const Text(\n                                'Enabled',\n                                style: TextStyle(fontSize: 16),\n                              ),\n                              CupertinoSwitch(\n                                activeColor: customColors.linkColor,\n                                value: modelEnabled,\n                                onChanged: (value) {\n                                  setState(() {\n                                    modelEnabled = value;\n                                  });\n                                },\n                              ),\n                            ],\n                          ),\n                          EnhancedTextField(\n                            labelPosition: LabelPosition.top,\n                            labelText: 'System prompt',\n                            customColors: customColors,\n                            controller: promptController,\n                            textAlignVertical: TextAlignVertical.top,\n                            hintText: 'Global system prompt',\n                            maxLength: 2000,\n                            maxLines: 3,\n                          ),\n                        ],\n                      ),\n                    const SizedBox(height: 15),\n                    Row(\n                      children: [\n                        EnhancedButton(\n                          title: showAdvancedOptions\n                              ? AppLocale.simpleMode.getString(context)\n                              : AppLocale.professionalMode.getString(context),\n                          width: 120,\n                          backgroundColor: Colors.transparent,\n                          color: customColors.weakLinkColor,\n                          fontSize: 15,\n                          icon: Icon(\n                            showAdvancedOptions ? Icons.unfold_less : Icons.unfold_more,\n                            color: customColors.weakLinkColor,\n                            size: 15,\n                          ),\n                          onPressed: () {\n                            setState(() {\n                              showAdvancedOptions = !showAdvancedOptions;\n                            });\n                          },\n                        ),\n                        const SizedBox(width: 10),\n                        Expanded(\n                          flex: 1,\n                          child: EnhancedButton(\n                            title: AppLocale.save.getString(context),\n                            onPressed: onSubmit,\n                            icon: editLocked\n                                ? const Icon(Icons.lock, color: Colors.white, size: 16)\n                                : const Icon(Icons.lock_open, color: Colors.white, size: 16),\n                          ),\n                        ),\n                      ],\n                    ),\n                  ],\n                ),\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  /// 提交\n  void onSubmit() async {\n    if (editLocked) {\n      return;\n    }\n\n    if (nameController.text.isEmpty) {\n      showErrorMessage('Please enter a model name');\n      return;\n    }\n\n    final ps = providers.where((e) => e.id != null || e.name != null).toList();\n    if (ps.isEmpty) {\n      showErrorMessage('At least one channel is required');\n      return;\n    }\n\n    if (avatarUrl != null && (!avatarUrl!.startsWith('http://') && !avatarUrl!.startsWith('https://'))) {\n      final cancel = BotToast.showCustomLoading(\n        toastBuilder: (cancel) {\n          return const LoadingIndicator(\n            message: 'Uploading avatar, please wait...',\n          );\n        },\n        allowClick: false,\n      );\n\n      try {\n        final res = await ImageUploader(widget.setting).upload(avatarUrl!, usage: 'avatar');\n        avatarUrl = res.url;\n      } catch (e) {\n        showErrorMessage('Failed to upload avatar');\n        cancel();\n        return;\n      } finally {\n        cancel();\n      }\n    }\n\n    final model = AdminModelUpdateReq(\n      name: nameController.text,\n      description: descriptionController.text,\n      shortName: shortNameController.text,\n      meta: AdminModelMeta(\n        maxContext: int.parse(maxContextController.text),\n        inputPrice: int.parse(inputPriceController.text),\n        outputPrice: int.parse(outputPriceController.text),\n        perReqPrice: int.parse(perRequestPriceController.text),\n        prompt: promptController.text,\n        vision: supportVision,\n        restricted: restricted,\n        category: categoryController.text,\n        tag: tagController.text,\n        tagTextColor: tagTextColor,\n        tagBgColor: tagBgColor,\n        isNew: isNew,\n        isRecommend: isRecommended,\n        search: enableSearch,\n        reasoning: enableReasoning,\n        temperature: temperature,\n        searchCount: searchCount,\n        searchPrice: int.parse(searchPriceController.text),\n      ),\n      status: modelEnabled ? 1 : 2,\n      providers: ps,\n      avatarUrl: avatarUrl,\n    );\n\n    setState(() {\n      editLocked = true;\n    });\n\n    // ignore: use_build_context_synchronously\n    context.read<ModelBloc>().add(ModelUpdateEvent(widget.modelId, model));\n  }\n\n  /// 渠道名称\n  String buildChannelName(AdminModelProvider provider) {\n    if (provider.id != null) {\n      return modelChannels.firstWhere((e) => e.id == provider.id).name;\n    }\n\n    if (provider.name != null) {\n      return modelChannels\n          .firstWhere(\n            (e) => e.type == provider.name! && e.id == null,\n            orElse: () => AdminChannel(name: 'Unknown', type: ''),\n          )\n          .display;\n    }\n\n    return 'Select';\n  }\n}\n"
  },
  {
    "path": "lib/page/admin/payments.dart",
    "content": "import 'package:askaide/bloc/admin_payment_bloc.dart';\nimport 'package:askaide/bloc/user_bloc.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/pagination.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api/admin/payment.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:flutter_slidable/flutter_slidable.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:intl/intl.dart';\n\nclass PaymentHistoriesPage extends StatefulWidget {\n  final SettingRepository setting;\n  const PaymentHistoriesPage({\n    super.key,\n    required this.setting,\n  });\n\n  @override\n  State<PaymentHistoriesPage> createState() => _PaymentHistoriesPageState();\n}\n\nclass _PaymentHistoriesPageState extends State<PaymentHistoriesPage> {\n  /// 当前页码\n  int page = 1;\n\n  /// 每页数量\n  int perPage = 20;\n\n  /// 搜索关键字\n  final TextEditingController keywordController = TextEditingController();\n\n  @override\n  void initState() {\n    context.read<AdminPaymentBloc>().add(AdminPaymentHistoriesLoadEvent(\n          perPage: perPage,\n          page: page,\n          keyword: keywordController.text,\n        ));\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    keywordController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: const Text(\n            'Payment Order History',\n            style: TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          child: Column(\n            children: [\n              Container(\n                padding: const EdgeInsets.only(left: 10, right: 10, bottom: 5),\n                child: TextField(\n                  controller: keywordController,\n                  textAlignVertical: TextAlignVertical.center,\n                  style: TextStyle(color: customColors.dialogDefaultTextColor),\n                  decoration: InputDecoration(\n                    hintText: AppLocale.search.getString(context),\n                    hintStyle: TextStyle(\n                      color: customColors.dialogDefaultTextColor,\n                    ),\n                    prefixIcon: Icon(\n                      Icons.search,\n                      color: customColors.dialogDefaultTextColor,\n                    ),\n                    isDense: true,\n                    border: InputBorder.none,\n                  ),\n                  onEditingComplete: () {\n                    context.read<AdminPaymentBloc>().add(AdminPaymentHistoriesLoadEvent(\n                          perPage: perPage,\n                          page: page,\n                          keyword: keywordController.text,\n                        ));\n                  },\n                ),\n              ),\n              Expanded(\n                child: RefreshIndicator(\n                  color: customColors.linkColor,\n                  onRefresh: () async {\n                    context.read<AdminPaymentBloc>().add(AdminPaymentHistoriesLoadEvent(\n                          perPage: perPage,\n                          page: page,\n                          keyword: keywordController.text,\n                        ));\n                  },\n                  displacement: 20,\n                  child: BlocConsumer<AdminPaymentBloc, AdminPaymentState>(\n                    listener: (context, state) {\n                      if (state is AdminPaymentOperationResult) {\n                        if (state.success) {\n                          showSuccessMessage(state.message);\n                          context.read<UserBloc>().add(UserListLoadEvent());\n                        } else {\n                          showErrorMessage(state.message);\n                        }\n                      }\n\n                      if (state is AdminPaymentHistoriesLoaded) {\n                        setState(() {\n                          page = state.histories.page;\n                          perPage = state.histories.perPage;\n                        });\n                      }\n                    },\n                    buildWhen: (previous, current) => current is AdminPaymentHistoriesLoaded,\n                    builder: (context, state) {\n                      if (state is AdminPaymentHistoriesLoaded) {\n                        return SafeArea(\n                          top: false,\n                          child: Column(\n                            children: [\n                              Expanded(\n                                child: ListView.builder(\n                                  padding: const EdgeInsets.all(5),\n                                  itemCount: state.histories.data.length,\n                                  itemBuilder: (context, index) {\n                                    return buildHistoryInfo(\n                                      context,\n                                      customColors,\n                                      state.histories.data[index],\n                                    );\n                                  },\n                                ),\n                              ),\n                              if (state.histories.lastPage != null && state.histories.lastPage! > 1)\n                                Container(\n                                  padding: const EdgeInsets.all(10),\n                                  child: Pagination(\n                                    numOfPages: state.histories.lastPage ?? 1,\n                                    selectedPage: page,\n                                    pagesVisible: 5,\n                                    onPageChanged: (selected) {\n                                      context.read<AdminPaymentBloc>().add(AdminPaymentHistoriesLoadEvent(\n                                            perPage: perPage,\n                                            page: selected,\n                                            keyword: keywordController.text,\n                                          ));\n                                    },\n                                  ),\n                                ),\n                            ],\n                          ),\n                        );\n                      }\n\n                      return const Center(\n                        child: CircularProgressIndicator(),\n                      );\n                    },\n                  ),\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget buildHistoryInfo(\n    BuildContext context,\n    CustomColors customColors,\n    AdminPaymentHistory his,\n  ) {\n    return Container(\n      margin: const EdgeInsets.symmetric(\n        horizontal: 10,\n        vertical: 5,\n      ),\n      decoration: BoxDecoration(borderRadius: CustomSize.borderRadius),\n      child: Slidable(\n        child: Material(\n          borderRadius: CustomSize.borderRadius,\n          color: customColors.columnBlockBackgroundColor,\n          child: InkWell(\n            borderRadius: CustomSize.borderRadiusAll,\n            onTap: () {\n              context.push('/admin/users/${his.userId}');\n            },\n            child: Stack(\n              children: [\n                Row(\n                  mainAxisSize: MainAxisSize.min,\n                  children: [\n                    // 头像\n                    buildAvatar(his, radius: CustomSize.borderRadiusAll),\n                    // 名称\n                    Expanded(\n                      child: Container(\n                        padding: const EdgeInsets.all(15),\n                        child: Column(\n                          crossAxisAlignment: CrossAxisAlignment.start,\n                          children: [\n                            Row(\n                              children: [\n                                Text(\n                                  '@${his.userId} Charge ',\n                                  style: const TextStyle(\n                                    overflow: TextOverflow.ellipsis,\n                                  ),\n                                ),\n                                Text(\n                                  '￥${(his.retailPrice / 100).ceil()}',\n                                  style: const TextStyle(\n                                    fontSize: 16,\n                                    fontWeight: FontWeight.bold,\n                                    color: Colors.red,\n                                  ),\n                                ),\n                                const SizedBox(width: 5),\n                                Text(\n                                  '#${his.id}',\n                                  style: TextStyle(\n                                    fontSize: 10,\n                                    color: customColors.weakTextColor,\n                                  ),\n                                ),\n                              ],\n                            ),\n                            const SizedBox(height: 5),\n                            buildTags(context, customColors, his),\n                          ],\n                        ),\n                      ),\n                    ),\n                  ],\n                ),\n                Positioned(\n                  right: 0,\n                  top: 0,\n                  child: Container(\n                    padding: const EdgeInsets.all(10),\n                    width: MediaQuery.of(context).size.width / 2.0,\n                    alignment: Alignment.centerRight,\n                    child: Column(\n                      crossAxisAlignment: CrossAxisAlignment.start,\n                      children: [\n                        Text(\n                          his.environment,\n                          style: TextStyle(\n                            fontSize: 10,\n                            overflow: TextOverflow.ellipsis,\n                            fontWeight: FontWeight.bold,\n                            color:\n                                his.environment.toLowerCase() == 'production' ? customColors.linkColor : Colors.amber,\n                          ),\n                        ),\n                      ],\n                    ),\n                  ),\n                ),\n                Positioned(\n                  right: 0,\n                  bottom: 0,\n                  child: Container(\n                    padding: const EdgeInsets.all(10),\n                    width: MediaQuery.of(context).size.width / 2.0,\n                    alignment: Alignment.centerRight,\n                    child: Column(\n                      crossAxisAlignment: CrossAxisAlignment.start,\n                      children: [\n                        Text(\n                          DateFormat('y-MM-dd HH:mm').format(his.purchaseAt.toLocal()),\n                          style: TextStyle(\n                            fontSize: 10,\n                            overflow: TextOverflow.ellipsis,\n                            color: customColors.weakTextColor,\n                          ),\n                        ),\n                      ],\n                    ),\n                  ),\n                ),\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nWidget buildAvatar(\n  AdminPaymentHistory his, {\n  BorderRadius radius = const BorderRadius.only(topLeft: CustomSize.radius, bottomLeft: CustomSize.radius),\n}) {\n  final source = (his.source ?? '').toLowerCase();\n\n  var image = '';\n  if (source.contains('支付宝') || source.contains('alipay')) {\n    image = 'assets/zhifubao.png';\n  } else if (source.contains('微信') || source.contains('wechat')) {\n    image = 'assets/wechat-pay.png';\n  } else if (source.contains('stripe')) {\n    image = 'assets/stripe.png';\n  } else if (source.contains('apple')) {\n    image = 'assets/apple.webp';\n  } else {\n    image = 'assets/app.png';\n  }\n\n  return SizedBox(\n    width: 70,\n    height: 70,\n    child: ClipRRect(\n      borderRadius: radius,\n      child: Image.asset(image),\n    ),\n  );\n}\n\nWidget buildTags(BuildContext context, CustomColors customColors, AdminPaymentHistory his) {\n  final tags = <Widget>[];\n\n  if (his.source != null) {\n    tags.add(buildTag(context, customColors, his.source!));\n  }\n\n  return Wrap(\n    spacing: 5,\n    runSpacing: 5,\n    children: tags,\n  );\n}\n\nWidget buildTag(BuildContext context, CustomColors customColors, String s) {\n  return Container(\n    padding: const EdgeInsets.symmetric(\n      horizontal: 5,\n      vertical: 2,\n    ),\n    decoration: BoxDecoration(color: customColors.tagsBackground, borderRadius: CustomSize.borderRadius),\n    child: Text(\n      s,\n      style: TextStyle(\n        fontSize: 10,\n        color: customColors.tagsText,\n      ),\n    ),\n  );\n}\n"
  },
  {
    "path": "lib/page/admin/recently_messages.dart",
    "content": "import 'package:askaide/bloc/admin_room_bloc.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/pagination.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:flutter_slidable/flutter_slidable.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:intl/intl.dart';\n\nclass AdminRecentlyMessagesPage extends StatefulWidget {\n  final SettingRepository setting;\n\n  const AdminRecentlyMessagesPage({super.key, required this.setting});\n\n  @override\n  State<AdminRecentlyMessagesPage> createState() => _AdminRecentlyMessagesPageState();\n}\n\nclass _AdminRecentlyMessagesPageState extends State<AdminRecentlyMessagesPage> {\n  /// 当前页码\n  int page = 1;\n\n  /// 每页数量\n  int perPage = 20;\n\n  /// 搜索关键字\n  final TextEditingController keywordController = TextEditingController();\n\n  @override\n  void initState() {\n    context.read<AdminRoomBloc>().add(AdminRecentlyMessagesLoadEvent(\n          perPage: perPage,\n          page: page,\n          keyword: keywordController.text,\n        ));\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    keywordController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: const Text(\n            'Chat History',\n            style: TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          backgroundColor: customColors.backgroundColor,\n          enabled: false,\n          child: Column(\n            children: [\n              Container(\n                padding: const EdgeInsets.only(left: 10, right: 10, bottom: 5),\n                child: TextField(\n                  controller: keywordController,\n                  textAlignVertical: TextAlignVertical.center,\n                  style: TextStyle(color: customColors.dialogDefaultTextColor),\n                  decoration: InputDecoration(\n                    hintText: AppLocale.search.getString(context),\n                    hintStyle: TextStyle(\n                      color: customColors.dialogDefaultTextColor,\n                    ),\n                    prefixIcon: Icon(\n                      Icons.search,\n                      color: customColors.dialogDefaultTextColor,\n                    ),\n                    isDense: true,\n                    border: InputBorder.none,\n                  ),\n                  onEditingComplete: () {\n                    context.read<AdminRoomBloc>().add(AdminRecentlyMessagesLoadEvent(\n                          perPage: perPage,\n                          page: page,\n                          keyword: keywordController.text,\n                        ));\n                  },\n                ),\n              ),\n              Expanded(\n                child: RefreshIndicator(\n                  color: customColors.linkColor,\n                  onRefresh: () async {\n                    context.read<AdminRoomBloc>().add(AdminRecentlyMessagesLoadEvent(\n                          perPage: perPage,\n                          page: page,\n                          keyword: keywordController.text,\n                        ));\n                  },\n                  displacement: 20,\n                  child: BlocConsumer<AdminRoomBloc, AdminRoomState>(\n                    listener: (context, state) {\n                      if (state is AdminRoomOperationResult) {\n                        if (state.success) {\n                          showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                        } else {\n                          showErrorMessage(AppLocale.operateFailed.getString(context));\n                        }\n                      }\n\n                      if (state is AdminRecentlyMessagesLoaded) {\n                        setState(() {\n                          page = state.messages.page;\n                          perPage = state.messages.perPage;\n                        });\n                      }\n                    },\n                    buildWhen: (previous, current) => current is AdminRecentlyMessagesLoaded,\n                    builder: (context, state) {\n                      if (state is AdminRecentlyMessagesLoaded) {\n                        return SafeArea(\n                          top: false,\n                          child: Column(\n                            children: [\n                              Expanded(\n                                child: ListView.builder(\n                                  padding: const EdgeInsets.all(5),\n                                  itemCount: state.messages.data.length,\n                                  itemBuilder: (context, index) {\n                                    final message = state.messages.data[index];\n                                    return Container(\n                                      margin: const EdgeInsets.symmetric(\n                                        horizontal: 10,\n                                        vertical: 5,\n                                      ),\n                                      decoration: BoxDecoration(borderRadius: CustomSize.borderRadius),\n                                      child: Slidable(\n                                        startActionPane: ActionPane(motion: const ScrollMotion(), children: [\n                                          SlidableAction(\n                                            label: AppLocale.character.getString(context),\n                                            borderRadius: const BorderRadius.only(\n                                              topLeft: CustomSize.radius,\n                                              bottomLeft: CustomSize.radius,\n                                              topRight: CustomSize.radius,\n                                              bottomRight: CustomSize.radius,\n                                            ),\n                                            backgroundColor: Colors.blue,\n                                            icon: Icons.people,\n                                            foregroundColor: Colors.white,\n                                            onPressed: (_) {\n                                              context.push('/admin/users/${message.userId}/rooms');\n                                            },\n                                          ),\n                                        ]),\n                                        endActionPane: ActionPane(\n                                          motion: const ScrollMotion(),\n                                          children: [\n                                            const SizedBox(width: 10),\n                                            SlidableAction(\n                                              label: 'User',\n                                              borderRadius: const BorderRadius.only(\n                                                topLeft: CustomSize.radius,\n                                                bottomLeft: CustomSize.radius,\n                                                topRight: CustomSize.radius,\n                                                bottomRight: CustomSize.radius,\n                                              ),\n                                              backgroundColor: customColors.linkColor ?? Colors.green,\n                                              icon: Icons.person,\n                                              foregroundColor: Colors.white,\n                                              onPressed: (_) {\n                                                context.push('/admin/users/${message.userId}');\n                                              },\n                                            ),\n                                          ],\n                                        ),\n                                        child: Material(\n                                          borderRadius: CustomSize.borderRadius,\n                                          color: customColors.columnBlockBackgroundColor,\n                                          child: InkWell(\n                                            borderRadius: CustomSize.borderRadiusAll,\n                                            onTap: () {\n                                              context.push(\n                                                  '/admin/users/${message.userId}/rooms/${message.roomId}/messages?room_type=1');\n                                            },\n                                            child: Stack(\n                                              children: [\n                                                Positioned(\n                                                  top: 0,\n                                                  right: 0,\n                                                  child: Container(\n                                                    padding: const EdgeInsets.symmetric(\n                                                      horizontal: 5,\n                                                      vertical: 2,\n                                                    ),\n                                                    decoration: BoxDecoration(\n                                                      color: customColors.columnBlockBackgroundColor?.withAlpha(100),\n                                                      borderRadius: const BorderRadius.only(\n                                                        topRight: CustomSize.radius,\n                                                        bottomLeft: CustomSize.radius,\n                                                      ),\n                                                    ),\n                                                    child: Text(\n                                                      '@ ${message.userId}',\n                                                      style: TextStyle(\n                                                        fontSize: 12,\n                                                        color: customColors.weakTextColor?.withAlpha(100),\n                                                      ),\n                                                    ),\n                                                  ),\n                                                ),\n                                                Container(\n                                                  padding: const EdgeInsets.all(15),\n                                                  child: Column(\n                                                    crossAxisAlignment: CrossAxisAlignment.start,\n                                                    children: [\n                                                      Text(\n                                                        state.messages.data[index].text,\n                                                        maxLines: 2,\n                                                        style: const TextStyle(\n                                                          overflow: TextOverflow.ellipsis,\n                                                        ),\n                                                      ),\n                                                      const SizedBox(height: 5),\n                                                      Row(\n                                                        mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                                                        children: [\n                                                          Expanded(\n                                                            child: Text(\n                                                              '${message.model}',\n                                                              style: const TextStyle(\n                                                                fontSize: 12,\n                                                                color: Colors.grey,\n                                                              ),\n                                                              overflow: TextOverflow.ellipsis,\n                                                              maxLines: 1,\n                                                            ),\n                                                          ),\n                                                          if (message.ts != null)\n                                                            Text(\n                                                              '  ${DateFormat('MM/dd HH:mm').format(message.ts!.toLocal())}',\n                                                              style: const TextStyle(\n                                                                fontSize: 12,\n                                                                color: Colors.grey,\n                                                              ),\n                                                            ),\n                                                        ],\n                                                      ),\n                                                    ],\n                                                  ),\n                                                ),\n                                              ],\n                                            ),\n                                          ),\n                                        ),\n                                      ),\n                                    );\n                                  },\n                                ),\n                              ),\n                              if (state.messages.lastPage != null && state.messages.lastPage! > 1)\n                                Container(\n                                  padding: const EdgeInsets.all(10),\n                                  child: Pagination(\n                                    numOfPages: state.messages.lastPage ?? 1,\n                                    selectedPage: page,\n                                    pagesVisible: 5,\n                                    onPageChanged: (selected) {\n                                      context.read<AdminRoomBloc>().add(AdminRecentlyMessagesLoadEvent(\n                                            perPage: perPage,\n                                            page: selected,\n                                            keyword: keywordController.text,\n                                          ));\n                                    },\n                                  ),\n                                ),\n                            ],\n                          ),\n                        );\n                      }\n\n                      return const Center(\n                        child: CircularProgressIndicator(),\n                      );\n                    },\n                  ),\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/admin/rooms.dart",
    "content": "import 'package:askaide/bloc/admin_room_bloc.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/helper/image.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/chat/component/group_avatar.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_initicon/flutter_initicon.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass AdminRoomsPage extends StatefulWidget {\n  final SettingRepository setting;\n  final int userId;\n  const AdminRoomsPage({\n    super.key,\n    required this.setting,\n    required this.userId,\n  });\n\n  @override\n  State<AdminRoomsPage> createState() => _AdminRoomsPageState();\n}\n\nclass _AdminRoomsPageState extends State<AdminRoomsPage> {\n  @override\n  void initState() {\n    context.read<AdminRoomBloc>().add(AdminRoomsLoadEvent(userId: widget.userId));\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: const Text(\n            'Characters',\n            style: TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          child: RefreshIndicator(\n            color: customColors.linkColor,\n            onRefresh: () async {\n              context.read<AdminRoomBloc>().add(AdminRoomsLoadEvent(userId: widget.userId));\n            },\n            displacement: 20,\n            child: BlocConsumer<AdminRoomBloc, AdminRoomState>(\n              listener: (context, state) {\n                if (state is AdminRoomOperationResult) {\n                  if (state.success) {\n                    showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                  } else {\n                    showErrorMessage(AppLocale.operateFailed.getString(context));\n                  }\n                }\n              },\n              buildWhen: (previous, current) => current is AdminRoomsLoaded,\n              builder: (context, state) {\n                if (state is AdminRoomsLoaded) {\n                  return SafeArea(\n                    top: false,\n                    child: ListView.builder(\n                      padding: const EdgeInsets.all(5),\n                      itemCount: state.rooms.length,\n                      itemBuilder: (context, index) {\n                        final room = state.rooms[index];\n                        return Container(\n                          margin: const EdgeInsets.symmetric(\n                            horizontal: 10,\n                            vertical: 5,\n                          ),\n                          decoration: BoxDecoration(borderRadius: CustomSize.borderRadius),\n                          child: Material(\n                            borderRadius: CustomSize.borderRadius,\n                            color: customColors.columnBlockBackgroundColor,\n                            child: InkWell(\n                              borderRadius: CustomSize.borderRadiusAll,\n                              onTap: () {\n                                context.push(\n                                    '/admin/users/${widget.userId}/rooms/${room.id}/messages?room_type=${room.roomType ?? 1}');\n                              },\n                              child: Stack(\n                                children: [\n                                  Row(\n                                    mainAxisSize: MainAxisSize.min,\n                                    children: [\n                                      _buildAvatar(room),\n                                      Expanded(\n                                        child: Container(\n                                          padding: const EdgeInsets.symmetric(horizontal: 10),\n                                          child: Column(\n                                            crossAxisAlignment: CrossAxisAlignment.start,\n                                            children: [\n                                              Row(\n                                                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                                                children: [\n                                                  Expanded(\n                                                    child: Text(\n                                                      room.name,\n                                                      overflow: TextOverflow.ellipsis,\n                                                    ),\n                                                  ),\n                                                  Text(\n                                                    humanTime(room.lastActiveTime),\n                                                    style: TextStyle(\n                                                      color: customColors.weakLinkColor?.withAlpha(65),\n                                                      fontSize: 10,\n                                                    ),\n                                                  ),\n                                                ],\n                                              ),\n                                            ],\n                                          ),\n                                        ),\n                                      ),\n                                    ],\n                                  ),\n                                  if (room.roomType == 4)\n                                    Positioned(\n                                      right: 0,\n                                      top: 0,\n                                      child: Container(\n                                        decoration: BoxDecoration(\n                                          color: customColors.backgroundContainerColor,\n                                          borderRadius: const BorderRadius.only(\n                                            topRight: CustomSize.radius,\n                                            bottomLeft: CustomSize.radius,\n                                          ),\n                                        ),\n                                        padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),\n                                        child: Text(\n                                          AppLocale.groupChat.getString(context),\n                                          style: TextStyle(\n                                            color: customColors.weakTextColor,\n                                            fontSize: 8,\n                                          ),\n                                        ),\n                                      ),\n                                    ),\n                                ],\n                              ),\n                            ),\n                          ),\n                        );\n                      },\n                    ),\n                  );\n                }\n\n                return const Center(\n                  child: CircularProgressIndicator(),\n                );\n              },\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget _buildAvatar(RoomInServer room) {\n    if (room.members.length == 1 && (room.avatarUrl == null || room.avatarUrl == '')) {\n      room.avatarUrl = room.members[0];\n    }\n\n    if (room.avatarUrl != null && room.avatarUrl!.startsWith('http')) {\n      return SizedBox(\n        width: 70,\n        height: 70,\n        child: ClipRRect(\n          borderRadius: const BorderRadius.only(topLeft: CustomSize.radius, bottomLeft: CustomSize.radius),\n          child: CachedNetworkImageEnhanced(\n            imageUrl: imageURL(room.avatarUrl!, qiniuImageTypeAvatar),\n            fit: BoxFit.fill,\n          ),\n        ),\n      );\n    }\n\n    if (room.members.isNotEmpty) {\n      return ClipRRect(\n        borderRadius: const BorderRadius.only(topLeft: CustomSize.radius, bottomLeft: CustomSize.radius),\n        child: GroupAvatar(\n          size: 70,\n          avatars: room.members,\n        ),\n      );\n    }\n\n    return Initicon(\n      text: room.name.split('、').join(' '),\n      size: 70,\n      backgroundColor: Colors.grey.withAlpha(100),\n      borderRadius: const BorderRadius.only(topLeft: CustomSize.radius, bottomLeft: CustomSize.radius),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/admin/user.dart",
    "content": "import 'package:askaide/bloc/user_bloc.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/admin/users.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/credit.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/enhanced_textfield.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/page/creative_island/gallery/gallery_item.dart';\nimport 'package:askaide/repo/api/quota.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:intl/intl.dart';\n\nclass AdminUserPage extends StatefulWidget {\n  final SettingRepository setting;\n  final int userId;\n  const AdminUserPage({\n    super.key,\n    required this.setting,\n    required this.userId,\n  });\n\n  @override\n  State<AdminUserPage> createState() => _AdminUserPageState();\n}\n\nclass _AdminUserPageState extends State<AdminUserPage> {\n  @override\n  void initState() {\n    context.read<UserBloc>().add(UserLoadEvent(widget.userId));\n    context.read<UserBloc>().add(UserQuotaLoadEvent(widget.userId));\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: const Text(\n            'User Info',\n            style: TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n          actions: [\n            IconButton(\n              icon: const Icon(Icons.card_giftcard_outlined),\n              tooltip: 'Give Credits',\n              onPressed: () {\n                int sendCount = 600;\n                String? note;\n                int validDays = 30;\n\n                openDialog(\n                  context,\n                  builder: Builder(builder: (context) {\n                    return Column(\n                      mainAxisSize: MainAxisSize.min,\n                      children: [\n                        const Text(\n                          'Give Credits',\n                          style: TextStyle(fontSize: 18),\n                        ),\n                        const SizedBox(height: 10),\n                        EnhancedTextField(\n                          labelText: 'Quantity',\n                          customColors: customColors,\n                          textAlignVertical: TextAlignVertical.top,\n                          showCounter: false,\n                          keyboardType: TextInputType.number,\n                          inputFormatters: [FilteringTextInputFormatter.digitsOnly],\n                          suffixIcon: Container(\n                            width: 110,\n                            alignment: Alignment.center,\n                            child: Text(\n                              'Credits',\n                              style: TextStyle(\n                                color: customColors.weakTextColor,\n                                fontSize: 12,\n                              ),\n                            ),\n                          ),\n                          onChanged: (value) {\n                            sendCount = int.tryParse(value) ?? 0;\n                          },\n                          initValue: sendCount.toString(),\n                        ),\n                        const SizedBox(height: 10),\n                        EnhancedTextField(\n                          labelText: 'Expiration',\n                          customColors: customColors,\n                          textAlignVertical: TextAlignVertical.top,\n                          showCounter: false,\n                          keyboardType: TextInputType.number,\n                          inputFormatters: [FilteringTextInputFormatter.digitsOnly],\n                          suffixIcon: Container(\n                            width: 110,\n                            alignment: Alignment.center,\n                            child: Text(\n                              'Days',\n                              style: TextStyle(\n                                color: customColors.weakTextColor,\n                                fontSize: 12,\n                              ),\n                            ),\n                          ),\n                          onChanged: (value) {\n                            validDays = int.tryParse(value) ?? 0;\n                          },\n                          initValue: validDays.toString(),\n                        ),\n                        const SizedBox(height: 10),\n                        EnhancedTextField(\n                          labelText: 'Note',\n                          customColors: customColors,\n                          textAlignVertical: TextAlignVertical.top,\n                          showCounter: false,\n                          hintText: 'Optional',\n                          onChanged: (value) {\n                            note = value;\n                          },\n                          initValue: note,\n                        ),\n                      ],\n                    );\n                  }),\n                  onSubmit: () {\n                    if (sendCount <= 0) {\n                      showErrorMessage('Quantity must be greater than 0');\n                      return false;\n                    }\n\n                    if (validDays <= 0) {\n                      showErrorMessage('Expiration date must be greater than 0');\n                      return false;\n                    }\n\n                    APIServer()\n                        .adminUserQuotaAssign(\n                      userId: widget.userId,\n                      quota: sendCount,\n                      validPeriod: validDays * 24,\n                      note: note,\n                    )\n                        .then((value) {\n                      showSuccessMessage('Gift sent successfully');\n                      context.read<UserBloc>().add(UserQuotaLoadEvent(widget.userId));\n                    }).onError(\n                      (error, stackTrace) => showErrorMessageEnhanced(context, error!),\n                    );\n\n                    return true;\n                  },\n                );\n              },\n            ),\n          ],\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          backgroundColor: customColors.backgroundColor,\n          enabled: false,\n          child: RefreshIndicator(\n            color: customColors.linkColor,\n            onRefresh: () async {\n              context.read<UserBloc>().add(UserLoadEvent(widget.userId));\n              context.read<UserBloc>().add(UserQuotaLoadEvent(widget.userId));\n            },\n            displacement: 20,\n            child: SafeArea(\n              top: false,\n              child: SingleChildScrollView(\n                child: Column(\n                  children: [\n                    BlocConsumer<UserBloc, UserState>(\n                      listenWhen: (previous, current) => current is UserOperationResult,\n                      listener: (context, state) {\n                        if (state is UserOperationResult) {\n                          if (state.success) {\n                            showSuccessMessage(state.message ?? AppLocale.operateSuccess.getString(context));\n                            context.read<UserBloc>().add(UserListLoadEvent());\n                          } else {\n                            showErrorMessage(state.message ?? AppLocale.operateFailed.getString(context));\n                          }\n                        }\n                      },\n                      buildWhen: (previous, current) => current is UserLoaded,\n                      builder: (context, state) {\n                        if (state is UserLoaded) {\n                          return ColumnBlock(\n                            innerPanding: 10,\n                            padding: const EdgeInsets.all(15),\n                            margin: const EdgeInsets.all(15),\n                            children: [\n                              Row(\n                                children: [\n                                  Expanded(\n                                    child: Column(\n                                      crossAxisAlignment: CrossAxisAlignment.start,\n                                      children: [\n                                        SizedBox(\n                                          width: double.infinity,\n                                          child: Row(\n                                            crossAxisAlignment: CrossAxisAlignment.center,\n                                            mainAxisSize: MainAxisSize.min,\n                                            children: [\n                                              Text(\n                                                'ID',\n                                                style: TextStyle(\n                                                  fontSize: 14,\n                                                  fontWeight: FontWeight.bold,\n                                                  color: customColors.weakTextColor,\n                                                ),\n                                              ),\n                                              const SizedBox(width: 10),\n                                              Text(\n                                                '${state.user.id}',\n                                                style: TextStyle(\n                                                  fontSize: 12,\n                                                  color: customColors.weakTextColor,\n                                                ),\n                                                maxLines: 5,\n                                                overflow: TextOverflow.ellipsis,\n                                              ),\n                                            ],\n                                          ),\n                                        ),\n                                        const SizedBox(height: 10),\n                                        buildTags(context, customColors, state.user),\n                                      ],\n                                    ),\n                                  ),\n                                  buildUserAvatar(state.user, radius: CustomSize.borderRadiusAll),\n                                ],\n                              ),\n                              TextItem(\n                                title: 'Type',\n                                value: state.user.userType ?? '-',\n                              ),\n                              if (state.user.phone != null && state.user.phone!.isNotEmpty)\n                                TextItem(\n                                  title: 'Photo',\n                                  value: state.user.phone!,\n                                ),\n                              if (state.user.email != null && state.user.email!.isNotEmpty)\n                                TextItem(\n                                  title: 'Email',\n                                  value: state.user.email!,\n                                ),\n                              if (state.user.realname != null && state.user.realname!.isNotEmpty)\n                                TextItem(\n                                  title: 'Nickname',\n                                  value: state.user.realname!,\n                                ),\n                              if (state.user.invitedBy != null && state.user.invitedBy! > 0)\n                                TextItem(\n                                  title: 'Inviter ID',\n                                  value: '${state.user.invitedBy}',\n                                ),\n                              if (state.user.createdAt != null)\n                                TextItem(\n                                  title: 'Creation time',\n                                  value: state.user.createdAt!.toLocal().toString(),\n                                ),\n                              TextItem(\n                                title: 'Status',\n                                value: state.user.status ?? '-',\n                              ),\n                            ],\n                          );\n                        }\n\n                        return Center(\n                          child: CircularProgressIndicator(\n                            color: customColors.linkColor,\n                          ),\n                        );\n                      },\n                    ),\n                    BlocBuilder<UserBloc, UserState>(\n                      buildWhen: (previous, current) => current is UserQuotaLoaded,\n                      builder: (context, state) {\n                        if (state is UserQuotaLoaded) {\n                          return ColumnBlock(\n                            innerPanding: 10,\n                            padding: const EdgeInsets.all(15),\n                            margin: const EdgeInsets.only(\n                              left: 15,\n                              right: 15,\n                              bottom: 15,\n                            ),\n                            children: [\n                              TextItem(\n                                title: 'Remaining credits',\n                                value: state.quota.total.toString(),\n                              ),\n                              buildPaymentDetails(customColors, state)\n                            ],\n                          );\n                        }\n\n                        return Center(\n                          child: CircularProgressIndicator(\n                            color: customColors.linkColor,\n                          ),\n                        );\n                      },\n                    ),\n                  ],\n                ),\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  // 购买历史记录\n  Widget buildPaymentDetails(\n    CustomColors customColors,\n    UserQuotaLoaded state,\n  ) {\n    return SizedBox(\n      width: double.infinity,\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          Text(\n            'Recharge History',\n            style: TextStyle(\n              fontSize: 14,\n              fontWeight: FontWeight.bold,\n              color: customColors.weakTextColor,\n            ),\n          ),\n          const SizedBox(height: 10),\n          if (state.quota.details.isEmpty)\n            const Text('No recharge record')\n          else\n            ListView(\n              shrinkWrap: true,\n              physics: const NeverScrollableScrollPhysics(),\n              children: [\n                for (var item in state.quota.details)\n                  Stack(\n                    children: [\n                      Container(\n                        margin: const EdgeInsets.symmetric(vertical: 6),\n                        padding: const EdgeInsets.only(\n                          top: 20,\n                          bottom: 10,\n                          left: 16,\n                          right: 16,\n                        ),\n                        decoration: BoxDecoration(\n                          color: customColors.paymentItemBackgroundColor,\n                          borderRadius: CustomSize.borderRadius,\n                        ),\n                        child: Column(\n                          mainAxisSize: MainAxisSize.min,\n                          children: [\n                            Row(\n                              mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                              children: [\n                                Expanded(\n                                  child: Column(\n                                    crossAxisAlignment: CrossAxisAlignment.start,\n                                    children: [\n                                      Text(\n                                        (item.note == null || item.note == '') ? 'Buy' : item.note!,\n                                        overflow: TextOverflow.ellipsis,\n                                      ),\n                                      const SizedBox(height: 5),\n                                      Text(\n                                        DateFormat(\n                                          'yyyy/MM/dd HH:mm',\n                                        ).format(item.createdAt.toLocal()),\n                                        textScaler: const TextScaler.linear(0.8),\n                                        style: TextStyle(\n                                          color: Colors.grey[600],\n                                        ),\n                                      ),\n                                    ],\n                                  ),\n                                ),\n                                Column(\n                                  crossAxisAlignment: CrossAxisAlignment.end,\n                                  children: [\n                                    Credit(\n                                      count: item.quota,\n                                      color: Colors.amber,\n                                      withAddPrefix: true,\n                                      fontWeight: FontWeight.w500,\n                                    ),\n                                    Text(\n                                      '${DateFormat('yyyy/MM/dd').format(item.periodEndAt.toLocal())} expired',\n                                      textScaler: const TextScaler.linear(0.7),\n                                    ),\n                                  ],\n                                ),\n                              ],\n                            ),\n                          ],\n                        ),\n                      ),\n                      _buildTagForItem(item),\n                    ],\n                  )\n              ],\n            ),\n        ],\n      ),\n    );\n  }\n\n  Widget _buildTagForItem(QuotaDetail item) {\n    if (item.rest <= 0) {\n      return _buildTag(AppLocale.usedUp.getString(context), Colors.orange);\n    }\n\n    if (item.expired) {\n      return _buildTag(AppLocale.expired.getString(context), Colors.grey[600]!);\n    }\n\n    return const SizedBox();\n  }\n\n  Widget _buildTag(String text, Color color) {\n    return Positioned(\n      right: 1,\n      top: 7,\n      child: Container(\n        decoration: BoxDecoration(\n          color: color,\n          borderRadius: const BorderRadius.only(topRight: CustomSize.radius, bottomLeft: CustomSize.radius),\n        ),\n        padding: const EdgeInsets.symmetric(\n          horizontal: 5,\n          vertical: 2,\n        ),\n        child: Text(\n          text,\n          textScaler: const TextScaler.linear(0.6),\n          style: const TextStyle(color: Colors.white70),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/admin/users.dart",
    "content": "import 'package:askaide/bloc/user_bloc.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/helper/image.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/pagination.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api/admin/users.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:cached_network_image/cached_network_image.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_initicon/flutter_initicon.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:flutter_slidable/flutter_slidable.dart';\nimport 'package:go_router/go_router.dart';\n\nclass AdminUsersPage extends StatefulWidget {\n  final SettingRepository setting;\n  const AdminUsersPage({\n    super.key,\n    required this.setting,\n  });\n\n  @override\n  State<AdminUsersPage> createState() => _AdminUsersPageState();\n}\n\nclass _AdminUsersPageState extends State<AdminUsersPage> {\n  /// 当前页码\n  int page = 1;\n\n  /// 每页数量\n  int perPage = 20;\n\n  /// 搜索关键字\n  final TextEditingController keywordController = TextEditingController();\n\n  @override\n  void initState() {\n    context.read<UserBloc>().add(UserListLoadEvent(\n          perPage: perPage,\n          page: page,\n          keyword: keywordController.text,\n        ));\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    keywordController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: const Text(\n            'User Management',\n            style: TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          child: Column(\n            children: [\n              Container(\n                padding: const EdgeInsets.only(left: 10, right: 10, bottom: 5),\n                child: TextField(\n                  controller: keywordController,\n                  textAlignVertical: TextAlignVertical.center,\n                  style: TextStyle(color: customColors.dialogDefaultTextColor),\n                  decoration: InputDecoration(\n                    hintText: AppLocale.search.getString(context),\n                    hintStyle: TextStyle(\n                      color: customColors.dialogDefaultTextColor,\n                    ),\n                    prefixIcon: Icon(\n                      Icons.search,\n                      color: customColors.dialogDefaultTextColor,\n                    ),\n                    isDense: true,\n                    border: InputBorder.none,\n                  ),\n                  onEditingComplete: () {\n                    context.read<UserBloc>().add(UserListLoadEvent(\n                          perPage: perPage,\n                          page: page,\n                          keyword: keywordController.text,\n                        ));\n                  },\n                ),\n              ),\n              Expanded(\n                child: RefreshIndicator(\n                  color: customColors.linkColor,\n                  onRefresh: () async {\n                    context.read<UserBloc>().add(UserListLoadEvent(\n                          perPage: perPage,\n                          page: page,\n                          keyword: keywordController.text,\n                        ));\n                  },\n                  displacement: 20,\n                  child: BlocConsumer<UserBloc, UserState>(\n                    listener: (context, state) {\n                      if (state is UserOperationResult) {\n                        if (state.success) {\n                          showSuccessMessage(state.message ?? AppLocale.operateSuccess.getString(context));\n                          context.read<UserBloc>().add(UserListLoadEvent());\n                        } else {\n                          showErrorMessage(state.message ?? AppLocale.operateFailed.getString(context));\n                        }\n                      }\n\n                      if (state is UsersLoaded) {\n                        setState(() {\n                          page = state.users.page;\n                          perPage = state.users.perPage;\n                        });\n                      }\n                    },\n                    buildWhen: (previous, current) => current is UsersLoaded,\n                    builder: (context, state) {\n                      if (state is UsersLoaded) {\n                        return SafeArea(\n                          top: false,\n                          child: Column(\n                            children: [\n                              Expanded(\n                                child: ListView.builder(\n                                  padding: const EdgeInsets.all(5),\n                                  itemCount: state.users.data.length,\n                                  itemBuilder: (context, index) {\n                                    return buildUserInfo(\n                                      context,\n                                      customColors,\n                                      state.users.data[index],\n                                    );\n                                  },\n                                ),\n                              ),\n                              if (state.users.lastPage != null && state.users.lastPage! > 1)\n                                Container(\n                                  padding: const EdgeInsets.all(10),\n                                  child: Pagination(\n                                    numOfPages: state.users.lastPage ?? 1,\n                                    selectedPage: page,\n                                    pagesVisible: 5,\n                                    onPageChanged: (selected) {\n                                      context.read<UserBloc>().add(UserListLoadEvent(\n                                            perPage: perPage,\n                                            page: selected,\n                                            keyword: keywordController.text,\n                                          ));\n                                    },\n                                  ),\n                                ),\n                            ],\n                          ),\n                        );\n                      }\n\n                      return const Center(\n                        child: CircularProgressIndicator(),\n                      );\n                    },\n                  ),\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget buildUserInfo(\n    BuildContext context,\n    CustomColors customColors,\n    AdminUser user,\n  ) {\n    return Container(\n      margin: const EdgeInsets.symmetric(\n        horizontal: 10,\n        vertical: 5,\n      ),\n      decoration: BoxDecoration(borderRadius: CustomSize.borderRadius),\n      child: Slidable(\n        endActionPane: ActionPane(\n          motion: const ScrollMotion(),\n          children: [\n            const SizedBox(width: 10),\n            SlidableAction(\n              label: AppLocale.character.getString(context),\n              borderRadius: const BorderRadius.only(\n                topLeft: CustomSize.radius,\n                bottomLeft: CustomSize.radius,\n                topRight: CustomSize.radius,\n                bottomRight: CustomSize.radius,\n              ),\n              backgroundColor: customColors.linkColor ?? Colors.green,\n              icon: Icons.people,\n              foregroundColor: Colors.white,\n              onPressed: (_) {\n                context.push('/admin/users/${user.id}/rooms');\n              },\n            ),\n          ],\n        ),\n        child: Material(\n          borderRadius: CustomSize.borderRadius,\n          color: customColors.columnBlockBackgroundColor,\n          child: InkWell(\n            borderRadius: CustomSize.borderRadiusAll,\n            onTap: () {\n              context.push('/admin/users/${user.id}');\n            },\n            child: Stack(\n              children: [\n                Row(\n                  mainAxisSize: MainAxisSize.min,\n                  children: [\n                    // 头像\n                    Stack(\n                      children: [\n                        buildUserAvatar(user),\n                        Positioned(\n                          bottom: 0,\n                          width: 70,\n                          child: ClipRRect(\n                            borderRadius: const BorderRadius.only(bottomLeft: CustomSize.radius),\n                            child: Container(\n                              color: Colors.black.withAlpha(100),\n                              padding: const EdgeInsets.symmetric(vertical: 2),\n                              child: Center(\n                                child: Text(\n                                  '#${user.id}',\n                                  style: const TextStyle(\n                                    fontSize: 10,\n                                    overflow: TextOverflow.ellipsis,\n                                    color: Colors.white,\n                                  ),\n                                ),\n                              ),\n                            ),\n                          ),\n                        ),\n                      ],\n                    ),\n                    // 名称\n                    Expanded(\n                      child: Container(\n                        padding: const EdgeInsets.all(15),\n                        child: Column(\n                          crossAxisAlignment: CrossAxisAlignment.start,\n                          children: [\n                            Text(\n                              user.displayName,\n                              style: const TextStyle(\n                                overflow: TextOverflow.ellipsis,\n                              ),\n                            ),\n                            const SizedBox(height: 5),\n                            buildTags(context, customColors, user),\n                          ],\n                        ),\n                      ),\n                    ),\n                  ],\n                ),\n                Positioned(\n                  right: 0,\n                  top: 0,\n                  child: Container(\n                    padding: const EdgeInsets.all(10),\n                    width: MediaQuery.of(context).size.width / 2.0,\n                    alignment: Alignment.centerRight,\n                    child: Column(\n                      crossAxisAlignment: CrossAxisAlignment.start,\n                      children: [\n                        Text(\n                          '${user.userType}',\n                          style: TextStyle(\n                            fontSize: 10,\n                            overflow: TextOverflow.ellipsis,\n                            color: customColors.weakTextColor,\n                          ),\n                        ),\n                      ],\n                    ),\n                  ),\n                ),\n                Positioned(\n                  right: 0,\n                  bottom: 0,\n                  child: Container(\n                    padding: const EdgeInsets.all(10),\n                    width: MediaQuery.of(context).size.width / 2.0,\n                    alignment: Alignment.centerRight,\n                    child: Column(\n                      crossAxisAlignment: CrossAxisAlignment.start,\n                      children: [\n                        Text(\n                          user.createdAt != null ? humanTime(user.createdAt) : '',\n                          style: TextStyle(\n                            fontSize: 10,\n                            overflow: TextOverflow.ellipsis,\n                            color: customColors.weakTextColor,\n                          ),\n                        ),\n                      ],\n                    ),\n                  ),\n                ),\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nWidget buildUserAvatar(\n  AdminUser user, {\n  BorderRadius radius = const BorderRadius.only(topLeft: CustomSize.radius, bottomLeft: CustomSize.radius),\n}) {\n  if (user.avatar != null && user.avatar!.startsWith('http')) {\n    return SizedBox(\n      width: 70,\n      height: 70,\n      child: ClipRRect(\n        borderRadius: radius,\n        child: CachedNetworkImage(\n          imageUrl: imageURL(user.avatar!, qiniuImageTypeAvatar),\n          fit: BoxFit.fill,\n        ),\n      ),\n    );\n  }\n\n  return Initicon(\n    text: user.displayName.split('、').join(' '),\n    size: 70,\n    backgroundColor: Colors.grey.withAlpha(100),\n    borderRadius: radius,\n  );\n}\n\nWidget buildTags(BuildContext context, CustomColors customColors, AdminUser user) {\n  final tags = <Widget>[];\n\n  if (user.email != null && user.email!.isNotEmpty) {\n    tags.add(buildTag(context, customColors, 'Email'));\n  }\n\n  if (user.phone != null && user.phone!.isNotEmpty) {\n    tags.add(buildTag(context, customColors, 'Phone'));\n  }\n\n  if (user.unionId != null && user.unionId!.isNotEmpty) {\n    tags.add(buildTag(context, customColors, 'WeChat'));\n  }\n\n  if (user.appleUid != null && user.appleUid!.isNotEmpty) {\n    tags.add(buildTag(context, customColors, 'Apple'));\n  }\n\n  return Wrap(\n    spacing: 5,\n    runSpacing: 5,\n    children: tags,\n  );\n}\n\nWidget buildTag(BuildContext context, CustomColors customColors, String s) {\n  return Container(\n    padding: const EdgeInsets.symmetric(\n      horizontal: 5,\n      vertical: 2,\n    ),\n    decoration: BoxDecoration(\n      color: customColors.tagsBackground,\n      borderRadius: CustomSize.borderRadius,\n    ),\n    child: Text(\n      s,\n      style: TextStyle(\n        fontSize: 10,\n        color: customColors.tagsText,\n      ),\n    ),\n  );\n}\n"
  },
  {
    "path": "lib/page/app_scaffold.dart",
    "content": "import 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/event.dart';\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass AppScaffold extends StatefulWidget {\n  final SettingRepository settingRepo;\n  final StatefulNavigationShell navigationShell;\n  const AppScaffold({\n    Key? key,\n    required this.settingRepo,\n    required this.navigationShell,\n  }) : super(key: key);\n  @override\n  State<AppScaffold> createState() => _AppScaffoldState();\n}\n\nclass _AppScaffoldState extends State<AppScaffold> {\n  var _showBottomNavigatorBar = true;\n\n  Function? cancelHideBottomNavigatorBarEventListener;\n  Function? cancelShowBottomNavigatorBarEventListener;\n\n  @override\n  void dispose() {\n    cancelHideBottomNavigatorBarEventListener?.call();\n    cancelShowBottomNavigatorBarEventListener?.call();\n\n    super.dispose();\n  }\n\n  @override\n  void initState() {\n    cancelHideBottomNavigatorBarEventListener =\n        GlobalEvent().on(\"hideBottomNavigatorBar\", (data) {\n      if (mounted) {\n        setState(() {\n          _showBottomNavigatorBar = false;\n        });\n      }\n    });\n\n    cancelShowBottomNavigatorBarEventListener =\n        GlobalEvent().on(\"showBottomNavigatorBar\", (data) {\n      if (mounted) {\n        setState(() {\n          _showBottomNavigatorBar = true;\n        });\n      }\n    });\n\n    super.initState();\n  }\n\n  List<BottomNavigationBarConfig> _bottomNavigationBarList(\n      {int? currentIndex}) {\n    return [\n      if (Ability().enableChat)\n        BottomNavigationBarConfig(\n          builder: (index, customColors) => createAnimatedNavBarItem(\n            icon: Icons.question_answer_outlined,\n            activatedIcon: Icons.question_answer,\n            activatedColor: customColors.linkColor,\n            label: AppLocale.chatAnywhere.getString(context),\n            activated: currentIndex == index,\n          ),\n          route: '/chat-chat',\n        ),\n      if (Ability().enableDigitalHuman)\n        BottomNavigationBarConfig(\n          builder: (index, customColors) => createAnimatedNavBarItem(\n            icon: Icons.group_outlined,\n            activatedIcon: Icons.group,\n            activatedColor: customColors.linkColor,\n            label: AppLocale.homeTitle.getString(context),\n            activated: currentIndex == index,\n          ),\n          route: '/',\n        ),\n      if (Ability().enableGallery)\n        BottomNavigationBarConfig(\n          builder: (index, customColors) => createAnimatedNavBarItem(\n            icon: Icons.auto_awesome_outlined,\n            activatedIcon: Icons.auto_awesome,\n            activatedColor: customColors.linkColor,\n            label: AppLocale.discover.getString(context),\n            activated: currentIndex == index,\n          ),\n          route: '/creative-gallery',\n        ),\n      if (Ability().enableCreationIsland)\n        BottomNavigationBarConfig(\n          builder: (index, customColors) => createAnimatedNavBarItem(\n            icon: Icons.palette_outlined,\n            activatedIcon: Icons.palette,\n            activatedColor: customColors.linkColor,\n            label: AppLocale.creativeIsland.getString(context),\n            activated: currentIndex == index,\n          ),\n          route: '/creative-draw',\n        ),\n      BottomNavigationBarConfig(\n        builder: (index, customColors) => createAnimatedNavBarItem(\n          icon: Icons.manage_accounts_outlined,\n          activatedIcon: Icons.manage_accounts,\n          activatedColor: customColors.linkColor,\n          label: AppLocale.me.getString(context),\n          activated: currentIndex == index,\n        ),\n        route: '/setting',\n      ),\n    ];\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final currentIndex = _calculateSelectedIndex(context);\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    final barItems = _bottomNavigationBarList(currentIndex: currentIndex);\n    return Scaffold(\n      backgroundColor: customColors.backgroundContainerColor,\n      body: BackgroundContainer(\n        setting: widget.settingRepo,\n        enabled: true,\n        child: widget.navigationShell,\n      ),\n      extendBody: false,\n      bottomNavigationBar: currentIndex > -1 && _showBottomNavigatorBar\n          ? BottomNavigationBar(\n              landscapeLayout: BottomNavigationBarLandscapeLayout.centered,\n              showSelectedLabels: true,\n              showUnselectedLabels: true,\n              currentIndex: _calculateSelectedIndex(context),\n              onTap: (int index) => _onTap(context, index),\n              selectedItemColor: customColors.linkColor,\n              unselectedItemColor: Colors.grey,\n              selectedFontSize: 10,\n              unselectedFontSize: 10,\n              type: BottomNavigationBarType.fixed,\n              enableFeedback: true,\n              backgroundColor: customColors.backgroundColor,\n              elevation: 0,\n              items: [\n                for (var i = 0; i < barItems.length; i++)\n                  barItems[i].builder(i, customColors),\n              ],\n            )\n          : null,\n    );\n  }\n\n  int _calculateSelectedIndex(BuildContext context) {\n    final GoRouter route = GoRouter.of(context);\n    final String location = route.location.split('?').first;\n\n    final barItems = _bottomNavigationBarList();\n    for (var i = 0; i < barItems.length; i++) {\n      if (barItems[i].route == location) return i;\n    }\n\n    return -1;\n  }\n\n  void _onTap(BuildContext context, int index) {\n    HapticFeedbackHelper.lightImpact();\n\n    widget.navigationShell.goBranch(\n      index,\n      initialLocation: index == widget.navigationShell.currentIndex,\n    );\n  }\n}\n\nBottomNavigationBarItem createAnimatedNavBarItem({\n  String? label,\n  bool activated = false,\n  Color? activatedColor,\n  required IconData icon,\n  required IconData activatedIcon,\n}) {\n  return BottomNavigationBarItem(\n    label: label,\n    icon: AnimatedCrossFade(\n      firstChild: Icon(icon),\n      secondChild: Icon(activatedIcon, color: activatedColor ?? Colors.green),\n      crossFadeState:\n          activated ? CrossFadeState.showSecond : CrossFadeState.showFirst,\n      duration: const Duration(milliseconds: 300),\n    ),\n  );\n}\n\nclass BottomNavigationBarConfig {\n  final BottomNavigationBarItem Function(int index, CustomColors customColors)\n      builder;\n  final String route;\n\n  BottomNavigationBarConfig({\n    required this.builder,\n    required this.route,\n  });\n}\n"
  },
  {
    "path": "lib/page/auth/signin_or_signup.dart",
    "content": "import 'dart:convert';\n\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/password_field.dart';\nimport 'package:askaide/page/component/verify_code_input.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass SigninOrSignupScreen extends StatefulWidget {\n  final SettingRepository settings;\n  final String username;\n  final String signInMethod;\n  final bool isSignup;\n  final String? wechatBindToken;\n\n  const SigninOrSignupScreen({\n    super.key,\n    required this.settings,\n    required this.username,\n    required this.isSignup,\n    required this.signInMethod,\n    this.wechatBindToken,\n  });\n\n  @override\n  State<SigninOrSignupScreen> createState() => _SigninOrSignupScreenState();\n}\n\nclass _SigninOrSignupScreenState extends State<SigninOrSignupScreen> {\n  final TextEditingController _inviteCodeController = TextEditingController();\n  final TextEditingController _verificationCodeController = TextEditingController();\n  final TextEditingController _passwordController = TextEditingController();\n\n  String verifyCodeId = '';\n  final phoneNumberValidator = RegExp(r\"^1[3456789]\\d{9}$\");\n  late String signInMethod;\n\n  @override\n  void initState() {\n    signInMethod = widget.signInMethod;\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    _inviteCodeController.dispose();\n    _verificationCodeController.dispose();\n    _passwordController.dispose();\n\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          backgroundColor: Colors.transparent,\n          title: Text(\n            AppLocale.verifyAccount.getString(context),\n            style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n          leading: IconButton(\n            icon: const Icon(Icons.arrow_back_ios),\n            onPressed: () {\n              context.pop();\n            },\n          ),\n        ),\n        backgroundColor: customColors.backgroundContainerColor,\n        body: BackgroundContainer(\n          setting: widget.settings,\n          enabled: false,\n          child: Align(\n            alignment: Alignment.topCenter,\n            child: ConstrainedBox(\n              constraints: const BoxConstraints(maxWidth: 400),\n              child: SingleChildScrollView(\n                  child: widget.isSignup || signInMethod == 'sms_code' || signInMethod == 'email_code'\n                      ? signInOrSignUpWithSMSOrEmailCode(customColors, context)\n                      : signInWithPassword(customColors, context)),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget signInWithPassword(\n    CustomColors customColors,\n    BuildContext context,\n  ) {\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.center,\n      children: <Widget>[\n        const SizedBox(height: 10),\n        Padding(\n          padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 20),\n          child: Text(\n            AppLocale.enterPasswordToSignin.getString(context),\n            style: TextStyle(\n              color: customColors.weakTextColor?.withAlpha(200),\n              fontSize: 15,\n            ),\n          ),\n        ),\n        const SizedBox(height: 10),\n        // 密码\n        Padding(\n          padding: const EdgeInsets.only(left: 15.0, right: 15.0, top: 15, bottom: 0),\n          child: PasswordField(\n            controller: _passwordController,\n            labelText: AppLocale.password.getString(context),\n            hintText: AppLocale.passwordInputTips.getString(context),\n          ),\n        ),\n\n        const SizedBox(height: 15),\n        // 登录\n        Container(\n          height: 45,\n          width: double.infinity,\n          margin: const EdgeInsets.symmetric(horizontal: 15),\n          decoration: BoxDecoration(color: customColors.linkColor, borderRadius: CustomSize.borderRadius),\n          child: TextButton(\n            onPressed: () {\n              FocusScope.of(context).requestFocus(FocusNode());\n\n              final password = _passwordController.text.trim();\n              if (password == '') {\n                showErrorMessage(AppLocale.passwordRequired.getString(context));\n                return;\n              }\n\n              if (password.length < 8 || password.length > 20) {\n                showErrorMessage(AppLocale.passwordFormatError.getString(context));\n                return;\n              }\n\n              APIServer()\n                  .signInWithPassword(\n                widget.username,\n                password,\n                wechatBindToken: widget.wechatBindToken,\n              )\n                  .then((value) async {\n                await widget.settings.set(settingAPIServerToken, value.token);\n                await widget.settings.set(settingUserInfo, jsonEncode(value));\n                if (context.mounted) {\n                  context.go(\n                      '${Ability().homeRoute}?show_initial_dialog=${value.isNewUser ? \"true\" : \"false\"}&reward=${value.reward}');\n                }\n              }).catchError((e) {\n                showErrorMessage(resolveError(context, e));\n              });\n            },\n            child: Text(\n              AppLocale.signIn.getString(context),\n              style: const TextStyle(color: Colors.white, fontSize: 18),\n            ),\n          ),\n        ),\n        // 找回密码\n        Container(\n          padding: const EdgeInsets.only(left: 15, right: 10),\n          width: double.infinity,\n          child: Row(\n            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n            children: [\n              TextButton(\n                onPressed: () {\n                  setState(() {\n                    signInMethod = phoneNumberValidator.hasMatch(widget.username) ? 'sms_code' : 'email_code';\n                  });\n                },\n                child: Text(\n                  AppLocale.verifyCodeLogin.getString(context),\n                  style: TextStyle(\n                    color: customColors.weakLinkColor?.withAlpha(120),\n                    fontSize: 14,\n                  ),\n                ),\n              ),\n              TextButton(\n                onPressed: () {\n                  context.push('/retrieve-password?username=${widget.username}');\n                },\n                child: Text(\n                  AppLocale.forgotPassword.getString(context),\n                  style: TextStyle(\n                    color: customColors.weakLinkColor?.withAlpha(120),\n                    fontSize: 14,\n                  ),\n                ),\n              ),\n            ],\n          ),\n        ),\n      ],\n    );\n  }\n\n  Widget signInOrSignUpWithSMSOrEmailCode(\n    CustomColors customColors,\n    BuildContext context,\n  ) {\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.center,\n      children: <Widget>[\n        const SizedBox(height: 10),\n        Padding(\n          padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 20),\n          child: Text(\n            AppLocale.verifyCodeLoginTips.getString(context),\n            style: TextStyle(\n              color: customColors.weakTextColor,\n              fontSize: 15,\n            ),\n          ),\n        ),\n        const SizedBox(height: 10),\n        // 验证码\n        Padding(\n          padding: const EdgeInsets.only(left: 15.0, right: 5.0, top: 15, bottom: 0),\n          child: VerifyCodeInput(\n            controller: _verificationCodeController,\n            onVerifyCodeSent: (id) {\n              verifyCodeId = id;\n            },\n            sendVerifyCode: () {\n              return APIServer().sendSigninOrSignupVerifyCode(\n                widget.username,\n                verifyType: phoneNumberValidator.hasMatch(widget.username) ? 'sms' : 'email',\n                isSignup: widget.isSignup,\n              );\n            },\n            sendCheck: () {\n              return true;\n            },\n          ),\n        ),\n\n        // 邀请码\n        if (widget.isSignup)\n          Padding(\n            padding: const EdgeInsets.only(left: 15.0, right: 15.0, top: 15, bottom: 0),\n            child: TextFormField(\n              controller: _inviteCodeController,\n              inputFormatters: [FilteringTextInputFormatter.singleLineFormatter],\n              decoration: InputDecoration(\n                border: const OutlineInputBorder(),\n                enabledBorder: const OutlineInputBorder(\n                  borderSide: BorderSide(color: Color.fromARGB(200, 192, 192, 192)),\n                ),\n                focusedBorder: OutlineInputBorder(\n                  borderSide: BorderSide(color: customColors.linkColor ?? Colors.green),\n                ),\n                isDense: true,\n                floatingLabelBehavior: FloatingLabelBehavior.always,\n                labelText: AppLocale.inviteCode.getString(context),\n                labelStyle: const TextStyle(fontSize: 17),\n                hintText: AppLocale.inviteCodeInputTips.getString(context),\n                hintStyle: TextStyle(\n                  color: customColors.textfieldHintColor,\n                  fontSize: 15,\n                ),\n              ),\n            ),\n          ),\n\n        const SizedBox(height: 15),\n        // 创建账号或登录\n        Container(\n          height: 45,\n          width: double.infinity,\n          margin: const EdgeInsets.symmetric(horizontal: 15),\n          decoration: BoxDecoration(color: customColors.linkColor, borderRadius: CustomSize.borderRadius),\n          child: TextButton(\n            onPressed: onCreateSubmit,\n            child: Text(\n              widget.isSignup ? AppLocale.createAccount.getString(context) : AppLocale.signIn.getString(context),\n              style: const TextStyle(color: Colors.white, fontSize: 18),\n            ),\n          ),\n        ),\n        if (!widget.isSignup)\n          Container(\n            padding: const EdgeInsets.only(left: 10, right: 10),\n            width: double.infinity,\n            child: Row(\n              mainAxisAlignment: MainAxisAlignment.spaceBetween,\n              children: [\n                TextButton(\n                  onPressed: () {\n                    setState(() {\n                      signInMethod = 'password';\n                    });\n                  },\n                  child: Text(\n                    AppLocale.usePasswordToSignin.getString(context),\n                    style: TextStyle(\n                      color: customColors.weakLinkColor?.withAlpha(120),\n                      fontSize: 14,\n                    ),\n                  ),\n                ),\n              ],\n            ),\n          ),\n      ],\n    );\n  }\n\n  onCreateSubmit() {\n    FocusScope.of(context).requestFocus(FocusNode());\n\n    if (verifyCodeId == '') {\n      showErrorMessage(AppLocale.pleaseGetVerifyCodeFirst.getString(context));\n      return;\n    }\n\n    final verificationCode = _verificationCodeController.text.trim();\n    if (verificationCode == '') {\n      showErrorMessage(AppLocale.verifyCodeRequired.getString(context));\n      return;\n    }\n    if (verificationCode.length != 6) {\n      showErrorMessage(AppLocale.verifyCodeFormatError.getString(context));\n      return;\n    }\n\n    final inviteCode = _inviteCodeController.text.trim();\n    if (inviteCode != '' && inviteCode.length > 20) {\n      showErrorMessage(AppLocale.inviteCodeFormatError.getString(context));\n      return;\n    }\n\n    final cancel = BotToast.showCustomLoading(\n      toastBuilder: (cancel) {\n        return LoadingIndicator(\n          message: AppLocale.processingWait.getString(context),\n        );\n      },\n      allowClick: false,\n      duration: const Duration(seconds: 120),\n    );\n\n    APIServer()\n        .signInOrUp(\n      username: widget.username,\n      inviteCode: inviteCode,\n      verifyCodeId: verifyCodeId,\n      verifyCode: verificationCode,\n      wechatBindToken: widget.wechatBindToken,\n    )\n        .then((value) async {\n      await widget.settings.set(settingAPIServerToken, value.token);\n      await widget.settings.set(settingUserInfo, jsonEncode(value));\n\n      if (value.needBindPhone) {\n        if (context.mounted) {\n          // ignore: use_build_context_synchronously\n          context.push('/bind-phone').then((value) async {\n            if (value == 'logout') {\n              await widget.settings.set(settingAPIServerToken, '');\n              await widget.settings.set(settingUserInfo, '');\n            }\n          });\n        }\n\n        return;\n      } else {\n        if (context.mounted) {\n          // ignore: use_build_context_synchronously\n          context.go(\n              '${Ability().homeRoute}?show_initial_dialog=${value.isNewUser ? \"true\" : \"false\"}&reward=${value.reward}');\n        }\n      }\n    }).catchError((e) {\n      showErrorMessage(resolveError(context, e));\n    }).whenComplete(() => cancel());\n  }\n}\n"
  },
  {
    "path": "lib/page/auth/signin_screen.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\n\nimport 'package:animated_text_kit/animated_text_kit.dart';\nimport 'package:askaide/bloc/version_bloc.dart';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/env.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/helper/logger.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/gestures.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:fluwx/fluwx.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:quickalert/models/quickalert_type.dart';\nimport 'package:sign_in_button/sign_in_button.dart';\nimport 'package:sign_in_with_apple/sign_in_with_apple.dart';\nimport 'package:askaide/helper/http.dart';\nimport 'package:url_launcher/url_launcher.dart';\n\nclass SignInScreen extends StatefulWidget {\n  final SettingRepository settings;\n  final String? username;\n\n  const SignInScreen({super.key, required this.settings, this.username});\n\n  @override\n  State<SignInScreen> createState() => _SignInScreenState();\n}\n\nclass _SignInScreenState extends State<SignInScreen> {\n  final TextEditingController _usernameController = TextEditingController();\n\n  final phoneNumberValidator = RegExp(r\"^1[3456789]\\d{9}$\");\n  final emailValidator = RegExp(r\"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\\.[a-zA-Z]+\");\n\n  var agreeProtocol = false;\n\n  StreamSubscription<BaseWeChatResponse>? _weChatResponse;\n\n  /// 微信登录 token，用于自动绑定微信\n  String? wechatBindToken;\n\n  @override\n  void initState() {\n    super.initState();\n    if (widget.username != null) {\n      _usernameController.text = widget.username!;\n    }\n\n    if (Ability().enableWechatSignin) {\n      _weChatResponse = weChatResponseEventHandler.distinct((a, b) => a == b).listen((event) {\n        if (event is WeChatAuthResponse) {\n          if (event.errCode != 0) {\n            showErrorMessage(event.errStr!);\n            return;\n          }\n\n          if (event.code == null) {\n            showErrorMessage(AppLocale.signInFailed.getString(context));\n            return;\n          }\n\n          processing = true;\n\n          APIServer().trySignInWithWechat(code: event.code!).then((tryRes) async {\n            if (tryRes.exist) {\n              await confirmWeChatSignin(tryRes.token);\n            } else {\n              await showBeautyDialog(\n                context,\n                type: QuickAlertType.confirm,\n                title: AppLocale.tips.getString(context),\n                text: AppLocale.wechatBindConfirm.getString(context),\n                confirmBtnText: AppLocale.directSignin.getString(context),\n                onConfirmBtnTap: () async {\n                  await confirmWeChatSignin(tryRes.token);\n                  // ignore: use_build_context_synchronously\n                  context.pop();\n                },\n                showCancelBtn: true,\n                cancelBtnText: AppLocale.bindExAccount.getString(context),\n                onCancelBtnTap: () {\n                  setState(() {\n                    wechatBindToken = tryRes.token;\n                  });\n                  context.pop();\n                },\n              );\n            }\n          }).whenComplete(() => processing = false);\n        }\n      });\n    }\n\n    context.read<VersionBloc>().add(VersionCheckEvent());\n  }\n\n  confirmWeChatSignin(String token) async {\n    try {\n      final value = await APIServer().signInWithWechat(token: token);\n\n      await widget.settings.set(settingAPIServerToken, value.token);\n      await widget.settings.set(settingUserInfo, jsonEncode(value));\n\n      await HttpClient.cleanCache();\n\n      if (value.needBindPhone) {\n        if (context.mounted) {\n          // ignore: use_build_context_synchronously\n          context.push('/bind-phone').then((value) async {\n            if (value == 'logout') {\n              await widget.settings.set(settingAPIServerToken, '');\n              await widget.settings.set(settingUserInfo, '');\n            }\n          });\n        }\n      } else {\n        // ignore: use_build_context_synchronously\n        context.go(\n            '${Ability().homeRoute}?show_initial_dialog=${value.isNewUser ? \"true\" : \"false\"}&reward=${value.reward}');\n      }\n    } catch (e) {\n      Logger.instance.e(e);\n      // ignore: use_build_context_synchronously\n      showErrorMessage(AppLocale.signInFailed.getString(context));\n    }\n  }\n\n  @override\n  void dispose() {\n    _usernameController.dispose();\n    _weChatResponse?.cancel();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          leading: IconButton(\n            icon: Icon(\n              Icons.close,\n              color: customColors.weakLinkColor,\n            ),\n            onPressed: () {\n              if (context.canPop()) {\n                context.pop();\n              } else {\n                context.go(Ability().homeRoute);\n              }\n            },\n          ),\n        ),\n        backgroundColor: customColors.backgroundContainerColor,\n        body: BackgroundContainer(\n          setting: widget.settings,\n          enabled: false,\n          child: Align(\n            alignment: Alignment.topCenter,\n            child: ConstrainedBox(\n              constraints: const BoxConstraints(maxWidth: 400),\n              child: SingleChildScrollView(\n                child: Column(\n                  children: <Widget>[\n                    Center(\n                      child: SizedBox(\n                        width: 200,\n                        height: 200,\n                        child: Image.asset('assets/app.png'),\n                      ),\n                    ),\n                    const SizedBox(height: 10),\n                    AnimatedTextKit(\n                      animatedTexts: [\n                        ColorizeAnimatedText(\n                          'AIdea',\n                          textStyle: const TextStyle(fontSize: 30.0),\n                          colors: [\n                            Colors.purple,\n                            Colors.blue,\n                            Colors.yellow,\n                            Colors.red,\n                          ],\n                        ),\n                      ],\n                    ),\n                    const SizedBox(height: 30),\n                    Padding(\n                      padding: const EdgeInsets.only(left: 15.0, right: 15.0, top: 15, bottom: 0),\n                      child: TextFormField(\n                        controller: _usernameController,\n                        inputFormatters: [FilteringTextInputFormatter.singleLineFormatter],\n                        decoration: InputDecoration(\n                          border: const OutlineInputBorder(),\n                          enabledBorder: const OutlineInputBorder(\n                            borderSide: BorderSide(color: Color.fromARGB(200, 192, 192, 192)),\n                          ),\n                          focusedBorder: OutlineInputBorder(\n                            borderSide: BorderSide(color: customColors.linkColor ?? Colors.green),\n                          ),\n                          isDense: true,\n                          floatingLabelBehavior: FloatingLabelBehavior.always,\n                          labelText: AppLocale.account.getString(context),\n                          labelStyle: const TextStyle(fontSize: 17),\n                          hintText: AppLocale.accountInputTips.getString(context),\n                          hintStyle: TextStyle(\n                            color: customColors.textfieldHintColor,\n                            fontSize: 15,\n                          ),\n                        ),\n                      ),\n                    ),\n\n                    const SizedBox(height: 8),\n                    Container(\n                      width: double.infinity,\n                      padding: const EdgeInsets.symmetric(horizontal: 15),\n                      child: Text(\n                        AppLocale.accountWillBeCreateAutomatically.getString(context),\n                        style: TextStyle(\n                          color: customColors.weakTextColor?.withAlpha(80),\n                          fontSize: 14,\n                        ),\n                      ),\n                    ),\n                    const SizedBox(height: 25),\n                    // 登录按钮\n                    Container(\n                      height: 45,\n                      width: double.infinity,\n                      margin: const EdgeInsets.symmetric(horizontal: 15),\n                      decoration: BoxDecoration(color: customColors.linkColor, borderRadius: CustomSize.borderRadius),\n                      child: TextButton(\n                        onPressed: onSigninSubmit,\n                        child: Text(\n                          AppLocale.verify.getString(context),\n                          style: const TextStyle(color: Colors.white, fontSize: 18),\n                        ),\n                      ),\n                    ),\n\n                    // Padding(\n                    //   padding: const EdgeInsets.symmetric(horizontal: 15),\n                    //   child: Column(\n                    //     children: [\n                    //       Row(\n                    //         mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                    //         children: [\n                    //           // 找回密码\n                    //           TextButton(\n                    //             onPressed: () {\n                    //               context.push(\n                    //                   '/retrieve-password?username=${_usernameController.text.trim()}');\n                    //             },\n                    //             child: Text(\n                    //               AppLocale.forgotPassword.getString(context),\n                    //               style: TextStyle(\n                    //                 color: customColors.weakLinkColor,\n                    //                 fontSize: 14,\n                    //               ),\n                    //             ),\n                    //           ),\n                    //           // 创建账号\n                    //           TextButton(\n                    //               onPressed: () {\n                    //                 context\n                    //                     .push(\n                    //                         '/signup?username=${_usernameController.text.trim()}')\n                    //                     .then((value) {\n                    //                   if (value != null) {\n                    //                     _usernameController.text = value as String;\n                    //                   }\n                    //                 });\n                    //               },\n                    //               child: Text(\n                    //                 AppLocale.createAccount.getString(context),\n                    //                 style: TextStyle(\n                    //                   color: customColors.linkColor,\n                    //                   fontSize: 14,\n                    //                 ),\n                    //               )),\n                    //         ],\n                    //       ),\n                    //     ],\n                    //   ),\n                    // ),\n                    _buildUserTermsAndPrivicy(customColors, context),\n                    const SizedBox(height: 50),\n                    // 三方登录\n                    BlocBuilder<VersionBloc, VersionState>(\n                      builder: (context, state) {\n                        return _buildThirdPartySignInButtons(context, customColors);\n                      },\n                    ),\n                    const SizedBox(height: 10),\n                  ],\n                ),\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  Row _buildUserTermsAndPrivicy(CustomColors customColors, BuildContext context) {\n    return Row(\n      mainAxisAlignment: MainAxisAlignment.center,\n      children: [\n        Transform.scale(\n          scale: 0.75,\n          child: Theme(\n            data: ThemeData(\n              unselectedWidgetColor: customColors.weakTextColor?.withAlpha(180),\n            ),\n            child: Checkbox(\n              activeColor: customColors.linkColor,\n              value: agreeProtocol,\n              onChanged: (agree) {\n                setState(() {\n                  agreeProtocol = !agreeProtocol;\n                });\n              },\n            ),\n          ),\n        ),\n        RichText(\n          text: TextSpan(\n            children: [\n              TextSpan(\n                text: AppLocale.readAndAgree.getString(context),\n                style: TextStyle(\n                  color: customColors.weakTextColor?.withAlpha(80),\n                  fontSize: 12,\n                ),\n                recognizer: TapGestureRecognizer()\n                  ..onTap = () {\n                    setState(() {\n                      agreeProtocol = !agreeProtocol;\n                    });\n                  },\n              ),\n              TextSpan(\n                text: '《${AppLocale.userTerms.getString(context)}》',\n                style: TextStyle(\n                  color: customColors.linkColor?.withAlpha(150),\n                  fontSize: 12,\n                ),\n                recognizer: TapGestureRecognizer()\n                  ..onTap = () {\n                    launchUrl(Uri.parse('$apiServerURL/public/info/terms-of-user'));\n                  },\n              ),\n              TextSpan(\n                text: AppLocale.andWord.getString(context),\n                style: TextStyle(\n                  color: customColors.weakTextColor?.withAlpha(80),\n                  fontSize: 12,\n                ),\n              ),\n              TextSpan(\n                text: '《${AppLocale.privacyPolicy.getString(context)}》',\n                style: TextStyle(\n                  color: customColors.linkColor?.withAlpha(150),\n                  fontSize: 12,\n                ),\n                recognizer: TapGestureRecognizer()\n                  ..onTap = () {\n                    launchUrl(Uri.parse('$apiServerURL/public/info/privacy-policy'));\n                  },\n              ),\n            ],\n          ),\n        )\n      ],\n    );\n  }\n\n  Widget _buildThirdPartySignInButtons(BuildContext context, CustomColors customColors) {\n    return FutureBuilder(\n      future: isWeChatInstalled,\n      builder: (context, installed) {\n        final signInItems = <Widget>[];\n\n        if (Ability().enableAppleSignin) {\n          signInItems.add(SignInButton(\n            Buttons.appleDark,\n            mini: true,\n            shape: const CircleBorder(),\n            onPressed: onAppleSigninSubmit,\n          ));\n        }\n\n        // 微信登录功能\n        if (Ability().enableWechatSignin) {\n          if (PlatformTool.isAndroid() || installed.data == true) {\n            signInItems.add(SignInButtonBuilder(\n              mini: true,\n              shape: const CircleBorder(),\n              onPressed: () async {\n                if (processing) {\n                  return;\n                }\n\n                if (!agreeProtocol) {\n                  showErrorMessage(AppLocale.pleaseReadAgreeProtocol.getString(context));\n                  return;\n                }\n\n                final ok = await sendWeChatAuth(scope: \"snsapi_userinfo\", state: \"wechat_sdk_demo_test\");\n                if (!ok) {\n                  showErrorMessage(AppLocale.installWechatFirst.getString(context));\n                }\n              },\n              backgroundColor: Colors.green,\n              text: AppLocale.wechat.getString(context),\n              icon: Icons.wechat,\n            ));\n          }\n        }\n\n        if (signInItems.isEmpty) {\n          return Container();\n        }\n\n        return Column(\n          children: [\n            Text(\n              AppLocale.otherLoginMethods.getString(context),\n              style: TextStyle(\n                fontSize: 13,\n                color: customColors.weakTextColor?.withAlpha(80),\n              ),\n            ),\n            const SizedBox(height: 8),\n            Row(\n              mainAxisAlignment: MainAxisAlignment.center,\n              mainAxisSize: MainAxisSize.min,\n              children: signInItems.map((e) => Padding(padding: const EdgeInsets.all(10), child: e)).toList(),\n            ),\n          ],\n        );\n      },\n    );\n  }\n\n  bool processing = false;\n\n  onAppleSigninSubmit() async {\n    if (processing) {\n      return;\n    }\n\n    if (!agreeProtocol) {\n      showErrorMessage(AppLocale.pleaseReadAgreeProtocol.getString(context));\n      return;\n    }\n\n    processing = true;\n\n    try {\n      final credential = await SignInWithApple.getAppleIDCredential(\n        webAuthenticationOptions: WebAuthenticationOptions(\n          clientId: 'cc.aicode.askaide',\n          redirectUri: Uri.parse('https://ai-api.aicode.cc/v1/callback/auth/sign_in_with_apple'),\n        ),\n        scopes: [\n          AppleIDAuthorizationScopes.email,\n          AppleIDAuthorizationScopes.fullName,\n        ],\n      );\n\n      APIServer()\n          .signInWithApple(\n        userIdentifier: credential.userIdentifier ?? '',\n        authorizationCode: credential.authorizationCode,\n        identityToken: credential.identityToken,\n        familyName: credential.familyName,\n        givenName: credential.givenName,\n        email: credential.email,\n        wechatBindToken: wechatBindToken,\n      )\n          .then((value) async {\n        await widget.settings.set(settingAPIServerToken, value.token);\n        await widget.settings.set(settingUserInfo, jsonEncode(value));\n\n        () {\n          if (value.needBindPhone) {\n            if (context.mounted) {\n              context.push('/bind-phone').then((value) async {\n                if (value == 'logout') {\n                  await widget.settings.set(settingAPIServerToken, '');\n                  await widget.settings.set(settingUserInfo, '');\n                }\n              });\n            }\n            return;\n          } else {\n            context.go(\n                '${Ability().homeRoute}?show_initial_dialog=${value.isNewUser ? \"true\" : \"false\"}&reward=${value.reward}');\n          }\n        }();\n\n        // HttpClient.cacheManager.clearAll().then((_) {\n        //   if (value.needBindPhone) {\n        //     if (context.mounted) {\n        //       context.push('/bind-phone').then((value) async {\n        //         if (value == 'logout') {\n        //           await widget.settings.set(settingAPIServerToken, '');\n        //           await widget.settings.set(settingUserInfo, '');\n        //         }\n        //       });\n        //     }\n        //     return;\n        //   } else {\n        //     context.go(\n        //         '${Ability().homeRoute}?show_initial_dialog=${value.isNewUser ? \"true\" : \"false\"}&reward=${value.reward}');\n        //   }\n        // });\n      }).catchError((e) {\n        Logger.instance.e(e);\n        showErrorMessage(AppLocale.signInFailed.getString(context));\n      }).onError((error, stackTrace) {\n        Logger.instance.e(error);\n        showErrorMessage(AppLocale.signInFailed.getString(context));\n      });\n    } finally {\n      processing = false;\n    }\n  }\n\n  onSigninSubmit() {\n    FocusScope.of(context).requestFocus(FocusNode());\n\n    if (processing) {\n      return;\n    }\n\n    final username = _usernameController.text.trim();\n    if (username == '') {\n      showErrorMessage(AppLocale.accountRequired.getString(context));\n      return;\n    }\n\n    if (!phoneNumberValidator.hasMatch(username) && !emailValidator.hasMatch(username)) {\n      showErrorMessage(AppLocale.accountFormatError.getString(context));\n      return;\n    }\n\n    if (!agreeProtocol) {\n      showErrorMessage(AppLocale.pleaseReadAgreeProtocol.getString(context));\n      return;\n    }\n\n    processing = true;\n\n    APIServer().checkPhoneExists(username).then((resp) async {\n      context.push(\n          '/signin-or-signup?username=$username&is_signup=${resp.exist ? \"false\" : \"true\"}&sign_in_method=${resp.signInMethod}${wechatBindToken != null ? '&wechat_bind_token=$wechatBindToken' : ''}');\n    }).catchError((e) {\n      showErrorMessage(resolveError(context, e));\n    }).whenComplete(() => processing = false);\n  }\n}\n"
  },
  {
    "path": "lib/page/auth/signup_screen.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\n\nimport 'package:animated_text_kit/animated_text_kit.dart';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/env.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/password_field.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:auto_size_text/auto_size_text.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/gestures.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:url_launcher/url_launcher.dart';\n\nclass SignupScreen extends StatefulWidget {\n  final SettingRepository settings;\n  final String? username;\n\n  const SignupScreen({super.key, required this.settings, this.username});\n\n  @override\n  State<SignupScreen> createState() => _SignupScreenState();\n}\n\nclass _SignupScreenState extends State<SignupScreen> {\n  final TextEditingController _usernameController = TextEditingController();\n  final TextEditingController _inviteCodeController = TextEditingController();\n  final TextEditingController _passwordController = TextEditingController();\n  final TextEditingController _verificationCodeController = TextEditingController();\n\n  String verifyCodeId = '';\n\n  final emailValidator = RegExp(r\"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\\.[a-zA-Z]+\");\n  final phoneNumberValidator = RegExp(r\"^1[3456789]\\d{9}$\");\n\n  var agreeProtocol = false;\n\n  //  下次发送验证码等待时间\n  int verifyCodeWaitSeconds = 0;\n  Timer? timer;\n\n  @override\n  void initState() {\n    super.initState();\n\n    if (widget.username != null) {\n      _usernameController.text = widget.username!;\n    }\n\n    // Clipboard.getData(Clipboard.kTextPlain).then((value) {\n    //   if (value == null || value.text == null || value.text == '') {\n    //     return;\n    //   }\n\n    //   if (value.text!.trim().contains(RegExp(r'\\$AIDEA\\.INV\\.\\w+\\$'))) {\n    //     final match = RegExp(r'\\$AIDEA\\.INV\\.(\\w+)\\$').firstMatch(value.text!);\n    //     if (match != null) {\n    //       final val = match.group(1);\n    //       if (val != null) {\n    //         _inviteCodeController.text = val;\n    //       }\n    //     }\n    //   }\n    // });\n  }\n\n  @override\n  void dispose() {\n    if (timer != null) {\n      timer!.cancel();\n    }\n\n    _usernameController.dispose();\n    _inviteCodeController.dispose();\n    _passwordController.dispose();\n    _verificationCodeController.dispose();\n\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      child: Scaffold(\n        appBar: AppBar(\n          backgroundColor: Colors.transparent,\n          leading: IconButton(\n            icon: const Icon(Icons.arrow_back_ios),\n            onPressed: () {\n              if (context.canPop()) {\n                context.pop(_usernameController.text.trim());\n              } else {\n                context.go('/login?username=${_usernameController.text.trim()}');\n              }\n            },\n          ),\n        ),\n        backgroundColor: Theme.of(context).colorScheme.surface,\n        body: BackgroundContainer(\n          setting: widget.settings,\n          enabled: false,\n          child: Center(\n            child: ConstrainedBox(\n              constraints: const BoxConstraints(maxWidth: 400),\n              child: SingleChildScrollView(\n                child: Column(\n                  children: <Widget>[\n                    Center(\n                      child: SizedBox(\n                        width: 150,\n                        height: 150,\n                        child: Image.asset('assets/app.png'),\n                      ),\n                    ),\n                    const SizedBox(height: 10),\n                    AnimatedTextKit(\n                      animatedTexts: [\n                        ColorizeAnimatedText(\n                          'AIdea',\n                          textStyle: const TextStyle(fontSize: 20.0),\n                          colors: [\n                            Colors.purple,\n                            Colors.blue,\n                            Colors.yellow,\n                            Colors.red,\n                          ],\n                        ),\n                      ],\n                    ),\n                    const SizedBox(height: 30),\n                    // 用户名\n                    Padding(\n                      padding: const EdgeInsets.only(left: 15.0, right: 15.0, top: 15, bottom: 0),\n                      child: TextFormField(\n                        controller: _usernameController,\n                        inputFormatters: [FilteringTextInputFormatter.singleLineFormatter],\n                        decoration: InputDecoration(\n                          border: const OutlineInputBorder(),\n                          enabledBorder: const OutlineInputBorder(\n                            borderSide: BorderSide(color: Color.fromARGB(255, 192, 192, 192)),\n                          ),\n                          focusedBorder: OutlineInputBorder(\n                            borderSide: BorderSide(color: customColors.linkColor!),\n                          ),\n                          floatingLabelStyle: TextStyle(color: customColors.linkColor!),\n                          isDense: true,\n                          floatingLabelBehavior: FloatingLabelBehavior.always,\n                          labelText: AppLocale.account.getString(context),\n                          hintText: AppLocale.accountInputTips.getString(context),\n                          hintStyle: TextStyle(\n                            color: customColors.textfieldHintColor,\n                            fontSize: 15,\n                          ),\n                        ),\n                      ),\n                    ),\n                    // 密码\n                    Padding(\n                      padding: const EdgeInsets.only(left: 15.0, right: 15.0, top: 15, bottom: 0),\n                      child: PasswordField(\n                        controller: _passwordController,\n                        labelText: AppLocale.password.getString(context),\n                        hintText: AppLocale.passwordInputTips.getString(context),\n                      ),\n                    ),\n                    // 邀请码\n                    Padding(\n                      padding: const EdgeInsets.only(left: 15.0, right: 15.0, top: 15, bottom: 0),\n                      child: TextFormField(\n                        controller: _inviteCodeController,\n                        inputFormatters: [FilteringTextInputFormatter.singleLineFormatter],\n                        decoration: InputDecoration(\n                          border: const OutlineInputBorder(),\n                          enabledBorder: const OutlineInputBorder(\n                            borderSide: BorderSide(color: Color.fromARGB(255, 192, 192, 192)),\n                          ),\n                          focusedBorder: OutlineInputBorder(\n                            borderSide: BorderSide(color: customColors.linkColor!),\n                          ),\n                          isDense: true,\n                          floatingLabelBehavior: FloatingLabelBehavior.always,\n                          labelText: AppLocale.inviteCode.getString(context),\n                          hintText: AppLocale.inviteCodeInputTips.getString(context),\n                          hintStyle: TextStyle(\n                            color: customColors.textfieldHintColor,\n                            fontSize: 15,\n                          ),\n                        ),\n                      ),\n                    ),\n                    // 验证码\n                    Padding(\n                      padding: const EdgeInsets.only(left: 15.0, right: 15.0, top: 15, bottom: 0),\n                      child: Row(children: [\n                        Expanded(\n                          child: TextFormField(\n                            controller: _verificationCodeController,\n                            inputFormatters: [\n                              FilteringTextInputFormatter.singleLineFormatter,\n                              FilteringTextInputFormatter.digitsOnly,\n                            ],\n                            maxLength: 6,\n                            keyboardType: TextInputType.number,\n                            decoration: InputDecoration(\n                              counterText: '',\n                              border: const OutlineInputBorder(),\n                              enabledBorder: const OutlineInputBorder(\n                                borderSide: BorderSide(color: Color.fromARGB(255, 192, 192, 192)),\n                              ),\n                              focusedBorder: OutlineInputBorder(\n                                borderSide: BorderSide(color: customColors.linkColor!),\n                              ),\n                              floatingLabelStyle: TextStyle(color: customColors.linkColor!),\n                              isDense: true,\n                              floatingLabelBehavior: FloatingLabelBehavior.always,\n                              labelText: AppLocale.verifyCode.getString(context),\n                              hintText: AppLocale.verifyCodeInputTips.getString(context),\n                              hintStyle: TextStyle(\n                                color: customColors.textfieldHintColor,\n                                fontSize: 15,\n                              ),\n                            ),\n                          ),\n                        ),\n                        const SizedBox(width: 20),\n                        SizedBox(\n                          width: 100,\n                          child: verifyCodeWaitSeconds > 0\n                              ? TextButton(\n                                  onPressed: null,\n                                  child: AutoSizeText(\n                                    '$verifyCodeWaitSeconds ${AppLocale.retryInSeconds.getString(context)}',\n                                    style: TextStyle(\n                                      color: customColors.weakTextColor,\n                                      fontSize: 15,\n                                    ),\n                                    maxLines: 1,\n                                  ),\n                                )\n                              : TextButton(\n                                  onPressed: () {\n                                    final username = _usernameController.text.trim();\n\n                                    final isEmail = emailValidator.hasMatch(username);\n\n                                    final isPhoneNumber = phoneNumberValidator.hasMatch(username);\n\n                                    if (_usernameController.text.trim() == '') {\n                                      showErrorMessage(AppLocale.accountRequired.getString(context));\n                                      return;\n                                    }\n\n                                    if (!isEmail && !isPhoneNumber) {\n                                      showErrorMessage(AppLocale.accountFormatError.getString(context));\n                                      return;\n                                    }\n\n                                    APIServer()\n                                        .sendSignupVerifyCode(\n                                      username,\n                                      verifyType: isEmail ? 'email' : 'sms',\n                                    )\n                                        .then((id) {\n                                      verifyCodeId = id;\n                                      setState(() {\n                                        verifyCodeWaitSeconds = 60;\n                                      });\n\n                                      if (timer != null) {\n                                        timer?.cancel();\n                                        timer = null;\n                                      }\n\n                                      timer = Timer.periodic(const Duration(seconds: 1), (timer) {\n                                        if (verifyCodeWaitSeconds <= 0) {\n                                          timer.cancel();\n                                          return;\n                                        }\n\n                                        setState(() {\n                                          verifyCodeWaitSeconds--;\n                                        });\n                                      });\n\n                                      showSuccessMessage(\n                                          '${AppLocale.verifyCodeSendSuccess.getString(context)}${isEmail ? AppLocale.email.getString(context) : AppLocale.phone.getString(context)}');\n                                    }).onError((error, stackTrace) {\n                                      setState(() {\n                                        verifyCodeWaitSeconds = 0;\n                                        timer?.cancel();\n                                      });\n\n                                      showErrorMessage(resolveError(context, error!));\n                                    });\n                                  },\n                                  child: Text(\n                                    AppLocale.sendVerifyCode.getString(context),\n                                    style: TextStyle(\n                                      color: customColors.linkColor,\n                                      fontSize: 15,\n                                    ),\n                                  ),\n                                ),\n                        ),\n                      ]),\n                    ),\n                    const SizedBox(height: 15),\n                    // 创建账号\n                    Container(\n                      height: 50,\n                      width: double.infinity,\n                      margin: const EdgeInsets.symmetric(horizontal: 15),\n                      decoration: BoxDecoration(color: customColors.linkColor, borderRadius: CustomSize.borderRadius),\n                      child: TextButton(\n                        onPressed: onCreateSubmit,\n                        child: Text(\n                          AppLocale.createAccount.getString(context),\n                          style: const TextStyle(color: Colors.white, fontSize: 20),\n                        ),\n                      ),\n                    ),\n                    // 直接登录\n                    Padding(\n                      padding: const EdgeInsets.symmetric(horizontal: 15),\n                      child: Column(children: [\n                        Row(\n                          mainAxisAlignment: MainAxisAlignment.end,\n                          children: [\n                            TextButton(\n                              onPressed: () {\n                                if (context.canPop()) {\n                                  context.pop(_usernameController.text.trim());\n                                } else {\n                                  context.go('/login?username=${_usernameController.text.trim()}');\n                                }\n                              },\n                              child: Text(\n                                AppLocale.directSigninDueHasAccount.getString(context),\n                                style: TextStyle(\n                                  color: customColors.linkColor,\n                                  fontSize: 14,\n                                ),\n                              ),\n                            ),\n                          ],\n                        ),\n                        const SizedBox(height: 10),\n                        _buildUserTermsAndPrivicy(customColors, context),\n                      ]),\n                    ),\n                  ],\n                ),\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  Row _buildUserTermsAndPrivicy(CustomColors customColors, BuildContext context) {\n    return Row(\n      mainAxisAlignment: MainAxisAlignment.center,\n      children: [\n        Transform.scale(\n          scale: 0.75,\n          child: Theme(\n            data: ThemeData(\n              unselectedWidgetColor: customColors.weakTextColor?.withAlpha(180),\n            ),\n            child: Checkbox(\n              activeColor: customColors.linkColor,\n              value: agreeProtocol,\n              onChanged: (agree) {\n                setState(() {\n                  agreeProtocol = !agreeProtocol;\n                });\n              },\n            ),\n          ),\n        ),\n        RichText(\n          text: TextSpan(\n            children: [\n              TextSpan(\n                text: AppLocale.readAndAgree.getString(context),\n                style: TextStyle(\n                  color: customColors.weakTextColor,\n                  fontSize: 13,\n                ),\n                recognizer: TapGestureRecognizer()\n                  ..onTap = () {\n                    setState(() {\n                      agreeProtocol = !agreeProtocol;\n                    });\n                  },\n              ),\n              TextSpan(\n                text: '《${AppLocale.userTerms.getString(context)}》',\n                style: TextStyle(\n                  color: customColors.weakLinkColor,\n                  fontSize: 13,\n                ),\n                recognizer: TapGestureRecognizer()\n                  ..onTap = () {\n                    launchUrl(Uri.parse('$apiServerURL/public/info/terms-of-user'));\n                  },\n              ),\n              TextSpan(\n                text: AppLocale.andWord.getString(context),\n                style: TextStyle(\n                  color: customColors.weakTextColor,\n                  fontSize: 13,\n                ),\n              ),\n              TextSpan(\n                text: '《${AppLocale.privacyPolicy.getString(context)}》',\n                style: TextStyle(color: customColors.weakLinkColor, fontSize: 13),\n                recognizer: TapGestureRecognizer()\n                  ..onTap = () {\n                    launchUrl(Uri.parse('$apiServerURL/public/info/privacy-policy'));\n                  },\n              ),\n            ],\n          ),\n        )\n      ],\n    );\n  }\n\n  onCreateSubmit() {\n    FocusScope.of(context).requestFocus(FocusNode());\n\n    final username = _usernameController.text.trim();\n    if (username == '') {\n      showErrorMessage(AppLocale.accountRequired.getString(context));\n      return;\n    }\n\n    if (!emailValidator.hasMatch(username) && !phoneNumberValidator.hasMatch(username)) {\n      showErrorMessage(AppLocale.accountFormatError.getString(context));\n      return;\n    }\n\n    final password = _passwordController.text.trim();\n    if (password == '' || password.length < 8 || password.length > 20) {\n      showErrorMessage(AppLocale.passwordFormatError.getString(context));\n      return;\n    }\n\n    if (verifyCodeId == '') {\n      showErrorMessage(AppLocale.pleaseGetVerifyCodeFirst.getString(context));\n      return;\n    }\n\n    final verificationCode = _verificationCodeController.text.trim();\n    if (verificationCode == '') {\n      showErrorMessage(AppLocale.verifyCodeRequired.getString(context));\n      return;\n    }\n    if (verificationCode.length != 6) {\n      showErrorMessage(AppLocale.verifyCodeFormatError.getString(context));\n      return;\n    }\n\n    final inviteCode = _inviteCodeController.text.trim();\n    if (inviteCode != '' && inviteCode.length > 20) {\n      showErrorMessage(AppLocale.inviteCodeFormatError.getString(context));\n      return;\n    }\n\n    if (!agreeProtocol) {\n      showErrorMessage(AppLocale.pleaseReadAgreeProtocol.getString(context));\n      return;\n    }\n\n    final cancel = BotToast.showCustomLoading(\n      toastBuilder: (cancel) {\n        return LoadingIndicator(\n          message: AppLocale.processingWait.getString(context),\n        );\n      },\n      allowClick: false,\n      duration: const Duration(seconds: 120),\n    );\n\n    APIServer()\n        .signupWithPassword(\n      username: username,\n      password: password,\n      inviteCode: inviteCode,\n      verifyCodeId: verifyCodeId,\n      verifyCode: verificationCode,\n    )\n        .then((value) async {\n      await widget.settings.set(settingAPIServerToken, value.token);\n      await widget.settings.set(settingUserInfo, jsonEncode(value));\n\n      if (value.needBindPhone) {\n        if (context.mounted) {\n          // ignore: use_build_context_synchronously\n          context.push('/bind-phone').then((value) async {\n            if (value == 'logout') {\n              await widget.settings.set(settingAPIServerToken, '');\n              await widget.settings.set(settingUserInfo, '');\n            }\n          });\n        }\n\n        return;\n      } else {\n        if (context.mounted) {\n          // ignore: use_build_context_synchronously\n          context.go(\n              '${Ability().homeRoute}?show_initial_dialog=${value.isNewUser ? \"true\" : \"false\"}&reward=${value.reward}');\n        }\n      }\n    }).catchError((e) {\n      showErrorMessage(resolveError(context, e));\n    }).whenComplete(() => cancel());\n  }\n}\n"
  },
  {
    "path": "lib/page/balance/free_statistics.dart",
    "content": "import 'package:askaide/bloc/free_count_bloc.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/message_box.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:quickalert/models/quickalert_type.dart';\n\nclass FreeStatisticsPage extends StatefulWidget {\n  final SettingRepository setting;\n\n  const FreeStatisticsPage({super.key, required this.setting});\n\n  @override\n  State<FreeStatisticsPage> createState() => _FreeStatisticsPageState();\n}\n\nclass _FreeStatisticsPageState extends State<FreeStatisticsPage> {\n  @override\n  void initState() {\n    super.initState();\n    context.read<FreeCountBloc>().add(FreeCountReloadAllEvent(checkSigninStatus: true));\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: Text(\n            AppLocale.freeQuota.getString(context),\n            style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          backgroundColor: customColors.backgroundColor,\n          enabled: false,\n          child: RefreshIndicator(\n            displacement: 20,\n            color: customColors.linkColor,\n            onRefresh: () async {\n              context.read<FreeCountBloc>().add(FreeCountReloadAllEvent());\n            },\n            child: SafeArea(\n              child: SizedBox(\n                height: double.infinity,\n                child: SingleChildScrollView(\n                  physics: const AlwaysScrollableScrollPhysics(),\n                  child: BlocConsumer<FreeCountBloc, FreeCountState>(\n                    listenWhen: (previous, current) => current is FreeCountLoadedState,\n                    listener: (BuildContext context, FreeCountState state) {\n                      if (state is FreeCountLoadedState) {\n                        if (state.needSignin) {\n                          showBeautyDialog(\n                            context,\n                            type: QuickAlertType.warning,\n                            text: AppLocale.freeModelNeedSignIn.getString(context),\n                            confirmBtnText: AppLocale.signIn.getString(context),\n                            onConfirmBtnTap: () {\n                              context.pop();\n                              context.go('/login');\n                            },\n                            showCancelBtn: true,\n                          );\n                        }\n                      }\n                    },\n                    builder: (context, state) {\n                      if (state is FreeCountLoadedState) {\n                        if (state.counts.isEmpty) {\n                          return Padding(\n                            padding: const EdgeInsets.symmetric(horizontal: 10),\n                            child: Center(\n                              child: MessageBox(\n                                message: AppLocale.noFreeModel.getString(context),\n                                type: MessageBoxType.warning,\n                              ),\n                            ),\n                          );\n                        }\n                        return Padding(\n                          padding: const EdgeInsets.symmetric(horizontal: 10),\n                          child: Column(\n                            children: [\n                              MessageBox(\n                                message: AppLocale.freeModelInfo.getString(context),\n                                type: MessageBoxType.info,\n                              ),\n                              const SizedBox(height: 10),\n                              ColumnBlock(\n                                innerPanding: 5,\n                                children: [\n                                  Padding(\n                                    padding: const EdgeInsets.symmetric(vertical: 5),\n                                    child: Row(\n                                      children: [\n                                        const Expanded(\n                                            child: Text(\n                                          'Model',\n                                          style: TextStyle(\n                                            fontWeight: FontWeight.bold,\n                                            fontSize: 14,\n                                          ),\n                                        )),\n                                        Row(\n                                          children: [\n                                            Text(\n                                              AppLocale.todayLeft.getString(context),\n                                              style: const TextStyle(\n                                                fontWeight: FontWeight.bold,\n                                                fontSize: 14,\n                                              ),\n                                            ),\n                                          ],\n                                        ),\n                                      ],\n                                    ),\n                                  ),\n                                  ...state.counts.map((e) {\n                                    return Padding(\n                                      padding: const EdgeInsets.symmetric(vertical: 5),\n                                      child: Row(\n                                        children: [\n                                          Expanded(\n                                            child: Row(\n                                              children: [\n                                                Text(\n                                                  e.name,\n                                                  style: const TextStyle(\n                                                    fontSize: 14,\n                                                  ),\n                                                ),\n                                                if (e.info != null && e.info != '') const SizedBox(width: 5),\n                                                if (e.info != null && e.info != '')\n                                                  InkWell(\n                                                    onTap: () {\n                                                      showBeautyDialog(\n                                                        context,\n                                                        type: QuickAlertType.info,\n                                                        text: e.info ?? '',\n                                                        confirmBtnText: AppLocale.gotIt.getString(context),\n                                                        showCancelBtn: false,\n                                                      );\n                                                    },\n                                                    child: Icon(\n                                                      Icons.help_outline,\n                                                      size: 16,\n                                                      color: customColors.weakLinkColor?.withAlpha(150),\n                                                    ),\n                                                  ),\n                                              ],\n                                            ),\n                                          ),\n                                          buildLeftCountWidget(\n                                            leftCount: e.leftCount,\n                                            maxCount: e.maxCount,\n                                          ),\n                                        ],\n                                      ),\n                                    );\n                                  }),\n                                ],\n                              ),\n                            ],\n                          ),\n                        );\n                      }\n\n                      return const Center(child: LoadingIndicator());\n                    },\n                  ),\n                ),\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget buildLeftCountWidget({required int leftCount, required int maxCount}) {\n    return Text(\n      '$leftCount',\n      style: const TextStyle(\n        fontSize: 14,\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/balance/payment.dart",
    "content": "import 'dart:async';\n\nimport 'package:askaide/bloc/payment_bloc.dart';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/helper/logger.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/balance/price_block.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/chat/markdown.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/item_selector_search.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api/payment.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:flutter_stripe/flutter_stripe.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:in_app_purchase/in_app_purchase.dart';\nimport 'package:qr_flutter/qr_flutter.dart';\nimport 'package:quickalert/models/quickalert_type.dart';\nimport 'package:tobias/tobias.dart';\nimport 'package:url_launcher/url_launcher_string.dart';\nimport 'package:fluwx/fluwx.dart' as fluwx;\n\nimport 'web/payment_element.dart' if (dart.library.js) 'web/payment_element_web.dart';\n\nclass PaymentScreen extends StatefulWidget {\n  final SettingRepository setting;\n  const PaymentScreen({super.key, required this.setting});\n\n  @override\n  State<PaymentScreen> createState() => _PaymentScreenState();\n}\n\nclass _PaymentScreenState extends State<PaymentScreen> {\n  StreamSubscription<List<PurchaseDetails>>? _subscription;\n  Function()? _cancelLoading;\n\n  @override\n  void initState() {\n    if (PlatformTool.isIOS()) {\n      final purchaseUpdated = InAppPurchase.instance.purchaseStream;\n      _subscription = purchaseUpdated.listen((purchaseDetailsList) {\n        _listenToPurchaseUpdated(purchaseDetailsList);\n      }, onDone: () {\n        _subscription?.cancel();\n      }, onError: (error) {\n        showErrorMessage(resolveError(context, error));\n      });\n    } else if (PlatformTool.isAndroid()) {\n      // 微信支付\n      fluwx.weChatResponseEventHandler.listen((res) {\n        if (res is fluwx.WeChatPaymentResponse) {\n          if (res.isSuccessful) {\n            showSuccessMessage('购买成功');\n          } else {\n            showErrorMessage(res.errStr ?? '支付失败');\n          }\n        }\n      });\n    }\n\n    // 加载支付产品列表\n    context.read<PaymentBloc>().add(PaymentLoadAppleProducts());\n\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    _subscription?.cancel();\n    super.dispose();\n  }\n\n  // 支付 ID\n  String? paymentId;\n\n  ProductDetails? selectedProduct;\n\n  /// 监听支付状态\n  void _listenToPurchaseUpdated(\n    List<PurchaseDetails> purchaseDetailsList,\n  ) async {\n    for (var purchaseDetails in purchaseDetailsList) {\n      switch (purchaseDetails.status) {\n        case PurchaseStatus.pending:\n          await APIServer().updateApplePay(\n            paymentId!,\n            productId: purchaseDetails.productID,\n            localVerifyData: purchaseDetails.verificationData.localVerificationData,\n            serverVerifyData: purchaseDetails.verificationData.serverVerificationData,\n            verifyDataSource: purchaseDetails.verificationData.source,\n          );\n\n          break;\n        case PurchaseStatus.error:\n          APIServer()\n              .cancelApplePay(\n            paymentId!,\n            reason: purchaseDetails.error.toString(),\n          )\n              .whenComplete(() {\n            _closePaymentLoading();\n            showErrorMessage(resolveError(context, purchaseDetails.error!));\n          });\n\n          break;\n        case PurchaseStatus.purchased: // fall through\n          if (paymentId != null) {\n            APIServer()\n                .verifyApplePay(\n              paymentId!,\n              productId: purchaseDetails.productID,\n              purchaseId: purchaseDetails.purchaseID,\n              transactionDate: purchaseDetails.transactionDate,\n              localVerifyData: purchaseDetails.verificationData.localVerificationData,\n              serverVerifyData: purchaseDetails.verificationData.serverVerificationData,\n              verifyDataSource: purchaseDetails.verificationData.source,\n              status: purchaseDetails.status.toString(),\n            )\n                .then((status) {\n              _closePaymentLoading();\n              showSuccessMessage('购买成功');\n            }).onError((error, stackTrace) {\n              _closePaymentLoading();\n              showErrorMessage(resolveError(context, error!));\n            });\n          }\n\n          break;\n        case PurchaseStatus.restored:\n          Logger.instance.d('恢复购买');\n          _closePaymentLoading();\n          showSuccessMessage('恢复成功');\n          break;\n        case PurchaseStatus.canceled:\n          APIServer().cancelApplePay(paymentId!).whenComplete(() {\n            _closePaymentLoading();\n            showErrorMessage('购买已取消');\n          });\n\n          break;\n      }\n\n      if (purchaseDetails.pendingCompletePurchase) {\n        await InAppPurchase.instance.completePurchase(purchaseDetails);\n      }\n    }\n  }\n\n  /// 关闭支付中的 loading\n  void _closePaymentLoading() {\n    paymentId = null;\n    if (_cancelLoading != null) {\n      _cancelLoading!();\n      _cancelLoading = null;\n    }\n  }\n\n  /// 开始支付中的 loading\n  void _startPaymentLoading() {\n    _cancelLoading = BotToast.showCustomLoading(\n      toastBuilder: (cancel) {\n        return LoadingIndicator(\n          message: AppLocale.processingWait.getString(context),\n        );\n      },\n      allowClick: false,\n      duration: const Duration(seconds: 120),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          elevation: 0,\n          title: Text(\n            AppLocale.buyCredits.getString(context),\n            style: const TextStyle(\n              fontSize: CustomSize.appBarTitleSize,\n            ),\n          ),\n          leading: IconButton(\n            icon: Icon(\n              Icons.arrow_back_ios,\n              color: customColors.weakLinkColor,\n            ),\n            onPressed: () {\n              if (context.canPop()) {\n                context.pop();\n              } else {\n                context.go('/setting');\n              }\n            },\n          ),\n          actions: [\n            if (Ability().isUserLogon())\n              TextButton(\n                style: ButtonStyle(\n                  overlayColor: WidgetStateProperty.all(Colors.transparent),\n                ),\n                onPressed: () {\n                  context.push('/quota-details');\n                },\n                child: Text(\n                  AppLocale.paymentHistory.getString(context),\n                  style: TextStyle(color: customColors.weakLinkColor),\n                  textScaler: const TextScaler.linear(0.9),\n                ),\n              ),\n          ],\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          child: SingleChildScrollView(\n            child: Container(\n              padding: const EdgeInsets.all(10),\n              child: BlocConsumer<PaymentBloc, PaymentState>(\n                listener: (context, state) {\n                  if (state is PaymentAppleProductsLoaded) {\n                    if (state.error != null) {\n                      showErrorMessage(resolveError(context, state.error!));\n                    } else {\n                      if (state.localProducts.isEmpty) {\n                        showErrorMessage('暂无可购买的产品');\n                      } else {\n                        final recommends = state.localProducts.where((e) => e.recommend).toList();\n                        if (recommends.isNotEmpty && !state.loading) {\n                          setState(() {\n                            selectedProduct = state.products.firstWhere((e) => e.id == recommends.first.id);\n                          });\n                        }\n                      }\n                    }\n                  }\n                },\n                buildWhen: (previous, current) => current is PaymentAppleProductsLoaded,\n                builder: (context, state) {\n                  if (state is! PaymentAppleProductsLoaded) {\n                    return const Center(child: LoadingIndicator());\n                  }\n\n                  if (state.error != null) {\n                    return Center(\n                      child: Text(\n                        state.error.toString(),\n                        style: const TextStyle(color: Colors.red),\n                      ),\n                    );\n                  }\n\n                  return Column(\n                    children: [\n                      Column(\n                        children: [\n                          for (var item in state.products)\n                            GestureDetector(\n                              onTap: () {\n                                setState(() {\n                                  selectedProduct = item;\n                                });\n                              },\n                              child: PriceBlock(\n                                customColors: customColors,\n                                detail: item,\n                                selectedProduct: selectedProduct,\n                                product: state.localProducts.firstWhere((e) => e.id == item.id),\n                                loading: state.loading,\n                              ),\n                            ),\n                        ],\n                      ),\n                      const SizedBox(height: 20),\n                      if (selectedProduct != null)\n                        Padding(\n                          padding: const EdgeInsets.symmetric(horizontal: 15),\n                          child: Text(\n                            state.localProducts.where((e) => e.id == selectedProduct!.id).first.description!,\n                            style: TextStyle(\n                              fontSize: 12,\n                              color: customColors.weakTextColor,\n                            ),\n                          ),\n                        ),\n                      const SizedBox(height: 10),\n                      Padding(\n                        padding: const EdgeInsets.symmetric(horizontal: 10),\n                        child: EnhancedButton(\n                          title: '${AppLocale.toPay.getString(context)}   ${selectedProduct?.price ?? ''}',\n                          onPressed: () async {\n                            if (state.loading) {\n                              showErrorMessage('价格加载中，请稍后');\n                              return;\n                            }\n                            if (selectedProduct == null) {\n                              showErrorMessage('请选择购买的产品');\n                              return;\n                            }\n\n                            if (!Ability().isUserLogon()) {\n                              showBeautyDialog(\n                                context,\n                                type: QuickAlertType.warning,\n                                text: '该功能需要登录账号后使用',\n                                onConfirmBtnTap: () {\n                                  context.pop();\n                                  context.push('/login');\n                                },\n                                showCancelBtn: true,\n                                confirmBtnText: '立即登录',\n                              );\n                              return;\n                            }\n\n                            // 根据当前平台不通，调用不同的支付方式\n                            if (PlatformTool.isAndroid()) {\n                              handlePaymentForAndroid(\n                                state,\n                                context,\n                                customColors,\n                              );\n                            } else if (PlatformTool.isIOS()) {\n                              _startPaymentLoading();\n                              try {\n                                await createAppApplePay();\n                              } catch (e) {\n                                _closePaymentLoading();\n                                // ignore: use_build_context_synchronously\n                                showErrorMessage(resolveError(context, e));\n                              }\n                            } else if (PlatformTool.isWeb()) {\n                              handlePaymentForWeb(state, context, customColors);\n                            } else {\n                              handlePaymentForPC(state, context, customColors);\n                            }\n                          },\n                        ),\n                      ),\n                      const SizedBox(height: 20),\n                      if (state.note != null)\n                        SizedBox(\n                          width: double.infinity,\n                          child: Column(\n                            crossAxisAlignment: CrossAxisAlignment.start,\n                            mainAxisAlignment: MainAxisAlignment.start,\n                            mainAxisSize: MainAxisSize.min,\n                            children: [\n                              Text(\n                                '   购买说明：',\n                                style: TextStyle(\n                                  fontSize: 12,\n                                  color: customColors.paymentItemTitleColor?.withOpacity(0.5),\n                                ),\n                              ),\n                              const SizedBox(height: 10),\n                              Markdown(\n                                data: state.note!,\n                                textStyle: TextStyle(\n                                  color: customColors.paymentItemTitleColor?.withOpacity(0.5),\n                                  fontSize: 12,\n                                ),\n                              ),\n                            ],\n                          ),\n                        ),\n                    ],\n                  );\n                },\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  void handlePaymentForWeb(PaymentAppleProductsLoaded state, BuildContext context, CustomColors customColors) {\n    // openConfirmDialog(\n    //   context,\n    //   '当前终端在线支付暂不可用，预计最晚 2023 年 10 月 15 日恢复，如需充值，请使用移动端 APP（支持 Android 手机、Apple 手机）。',\n    //   () {\n    //     launchUrlString(\n    //       'https://aidea.aicode.cc',\n    //       mode: LaunchMode.externalApplication,\n    //     );\n    //   },\n    //   confirmText: '前往下载移动端 APP',\n    // )\n\n    final localProduct = state.localProducts.firstWhere((e) => e.id == selectedProduct!.id);\n\n    final enableStripe = Ability().enableStripe && localProduct.supportStripe;\n\n    openListSelectDialog(\n      context,\n      <SelectorItem>[\n        if (Ability().enableWechatPay)\n          SelectorItem(\n            const PaymentMethodItem(\n              title: Text('微信支付'),\n              image: 'assets/wechat-pay.png',\n            ),\n            'wechat-pay',\n          ),\n        SelectorItem(\n          const PaymentMethodItem(\n            title: Text('支付宝扫码'),\n            image: 'assets/zhifubao.png',\n          ),\n          'web',\n        ),\n        SelectorItem(\n          const PaymentMethodItem(\n            title: Text('支付宝手机版'),\n            image: 'assets/zhifubao.png',\n          ),\n          'wap',\n        ),\n        if (enableStripe)\n          SelectorItem(\n            PaymentMethodItem(\n              title: Row(\n                mainAxisAlignment: MainAxisAlignment.center,\n                children: [\n                  const Text('Stripe'),\n                  const SizedBox(width: 5),\n                  Text(\n                    '(${localProduct.retailPriceUSDText})',\n                    style: TextStyle(\n                      color: customColors.paymentItemTitleColor?.withOpacity(0.5),\n                      fontSize: 12,\n                    ),\n                  ),\n                ],\n              ),\n              image: 'assets/stripe.png',\n            ),\n            'stripe',\n          ),\n      ],\n      (value) {\n        _startPaymentLoading();\n        if (value.value == 'stripe') {\n          createStripePayment(localProduct);\n        } else if (value.value == 'wechat-pay') {\n          createWechatPayment(localProduct);\n        } else {\n          createWebOrWapAlipay(source: value.value).onError((error, stackTrace) {\n            _closePaymentLoading();\n            showErrorMessageEnhanced(context, error!);\n          });\n        }\n\n        return true;\n      },\n      title: AppLocale.selectPaymentMethod.getString(context),\n      heightFactor: 0.4,\n    );\n  }\n\n  /// 处理 PC 端支付\n  void handlePaymentForPC(\n    PaymentAppleProductsLoaded state,\n    BuildContext context,\n    CustomColors customColors,\n  ) async {\n    final localProduct = state.localProducts.firstWhere((e) => e.id == selectedProduct!.id);\n    final enableStripe = Ability().enableStripe && localProduct.supportStripe;\n    openListSelectDialog(\n      context,\n      <SelectorItem>[\n        if (Ability().enableWechatPay)\n          SelectorItem(\n            const PaymentMethodItem(\n              title: Text('微信支付'),\n              image: 'assets/wechat-pay.png',\n            ),\n            'wechat-pay',\n          ),\n        if (Ability().enableOtherPay)\n          SelectorItem(\n            const PaymentMethodItem(\n              title: Text('支付宝'),\n              image: 'assets/zhifubao.png',\n            ),\n            'alipay',\n          ),\n        if (enableStripe)\n          SelectorItem(\n            PaymentMethodItem(\n              title: Row(\n                mainAxisAlignment: MainAxisAlignment.center,\n                children: [\n                  const Text('Stripe'),\n                  const SizedBox(width: 5),\n                  Text(\n                    '(${localProduct.retailPriceUSDText})',\n                    style: TextStyle(\n                      color: customColors.paymentItemTitleColor?.withOpacity(0.5),\n                      fontSize: 12,\n                    ),\n                  ),\n                ],\n              ),\n              image: 'assets/stripe.png',\n            ),\n            'stripe',\n          ),\n      ],\n      (value) {\n        _startPaymentLoading();\n\n        if (value.value == 'alipay') {\n          createWebOrWapAlipay(source: 'web').onError((error, stackTrace) {\n            _closePaymentLoading();\n            showErrorMessageEnhanced(context, error!);\n          });\n        } else if (value.value == 'wechat-pay') {\n          createWechatPayment(localProduct);\n        } else {\n          createStripePayment(localProduct);\n        }\n\n        return true;\n      },\n      title: AppLocale.selectPaymentMethod.getString(context),\n      heightFactor: 0.4,\n    );\n  }\n\n  void handlePaymentForAndroid(\n    PaymentAppleProductsLoaded state,\n    BuildContext context,\n    CustomColors customColors,\n  ) {\n    final localProduct = state.localProducts.firstWhere((e) => e.id == selectedProduct!.id);\n    final enableStripe = Ability().enableStripe && localProduct.supportStripe;\n    openListSelectDialog(\n      context,\n      <SelectorItem>[\n        if (Ability().enableWechatPay)\n          SelectorItem(\n            const PaymentMethodItem(\n              title: Text('微信支付'),\n              image: 'assets/wechat-pay.png',\n            ),\n            'wechat-pay',\n          ),\n        if (Ability().enableOtherPay)\n          SelectorItem(\n            const PaymentMethodItem(\n              title: Text('支付宝'),\n              image: 'assets/zhifubao.png',\n            ),\n            'alipay',\n          ),\n        if (enableStripe)\n          SelectorItem(\n            PaymentMethodItem(\n              title: Row(\n                mainAxisAlignment: MainAxisAlignment.center,\n                children: [\n                  const Text('Stripe'),\n                  const SizedBox(width: 5),\n                  Text(\n                    '(${localProduct.retailPriceUSDText})',\n                    style: TextStyle(\n                      color: customColors.paymentItemTitleColor?.withOpacity(0.5),\n                      fontSize: 12,\n                    ),\n                  ),\n                ],\n              ),\n              image: 'assets/stripe.png',\n            ),\n            'stripe',\n          ),\n      ],\n      (value) {\n        _startPaymentLoading();\n\n        if (value.value == 'alipay') {\n          createAppAlipay().onError((error, stackTrace) {\n            _closePaymentLoading();\n            showErrorMessageEnhanced(context, error!);\n          });\n        } else if (value.value == 'wechat-pay') {\n          createWechatPayment(localProduct);\n        } else {\n          createStripePayment(localProduct);\n        }\n\n        return true;\n      },\n      title: AppLocale.selectPaymentMethod.getString(context),\n      heightFactor: 0.3,\n    );\n  }\n\n  /// 创建苹果应用内支付\n  Future<void> createAppApplePay() async {\n    // 创建支付，服务端保存支付信息，创建支付订单\n    paymentId = await APIServer().createApplePay(selectedProduct!.id);\n    // 发起 Apple 支付\n    InAppPurchase.instance.buyConsumable(\n      purchaseParam: PurchaseParam(productDetails: selectedProduct!),\n    );\n  }\n\n  /// 创建其它付款（Web 或 Wap）\n  Future<void> createWebOrWapAlipay({required String source}) async {\n    final created = await APIServer().createOtherPay(\n      selectedProduct!.id,\n      source: source,\n    );\n    paymentId = created.paymentId;\n\n    // 调起其它支付\n    launchUrlString(created.params).then((value) {\n      _closePaymentLoading();\n      openConfirmDialog(\n        context,\n        '请确认支付宝支付是否已完成',\n        () async {\n          _startPaymentLoading();\n          try {\n            final resp = await APIServer().queryPaymentStatus(created.paymentId);\n            if (resp.success) {\n              showSuccessMessage(resp.note ?? '支付成功');\n              _closePaymentLoading();\n            } else {\n              // 支付失败，延迟 5s 再次查询支付状态\n              await Future.delayed(const Duration(seconds: 5), () async {\n                try {\n                  final value = await APIServer().queryPaymentStatus(created.paymentId);\n\n                  if (value.success) {\n                    showSuccessMessage(value.note ?? '支付成功');\n                  } else {\n                    showErrorMessage('支付未完成，我们接收到的状态为：${value.note}');\n                  }\n                  _closePaymentLoading();\n                } catch (e) {\n                  _closePaymentLoading();\n                  // ignore: use_build_context_synchronously\n                  showErrorMessage(resolveError(context, e));\n                }\n              });\n            }\n          } catch (e) {\n            _closePaymentLoading();\n            // ignore: use_build_context_synchronously\n            showErrorMessage(resolveError(context, e));\n          }\n        },\n        confirmText: '已完成支付',\n        cancelText: '支付遇到问题，稍后继续',\n      );\n    });\n  }\n\n  /// 获取当前支付来源参数\n  String paymentSource() {\n    if (PlatformTool.isWeb()) {\n      return 'web';\n    } else if (PlatformTool.isIOS() || PlatformTool.isAndroid()) {\n      return 'app';\n    }\n    return 'pc';\n  }\n\n  /// 创建微信支付\n  Future<void> createWechatPayment(PaymentProduct product) async {\n    try {\n      final created = await APIServer().createWechatPayment(\n        productId: product.id,\n        source: paymentSource(),\n      );\n      paymentId = created.paymentId;\n\n      if (PlatformTool.isAndroid() || PlatformTool.isIOS()) {\n        await fluwx.payWithWeChat(\n          appId: created.appId!,\n          partnerId: created.partnerId!,\n          prepayId: created.prepayId!,\n          packageValue: created.package!,\n          nonceStr: created.noncestr!,\n          timeStamp: int.parse(created.timestamp!),\n          sign: created.sign!,\n        );\n      } else {\n        openDialog(\n          // ignore: use_build_context_synchronously\n          context,\n          builder: Builder(builder: (context) {\n            return Container(\n              alignment: Alignment.center,\n              height: 250,\n              width: 220,\n              margin: const EdgeInsets.only(top: 20),\n              child: Column(\n                children: [\n                  ClipRRect(\n                    borderRadius: CustomSize.borderRadius,\n                    child: QrImageView(\n                      data: created.codeUrl!,\n                      version: QrVersions.auto,\n                      size: 200,\n                      backgroundColor: Colors.white,\n                    ),\n                  ),\n                  const SizedBox(height: 10),\n                  const Text(\n                    '请使用微信扫码支付',\n                    style: TextStyle(\n                      fontSize: 14,\n                    ),\n                  ),\n                ],\n              ),\n            );\n          }),\n          onSubmit: () {\n            _startPaymentLoading();\n            APIServer().queryPaymentStatus(created.paymentId).then((resp) {\n              if (resp.success) {\n                showSuccessMessage(resp.note ?? '支付成功');\n                _closePaymentLoading();\n              } else {\n                // 支付失败，延迟 5s 再次查询支付状态\n                Future.delayed(const Duration(seconds: 5), () async {\n                  try {\n                    final value = await APIServer().queryPaymentStatus(created.paymentId);\n\n                    if (value.success) {\n                      showSuccessMessage(value.note ?? '支付成功');\n                    } else {\n                      showErrorMessage('支付未完成，我们接收到的状态为：${value.note}');\n                    }\n                  } catch (e) {\n                    // ignore: use_build_context_synchronously\n                    showErrorMessage(resolveError(context, e));\n                  } finally {\n                    _closePaymentLoading();\n                  }\n                });\n              }\n            });\n\n            return true;\n          },\n          confirmText: '已完成支付',\n          barrierDismissible: false,\n        );\n      }\n    } on Exception catch (e) {\n      // ignore: use_build_context_synchronously\n      showErrorMessageEnhanced(context, e);\n    } finally {\n      _closePaymentLoading();\n    }\n  }\n\n  /// 创建 Stripe 支付\n  Future<void> createStripePayment(PaymentProduct product) async {\n    try {\n      final created = await APIServer().createStripePaymentSheet(\n        productId: product.id,\n        source: paymentSource(),\n      );\n      paymentId = created.paymentId;\n\n      if (PlatformTool.isWeb() || PlatformTool.isAndroid() || PlatformTool.isIOS()) {\n        Stripe.publishableKey = created.publishableKey;\n        Stripe.urlScheme = 'flutterstripe';\n\n        await Stripe.instance.applySettings();\n      }\n\n      if (PlatformTool.isWeb()) {\n        Navigator.push(\n          // ignore: use_build_context_synchronously\n          context,\n          MaterialPageRoute(\n            fullscreenDialog: true,\n            builder: (context) {\n              return Scaffold(\n                appBar: AppBar(),\n                body: SafeArea(\n                  child: Column(\n                    children: [\n                      Expanded(\n                        child: Container(\n                          padding: const EdgeInsets.all(15),\n                          child: Builder(\n                            builder: (context) {\n                              return PlatformPaymentElement(\n                                created.paymentIntent,\n                              );\n                            },\n                          ),\n                        ),\n                      ),\n                      Container(\n                        padding: const EdgeInsets.all(15),\n                        child: EnhancedButton(\n                          title: '确定付款（${product.retailPriceUSDText}）',\n                          onPressed: () async {\n                            final cancel = BotToast.showCustomLoading(\n                              toastBuilder: (cancel) {\n                                return LoadingIndicator(\n                                  message: AppLocale.processingWait.getString(context),\n                                );\n                              },\n                              allowClick: false,\n                              duration: const Duration(seconds: 120),\n                            );\n\n                            try {\n                              await pay(created.paymentId);\n                            } catch (e) {\n                              Logger.instance.e('支付失败：$e');\n                              // ignore: use_build_context_synchronously\n                              showErrorMessageEnhanced(context, '请填写完整的支付信息');\n                            } finally {\n                              cancel();\n                            }\n                          },\n                        ),\n                      )\n                    ],\n                  ),\n                ),\n              );\n            },\n          ),\n        );\n      } else if (PlatformTool.isAndroid() || PlatformTool.isIOS()) {\n        // 调起 Stripe 支付\n        await Stripe.instance.initPaymentSheet(\n          paymentSheetParameters: SetupPaymentSheetParameters(\n            paymentIntentClientSecret: created.paymentIntent,\n            merchantDisplayName: 'AIdea',\n            customerId: created.customer,\n            customerEphemeralKeySecret: created.ephemeralKey,\n            returnURL: 'flutterstripe://redirect',\n            // ignore: use_build_context_synchronously\n            style: Ability().themeMode == 'dark' ? ThemeMode.dark : ThemeMode.light,\n          ),\n        );\n\n        // 确认支付\n        await Stripe.instance.presentPaymentSheet();\n\n        showSuccessMessage('购买成功');\n      } else {\n        // PC 端支付，发起 Web 页面\n        if (created.proxyUrl == '') {\n          showErrorMessage('支付失败：未能获取支付链接');\n          return;\n        }\n\n        Logger.instance.d(created.proxyUrl);\n\n        launchUrlString(\n          created.proxyUrl,\n          mode: LaunchMode.externalApplication,\n        ).then((value) {\n          _closePaymentLoading();\n          openConfirmDialog(\n            context,\n            '请确认支付是否已完成',\n            () async {\n              _startPaymentLoading();\n              try {\n                final resp = await APIServer().queryPaymentStatus(created.paymentId);\n                if (resp.success) {\n                  showSuccessMessage(resp.note ?? '支付成功');\n                  _closePaymentLoading();\n                } else {\n                  // 支付失败，延迟 5s 再次查询支付状态\n                  await Future.delayed(const Duration(seconds: 5), () async {\n                    try {\n                      final value = await APIServer().queryPaymentStatus(created.paymentId);\n\n                      if (value.success) {\n                        showSuccessMessage(value.note ?? '支付成功');\n                      } else {\n                        showErrorMessage('支付未完成，我们接收到的状态为：${value.note}');\n                      }\n                      _closePaymentLoading();\n                    } catch (e) {\n                      _closePaymentLoading();\n                      // ignore: use_build_context_synchronously\n                      showErrorMessage(resolveError(context, e));\n                    }\n                  });\n                }\n              } catch (e) {\n                _closePaymentLoading();\n                // ignore: use_build_context_synchronously\n                showErrorMessage(resolveError(context, e));\n              }\n            },\n            confirmText: '已完成支付',\n            cancelText: '支付遇到问题，稍后继续',\n          );\n        });\n      }\n    } on Exception catch (e) {\n      if (e is StripeException) {\n        showErrorMessage('支付失败：${e.error.localizedMessage}');\n      } else {\n        // ignore: use_build_context_synchronously\n        showErrorMessageEnhanced(context, e);\n      }\n    } finally {\n      _closePaymentLoading();\n    }\n  }\n\n  /// 创建其它付款（App）\n  Future<void> createAppAlipay() async {\n    // 其它支付\n    final created = await APIServer().createOtherPay(\n      selectedProduct!.id,\n      source: 'app',\n    );\n    paymentId = created.paymentId;\n\n    // 沙箱环境支持\n    final env = created.sandbox ? AliPayEvn.SANDBOX : AliPayEvn.ONLINE;\n\n    // 调起其它支付\n    final aliPayRes = await aliPay(\n      created.params,\n      evn: env,\n    ).whenComplete(() => _closePaymentLoading());\n    Logger.instance.d(\"=================\");\n    Logger.instance.d(aliPayRes);\n    Logger.instance.d(aliPayRes[\"resultStatus\"]);\n    if (aliPayRes['resultStatus'] == '9000') {\n      await APIServer().otherPayClientConfirm(\n        aliPayRes.map((key, value) => MapEntry(key.toString(), value)),\n      );\n\n      showSuccessMessage('购买成功');\n    } else {\n      switch (aliPayRes['resultStatus']) {\n        case 8000: // fall through\n        case 6004:\n          showErrorMessage('支付处理中，请稍后查看购买历史确认结果');\n          break;\n        case 4000:\n          showErrorMessage('支付失败');\n          break;\n        case 5000:\n          showErrorMessage('重复请求');\n          break;\n        case 6001:\n          showErrorMessage('支付已取消');\n          break;\n        case 6002:\n          showErrorMessage('网络连接出错');\n          break;\n        default:\n          showErrorMessage('支付失败');\n      }\n    }\n    Logger.instance.d(\"-----------------\");\n  }\n}\n\n/// 支付方式选择项\nclass PaymentMethodItem extends StatelessWidget {\n  final Widget title;\n  final String? image;\n\n  const PaymentMethodItem({super.key, required this.title, this.image});\n\n  @override\n  Widget build(BuildContext context) {\n    return Row(\n      mainAxisAlignment: MainAxisAlignment.center,\n      children: [\n        if (image != null) ...[\n          ClipRRect(\n            borderRadius: CustomSize.borderRadius,\n            child: Image.asset(\n              image!,\n              width: 20,\n              height: 20,\n            ),\n          ),\n          const SizedBox(width: 10),\n        ],\n        title,\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/balance/payment_history.dart",
    "content": "import 'package:askaide/helper/helper.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/credit.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api/quota.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:intl/intl.dart';\n\nclass PaymentHistoryScreen extends StatefulWidget {\n  final SettingRepository setting;\n  const PaymentHistoryScreen({super.key, required this.setting});\n\n  @override\n  State<PaymentHistoryScreen> createState() => _PaymentHistoryScreenState();\n}\n\nclass _PaymentHistoryScreenState extends State<PaymentHistoryScreen> {\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: const Text(\n            '购买历史',\n            style: TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n          elevation: 0,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          child: Container(\n            padding: const EdgeInsets.all(16),\n            child: FutureBuilder(\n              future: APIServer().quotaDetails(),\n              builder: (context, snapshot) {\n                if (snapshot.hasError) {\n                  return Center(\n                    child: Column(\n                      mainAxisSize: MainAxisSize.min,\n                      children: [\n                        const Icon(\n                          Icons.error_outline,\n                          size: 50,\n                          color: Colors.red,\n                        ),\n                        const SizedBox(height: 10),\n                        Text(\n                          resolveError(context, snapshot.error!),\n                          style: const TextStyle(color: Colors.red),\n                        ),\n                      ],\n                    ),\n                  );\n                }\n\n                if (!snapshot.hasData) {\n                  return const Center(\n                    child: CircularProgressIndicator(),\n                  );\n                }\n\n                return _buildQuotaDetailPage(context, snapshot.data!, customColors);\n              },\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget _buildQuotaDetailPage(BuildContext context, QuotaResp quota, CustomColors customColors) {\n    return Column(\n      children: [\n        Expanded(\n          child: ListView(\n            shrinkWrap: true,\n            children: [\n              for (var item in quota.details)\n                Stack(\n                  children: [\n                    Container(\n                      margin: const EdgeInsets.symmetric(vertical: 6),\n                      padding: const EdgeInsets.only(\n                        top: 20,\n                        bottom: 10,\n                        left: 16,\n                        right: 16,\n                      ),\n                      decoration: BoxDecoration(\n                        color: customColors.listTileBackgroundColor,\n                        borderRadius: CustomSize.borderRadius,\n                      ),\n                      child: Column(\n                        mainAxisSize: MainAxisSize.min,\n                        children: [\n                          Row(\n                            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                            children: [\n                              Expanded(\n                                child: Column(\n                                  crossAxisAlignment: CrossAxisAlignment.start,\n                                  children: [\n                                    Text(\n                                      (item.note == null || item.note == '') ? '购买' : item.note!,\n                                      overflow: TextOverflow.ellipsis,\n                                    ),\n                                    const SizedBox(height: 5),\n                                    Text(\n                                      DateFormat(\n                                        'yyyy/MM/dd HH:mm',\n                                      ).format(item.createdAt.toLocal()),\n                                      textScaler: const TextScaler.linear(0.8),\n                                      style: TextStyle(\n                                        color: Colors.grey[600],\n                                      ),\n                                    ),\n                                  ],\n                                ),\n                              ),\n                              Column(\n                                crossAxisAlignment: CrossAxisAlignment.end,\n                                children: [\n                                  Credit(\n                                    count: item.quota,\n                                    color: Colors.amber,\n                                    withAddPrefix: true,\n                                    fontWeight: FontWeight.w500,\n                                  ),\n                                  Text(\n                                    '${DateFormat('yyyy/MM/dd').format(item.periodEndAt.toLocal())} 过期',\n                                    textScaler: const TextScaler.linear(0.7),\n                                  ),\n                                ],\n                              ),\n                            ],\n                          ),\n                        ],\n                      ),\n                    ),\n                    _buildTagForItem(item),\n                  ],\n                )\n            ],\n          ),\n        ),\n      ],\n    );\n  }\n\n  Widget _buildTagForItem(QuotaDetail item) {\n    if (item.rest <= 0) {\n      return _buildTag(AppLocale.usedUp.getString(context), Colors.orange);\n    }\n\n    if (item.expired) {\n      return _buildTag(AppLocale.expired.getString(context), Colors.grey[600]!);\n    }\n\n    return const SizedBox();\n  }\n\n  Widget _buildTag(String text, Color color) {\n    return Positioned(\n      right: 1,\n      top: 7,\n      child: Container(\n        decoration: BoxDecoration(\n          color: color,\n          borderRadius: const BorderRadius.only(topRight: CustomSize.radius, bottomLeft: CustomSize.radius),\n        ),\n        padding: const EdgeInsets.symmetric(\n          horizontal: 5,\n          vertical: 2,\n        ),\n        child: Text(\n          text,\n          textScaler: const TextScaler.linear(0.6),\n          style: const TextStyle(color: Colors.white70),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/balance/price_block.dart",
    "content": "import 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/credit.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/api/payment.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:in_app_purchase/in_app_purchase.dart';\n\nclass PriceBlock extends StatelessWidget {\n  final CustomColors customColors;\n  final ProductDetails detail;\n  final ProductDetails? selectedProduct;\n  final PaymentProduct product;\n  final bool loading;\n\n  const PriceBlock({\n    super.key,\n    required this.customColors,\n    required this.detail,\n    this.selectedProduct,\n    required this.product,\n    required this.loading,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return Stack(\n      children: [\n        Container(\n          margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),\n          padding: const EdgeInsets.all(20),\n          alignment: Alignment.center,\n          decoration: BoxDecoration(\n            color: customColors.listTileBackgroundColor,\n            border: Border.all(\n              color: (selectedProduct != null && selectedProduct!.id == detail.id)\n                  ? customColors.linkColor ?? Colors.green\n                  : customColors.paymentItemBackgroundColor!,\n            ),\n            borderRadius: CustomSize.borderRadius,\n          ),\n          child: Row(\n            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n            children: [\n              Column(\n                crossAxisAlignment: CrossAxisAlignment.start,\n                children: [\n                  Credit(\n                    count: product.quota,\n                    color: customColors.paymentItemTitleColor,\n                  ),\n                  const SizedBox(height: 6),\n                  Row(\n                    children: [\n                      const Icon(\n                        Icons.info_outline_rounded,\n                        size: 11,\n                        color: Color.fromARGB(255, 224, 170, 7),\n                      ),\n                      const SizedBox(width: 1),\n                      Text(\n                        '${product.expirePolicyText} ${AppLocale.validDays.getString(context)}',\n                        style: const TextStyle(\n                          fontSize: 11,\n                          color: Color.fromARGB(255, 224, 170, 7),\n                        ),\n                      ),\n                    ],\n                  ),\n                ],\n              ),\n              const SizedBox(height: 10),\n              loading\n                  ? const Text('加载中...')\n                  : Text(\n                      detail.price,\n                      style: TextStyle(\n                        fontSize: 22,\n                        fontWeight: FontWeight.bold,\n                        color: customColors.linkColor,\n                      ),\n                    ),\n            ],\n          ),\n        ),\n        if (product.recommend)\n          Positioned(\n            right: 11,\n            top: 6,\n            child: Container(\n              decoration: const BoxDecoration(\n                color: Color.fromARGB(255, 224, 68, 7),\n                borderRadius: BorderRadius.only(topRight: CustomSize.radius, bottomLeft: CustomSize.radius),\n              ),\n              padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2),\n              child: const Text(\n                'Best Deal',\n                style: TextStyle(\n                  color: Colors.white,\n                  fontSize: 10,\n                ),\n              ),\n            ),\n          )\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/balance/quota_usage_details.dart",
    "content": "import 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\n\nclass QuotaUsageDetailScreen extends StatefulWidget {\n  final SettingRepository setting;\n  final String date;\n\n  const QuotaUsageDetailScreen({\n    super.key,\n    required this.setting,\n    required this.date,\n  });\n\n  @override\n  State<QuotaUsageDetailScreen> createState() => _QuotaUsageDetailScreenState();\n}\n\nclass _QuotaUsageDetailScreenState extends State<QuotaUsageDetailScreen> {\n  List<QuotaUsageDetailInDay> usages = [];\n  bool loaded = false;\n\n  @override\n  void initState() {\n    APIServer().quotaUsedDetails(date: widget.date).then((value) {\n      setState(() {\n        usages = value;\n        loaded = true;\n      });\n    });\n\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: Text(\n            widget.date,\n            style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n          elevation: 0,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          child: Container(\n            padding: const EdgeInsets.all(16),\n            child: _buildQuotaUsagePage(context, customColors),\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget _buildQuotaUsagePage(\n    BuildContext context,\n    CustomColors customColors,\n  ) {\n    if (!loaded) {\n      return const Center(\n        child: LoadingIndicator(),\n      );\n    }\n\n    final usageGt0 = usages.where((e) => e.used > 0).toList();\n    if (usageGt0.isEmpty) {\n      return const Center(\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          children: [\n            Icon(\n              Icons.error_outline,\n              size: 50,\n            ),\n            SizedBox(height: 10),\n            Text(\n              '暂无使用记录',\n            ),\n          ],\n        ),\n      );\n    }\n\n    return Column(\n      children: [\n        Expanded(\n          child: ListView(\n            shrinkWrap: true,\n            children: [\n              for (var item in usageGt0)\n                Container(\n                  margin: const EdgeInsets.symmetric(vertical: 6),\n                  padding: const EdgeInsets.all(16),\n                  decoration: BoxDecoration(\n                    color: customColors.listTileBackgroundColor,\n                    borderRadius: CustomSize.borderRadius,\n                  ),\n                  child: Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                    children: [\n                      Text(item.createdAt, style: const TextStyle(fontWeight: FontWeight.bold)),\n                      const SizedBox(width: 20),\n                      Expanded(\n                        child: Text('使用 ${item.type} 消耗 ${item.used} 个智慧果'),\n                      ),\n                    ],\n                  ),\n                )\n            ],\n          ),\n        ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/balance/quota_usage_statistics.dart",
    "content": "import 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/message_box.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass QuotaUsageStatisticsScreen extends StatefulWidget {\n  final SettingRepository setting;\n  const QuotaUsageStatisticsScreen({super.key, required this.setting});\n\n  @override\n  State<QuotaUsageStatisticsScreen> createState() => _QuotaUsageStatisticsScreenState();\n}\n\nclass _QuotaUsageStatisticsScreenState extends State<QuotaUsageStatisticsScreen> {\n  List<QuotaUsageInDay> usages = [];\n  bool loaded = false;\n\n  @override\n  void initState() {\n    APIServer().quotaUsedStatistics().then((value) {\n      setState(() {\n        usages = value;\n        loaded = true;\n      });\n    });\n\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: Text(\n            AppLocale.creditsUsage.getString(context),\n            style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n          elevation: 0,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          child: Container(\n            padding: const EdgeInsets.all(16),\n            child: Column(\n              children: [\n                MessageBox(\n                  message: AppLocale.creditUsageTips.getString(context),\n                  type: MessageBoxType.info,\n                ),\n                const SizedBox(height: 10),\n                Expanded(\n                  child: _buildQuotaUsagePage(context, customColors),\n                ),\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget _buildQuotaUsagePage(\n    BuildContext context,\n    CustomColors customColors,\n  ) {\n    if (!loaded) {\n      return const Center(\n        child: LoadingIndicator(),\n      );\n    }\n\n    final usageGt0 = usages.where((e) => e.used > 0 || e.used == -1).toList();\n    if (usageGt0.isEmpty) {\n      return const Center(\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          children: [\n            Icon(\n              Icons.error_outline,\n              size: 50,\n            ),\n            SizedBox(height: 10),\n            Text(\n              'No records yet',\n            ),\n          ],\n        ),\n      );\n    }\n\n    return Column(\n      children: [\n        Expanded(\n          child: ListView(\n            shrinkWrap: true,\n            children: [\n              for (var item in usageGt0)\n                Container(\n                  margin: const EdgeInsets.symmetric(vertical: 6),\n                  padding: const EdgeInsets.all(16),\n                  decoration: BoxDecoration(\n                    color: customColors.listTileBackgroundColor,\n                    borderRadius: CustomSize.borderRadius,\n                  ),\n                  child: InkWell(\n                    onTap: () {\n                      context.push('/quota-usage-daily-details?date=${item.date}');\n                    },\n                    child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      children: [\n                        Text(\n                          item.date,\n                          style: const TextStyle(\n                            fontWeight: FontWeight.bold,\n                          ),\n                        ),\n                        if (item.used == -1)\n                          Text(AppLocale.unbilled.getString(context))\n                        else\n                          Text('${item.used > 0 ? \"-\" : \"\"}${AppLocale.creditUnit.getString(context)}${item.used}'),\n                      ],\n                    ),\n                  ),\n                )\n            ],\n          ),\n        ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/balance/web/payment_element.dart",
    "content": "import 'package:flutter/widgets.dart';\nimport 'package:flutter_stripe/flutter_stripe.dart';\n\nFuture<PaymentIntent> pay(String paymentId, {String? action}) async {\n  throw UnimplementedError();\n}\n\nvoid closeWindow() {\n  throw UnimplementedError();\n}\n\nclass PlatformPaymentElement extends StatelessWidget {\n  const PlatformPaymentElement(this.clientSecret, {super.key});\n\n  final String? clientSecret;\n\n  @override\n  Widget build(BuildContext context) {\n    return Container();\n  }\n}\n"
  },
  {
    "path": "lib/page/balance/web/payment_element_web.dart",
    "content": "import 'package:askaide/helper/ability.dart';\nimport 'package:flutter/widgets.dart';\nimport 'package:flutter_stripe_web/flutter_stripe_web.dart';\nimport 'dart:html' as html;\nimport 'package:stripe_js/stripe_api.dart' as js;\n\nFuture<PaymentIntent> pay(String paymentId, {String? action}) async {\n  final currentUrl = Uri.parse(html.window.location.href);\n  var href = Uri(\n    scheme: currentUrl.scheme,\n    host: currentUrl.host,\n    port: currentUrl.port,\n    fragment: '/payment/result?payment_id=$paymentId&action=$action',\n  ).toString();\n\n  return await WebStripe.instance.confirmPaymentElement(\n    ConfirmPaymentElementOptions(\n      confirmParams: ConfirmPaymentParams(\n        return_url: href,\n      ),\n    ),\n  );\n}\n\nvoid closeWindow() {\n  html.window.close();\n}\n\nclass PlatformPaymentElement extends StatelessWidget {\n  const PlatformPaymentElement(this.clientSecret, {super.key});\n\n  final String? clientSecret;\n\n  @override\n  Widget build(BuildContext context) {\n    return PaymentElement(\n      autofocus: true,\n      enablePostalCode: true,\n      onCardChanged: (_) {},\n      clientSecret: clientSecret ?? '',\n      appearance: js.ElementAppearance(\n        theme: Ability().themeMode == 'dark'\n            ? js.ElementTheme.night\n            : js.ElementTheme.stripe,\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/balance/web_payment_proxy.dart",
    "content": "import 'package:askaide/helper/logger.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:flutter_stripe/flutter_stripe.dart';\n\nimport 'web/payment_element.dart' if (dart.library.js) 'web/payment_element_web.dart';\n\nclass WebPaymentProxy extends StatefulWidget {\n  final SettingRepository setting;\n  final String paymentId;\n  final String paymentIntent;\n  final String price;\n  final String publishableKey;\n  final String? finishAction;\n\n  const WebPaymentProxy({\n    super.key,\n    required this.setting,\n    required this.paymentId,\n    required this.paymentIntent,\n    required this.price,\n    required this.publishableKey,\n    this.finishAction,\n  });\n\n  @override\n  State<WebPaymentProxy> createState() => _WebPaymentProxyState();\n}\n\nclass _WebPaymentProxyState extends State<WebPaymentProxy> {\n  @override\n  void initState() {\n    super.initState();\n\n    Stripe.publishableKey = widget.publishableKey;\n    Stripe.urlScheme = 'flutterstripe';\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: const Text(\n            '',\n            style: TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n          elevation: 0,\n        ),\n        backgroundColor: customColors.backgroundContainerColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          maxWidth: CustomSize.smallWindowSize,\n          child: Center(\n            child: FutureBuilder(\n                future: Stripe.instance.applySettings(),\n                builder: (context, snapshot) {\n                  if (snapshot.hasError) {\n                    Logger.instance.e('Stripe 初始化失败：${snapshot.error}');\n                    return Center(\n                      child: Text(\n                        snapshot.error.toString(),\n                        style: const TextStyle(color: Colors.red),\n                      ),\n                    );\n                  }\n\n                  return Column(\n                    mainAxisAlignment: MainAxisAlignment.center,\n                    children: [\n                      Expanded(\n                        child: Container(\n                          padding: const EdgeInsets.all(15),\n                          child: Builder(\n                            builder: (context) {\n                              return PlatformPaymentElement(\n                                widget.paymentIntent,\n                              );\n                            },\n                          ),\n                        ),\n                      ),\n                      Container(\n                        padding: const EdgeInsets.all(15),\n                        child: EnhancedButton(\n                          title: '确定付款（${widget.price}）',\n                          onPressed: () async {\n                            final cancel = BotToast.showCustomLoading(\n                              toastBuilder: (cancel) {\n                                return LoadingIndicator(\n                                  message: AppLocale.processingWait.getString(context),\n                                );\n                              },\n                              allowClick: false,\n                              duration: const Duration(seconds: 120),\n                            );\n\n                            try {\n                              await pay(\n                                widget.paymentId,\n                                action: widget.finishAction,\n                              );\n                            } catch (e) {\n                              Logger.instance.e('支付失败：$e');\n                              // ignore: use_build_context_synchronously\n                              showErrorMessageEnhanced(context, '请填写完整的支付信息');\n                            } finally {\n                              cancel();\n                            }\n                          },\n                        ),\n                      )\n                    ],\n                  );\n                }),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/balance/web_payment_result.dart",
    "content": "import 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api/payment.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:flutter/material.dart';\nimport 'package:go_router/go_router.dart';\nimport 'web/payment_element.dart' if (dart.library.js) 'web/payment_element_web.dart';\n\nclass WebPaymentResult extends StatefulWidget {\n  final String paymentId;\n  final String? action;\n  const WebPaymentResult({\n    super.key,\n    required this.paymentId,\n    this.action,\n  });\n\n  @override\n  State<WebPaymentResult> createState() => _WebPaymentResultState();\n}\n\nclass _WebPaymentResultState extends State<WebPaymentResult> {\n  PaymentStatus? paymentStatus;\n  DateTime startTime = DateTime.now();\n\n  @override\n  void initState() {\n    super.initState();\n\n    updatePaymentStatus();\n  }\n\n  updatePaymentStatus() {\n    if (!context.mounted) {\n      return;\n    }\n\n    if (DateTime.now().difference(startTime).inSeconds > 60) {\n      setState(() {\n        paymentStatus = PaymentStatus(false, note: '查询超时');\n      });\n      return;\n    }\n\n    APIServer().queryPaymentStatus(widget.paymentId).then((value) {\n      if (!value.success) {\n        Future.delayed(const Duration(seconds: 3), () {\n          if (context.mounted) {\n            updatePaymentStatus();\n          }\n        });\n      } else {\n        setState(() {\n          paymentStatus = value;\n        });\n      }\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          title: const Text('支付结果'),\n          leading: IconButton(\n            icon: Icon(\n              Icons.close,\n              color: customColors.weakLinkColor,\n            ),\n            onPressed: () {\n              if (widget.action != null && widget.action == 'close') {\n                closeWindow();\n              } else {\n                if (context.canPop()) {\n                  context.pop();\n                } else {\n                  context.go('/payment');\n                }\n              }\n            },\n          ),\n        ),\n        body: Center(\n          child: Column(\n            mainAxisAlignment: MainAxisAlignment.center,\n            children: buildResult(),\n          ),\n        ),\n      ),\n    );\n  }\n\n  List<Widget> buildResult() {\n    if (paymentStatus == null) {\n      return const [\n        CircularProgressIndicator(),\n        SizedBox(height: 20),\n        Text('正在查询支付结果'),\n      ];\n    }\n\n    if (!paymentStatus!.success) {\n      return [\n        const Icon(\n          Icons.error,\n          color: Colors.red,\n          size: 100,\n        ),\n        Text(paymentStatus!.note ?? '支付失败'),\n      ];\n    }\n\n    return [\n      const Icon(\n        Icons.check_circle,\n        color: Colors.green,\n        size: 100,\n      ),\n      const Text(\n        '支付成功',\n        style: TextStyle(fontSize: 24),\n      ),\n    ];\n  }\n}\n"
  },
  {
    "path": "lib/page/chat/character_chat.dart",
    "content": "import 'dart:convert';\n\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/helper/model.dart';\nimport 'package:askaide/helper/upload.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/chat/component/model_switcher.dart';\nimport 'package:askaide/page/chat/component/stop_button.dart';\nimport 'package:askaide/page/component/audio_player.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/chat/chat_input_button.dart';\nimport 'package:askaide/page/component/chat/empty.dart';\nimport 'package:askaide/page/component/chat/file_upload.dart';\nimport 'package:askaide/page/component/chat/message_state_manager.dart';\nimport 'package:askaide/page/component/chat/role_avatar.dart';\nimport 'package:askaide/page/component/effect/glass.dart';\nimport 'package:askaide/page/component/enhanced_textfield.dart';\nimport 'package:askaide/page/component/global_alert.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/select_mode_toolbar.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/bloc/chat_message_bloc.dart';\nimport 'package:askaide/bloc/room_bloc.dart';\nimport 'package:askaide/bloc/notify_bloc.dart';\nimport 'package:askaide/page/component/chat/chat_input.dart';\nimport 'package:askaide/page/component/chat/chat_preview.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/model/message.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:askaide/repo/model/room.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:askaide/repo/model/model.dart' as mm;\n\nimport '../component/dialog.dart';\n\nclass CharacterChatPage extends StatefulWidget {\n  final int roomId;\n  final MessageStateManager stateManager;\n  final SettingRepository setting;\n\n  const CharacterChatPage({\n    super.key,\n    required this.roomId,\n    required this.stateManager,\n    required this.setting,\n  });\n\n  @override\n  State<CharacterChatPage> createState() => _CharacterChatPageState();\n}\n\nclass _CharacterChatPageState extends State<CharacterChatPage> {\n  final ScrollController _scrollController = ScrollController();\n  final ValueNotifier<bool> _inputEnabled = ValueNotifier(true);\n  final ChatPreviewController _chatPreviewController = ChatPreviewController();\n  final AudioPlayerController _audioPlayerController = AudioPlayerController(useRemoteAPI: true);\n  bool showAudioPlayer = false;\n  bool audioLoadding = false;\n\n  // The selected image files for image upload\n  List<FileUpload> selectedImageFiles = [];\n  // The selected file for file upload\n  FileUpload? selectedFile;\n\n  /// Currently selected model\n  mm.Model? tempModel;\n\n  // 全量模型列表\n  List<mm.Model> supportModels = [];\n\n  // 聊天室 ID，当没有值时，会在第一个聊天消息发送后自动设置新值\n  int? chatId;\n\n  /// 是否启用搜索\n  bool enableSearch = false;\n\n  /// 是否启用推理\n  bool enableReasoning = false;\n\n  @override\n  void initState() {\n    super.initState();\n\n    reloadPage();\n\n    _chatPreviewController.addListener(() {\n      setState(() {});\n    });\n\n    _audioPlayerController.onPlayStopped = () {\n      setState(() {\n        showAudioPlayer = false;\n      });\n    };\n    _audioPlayerController.onPlayAudioStarted = () {\n      setState(() {\n        showAudioPlayer = true;\n      });\n    };\n    _audioPlayerController.onPlayAudioLoading = (loading) {\n      setState(() {\n        audioLoadding = loading;\n      });\n    };\n\n    // 加载模型列表，用于查询模型名称\n    ModelAggregate.models().then((value) {\n      setState(() {\n        supportModels = value;\n      });\n    });\n  }\n\n  reloadPage() {\n    context.read<ChatMessageBloc>().add(ChatMessageGetRecentEvent(chatHistoryId: chatId));\n    context.read<RoomBloc>().add(RoomLoadEvent(widget.roomId, cascading: true));\n  }\n\n  @override\n  void dispose() {\n    _scrollController.dispose();\n    _chatPreviewController.dispose();\n    _audioPlayerController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      child: Scaffold(\n        appBar: _buildAppBar(context, customColors),\n        backgroundColor: customColors.backgroundContainerColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          child: _buildChatComponents(customColors),\n        ),\n      ),\n    );\n  }\n\n  mm.Model? roomModel;\n\n  Widget _buildChatComponents(CustomColors customColors) {\n    return BlocConsumer<RoomBloc, RoomState>(\n      listenWhen: (previous, current) => current is RoomLoaded,\n      listener: (context, state) {\n        if (state is RoomLoaded) {\n          ModelAggregate.model(state.room.model).then((value) {\n            setState(() {\n              roomModel = value;\n            });\n          });\n        }\n      },\n      buildWhen: (previous, current) => current is RoomLoaded,\n      builder: (context, room) {\n        if (room is RoomLoaded) {\n          final enableImageUpload =\n              tempModel == null ? (roomModel != null && roomModel!.supportVision) : (tempModel?.supportVision ?? false);\n          final showReasoning = tempModel == null\n              ? (roomModel != null && roomModel!.supportReasoning)\n              : (tempModel?.supportReasoning ?? false);\n          final showSearch =\n              tempModel == null ? (roomModel != null && roomModel!.supportSearch) : (tempModel?.supportSearch ?? false);\n          return SafeArea(\n            top: false,\n            bottom: false,\n            child: Column(\n              children: [\n                if (Ability().showGlobalAlert) const GlobalAlert(pageKey: 'chat'),\n                // 语音输出中提示\n                if (showAudioPlayer)\n                  EnhancedAudioPlayer(\n                    controller: _audioPlayerController,\n                    loading: audioLoadding,\n                  ),\n                // 聊天内容窗口\n                Expanded(\n                  child: Stack(\n                    fit: StackFit.expand,\n                    children: [\n                      _buildChatPreviewArea(\n                        room,\n                        customColors,\n                        _chatPreviewController.selectMode,\n                      ),\n                      if (!_inputEnabled.value)\n                        Positioned(\n                          bottom: 10,\n                          width: CustomSize.adaptiveMaxWindowWidth(context),\n                          child: Center(\n                            child: StopButton(\n                              label: AppLocale.stopOutput.getString(context),\n                              onPressed: () {\n                                HapticFeedbackHelper.mediumImpact();\n                                context.read<ChatMessageBloc>().add(ChatMessageStopEvent());\n                              },\n                            ),\n                          ),\n                        ),\n                    ],\n                  ),\n                ),\n\n                // 聊天输入窗口\n                Container(\n                  decoration: BoxDecoration(\n                    borderRadius: const BorderRadius.only(topLeft: CustomSize.radius, topRight: CustomSize.radius),\n                    color: customColors.chatInputPanelBackground,\n                  ),\n                  child: _chatPreviewController.selectMode\n                      ? SelectModeToolbar(\n                          chatPreviewController: _chatPreviewController,\n                        )\n                      : ChatInput(\n                          enableNotifier: _inputEnabled,\n                          onSubmit: (value) {\n                            _handleSubmit(value);\n                            FocusManager.instance.primaryFocus?.unfocus();\n                          },\n                          enableImageUpload: enableImageUpload && selectedFile == null,\n                          onImageSelected: (files) {\n                            setState(() {\n                              selectedImageFiles = files;\n                            });\n                          },\n                          selectedImageFiles: selectedImageFiles,\n                          // enableFileUpload: selectedImageFiles.isEmpty,\n                          onFileSelected: (file) {\n                            setState(() {\n                              selectedFile = file;\n                            });\n                          },\n                          selectedFile: selectedFile,\n                          hintText: AppLocale.askMeAnyQuestion.getString(context),\n                          onVoiceRecordTappedEvent: () {\n                            _audioPlayerController.stop();\n                          },\n                          onStopGenerate: () {\n                            context.read<ChatMessageBloc>().add(ChatMessageStopEvent());\n                          },\n                          toolsBuilder: () {\n                            return [\n                              if (showReasoning)\n                                ChatInputButton(\n                                  text: AppLocale.reasoning.getString(context),\n                                  icon: Icons.tips_and_updates_outlined,\n                                  onPressed: () {\n                                    setState(() {\n                                      enableReasoning = !enableReasoning;\n                                    });\n                                  },\n                                  isActive: enableReasoning,\n                                ),\n                              if (showSearch)\n                                ChatInputButton(\n                                  text: AppLocale.onlineSearch.getString(context),\n                                  icon: Icons.language_outlined,\n                                  onPressed: () {\n                                    setState(() {\n                                      enableSearch = !enableSearch;\n                                    });\n                                  },\n                                  isActive: enableSearch,\n                                ),\n                            ];\n                          },\n                        ),\n                ),\n              ],\n            ),\n          );\n        } else {\n          return Container();\n        }\n      },\n    );\n  }\n\n  BlocConsumer<ChatMessageBloc, ChatMessageState> _buildChatPreviewArea(\n    RoomLoaded room,\n    CustomColors customColors,\n    bool selectMode,\n  ) {\n    return BlocConsumer<ChatMessageBloc, ChatMessageState>(\n      listener: (context, state) {\n        if (state is ChatHistoryInited) {\n          setState(() {\n            chatId = state.chatId;\n          });\n        }\n\n        if (state is ChatMessagesLoaded && state.error == null) {\n          setState(() {\n            selectedImageFiles = [];\n            selectedFile = null;\n          });\n        }\n        // 显示错误提示\n        else if (state is ChatMessagesLoaded && state.error != null) {\n          showErrorMessageEnhanced(context, state.error);\n        } else if (state is ChatMessageUpdated) {\n          // 聊天内容窗口滚动到底部\n          if (!state.processing && _scrollController.hasClients) {\n            _scrollController.animateTo(\n              0,\n              duration: const Duration(milliseconds: 500),\n              curve: Curves.easeOut,\n            );\n          }\n\n          if (state.processing && _inputEnabled.value) {\n            // 聊天回复中时，禁止输入框编辑\n            setState(() {\n              _inputEnabled.value = false;\n            });\n          } else if (!state.processing && !_inputEnabled.value) {\n            // 聊天回复完成时，取消输入框的禁止编辑状态\n            setState(() {\n              _inputEnabled.value = true;\n            });\n          }\n        }\n      },\n      buildWhen: (prv, cur) => cur is ChatMessagesLoaded,\n      builder: (context, state) {\n        if (state is ChatMessagesLoaded) {\n          final loadedMessages = List<Message>.from(state.messages);\n          if (room.room.initMessage != null && room.room.initMessage != '' && loadedMessages.isEmpty) {\n            loadedMessages.add(\n              Message(\n                Role.receiver,\n                room.room.initMessage!,\n                type: MessageType.initMessage,\n                id: 0,\n              ),\n            );\n          }\n\n          if (loadedMessages.isEmpty) {\n            // 聊天内容为空时，显示示例页面\n            if (loadedMessages.isEmpty) {\n              return EmptyPreview(\n                examples: room.examples ?? [],\n                onSubmit: _handleSubmit,\n                cardMode: true,\n              );\n            }\n          }\n\n          final messages = loadedMessages.map((e) {\n            if (e.model != null && !e.model!.startsWith('v2@')) {\n              final mod = supportModels.where((m) => m.id == e.model).firstOrNull;\n              if (mod != null) {\n                e.senderName = mod.shortName;\n                e.avatarUrl = mod.avatarUrl;\n              }\n            }\n            if (e.avatarUrl == null || e.senderName == null) {\n              e.avatarUrl = room.room.avatarUrl;\n              e.senderName = room.room.name;\n            }\n\n            return MessageWithState(\n              e,\n              room.states[widget.stateManager.getKey(e.roomId ?? 0, e.id ?? 0)] ?? MessageState(),\n            );\n          }).toList();\n\n          _chatPreviewController.setAllMessageIds(messages);\n\n          return ChatPreview(\n            padding: _inputEnabled.value ? null : const EdgeInsets.only(bottom: 35),\n            messages: messages,\n            scrollController: _scrollController,\n            controller: _chatPreviewController,\n            stateManager: widget.stateManager,\n            robotAvatar: selectMode\n                ? null\n                : RoleAvatar(\n                    avatarUrl: room.room.avatarUrl,\n                    name: room.room.name,\n                  ),\n            senderNameBuilder: (message) {\n              if (message.senderName == null) {\n                return null;\n              }\n\n              return Container(\n                margin: const EdgeInsets.fromLTRB(0, 0, 10, 7),\n                padding: const EdgeInsets.symmetric(horizontal: 13),\n                child: Text(\n                  room.room.name,\n                  style: TextStyle(\n                    color: customColors.weakTextColor,\n                    fontSize: 12,\n                  ),\n                ),\n              );\n            },\n            onDeleteMessage: (id) {\n              handleDeleteMessage(context, id, chatHistoryId: chatId);\n            },\n            onSpeakEvent: (message) {\n              _audioPlayerController.playAudio(message.text);\n            },\n            onResentEvent: (message, index) {\n              _scrollController.animateTo(0, duration: const Duration(milliseconds: 500), curve: Curves.easeOut);\n              _handleSubmit(message.text, messagetType: message.type, index: index, isResent: true);\n            },\n          );\n        }\n        return const Center(child: CircularProgressIndicator());\n      },\n    );\n  }\n\n  /// 构建 AppBar\n  AppBar _buildAppBar(BuildContext context, CustomColors customColors) {\n    return _chatPreviewController.selectMode\n        ? AppBar(\n            title: Text(\n              AppLocale.select.getString(context),\n              style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n            ),\n            centerTitle: true,\n            elevation: 0,\n            leadingWidth: 80,\n            leading: TextButton(\n              onPressed: () {\n                _chatPreviewController.exitSelectMode();\n              },\n              child: Text(\n                AppLocale.cancel.getString(context),\n                style: TextStyle(color: customColors.linkColor),\n              ),\n            ),\n            toolbarHeight: CustomSize.toolbarHeight,\n          )\n        : AppBar(\n            centerTitle: true,\n            elevation: 0,\n            // backgroundColor: customColors.chatRoomBackground,\n            title: BlocBuilder<RoomBloc, RoomState>(\n              buildWhen: (previous, current) => current is RoomLoaded,\n              builder: (context, state) {\n                if (state is RoomLoaded) {\n                  return GestureDetector(\n                    onTap: () {\n                      ModelSwitcher.openActionDialog(\n                        context: context,\n                        onSelected: (selected) {\n                          setState(() {\n                            tempModel = selected;\n                          });\n                        },\n                        initValue: tempModel,\n                      );\n                    },\n                    child: Text(\n                      state.room.name,\n                      overflow: TextOverflow.ellipsis,\n                      maxLines: 1,\n                      style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n                    ),\n                  );\n                }\n\n                return Container();\n              },\n            ),\n            actions: [\n              IconButton(\n                icon: const Icon(Icons.maps_ugc_outlined),\n                onPressed: createNewChat,\n              ),\n            ],\n            toolbarHeight: CustomSize.toolbarHeight,\n          );\n  }\n\n  /// 创建新的聊天\n  void createNewChat() {\n    setState(() {\n      chatId = null;\n    });\n    reloadPage();\n  }\n\n  /// 提交新消息\n  void _handleSubmit(\n    String text, {\n    messagetType = MessageType.text,\n    int? index,\n    bool isResent = false,\n  }) async {\n    setState(() {\n      _inputEnabled.value = false;\n    });\n\n    if (selectedFile != null) {\n      final cancel = BotToast.showCustomLoading(\n        toastBuilder: (cancel) {\n          return const LoadingIndicator(\n            message: '正在上传，请稍后...',\n          );\n        },\n        allowClick: false,\n      );\n\n      try {\n        final uploader = QiniuUploader(widget.setting);\n\n        if (!selectedFile!.uploaded) {\n          final path = selectedFile!.file.path;\n          if (path != null && path.isNotEmpty) {\n            final uploadRes = await uploader.uploadFile(path, usage: 'document');\n            selectedFile!.setUrl(uploadRes.url);\n          } else if (selectedFile!.file.bytes != null && selectedFile!.file.bytes!.isNotEmpty) {\n            final uploadRes = await uploader.upload(\n              'file-${DateTime.now().millisecondsSinceEpoch}.${selectedFile!.file.name}',\n              selectedFile!.file.bytes!,\n              usage: 'document',\n            );\n            selectedFile!.setUrl(uploadRes.url);\n          }\n        }\n      } catch (e) {\n        // ignore: use_build_context_synchronously\n        showErrorMessageEnhanced(context, e);\n        return;\n      } finally {\n        cancel();\n      }\n    }\n\n    if (selectedImageFiles.isNotEmpty) {\n      final cancel = BotToast.showCustomLoading(\n        toastBuilder: (cancel) {\n          return const LoadingIndicator(\n            message: '正在上传，请稍后...',\n          );\n        },\n        allowClick: false,\n      );\n\n      try {\n        final uploader = ImageUploader(widget.setting);\n\n        for (var file in selectedImageFiles) {\n          if (file.uploaded) {\n            continue;\n          }\n\n          if (file.file.bytes != null) {\n            final res = await uploader.base64(\n              imageData: file.file.bytes,\n              maxSize: 1024 * 1024,\n              compressWidth: 512,\n              compressHeight: 512,\n            );\n            file.setUrl(res);\n          } else {\n            final res = await uploader.base64(\n              path: file.file.path!,\n              maxSize: 1024 * 1024,\n              compressWidth: 512,\n              compressHeight: 512,\n            );\n            file.setUrl(res);\n          }\n        }\n      } catch (e) {\n        // ignore: use_build_context_synchronously\n        showErrorMessageEnhanced(context, e);\n        return;\n      } finally {\n        cancel();\n      }\n    }\n\n    // showSuccessMessage('Model: ${roomModel?.id}/${tempModel?.id}');\n\n    // ignore: use_build_context_synchronously\n    context.read<ChatMessageBloc>().add(\n          ChatMessageSendEvent(\n            Message(\n              Role.sender,\n              text,\n              user: 'me',\n              ts: DateTime.now(),\n              type: messagetType,\n              images: selectedImageFiles.where((e) => e.uploaded).map((e) => e.url!).toList(),\n              file: selectedFile != null && selectedFile!.uploaded\n                  ? jsonEncode({\n                      'name': selectedFile!.file.name,\n                      'url': selectedFile!.url,\n                    })\n                  : null,\n              chatHistoryId: chatId,\n              model: roomModel?.id,\n              flags: [\n                if (enableSearch) 'search',\n                if (enableReasoning) 'reasoning',\n              ],\n            ),\n            index: index,\n            isResent: isResent,\n            tempModel: tempModel?.id,\n          ),\n        );\n\n    // ignore: use_build_context_synchronously\n    context.read<NotifyBloc>().add(NotifyResetEvent());\n  }\n}\n\n/// 处理消息删除事件\nvoid handleDeleteMessage(BuildContext context, int id, {int? chatHistoryId}) {\n  openConfirmDialog(\n    context,\n    AppLocale.confirmDelete.getString(context),\n    () => context.read<ChatMessageBloc>().add(ChatMessageDeleteEvent([id], chatHistoryId: chatHistoryId)),\n    danger: true,\n  );\n}\n\n/// 打开示例问题列表\nvoid handleOpenExampleQuestion(\n  BuildContext context,\n  Room room,\n  List<ChatExample> examples,\n  Function(String text) onSubmit,\n) {\n  final customColors = Theme.of(context).extension<CustomColors>()!;\n\n  openModalBottomSheet(\n    context,\n    (context) {\n      return FractionallySizedBox(\n        heightFactor: 0.8,\n        child: GlassEffect(\n          child: Column(\n            mainAxisSize: MainAxisSize.min,\n            children: [\n              Container(\n                padding: const EdgeInsets.symmetric(\n                  vertical: 10,\n                ),\n                child: Text(\n                  AppLocale.examples.getString(context),\n                  textScaler: const TextScaler.linear(1.2),\n                ),\n              ),\n              Expanded(\n                child: ListView.builder(\n                  itemCount: examples.length,\n                  itemBuilder: (context, i) {\n                    return ListTile(\n                      title: Container(\n                        alignment: Alignment.center,\n                        padding: const EdgeInsets.symmetric(\n                          horizontal: 10,\n                          vertical: 10,\n                        ),\n                        decoration: BoxDecoration(\n                          borderRadius: CustomSize.borderRadius,\n                          color: customColors.chatExampleItemBackground,\n                        ),\n                        child: Column(\n                          children: [\n                            Text(\n                              examples[i].title,\n                              textAlign: TextAlign.center,\n                              overflow: TextOverflow.ellipsis,\n                              style: TextStyle(\n                                color: customColors.chatExampleItemText,\n                              ),\n                            ),\n                            if (examples[i].content != null) const SizedBox(height: 5),\n                            if (examples[i].content != null)\n                              Text(\n                                examples[i].content!,\n                                textAlign: TextAlign.center,\n                                overflow: TextOverflow.ellipsis,\n                                maxLines: 2,\n                                style: TextStyle(\n                                  fontSize: 12,\n                                  color: customColors.chatExampleItemText,\n                                ),\n                              ),\n                          ],\n                        ),\n                      ),\n                      onTap: () {\n                        final controller = TextEditingController();\n                        controller.text = examples[i].text;\n\n                        openDialog(\n                          context,\n                          title: Text(\n                            AppLocale.confirmSend.getString(context),\n                            textAlign: TextAlign.left,\n                            textScaler: const TextScaler.linear(0.8),\n                          ),\n                          builder: Builder(\n                            builder: (context) {\n                              return EnhancedTextField(\n                                controller: controller,\n                                maxLines: 5,\n                                maxLength: 4000,\n                                customColors: customColors,\n                              );\n                            },\n                          ),\n                          onSubmit: () {\n                            onSubmit(controller.text.trim());\n                            return true;\n                          },\n                          afterSubmit: () => context.pop(),\n                        );\n                      },\n                    );\n                  },\n                ),\n              ),\n            ],\n          ),\n        ),\n      );\n    },\n  );\n}\n"
  },
  {
    "path": "lib/page/chat/character_create.dart",
    "content": "import 'dart:io';\nimport 'dart:math';\n\nimport 'package:askaide/bloc/room_bloc.dart';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/helper/upload.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/advanced_button.dart';\nimport 'package:askaide/page/component/avatar_selector.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/enhanced_input.dart';\nimport 'package:askaide/page/component/enhanced_textfield.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/item_selector_search.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/random_avatar.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:askaide/helper/model.dart';\nimport 'package:askaide/page/component/model_item.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/model/model.dart' as mm;\nimport 'package:go_router/go_router.dart';\n\n/// 创建聊天室对话框\nclass CharacterCreatePage extends StatefulWidget {\n  final SettingRepository setting;\n  const CharacterCreatePage({super.key, required this.setting});\n\n  @override\n  State<CharacterCreatePage> createState() => _CharacterCreatePageState();\n}\n\nclass _CharacterCreatePageState extends State<CharacterCreatePage> {\n  final _nameController = TextEditingController(text: '');\n  final _promptController = TextEditingController(text: '');\n\n  final randomSeed = Random().nextInt(10000);\n\n  String? _avatarUrl;\n  int? _avatarId;\n\n  List<String> avatarPresets = [];\n\n  int maxContext = 6;\n\n  List<ChatMemory> validMemories = [\n    ChatMemory('Ephemeral', 1, description: 'Each conversation is independent, often used for one-off Q&A'),\n    ChatMemory('Basic', 3, description: 'Remembers the last 3 conversations'),\n    ChatMemory('Medium', 6, description: 'Remembers the last 6 conversations'),\n    ChatMemory('Deep', 10, description: 'Remembers the last 10 conversations')\n  ];\n\n  bool showAdvancedOptions = false;\n\n  mm.Model? _selectedModel;\n\n  List<String> tags = [];\n\n  @override\n  void initState() {\n    super.initState();\n\n    if (Ability().isUserLogon()) {\n      APIServer().avatars().then((value) {\n        avatarPresets = value;\n      });\n    }\n  }\n\n  @override\n  void dispose() {\n    _nameController.dispose();\n    _promptController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          title: Text(\n            AppLocale.createRoom.getString(context),\n            style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          backgroundColor: customColors.backgroundColor,\n          centerTitle: true,\n          toolbarHeight: CustomSize.toolbarHeight,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          maxWidth: CustomSize.maxWindowSize,\n          backgroundColor: customColors.backgroundColor,\n          child: BlocListener<RoomBloc, RoomState>(\n            listenWhen: (previous, current) => current is RoomOperationResult,\n            listener: (context, state) {\n              if (state is RoomOperationResult) {\n                if (state.success) {\n                  if (state.redirect != null) {\n                    context.push(state.redirect!).then((value) {\n                      if (context.mounted) {\n                        context.read<RoomBloc>().add(RoomsLoadEvent());\n                      }\n                    });\n                  } else {\n                    context.pop();\n                  }\n                } else {\n                  showErrorMessageEnhanced(context, state.error ?? AppLocale.operateFailed.getString(context));\n                }\n              }\n            },\n            child: Padding(\n              padding: const EdgeInsets.symmetric(horizontal: 10),\n              child: buildCustomCharacter(customColors, context),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget buildCustomCharacter(CustomColors customColors, BuildContext context) {\n    return SingleChildScrollView(\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: [\n          const SizedBox(height: 10),\n          ColumnBlock(\n            children: [\n              // 名称\n              EnhancedTextField(\n                customColors: customColors,\n                controller: _nameController,\n                maxLength: 50,\n                maxLines: 1,\n                showCounter: false,\n                labelText: AppLocale.roomName.getString(context),\n                labelPosition: LabelPosition.left,\n                hintText: AppLocale.required.getString(context),\n                textDirection: TextDirection.rtl,\n              ),\n              if (Ability().isUserLogon())\n                EnhancedInput(\n                  padding: const EdgeInsets.only(top: 10, bottom: 5),\n                  title: Text(\n                    AppLocale.avatar.getString(context),\n                    style: TextStyle(\n                      color: customColors.textfieldLabelColor,\n                      fontSize: 16,\n                    ),\n                  ),\n                  value: Row(\n                    mainAxisAlignment: MainAxisAlignment.end,\n                    mainAxisSize: MainAxisSize.min,\n                    children: [\n                      Container(\n                        width: 45,\n                        height: 45,\n                        decoration: BoxDecoration(\n                          borderRadius: CustomSize.borderRadius,\n                          image: _avatarUrl == null\n                              ? null\n                              : DecorationImage(\n                                  image: (_avatarUrl!.startsWith('http')\n                                      ? CachedNetworkImageProviderEnhanced(_avatarUrl!)\n                                      : FileImage(File(_avatarUrl!))) as ImageProvider,\n                                  fit: BoxFit.cover,\n                                ),\n                        ),\n                        child: _avatarUrl == null && _avatarId == null\n                            ? const Center(\n                                child: Icon(\n                                  Icons.interests,\n                                  color: Colors.grey,\n                                ),\n                              )\n                            : (_avatarId == null\n                                ? const SizedBox()\n                                : RandomAvatar(\n                                    id: _avatarId!,\n                                    usage: AvatarUsage.room,\n                                  )),\n                      ),\n                    ],\n                  ),\n                  onPressed: () {\n                    openModalBottomSheet(\n                      context,\n                      (context) {\n                        return AvatarSelector(\n                          onSelected: (selected) {\n                            setState(() {\n                              _avatarUrl = selected.url;\n                              _avatarId = selected.id;\n                            });\n                            context.pop();\n                          },\n                          usage: AvatarUsage.room,\n                          defaultAvatarId: _avatarId,\n                          defaultAvatarUrl: _avatarUrl,\n                          externalAvatarUrls: [\n                            ...avatarPresets,\n                          ],\n                        );\n                      },\n                      heightFactor: 0.8,\n                    );\n                  },\n                ),\n            ],\n          ),\n          ColumnBlock(\n            innerPanding: 10,\n            padding: const EdgeInsets.only(top: 15, left: 15, right: 15),\n            children: [\n              // 提示语\n              EnhancedTextField(\n                fontSize: 12,\n                customColors: customColors,\n                controller: _promptController,\n                labelText: AppLocale.prompt.getString(context),\n                labelPosition: LabelPosition.top,\n                hintText: AppLocale.promptHint.getString(context),\n                bottomButton: Row(\n                  children: [\n                    Icon(\n                      Icons.tips_and_updates_outlined,\n                      size: 13,\n                      color: customColors.linkColor?.withAlpha(150),\n                    ),\n                    const SizedBox(width: 5),\n                    Text(\n                      AppLocale.examples.getString(context),\n                      style: TextStyle(\n                        color: customColors.linkColor?.withAlpha(150),\n                        fontSize: 13,\n                      ),\n                    ),\n                  ],\n                ),\n                bottomButtonOnPressed: () async {\n                  openSystemPromptSelectDialog(\n                    context,\n                    customColors,\n                    _promptController,\n                  );\n                },\n                minLines: 4,\n                maxLines: 20,\n                showCounter: false,\n              ),\n            ],\n          ),\n          if (showAdvancedOptions)\n            ColumnBlock(\n              innerPanding: 10,\n              padding: const EdgeInsets.only(top: 15, left: 15, right: 15, bottom: 5),\n              children: [\n                // 模型\n                EnhancedInputSimple(\n                  title: AppLocale.model.getString(context),\n                  padding: const EdgeInsets.only(top: 10, bottom: 10),\n                  onPressed: () {\n                    openSelectModelDialog(\n                      context,\n                      (selected) {\n                        setState(() {\n                          _selectedModel = selected;\n                        });\n                      },\n                      initValue: _selectedModel?.uid(),\n                    );\n                  },\n                  value: _selectedModel != null ? _selectedModel!.name : AppLocale.select.getString(context),\n                ),\n                EnhancedInput(\n                  title: Text(\n                    AppLocale.memoryDepth.getString(context),\n                    style: TextStyle(\n                      color: customColors.textfieldLabelColor,\n                      fontSize: 16,\n                    ),\n                  ),\n                  value: Text(\n                    validMemories.where((element) => element.number == maxContext).firstOrNull?.name ?? '',\n                  ),\n                  onPressed: () {\n                    openListSelectDialog(\n                      context,\n                      validMemories\n                          .map(\n                            (e) => SelectorItem(\n                              Column(\n                                children: [\n                                  Text(\n                                    e.name,\n                                    textAlign: TextAlign.center,\n                                  ),\n                                  const SizedBox(height: 10),\n                                  Text(\n                                    e.description ?? '',\n                                    textAlign: TextAlign.center,\n                                    style: TextStyle(\n                                      color: customColors.weakTextColor,\n                                      fontSize: 12,\n                                    ),\n                                  ),\n                                ],\n                              ),\n                              e.number,\n                            ),\n                          )\n                          .toList(),\n                      (value) {\n                        setState(() {\n                          maxContext = value.value;\n                        });\n                        return true;\n                      },\n                      heightFactor: 0.5,\n                      value: validMemories.where((element) => element.number == maxContext).firstOrNull,\n                    );\n                  },\n                ),\n              ],\n            ),\n          AdvancedButton(\n            showAdvancedOptions: showAdvancedOptions,\n            onPressed: (value) {\n              setState(() {\n                showAdvancedOptions = value;\n              });\n            },\n          ),\n          const SizedBox(height: 10),\n          EnhancedButton(\n            title: AppLocale.ok.getString(context),\n            onPressed: () async {\n              if (_nameController.text == '') {\n                showErrorMessage(AppLocale.nameRequiredMessage.getString(context));\n                return;\n              }\n\n              if (_promptController.text == '') {\n                showErrorMessage(AppLocale.charactorPromptRequiredMessage.getString(context));\n                return;\n              }\n\n              if (_avatarUrl != null) {\n                if (!(_avatarUrl!.startsWith('http://') || _avatarUrl!.startsWith('https://'))) {\n                  // 上传文件，获取 URL\n                  final cancel = BotToast.showCustomLoading(\n                    toastBuilder: (cancel) {\n                      return LoadingIndicator(\n                        message: AppLocale.imageUploading.getString(context),\n                      );\n                    },\n                    allowClick: false,\n                  );\n\n                  final uploadRes = await ImageUploader(widget.setting)\n                      .upload(_avatarUrl!, usage: 'avatar')\n                      .whenComplete(() => cancel());\n                  _avatarUrl = uploadRes.url;\n                }\n              }\n\n              if (context.mounted) {\n                context.read<RoomBloc>().add(\n                      RoomCreateEvent(\n                        _nameController.text,\n                        _promptController.text,\n                        model: _selectedModel?.uid(),\n                        avatarId: _avatarId,\n                        avatarUrl: _avatarUrl,\n                        maxContext: maxContext,\n                      ),\n                    );\n              }\n            },\n          ),\n          const SizedBox(height: 15),\n        ],\n      ),\n    );\n  }\n}\n\nvoid openSelectModelDialog(\n  BuildContext context,\n  Function(mm.Model? selected) onSelected, {\n  String? initValue,\n  List<String>? reservedModels,\n  String? title,\n  String? priorityModelId,\n  bool withCustom = false,\n}) {\n  future() async {\n    final models = await ModelAggregate.models(cache: true);\n\n    if (priorityModelId != null) {\n      // 将 models 中，id 与 priorityModelId 相同的元素排序到最前面\n      final index = models.indexWhere((e) => e.id == priorityModelId || e.uid() == priorityModelId);\n      if (index != -1) {\n        models.insert(\n            0,\n            models[index]\n                // ignore: use_build_context_synchronously\n                .copyWith(category: AppLocale.recentlyUsed.getString(context)));\n      }\n    }\n\n    // 再请求一次，用于异步更新 Cache，下次打开时将显示最新数据\n    ModelAggregate.models(cache: false);\n\n    return models;\n  }\n\n  openModalBottomSheet(\n    context,\n    (context) {\n      return FutureBuilder(\n          future: future(),\n          builder: (context, snapshot) {\n            if (snapshot.hasError) {\n              showErrorMessage(resolveError(context, snapshot.error!));\n            }\n\n            if (!snapshot.hasData) {\n              return const Center(child: CircularProgressIndicator());\n            }\n\n            return ModelItem(\n              models: snapshot.data!\n                  .where((e) => !e.disabled || (reservedModels != null && reservedModels.contains(e.id)))\n                  .toList(),\n              onSelected: (selected) {\n                onSelected(selected);\n                context.pop();\n              },\n              initValue: initValue,\n            );\n          });\n    },\n    heightFactor: 0.9,\n    title: title,\n  );\n}\n\nvoid openSystemPromptSelectDialog(\n  BuildContext context,\n  CustomColors customColors,\n  TextEditingController promptController,\n) {\n  openModalBottomSheet(\n    context,\n    (context) {\n      return FutureBuilder(\n        future: APIServer().prompts(),\n        builder: (context, snapshot) {\n          if (snapshot.hasError) {\n            showErrorMessage(resolveError(context, snapshot.error!));\n          }\n\n          return ItemSearchSelector(\n            items: (snapshot.data ?? [])\n                .map(\n                  (e) => SelectorItem<String>(\n                    Column(\n                      mainAxisSize: MainAxisSize.min,\n                      children: [\n                        Text(\n                          e.title,\n                          textAlign: TextAlign.center,\n                          overflow: TextOverflow.ellipsis,\n                          style: TextStyle(\n                            color: customColors.chatExampleItemText,\n                          ),\n                        ),\n                        Text(\n                          e.content,\n                          textAlign: TextAlign.left,\n                          overflow: TextOverflow.ellipsis,\n                          maxLines: 2,\n                          style: TextStyle(\n                            color: customColors.weakTextColor,\n                          ),\n                          textScaler: const TextScaler.linear(0.8),\n                        )\n                      ],\n                    ),\n                    e.content,\n                    search: (keywrod) => e.title.toLowerCase().contains(keywrod.toLowerCase()),\n                  ),\n                )\n                .toList(),\n            onSelected: (value) {\n              promptController.text = value.value;\n              return true;\n            },\n          );\n        },\n      );\n    },\n    heightFactor: 0.9,\n  );\n}\n\nclass ChatMemory {\n  String name;\n  String? description;\n  int number;\n\n  ChatMemory(this.name, this.number, {this.description});\n}\n"
  },
  {
    "path": "lib/page/chat/character_edit.dart",
    "content": "import 'dart:io';\nimport 'dart:math';\n\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/model.dart';\nimport 'package:askaide/helper/upload.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/chat/character_create.dart';\nimport 'package:askaide/page/component/advanced_button.dart';\nimport 'package:askaide/page/component/avatar_selector.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/enhanced_input.dart';\nimport 'package:askaide/page/component/enhanced_textfield.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/item_selector_search.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/random_avatar.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/bloc/room_bloc.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/model/model.dart' as mm;\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass CharacterEditPage extends StatefulWidget {\n  final int roomId;\n  final SettingRepository setting;\n  const CharacterEditPage({super.key, required this.roomId, required this.setting});\n\n  @override\n  State<CharacterEditPage> createState() => _CharacterEditPageState();\n}\n\nclass _CharacterEditPageState extends State<CharacterEditPage> {\n  final _nameController = TextEditingController();\n  final _promptController = TextEditingController(text: '');\n\n  final randomSeed = Random().nextInt(10000);\n\n  String? _originalAvatarUrl;\n  int? _originalAvatarId;\n\n  String? _avatarUrl;\n  int? _avatarId;\n\n  List<String> avatarPresets = [];\n\n  int maxContext = 5;\n\n  List<ChatMemory> validMemories = [\n    ChatMemory('Ephemeral', 1, description: 'Each conversation is independent, often used for one-off Q&A'),\n    ChatMemory('Basic', 3, description: 'Remembers the last 3 conversations'),\n    ChatMemory('Medium', 6, description: 'Remembers the last 6 conversations'),\n    ChatMemory('Deep', 10, description: 'Remembers the last 10 conversations')\n  ];\n\n  bool showAdvancedOptions = false;\n\n  mm.Model? _selectedModel;\n  String? reservedModel;\n\n  @override\n  void initState() {\n    super.initState();\n\n    BlocProvider.of<RoomBloc>(context).add(RoomLoadEvent(widget.roomId, cascading: false));\n\n    // 获取预设头像\n    if (Ability().isUserLogon()) {\n      APIServer().avatars().then((value) {\n        avatarPresets = value;\n      });\n    }\n  }\n\n  @override\n  void dispose() {\n    _nameController.dispose();\n    _promptController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          title: Text(\n            AppLocale.configure.getString(context),\n            style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n          elevation: 0,\n          toolbarHeight: CustomSize.toolbarHeight,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          child: BlocConsumer<RoomBloc, RoomState>(\n            listener: (context, state) {\n              if (state is RoomLoaded) {\n                _nameController.text = state.room.name;\n                _promptController.text = state.room.systemPrompt ?? '';\n                maxContext = state.room.maxContext;\n\n                ModelAggregate.model(state.room.model).then((value) {\n                  setState(() {\n                    _selectedModel = value;\n                    reservedModel = value.id;\n                  });\n                });\n\n                if (state.room.avatarUrl != null && state.room.avatarUrl != '') {\n                  setState(() {\n                    _avatarUrl = state.room.avatarUrl;\n                    _avatarId = null;\n\n                    _originalAvatarUrl = state.room.avatarUrl;\n                    _originalAvatarId = null;\n                  });\n                } else if (state.room.avatarId != null && state.room.avatarId != 0) {\n                  setState(() {\n                    _avatarId = state.room.avatarId;\n                    _avatarUrl = null;\n\n                    _originalAvatarId = state.room.avatarId;\n                    _originalAvatarUrl = null;\n                  });\n                } else {\n                  setState(() {\n                    _avatarId = null;\n                    _avatarUrl = null;\n\n                    _originalAvatarId = state.room.id;\n                    _originalAvatarUrl = null;\n                  });\n                }\n              }\n\n              if (state is RoomOperationResult) {\n                if (state.success) {\n                  if (state.redirect != null) {\n                    context.push(state.redirect!).then((value) {\n                      context.read<RoomBloc>().add(RoomsLoadEvent());\n                    });\n                  }\n                } else {\n                  showErrorMessageEnhanced(context, state.error ?? AppLocale.operateFailed.getString(context));\n                }\n              }\n            },\n            buildWhen: (previous, current) => current is RoomLoaded,\n            builder: (context, state) {\n              if (state is RoomLoaded) {\n                return SingleChildScrollView(\n                  child: Padding(\n                    padding: const EdgeInsets.all(10.0),\n                    child: Column(\n                      crossAxisAlignment: CrossAxisAlignment.start,\n                      children: [\n                        const SizedBox(height: 10),\n                        // 名称\n                        if (state.room.category != 'system')\n                          ColumnBlock(\n                            children: [\n                              EnhancedTextField(\n                                customColors: customColors,\n                                controller: _nameController,\n                                maxLength: 50,\n                                maxLines: 1,\n                                showCounter: false,\n                                labelText: AppLocale.roomName.getString(context),\n                                labelPosition: LabelPosition.left,\n                                hintText: AppLocale.required.getString(context),\n                                textDirection: TextDirection.rtl,\n                              ),\n                              if (Ability().isUserLogon())\n                                EnhancedInput(\n                                  padding: const EdgeInsets.only(top: 10, bottom: 5),\n                                  title: Text(\n                                    AppLocale.avatar.getString(context),\n                                    style: TextStyle(\n                                      color: customColors.textfieldLabelColor,\n                                      fontSize: 16,\n                                    ),\n                                  ),\n                                  value: Row(\n                                    mainAxisAlignment: MainAxisAlignment.end,\n                                    mainAxisSize: MainAxisSize.min,\n                                    children: [\n                                      Container(\n                                        width: 45,\n                                        height: 45,\n                                        decoration: BoxDecoration(\n                                          borderRadius: CustomSize.borderRadius,\n                                          image: _avatarUrl == null\n                                              ? null\n                                              : DecorationImage(\n                                                  image: (_avatarUrl!.startsWith('http')\n                                                      ? CachedNetworkImageProviderEnhanced(_avatarUrl!)\n                                                      : FileImage(File(_avatarUrl!))) as ImageProvider,\n                                                  fit: BoxFit.cover,\n                                                ),\n                                        ),\n                                        child: _avatarUrl == null && _avatarId == null\n                                            ? const Center(\n                                                child: Icon(\n                                                  Icons.interests,\n                                                  color: Colors.grey,\n                                                ),\n                                              )\n                                            : (_avatarId == null\n                                                ? const SizedBox()\n                                                : RandomAvatar(\n                                                    id: _avatarId!,\n                                                    usage: AvatarUsage.room,\n                                                  )),\n                                      ),\n                                    ],\n                                  ),\n                                  onPressed: () {\n                                    openModalBottomSheet(\n                                      context,\n                                      (context) {\n                                        return AvatarSelector(\n                                          onSelected: (selected) {\n                                            setState(() {\n                                              _avatarUrl = selected.url;\n                                              _avatarId = selected.id;\n                                            });\n                                            context.pop();\n                                          },\n                                          usage: AvatarUsage.room,\n                                          defaultAvatarId: _avatarId,\n                                          defaultAvatarUrl: _avatarUrl,\n                                          externalAvatarIds: _originalAvatarId == null ? [] : [_originalAvatarId!],\n                                          externalAvatarUrls: _originalAvatarUrl == null\n                                              ? [...avatarPresets]\n                                              : [_originalAvatarUrl!, ...avatarPresets],\n                                        );\n                                      },\n                                      heightFactor: 0.8,\n                                    );\n                                  },\n                                ),\n                            ],\n                          ),\n\n                        ColumnBlock(\n                          innerPanding: 10,\n                          padding: const EdgeInsets.only(top: 15, left: 15, right: 15),\n                          children: [\n                            // 提示语\n                            EnhancedTextField(\n                              fontSize: 12,\n                              customColors: customColors,\n                              controller: _promptController,\n                              labelText: AppLocale.prompt.getString(context),\n                              labelPosition: LabelPosition.top,\n                              hintText: AppLocale.promptHint.getString(context),\n                              bottomButton: Row(\n                                children: [\n                                  Icon(\n                                    Icons.tips_and_updates_outlined,\n                                    size: 13,\n                                    color: customColors.linkColor?.withAlpha(150),\n                                  ),\n                                  const SizedBox(width: 5),\n                                  Text(\n                                    AppLocale.examples.getString(context),\n                                    style: TextStyle(\n                                      color: customColors.linkColor?.withAlpha(150),\n                                      fontSize: 13,\n                                    ),\n                                  ),\n                                ],\n                              ),\n                              bottomButtonOnPressed: () async {\n                                openSystemPromptSelectDialog(\n                                  context,\n                                  customColors,\n                                  _promptController,\n                                );\n                              },\n                              minLines: 4,\n                              maxLines: 20,\n                              showCounter: false,\n                            ),\n                          ],\n                        ),\n                        if (showAdvancedOptions)\n                          ColumnBlock(\n                            innerPanding: 10,\n                            padding: const EdgeInsets.only(top: 15, left: 15, right: 15, bottom: 0),\n                            children: [\n                              // 模型\n                              EnhancedInputSimple(\n                                title: AppLocale.model.getString(context),\n                                padding: const EdgeInsets.only(top: 10, bottom: 0),\n                                onPressed: () {\n                                  openSelectModelDialog(\n                                    context,\n                                    (selected) {\n                                      setState(() {\n                                        _selectedModel = selected;\n                                      });\n                                    },\n                                    initValue: _selectedModel?.uid(),\n                                    reservedModels: reservedModel != null ? [reservedModel!] : [],\n                                  );\n                                },\n                                value:\n                                    _selectedModel != null ? _selectedModel!.name : AppLocale.select.getString(context),\n                              ),\n                              EnhancedInput(\n                                title: Text(\n                                  AppLocale.memoryDepth.getString(context),\n                                  style: TextStyle(\n                                    color: customColors.textfieldLabelColor,\n                                    fontSize: 16,\n                                  ),\n                                ),\n                                value: Text(\n                                  validMemories.where((element) => element.number == maxContext).firstOrNull?.name ??\n                                      '',\n                                ),\n                                onPressed: () {\n                                  openListSelectDialog(\n                                    context,\n                                    validMemories\n                                        .map(\n                                          (e) => SelectorItem(\n                                            Column(\n                                              children: [\n                                                Text(\n                                                  e.name,\n                                                  textAlign: TextAlign.center,\n                                                ),\n                                                const SizedBox(height: 10),\n                                                Text(\n                                                  e.description ?? '',\n                                                  textAlign: TextAlign.center,\n                                                  style: TextStyle(\n                                                    color: customColors.weakTextColor,\n                                                    fontSize: 12,\n                                                  ),\n                                                ),\n                                              ],\n                                            ),\n                                            e.number,\n                                          ),\n                                        )\n                                        .toList(),\n                                    (value) {\n                                      setState(() {\n                                        maxContext = value.value;\n                                      });\n                                      return true;\n                                    },\n                                    heightFactor: 0.5,\n                                    value: validMemories.where((element) => element.number == maxContext).firstOrNull,\n                                  );\n                                },\n                              ),\n                            ],\n                          ),\n                        AdvancedButton(\n                          showAdvancedOptions: showAdvancedOptions,\n                          onPressed: (value) {\n                            setState(() {\n                              showAdvancedOptions = value;\n                            });\n                          },\n                        ),\n                        const SizedBox(height: 10),\n                        EnhancedButton(\n                          title: AppLocale.save.getString(context),\n                          onPressed: () async {\n                            if (_nameController.text == '') {\n                              showErrorMessage(AppLocale.nameRequiredMessage.getString(context));\n                              return;\n                            }\n\n                            if (_promptController.text == '') {\n                              showErrorMessage(AppLocale.charactorPromptRequiredMessage.getString(context));\n                              return;\n                            }\n\n                            if (_avatarUrl != null) {\n                              if (!(_avatarUrl!.startsWith('http://') || _avatarUrl!.startsWith('https://'))) {\n                                // 上传文件，获取 URL\n                                final cancel = BotToast.showCustomLoading(\n                                  toastBuilder: (cancel) {\n                                    return LoadingIndicator(\n                                      message: AppLocale.imageUploading.getString(context),\n                                    );\n                                  },\n                                  allowClick: false,\n                                );\n\n                                final uploadRes = await ImageUploader(widget.setting)\n                                    .upload(_avatarUrl!, usage: 'avatar')\n                                    .whenComplete(() => cancel());\n                                _avatarUrl = uploadRes.url;\n                              }\n                            }\n\n                            if (context.mounted) {\n                              context.read<RoomBloc>().add(\n                                    RoomUpdateEvent(\n                                      widget.roomId,\n                                      name: _nameController.text,\n                                      model: _selectedModel?.uid(),\n                                      prompt: _promptController.text,\n                                      avatarUrl: _avatarUrl,\n                                      avatarId: _avatarId,\n                                      maxContext: maxContext,\n                                    ),\n                                  );\n\n                              showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                            }\n                          },\n                        ),\n                      ],\n                    ),\n                  ),\n                );\n              }\n\n              return const Center(\n                child: CircularProgressIndicator(),\n              );\n            },\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/chat/characters.dart",
    "content": "import 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/event.dart';\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/bloc/room_bloc.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/enhanced_error.dart';\nimport 'package:askaide/page/component/global_alert.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/weak_text_button.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/chat/component/character_box.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api/room_gallery.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass CharactersPage extends StatefulWidget {\n  final SettingRepository setting;\n  const CharactersPage({Key? key, required this.setting}) : super(key: key);\n\n  @override\n  State<CharactersPage> createState() => _CharactersPageState();\n}\n\nclass _CharactersPageState extends State<CharactersPage> {\n  @override\n  void initState() {\n    context.read<RoomBloc>().add(RoomsLoadEvent());\n\n    super.initState();\n  }\n\n  List<RoomGallery> selectedSuggestions = [];\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          title: Text(\n            AppLocale.homeTitle.getString(context),\n            style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n          toolbarHeight: CustomSize.toolbarHeight,\n          actions: [\n            IconButton(\n              icon: const Icon(Icons.add_circle_outline),\n              onPressed: () {\n                context.push('/create-room').whenComplete(() {\n                  if (context.mounted) {\n                    context.read<RoomBloc>().add(RoomsLoadEvent());\n                  }\n                });\n              },\n            ),\n          ],\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          backgroundColor: customColors.backgroundColor,\n          enabled: false,\n          child: SafeArea(\n            top: false,\n            left: false,\n            right: false,\n            child: BlocConsumer<RoomBloc, RoomState>(\n              listener: (context, state) {\n                if (state is RoomsLoaded) {\n                  if (state.rooms.isNotEmpty) {\n                    selectedSuggestions.clear();\n                    setState(() {});\n                    GlobalEvent().emit('showBottomNavigatorBar');\n                  }\n                }\n\n                if (state is RoomCreateError) {\n                  showErrorMessageEnhanced(context, state.error);\n                }\n\n                if (state is RoomOperationResult) {\n                  if (!state.success) {\n                    showErrorMessageEnhanced(context, state.error ?? AppLocale.operateFailed.getString(context));\n                  } else {\n                    if (state.redirect != null) {\n                      context.push(state.redirect!);\n                    }\n                  }\n                }\n              },\n              buildWhen: (previous, current) => current is RoomsLoading || current is RoomsLoaded,\n              builder: (context, state) {\n                if (state is RoomsLoaded) {\n                  if (state.error != null) {\n                    return EnhancedErrorWidget(error: state.error);\n                  }\n\n                  return Column(\n                    children: [\n                      Expanded(\n                        child: RefreshIndicator(\n                          color: customColors.linkColor,\n                          onRefresh: () async {\n                            context.read<RoomBloc>().add(RoomsLoadEvent(forceRefresh: true));\n                          },\n                          displacement: 20,\n                          child: Column(\n                            children: [\n                              if (Ability().showGlobalAlert) const GlobalAlert(pageKey: 'rooms'),\n                              Expanded(child: buildBody(customColors, state, context)),\n                            ],\n                          ),\n                        ),\n                      ),\n                      if (selectedSuggestions.isNotEmpty)\n                        Container(\n                          height: 70,\n                          width: double.infinity,\n                          padding: const EdgeInsets.symmetric(\n                            horizontal: 20,\n                            vertical: 10,\n                          ),\n                          child: Row(\n                            children: [\n                              WeakTextButton(\n                                title: AppLocale.cancel.getString(context),\n                                onPressed: () {\n                                  selectedSuggestions.clear();\n                                  setState(() {});\n                                  GlobalEvent().emit('showBottomNavigatorBar');\n                                },\n                              ),\n                              const SizedBox(width: 20),\n                              Expanded(\n                                child: EnhancedButton(\n                                    title: AppLocale.ok.getString(context),\n                                    onPressed: () {\n                                      context\n                                          .read<RoomBloc>()\n                                          .add(GalleryRoomCopyEvent(selectedSuggestions.map((e) => e.id).toList()));\n                                      showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                                    }),\n                              )\n                            ],\n                          ),\n                        ),\n                    ],\n                  );\n                }\n\n                return const Center(\n                  child: LoadingIndicator(),\n                );\n              },\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget buildColumnTitle(BuildContext context, CustomColors customColors, String title) {\n    return Container(\n      padding: const EdgeInsets.only(left: 5),\n      margin: const EdgeInsets.only(\n        left: 10,\n        right: 10,\n      ),\n      child: Text(title, style: const TextStyle(fontSize: 16)),\n    );\n  }\n\n  Widget buildBody(CustomColors customColors, RoomsLoaded state, BuildContext context) {\n    List<Widget> children = [];\n\n    if (state.rooms.isNotEmpty) {\n      children.addAll([\n        buildColumnTitle(context, customColors, AppLocale.myCharacters.getString(context)),\n        ListView.builder(\n          padding: const EdgeInsets.all(5),\n          itemCount: state.rooms.length,\n          shrinkWrap: true,\n          physics: const NeverScrollableScrollPhysics(),\n          itemBuilder: (context, index) {\n            final room = state.rooms[index];\n\n            return CharacterBox(room: room);\n          },\n        ),\n      ]);\n    }\n\n    if (state.suggests.isNotEmpty) {\n      children.addAll([\n        const SizedBox(height: 10),\n        buildColumnTitle(context, customColors, AppLocale.robotRecommand.getString(context)),\n        ListView.builder(\n          padding: const EdgeInsets.all(5),\n          itemCount: state.suggests.length,\n          shrinkWrap: true,\n          physics: const NeverScrollableScrollPhysics(),\n          itemBuilder: (context, index) {\n            return Container(\n              margin: const EdgeInsets.symmetric(\n                horizontal: 10,\n                vertical: 5,\n              ),\n              decoration: BoxDecoration(borderRadius: CustomSize.borderRadius),\n              child: CharacterBoxItem(\n                onTap: () {\n                  HapticFeedbackHelper.lightImpact();\n                  context.read<RoomBloc>().add(GalleryRoomCopyEvent([state.suggests[index].id]));\n                },\n                name: state.suggests[index].name,\n                desc: state.suggests[index].description,\n                avatarUrl: state.suggests[index].avatarUrl,\n              ),\n            );\n          },\n        ),\n      ]);\n    }\n\n    return SingleChildScrollView(\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: children,\n      ),\n    );\n  }\n\n  void onItemSelected(RoomGallery item) {\n    if (selectedSuggestions.contains(item)) {\n      selectedSuggestions.remove(item);\n    } else {\n      selectedSuggestions.add(item);\n    }\n\n    setState(() {});\n\n    if (selectedSuggestions.isEmpty) {\n      GlobalEvent().emit('showBottomNavigatorBar');\n    } else {\n      GlobalEvent().emit('hideBottomNavigatorBar');\n    }\n  }\n}\n"
  },
  {
    "path": "lib/page/chat/component/character_box.dart",
    "content": "import 'package:askaide/bloc/room_bloc.dart';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/helper/image.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/model/room.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_initicon/flutter_initicon.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:flutter_slidable/flutter_slidable.dart';\nimport 'package:go_router/go_router.dart';\n\nclass CharacterBox extends StatelessWidget {\n  final Room room;\n  const CharacterBox({super.key, required this.room});\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return Container(\n      margin: const EdgeInsets.symmetric(\n        horizontal: 10,\n        vertical: 5,\n      ),\n      decoration: BoxDecoration(borderRadius: CustomSize.borderRadius),\n      child: Slidable(\n        endActionPane: ActionPane(\n          motion: const ScrollMotion(),\n          children: [\n            const SizedBox(width: 10),\n            SlidableAction(\n              label: AppLocale.configure.getString(context),\n              backgroundColor: Colors.green,\n              borderRadius: room.category == 'system'\n                  ? CustomSize.borderRadiusAll\n                  : const BorderRadius.only(topLeft: CustomSize.radius, bottomLeft: CustomSize.radius),\n              icon: Icons.edit,\n              onPressed: (_) {\n                final chatRoomBloc = context.read<RoomBloc>();\n                final redirectUrl = room.roomType == 4 ? '/group-chat/${room.id}/edit' : '/room/${room.id}/setting';\n\n                context.push(redirectUrl).then((value) {\n                  chatRoomBloc.add(RoomsLoadEvent());\n                });\n              },\n            ),\n            if (room.category != 'system')\n              SlidableAction(\n                label: AppLocale.delete.getString(context),\n                borderRadius: const BorderRadius.only(topRight: CustomSize.radius, bottomRight: CustomSize.radius),\n                backgroundColor: Colors.red,\n                icon: Icons.delete,\n                onPressed: (_) {\n                  openConfirmDialog(\n                    context,\n                    AppLocale.confirmToDeleteRoom.getString(context),\n                    () => context.read<RoomBloc>().add(RoomDeleteEvent(room.id!)),\n                    danger: true,\n                  );\n                },\n              ),\n          ],\n        ),\n        child: buildItem(customColors, context),\n      ),\n    );\n  }\n\n  Widget buildItem(CustomColors customColors, BuildContext context) {\n    return CharacterBoxItem(\n      onTap: () {\n        HapticFeedbackHelper.lightImpact();\n        final chatRoomBloc = context.read<RoomBloc>();\n        context.push('/room/${room.id}/chat').then((value) {\n          chatRoomBloc.add(RoomsLoadEvent(forceRefresh: true));\n        });\n      },\n      name: room.name,\n      desc: room.description ?? room.systemPrompt,\n      model: room.model,\n      avatarUrl: room.avatarUrl,\n    );\n  }\n}\n\nclass CharacterBoxItem extends StatelessWidget {\n  final Function() onTap;\n  final String? avatarUrl;\n  final String name;\n  final String? model;\n  final String? desc;\n  const CharacterBoxItem({\n    super.key,\n    required this.onTap,\n    required this.name,\n    this.avatarUrl,\n    this.model,\n    this.desc,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return Container(\n      decoration: BoxDecoration(\n        borderRadius: CustomSize.borderRadius,\n        color: customColors.listTileBackgroundColor,\n      ),\n      child: InkWell(\n        borderRadius: CustomSize.borderRadiusAll,\n        onTap: onTap,\n        overlayColor: WidgetStateProperty.all(Colors.transparent),\n        child: Stack(\n          children: [\n            Row(\n              mainAxisSize: MainAxisSize.min,\n              children: [\n                buildAvatar(),\n                Expanded(\n                  child: Container(\n                    padding: const EdgeInsets.symmetric(horizontal: 10),\n                    child: Column(\n                      crossAxisAlignment: CrossAxisAlignment.start,\n                      children: [\n                        Text(\n                          name,\n                          overflow: TextOverflow.ellipsis,\n                        ),\n                        const SizedBox(height: 5),\n                        buildRoomDesc(customColors),\n                      ],\n                    ),\n                  ),\n                ),\n              ],\n            ),\n            if (model != null && Ability().usingLocalOpenAIModel(model!))\n              Positioned(\n                right: 0,\n                top: 0,\n                child: Container(\n                  decoration: BoxDecoration(\n                    color: customColors.backgroundContainerColor,\n                    borderRadius: const BorderRadius.only(topRight: CustomSize.radius, bottomLeft: CustomSize.radius),\n                  ),\n                  padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),\n                  child: Text(\n                    'local',\n                    style: TextStyle(\n                      color: customColors.weakTextColor,\n                      fontSize: 8,\n                    ),\n                  ),\n                ),\n              ),\n          ],\n        ),\n      ),\n    );\n  }\n\n  Widget buildRoomDesc(CustomColors customColors) {\n    if (desc != null && desc != '') {\n      return Text(\n        desc!,\n        style: TextStyle(\n          color: customColors.weakLinkColor?.withAlpha(150),\n          fontSize: 13,\n        ),\n        maxLines: 1,\n        overflow: TextOverflow.ellipsis,\n      );\n    }\n\n    return const SizedBox();\n  }\n\n  Widget buildAvatar() {\n    if (avatarUrl != null && avatarUrl!.startsWith('http')) {\n      return SizedBox(\n        width: 70,\n        height: 70,\n        child: ClipRRect(\n          borderRadius: const BorderRadius.only(topLeft: CustomSize.radius, bottomLeft: CustomSize.radius),\n          child: CachedNetworkImageEnhanced(\n            imageUrl: imageURL(avatarUrl!, qiniuImageTypeAvatar),\n            fit: BoxFit.fill,\n          ),\n        ),\n      );\n    }\n\n    return Initicon(\n      text: name.split('、').join(' '),\n      size: 70,\n      backgroundColor: Colors.grey.withAlpha(100),\n      borderRadius: const BorderRadius.only(topLeft: CustomSize.radius, bottomLeft: CustomSize.radius),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/chat/component/group_avatar.dart",
    "content": "import 'package:askaide/helper/image.dart';\nimport 'package:cached_network_image/cached_network_image.dart';\nimport 'package:flutter/material.dart';\n\n// ignore: must_be_immutable\nclass GroupAvatar extends StatelessWidget {\n  final double size;\n  final double padding;\n  final double margin;\n  final List<String> avatars;\n  final Color? backgroundColor;\n\n  var row = 0, column = 0;\n\n  GroupAvatar({\n    super.key,\n    this.size = 40,\n    this.padding = 2,\n    this.margin = 3,\n    required this.avatars,\n    this.backgroundColor,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final avatar = buildAvatar(context);\n\n    return Container(\n      padding: const EdgeInsets.all(4),\n      width: size,\n      height: size,\n      color: backgroundColor ?? Colors.grey.withAlpha(100),\n      child: avatar,\n    );\n  }\n\n  double get innerSize => size - 8;\n\n  Widget buildAvatar(BuildContext context) {\n    var childCount = avatars.length;\n    int columnMax;\n    List<Widget> icons = [];\n    List<Widget> stacks = [];\n    // 五张图片之后（包含5张），每行的最大列数是3\n    double imgWidth;\n\n    if (childCount < 2) {\n      return Container(\n        width: innerSize,\n        height: innerSize,\n        color: Colors.transparent,\n      );\n    }\n\n    if (childCount >= 5) {\n      columnMax = 3;\n      imgWidth = (innerSize - (padding * columnMax) - margin) / columnMax;\n    } else {\n      columnMax = 2;\n      imgWidth = (innerSize - (padding * columnMax) - margin) / columnMax;\n    }\n    for (var i = 0; i < childCount; i++) {\n      icons.add(_weChatGroupChatChildIcon(avatars[i], imgWidth));\n    }\n    row = 0;\n    column = 0;\n    var centerTop = 0.0;\n    if (childCount == 2 || childCount == 5 || childCount == 6) {\n      centerTop = imgWidth / 2;\n    }\n    for (var i = 0; i < childCount; i++) {\n      var left = imgWidth * row + padding * (row + 1);\n      var top = imgWidth * column + margin * column + centerTop;\n      switch (childCount) {\n        case 3:\n        case 7:\n          _topOneIcon(stacks, icons[i], childCount, i, imgWidth, left, top);\n          break;\n        case 5:\n        case 8:\n          _topTwoIcon(stacks, icons[i], childCount, i, imgWidth, left, top);\n          break;\n        default:\n          _otherIcon(\n              stacks, icons[i], childCount, i, imgWidth, left, top, columnMax);\n          break;\n      }\n    }\n\n    return Container(\n      width: innerSize,\n      height: innerSize,\n      color: Colors.transparent,\n      padding: EdgeInsets.only(top: padding),\n      alignment: AlignmentDirectional.bottomCenter,\n      child: Stack(\n        children: stacks,\n      ),\n    );\n  }\n\n  _weChatGroupChatChildIcon(String avatar, double width) {\n    return ClipRRect(\n      borderRadius: BorderRadius.circular(2),\n      child: CachedNetworkImage(\n        imageUrl: imageURL(avatar, 'avatar'),\n        height: width,\n        width: width,\n        fit: BoxFit.fill,\n      ),\n    );\n  }\n\n  // 顶部为一张图片\n  _topOneIcon(List<Widget> stacks, Widget child, int childCount, i, imgWidth,\n      left, top) {\n    if (i == 0) {\n      var firstLeft = imgWidth / 2 + left + margin / 2;\n      if (childCount == 7) {\n        firstLeft = imgWidth + left + margin;\n      }\n      stacks.add(Positioned(\n        left: firstLeft,\n        child: child,\n      ));\n      row = 0;\n      // 换行\n      column++;\n    } else {\n      stacks.add(Positioned(\n        left: left,\n        top: top,\n        child: child,\n      ));\n      // 换列\n      row++;\n      if (i == 3) {\n        // 第一例\n        row = 0;\n        // 换行\n        column++;\n      }\n    }\n  }\n\n// 顶部为两张图片\n  _topTwoIcon(List<Widget> stacks, Widget child, int childCount, i, imgWidth,\n      left, top) {\n    if (i == 0 || i == 1) {\n      stacks.add(Positioned(\n        left: imgWidth / 2 + left + margin / 2,\n        top: childCount == 5 ? top : 0.0,\n        child: child,\n      ));\n      row++;\n      if (i == 1) {\n        row = 0;\n        // 换行\n        column++;\n      }\n    } else {\n      stacks.add(Positioned(\n        left: left,\n        top: top,\n        child: child,\n      ));\n      // 换列\n      row++;\n      if (i == 4) {\n        // 第一例\n        row = 0;\n        // 换行\n        column++;\n      }\n    }\n  }\n\n  _otherIcon(List<Widget> stacks, Widget child, int childCount, i, imgWidth,\n      left, top, columnMax) {\n    stacks.add(Positioned(\n      left: left,\n      top: top,\n      child: child,\n    ));\n    // 换列\n    row++;\n    if ((i + 1) % columnMax == 0) {\n      // 第一例\n      row = 0;\n      // 换行\n      column++;\n    }\n  }\n}\n"
  },
  {
    "path": "lib/page/chat/component/group_empty.dart",
    "content": "import 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\n\nclass GroupEmptyBoard extends StatelessWidget {\n  const GroupEmptyBoard({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return Center(\n      child: Column(\n        mainAxisAlignment: MainAxisAlignment.start,\n        children: [\n          const SizedBox(height: 30),\n          Container(\n            decoration: BoxDecoration(\n              color: customColors.backgroundColor?.withAlpha(200),\n              borderRadius: CustomSize.borderRadius,\n            ),\n            padding: const EdgeInsets.only(top: 20, left: 15, right: 10, bottom: 3),\n            width: _resolveTipWidth(context),\n            child: Column(\n              mainAxisSize: MainAxisSize.min,\n              crossAxisAlignment: CrossAxisAlignment.start,\n              children: [\n                Row(\n                  children: [\n                    Image.asset('assets/app-256-transparent.png', width: 20, height: 20),\n                    const SizedBox(width: 5),\n                    const Text(\n                      '小提示',\n                      style: TextStyle(\n                        fontSize: 16,\n                        fontWeight: FontWeight.bold,\n                      ),\n                    ),\n                  ],\n                ),\n                const SizedBox(height: 20),\n                Column(\n                  crossAxisAlignment: CrossAxisAlignment.start,\n                  children: [\n                    buildTextLine(\n                      customColors,\n                      \"点击 @ 按钮，快速指定应答成员\",\n                      Icons.touch_app,\n                    ),\n                    buildTextLine(\n                      customColors,\n                      '未选择成员时，系统将随机指派',\n                      Icons.shuffle,\n                    ),\n                    buildTextLine(\n                      customColors,\n                      '系统会记住上次使用的成员',\n                      Icons.memory,\n                    ),\n                  ],\n                ),\n                const SizedBox(height: 20),\n              ],\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n\n  Widget buildTextLine(CustomColors customColors, String text, IconData? icon) {\n    return Padding(\n      padding: const EdgeInsets.only(bottom: 10, left: 15),\n      child: Row(\n        crossAxisAlignment: CrossAxisAlignment.center,\n        children: [\n          Icon(\n            icon,\n            size: 14,\n            color: customColors.chatExampleItemText?.withAlpha(120),\n          ),\n          const SizedBox(width: 5),\n          Expanded(\n            child: Text(\n              text,\n              maxLines: 1,\n              style: TextStyle(\n                color: customColors.weakTextColor,\n                height: 1.5,\n                fontSize: 14,\n                overflow: TextOverflow.ellipsis,\n              ),\n            ),\n          )\n        ],\n      ),\n    );\n  }\n\n  double _resolveTipWidth(BuildContext context) {\n    final screenWidth = MediaQuery.of(context).size.width;\n    if (screenWidth < 400) {\n      return screenWidth / 1.15;\n    }\n\n    return 400;\n  }\n}\n"
  },
  {
    "path": "lib/page/chat/component/model_switcher.dart",
    "content": "import 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/chat/character_create.dart';\nimport 'package:askaide/page/component/random_avatar.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\nimport 'package:askaide/repo/model/model.dart' as mm;\nimport 'package:flutter_initicon/flutter_initicon.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\n\nclass ModelSwitcher extends StatelessWidget {\n  final mm.Model? value;\n  final Function(mm.Model? selected) onSelected;\n\n  const ModelSwitcher({\n    super.key,\n    required this.onSelected,\n    this.value,\n  });\n\n  static void openActionDialog({\n    required BuildContext context,\n    required Function(mm.Model? selected) onSelected,\n    mm.Model? initValue,\n  }) {\n    HapticFeedbackHelper.mediumImpact();\n    openSelectModelDialog(\n      context,\n      (selected) {\n        onSelected(selected);\n      },\n      initValue: initValue?.uid(),\n      withCustom: true,\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return IconButton(\n      onPressed: () async {\n        openActionDialog(\n          context: context,\n          onSelected: onSelected,\n          initValue: value,\n        );\n      },\n      icon: value == null\n          ? const Icon(Icons.alternate_email_outlined)\n          // Icons.theater_comedy_outlined\n          // Icons.model_training_outlined\n          // Icons.switch_access_shortcut_outlined\n          // Icons.assistant_outlined\n          : value!.avatarUrl == null\n              ? Initicon(\n                  text: value!.name.split('、').join(' '),\n                  size: 25,\n                  backgroundColor: Colors.grey.withAlpha(100),\n                  borderRadius: BorderRadius.circular(100),\n                )\n              : RemoteAvatar(\n                  avatarUrl: value!.avatarUrl!,\n                  size: 25,\n                  radius: 100,\n                ),\n      color: customColors.chatInputPanelText,\n      splashRadius: 20,\n      tooltip: AppLocale.switchModel.getString(context),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/chat/component/stop_button.dart",
    "content": "import 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\n\nclass StopButton extends StatelessWidget {\n  final Function()? onPressed;\n  final String label;\n  const StopButton({super.key, this.onPressed, required this.label});\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return TextButton.icon(\n      style: ButtonStyle(\n        tapTargetSize: MaterialTapTargetSize.shrinkWrap,\n        iconColor: const WidgetStatePropertyAll(Colors.red),\n        backgroundColor: WidgetStatePropertyAll(customColors.backgroundContainerColor),\n      ),\n      label: Text(\n        label,\n        style: TextStyle(\n          fontSize: 12,\n          color: customColors.textfieldLabelColor,\n        ),\n      ),\n      icon: const Icon(\n        Icons.stop_circle_outlined,\n        size: 13,\n      ),\n      onPressed: onPressed,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/chat/group/chat.dart",
    "content": "import 'dart:async';\n\nimport 'package:askaide/bloc/group_chat_bloc.dart';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/helper/image.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/chat/component/group_empty.dart';\nimport 'package:askaide/page/component/audio_player.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/chat/chat_share.dart';\nimport 'package:askaide/page/component/chat/help_tips.dart';\nimport 'package:askaide/page/component/chat/message_state_manager.dart';\nimport 'package:askaide/page/component/enhanced_popup_menu.dart';\nimport 'package:askaide/page/component/global_alert.dart';\nimport 'package:askaide/page/component/multi_item_selector.dart';\nimport 'package:askaide/page/component/random_avatar.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/chat/chat_input.dart';\nimport 'package:askaide/page/component/chat/chat_preview.dart';\nimport 'package:askaide/repo/model/group.dart';\nimport 'package:askaide/repo/model/message.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass GroupChatPage extends StatefulWidget {\n  final SettingRepository setting;\n  final int groupId;\n  final MessageStateManager stateManager;\n\n  const GroupChatPage({\n    super.key,\n    required this.setting,\n    required this.groupId,\n    required this.stateManager,\n  });\n\n  @override\n  State<GroupChatPage> createState() => _GroupChatPageState();\n}\n\nclass _GroupChatPageState extends State<GroupChatPage> {\n  final ScrollController _scrollController = ScrollController();\n  final ValueNotifier<bool> _inputEnabled = ValueNotifier(true);\n  final ChatPreviewController _chatPreviewController = ChatPreviewController();\n  final AudioPlayerController _audioPlayerController = AudioPlayerController(useRemoteAPI: true);\n  bool showAudioPlayer = false;\n  bool audioLoadding = false;\n\n  List<GroupMember>? selectedMembers = [];\n  List<GroupMessage> messages = [];\n\n  ChatGroup? group;\n\n  Timer? timer;\n\n  @override\n  void initState() {\n    super.initState();\n\n    context.read<GroupChatBloc>().add(GroupChatLoadEvent(widget.groupId));\n\n    _chatPreviewController.addListener(() {\n      setState(() {});\n    });\n\n    _audioPlayerController.onPlayStopped = () {\n      setState(() {\n        showAudioPlayer = false;\n      });\n    };\n    _audioPlayerController.onPlayAudioStarted = () {\n      setState(() {\n        showAudioPlayer = true;\n      });\n    };\n\n    _audioPlayerController.onPlayAudioLoading = (loading) {\n      setState(() {\n        audioLoadding = loading;\n      });\n    };\n  }\n\n  @override\n  void dispose() {\n    timer?.cancel();\n    _scrollController.dispose();\n    _chatPreviewController.dispose();\n    _audioPlayerController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return BackgroundContainer(\n      setting: widget.setting,\n      child: Scaffold(\n        appBar: _buildAppBar(context, customColors),\n        backgroundColor: Colors.transparent,\n        body: _buildChatComponents(customColors),\n      ),\n    );\n  }\n\n  Widget _buildChatComponents(CustomColors customColors) {\n    return BlocConsumer<GroupChatBloc, GroupChatState>(\n      listenWhen: (previous, current) => current is GroupChatLoaded || current is GroupDefaultMemberSelected,\n      listener: (context, state) {\n        if (state is GroupChatLoaded) {\n          // 加载聊天记录列表\n          context.read<GroupChatBloc>().add(GroupChatMessagesLoadEvent(widget.groupId, isInitRequest: true));\n\n          // 选中默认的聊天成员\n          selectedMembers =\n              state.group.members.where((e) => state.defaultChatMembers?.contains(e.id) ?? false).toList();\n\n          setState(() {\n            group = state.group;\n          });\n        }\n\n        if (state is GroupDefaultMemberSelected) {\n          // 选中默认的聊天成员\n          if (group != null) {\n            selectedMembers = group?.members.where((e) => state.members.contains(e.id)).toList();\n          }\n        }\n      },\n      buildWhen: (previous, current) => current is GroupChatLoaded,\n      builder: (context, groupState) {\n        if (groupState is GroupChatLoaded) {\n          return SafeArea(\n            top: false,\n            bottom: false,\n            child: Column(\n              children: [\n                if (Ability().showGlobalAlert) const GlobalAlert(pageKey: 'chat'),\n                // 语音输出中提示\n                if (showAudioPlayer)\n                  EnhancedAudioPlayer(\n                    controller: _audioPlayerController,\n                    loading: audioLoadding,\n                  ),\n                // 聊天内容窗口\n                Expanded(\n                  child: _buildChatPreviewArea(\n                    groupState,\n                    customColors,\n                    _chatPreviewController.selectMode,\n                  ),\n                ),\n\n                // 聊天输入窗口\n                Container(\n                  decoration: BoxDecoration(\n                    borderRadius: const BorderRadius.only(topLeft: CustomSize.radius, topRight: CustomSize.radius),\n                    color: customColors.chatInputPanelBackground,\n                  ),\n                  child: SafeArea(\n                    child: _chatPreviewController.selectMode\n                        ? buildSelectModeToolbars(\n                            context,\n                            _chatPreviewController,\n                            customColors,\n                          )\n                        : ChatInput(\n                            enableNotifier: _inputEnabled,\n                            enableImageUpload: false,\n                            onSubmit: (value) {\n                              _handleSubmit(value);\n                              FocusManager.instance.primaryFocus?.unfocus();\n                            },\n                            onNewChat: () => handleResetContext(context),\n                            hintText: AppLocale.askMeAnyQuestion.getString(context),\n                            onVoiceRecordTappedEvent: () {\n                              _audioPlayerController.stop();\n                            },\n                            toolsBuilder: () {\n                              return [\n                                Stack(\n                                  children: [\n                                    Container(\n                                      width: 40,\n                                      height: 40,\n                                      padding: const EdgeInsets.all(5),\n                                      child: InkWell(\n                                        onTap: () {\n                                          onModelSelect(\n                                            context,\n                                            groupState,\n                                            customColors,\n                                          );\n                                        },\n                                        child: Icon(\n                                          Icons.alternate_email,\n                                          color: selectedMembers != null && selectedMembers!.isNotEmpty\n                                              ? customColors.linkColor\n                                              : customColors.chatInputPanelText,\n                                        ),\n                                      ),\n                                    ),\n                                    if (selectedMembers != null && selectedMembers!.isNotEmpty)\n                                      Positioned(\n                                        right: 2,\n                                        top: 0,\n                                        child: Container(\n                                          padding: const EdgeInsets.symmetric(horizontal: 3, vertical: 3),\n                                          child: Text('x${selectedMembers!.length}',\n                                              style: TextStyle(\n                                                fontSize: 7,\n                                                color: customColors.linkColor,\n                                              )),\n                                        ),\n                                      ),\n                                  ],\n                                )\n                              ];\n                            },\n                          ),\n                  ),\n                ),\n              ],\n            ),\n          );\n        } else {\n          return Container();\n        }\n      },\n    );\n  }\n\n  void onModelSelect(\n    BuildContext context,\n    GroupChatLoaded groupState,\n    CustomColors customColors,\n  ) {\n    openModalBottomSheet(\n      context,\n      (context) {\n        return Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            Container(\n              padding: const EdgeInsets.only(top: 15, left: 20),\n              child: Text(\n                AppLocale.selectMember.getString(context),\n                style: TextStyle(\n                  fontSize: 14,\n                  color: customColors.weakLinkColor,\n                ),\n              ),\n            ),\n            Expanded(\n              child: MultiItemSelector(\n                itemBuilder: (item) {\n                  return Text(item.modelName);\n                },\n                items: groupState.group.members.where((e) => e.status != 2).toList(),\n                onChanged: (selected) {\n                  setState(() {\n                    selectedMembers = selected;\n                  });\n                },\n                itemAvatarBuilder: (item) {\n                  return _buildAvatar(\n                    avatarUrl: item.avatarUrl,\n                    id: item.id,\n                    size: 30,\n                  );\n                },\n                selectedItems: selectedMembers,\n              ),\n            ),\n          ],\n        );\n      },\n      heightFactor: 0.6,\n    );\n  }\n\n  BlocConsumer<GroupChatBloc, GroupChatState> _buildChatPreviewArea(\n    GroupChatLoaded group,\n    CustomColors customColors,\n    bool selectMode,\n  ) {\n    return BlocConsumer<GroupChatBloc, GroupChatState>(\n      listenWhen: (previous, current) => current is GroupChatMessagesLoaded,\n      listener: (context, state) {\n        if (state is GroupChatMessagesLoaded) {\n          if (state.error != null) {\n            showErrorMessageEnhanced(context, state.error);\n          }\n\n          messages = state.messages;\n\n          // 聊天内容窗口滚动到底部\n          if (!state.hasWaitTasks && _scrollController.hasClients) {\n            _scrollController.animateTo(\n              0,\n              duration: const Duration(milliseconds: 500),\n              curve: Curves.easeOut,\n            );\n          }\n\n          if (state.hasWaitTasks && _inputEnabled.value) {\n            // 聊天回复中时，禁止输入框编辑\n            setState(() {\n              _inputEnabled.value = false;\n            });\n          } else if (!state.hasWaitTasks && !_inputEnabled.value) {\n            // 聊天回复完成时，取消输入框的禁止编辑状态\n            setState(() {\n              _inputEnabled.value = true;\n            });\n          }\n\n          // 启动定时器，定时刷新聊天记录\n          timer ??= Timer.periodic(const Duration(seconds: 3), (timer) {\n            context.read<GroupChatBloc>().add(GroupChatUpdateMessageStatusEvent(widget.groupId));\n          });\n        }\n      },\n      buildWhen: (prv, cur) => cur is GroupChatMessagesLoaded,\n      builder: (context, state) {\n        if (state is GroupChatMessagesLoaded) {\n          if (state.messages.isEmpty) {\n            return const Padding(\n              padding: EdgeInsets.only(left: 15, right: 15, top: 10),\n              child: GroupEmptyBoard(),\n            );\n          }\n\n          final loadedMessages = state.messages.map((e) {\n            var member = e.memberId != null ? group.group.findMember(e.memberId!) : null;\n\n            return Message(\n              id: e.id,\n              Role.getRoleFromText(e.role),\n              e.message,\n              type: MessageType.getTypeFromText(e.type),\n              status: e.status,\n              refId: e.pid,\n              ts: e.createdAt,\n              avatarUrl: member?.avatarUrl,\n              senderName: member?.modelName,\n              roomId: e.groupId,\n            );\n          }).toList();\n\n          final messages = loadedMessages.map((e) {\n            return MessageWithState(\n              e,\n              group.states[widget.stateManager.getKey(e.roomId ?? 0, e.id ?? 0)] ?? MessageState(),\n            );\n          }).toList();\n\n          _chatPreviewController.setAllMessageIds(messages);\n\n          return ChatPreview(\n            supportBloc: false,\n            messages: messages,\n            scrollController: _scrollController,\n            controller: _chatPreviewController,\n            stateManager: widget.stateManager,\n            robotAvatar: selectMode\n                ? null\n                : _buildAvatar(\n                    avatarUrl: group.group.group.avatarUrl,\n                    id: group.group.group.id,\n                  ),\n            avatarBuilder: selectMode\n                ? null\n                : (Message message) {\n                    if (message.avatarUrl == null) {\n                      return null;\n                    }\n\n                    return _buildAvatar(avatarUrl: message.avatarUrl!);\n                  },\n            senderNameBuilder: (message) {\n              if (message.senderName == null) {\n                return null;\n              }\n\n              return Container(\n                margin: const EdgeInsets.fromLTRB(0, 0, 10, 7),\n                padding: const EdgeInsets.symmetric(horizontal: 13),\n                child: Text(\n                  message.senderName!,\n                  style: TextStyle(\n                    color: customColors.weakTextColor,\n                    fontSize: 12,\n                  ),\n                ),\n              );\n            },\n            onDeleteMessage: (id) {\n              handleDeleteMessage(context, id);\n            },\n            onResetContext: () => handleResetContext(context),\n            onSpeakEvent: (message) {\n              _audioPlayerController.playAudio(message.text);\n            },\n            onResentEvent: (message, index) {\n              _scrollController.animateTo(0, duration: const Duration(milliseconds: 500), curve: Curves.easeOut);\n              _handleSubmit(message.text, index: index, isResent: true);\n            },\n            helpWidgets: state.hasWaitTasks || loadedMessages.isEmpty\n                ? null\n                : [\n                    HelpTips(\n                      onSubmitMessage: _handleSubmit,\n                      onNewChat: () => handleResetContext(context),\n                    )\n                  ],\n          );\n        }\n        return const Center(child: CircularProgressIndicator());\n      },\n    );\n  }\n\n  /// 构建 AppBar\n  AppBar _buildAppBar(BuildContext context, CustomColors customColors) {\n    return _chatPreviewController.selectMode\n        ? AppBar(\n            title: Text(\n              AppLocale.select.getString(context),\n              style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n            ),\n            centerTitle: true,\n            elevation: 0,\n            leading: TextButton(\n              onPressed: () {\n                _chatPreviewController.exitSelectMode();\n              },\n              child: Text(\n                AppLocale.cancel.getString(context),\n                style: TextStyle(color: customColors.linkColor),\n              ),\n            ),\n            toolbarHeight: CustomSize.toolbarHeight,\n          )\n        : AppBar(\n            centerTitle: true,\n            elevation: 0,\n            // backgroundColor: customColors.chatRoomBackground,\n            title: BlocBuilder<GroupChatBloc, GroupChatState>(\n              buildWhen: (previous, current) => current is GroupChatLoaded,\n              builder: (context, state) {\n                if (state is GroupChatLoaded) {\n                  return Column(\n                    crossAxisAlignment: CrossAxisAlignment.center,\n                    children: [\n                      // 房间名称\n                      Text(\n                        state.group.group.name,\n                        style: const TextStyle(fontSize: 16),\n                      ),\n                    ],\n                  );\n                }\n\n                return Container();\n              },\n            ),\n            actions: [\n              buildChatMoreMenu(context, widget.groupId),\n            ],\n            toolbarHeight: CustomSize.toolbarHeight,\n          );\n  }\n\n  Widget _buildAvatar({String? avatarUrl, int? id, int size = 30}) {\n    if (avatarUrl != null && avatarUrl.startsWith('http')) {\n      return RemoteAvatar(\n        avatarUrl: imageURL(avatarUrl, qiniuImageTypeAvatar),\n        size: size,\n      );\n    }\n\n    return RandomAvatar(\n      id: id ?? 0,\n      size: size,\n      usage: Ability().isUserLogon() ? AvatarUsage.room : AvatarUsage.legacy,\n    );\n  }\n\n  /// 提交新消息\n  void _handleSubmit(\n    String text, {\n    int? index,\n    bool isResent = false,\n  }) {\n    setState(() {\n      _inputEnabled.value = false;\n    });\n\n    var replyMemberIds = (selectedMembers ?? []).map((e) => e.id!).toList();\n    context.read<GroupChatBloc>().add(GroupChatSendEvent(\n          widget.groupId,\n          text,\n          replyMemberIds,\n          index: index,\n          isResent: isResent,\n        ));\n  }\n\n  /// 处理消息删除事件\n  void handleDeleteMessage(BuildContext context, int id, {int? chatHistoryId}) {\n    openConfirmDialog(\n      context,\n      AppLocale.confirmDelete.getString(context),\n      () {\n        context.read<GroupChatBloc>().add(GroupChatDeleteEvent(widget.groupId, id));\n        HapticFeedbackHelper.mediumImpact();\n      },\n      danger: true,\n    );\n  }\n\n  /// 重置上下文\n  void handleResetContext(BuildContext context) {\n    context.read<GroupChatBloc>().add(GroupChatSendSystemEvent(\n          widget.groupId,\n          MessageType.contextBreak,\n          message: 'context-break-message',\n        ));\n    HapticFeedbackHelper.mediumImpact();\n  }\n\n  /// 清空历史消息\n  void handleClearHistory(BuildContext context) {\n    openConfirmDialog(\n      context,\n      AppLocale.confirmClearMessages.getString(context),\n      () {\n        context.read<GroupChatBloc>().add(GroupChatDeleteAllEvent(widget.groupId));\n        HapticFeedbackHelper.mediumImpact();\n      },\n      danger: true,\n    );\n  }\n\n  /// 构建聊天内容窗口\n  Widget buildSelectModeToolbars(\n    BuildContext context,\n    ChatPreviewController chatPreviewController,\n    CustomColors customColors,\n  ) {\n    return Container(\n      padding: const EdgeInsets.all(10),\n      decoration: BoxDecoration(\n        borderRadius: const BorderRadius.only(topLeft: CustomSize.radius, topRight: CustomSize.radius),\n        color: customColors.backgroundColor,\n      ),\n      child: Row(\n        mainAxisAlignment: MainAxisAlignment.spaceAround,\n        children: [\n          TextButton.icon(\n            onPressed: () {\n              var messages = chatPreviewController.selectedMessages();\n              if (messages.isEmpty) {\n                showErrorMessageEnhanced(context, AppLocale.noMessageSelected.getString(context));\n                return;\n              }\n\n              Navigator.push(\n                context,\n                MaterialPageRoute(\n                  fullscreenDialog: true,\n                  builder: (context) => ChatShareScreen(\n                    messages: messages\n                        .map((e) => ChatShareMessage(\n                              content: e.message.text,\n                              username: e.message.senderName,\n                              avatarURL: e.message.avatarUrl,\n                              leftSide: e.message.role == Role.receiver,\n                              images: e.message.images,\n                            ))\n                        .toList(),\n                  ),\n                ),\n              );\n              // var shareText = messages.map((e) {\n              //   if (e.message.role == Role.sender) {\n              //     return '我：\\n${e.message.text}';\n              //   }\n\n              //   return '${e.message.senderName ?? \"助理\"}：\\n${e.message.text}';\n              // }).join('\\n\\n');\n\n              // shareTo(\n              //   context,\n              //   content: shareText,\n              //   title: AppLocale.chatHistory.getString(context),\n              // );\n            },\n            icon: Icon(Icons.share, color: customColors.linkColor),\n            label: Text(\n              AppLocale.share.getString(context),\n              style: TextStyle(color: customColors.linkColor),\n            ),\n          ),\n          TextButton.icon(\n            onPressed: () {\n              chatPreviewController.selectAllMessage();\n            },\n            icon: Icon(Icons.select_all_outlined, color: customColors.linkColor),\n            label: Text(\n              AppLocale.selectAll.getString(context),\n              style: TextStyle(color: customColors.linkColor),\n            ),\n          ),\n          TextButton.icon(\n            onPressed: () {\n              if (chatPreviewController.selectedMessageIds.isEmpty) {\n                showErrorMessageEnhanced(context, AppLocale.noMessageSelected.getString(context));\n                return;\n              }\n\n              openConfirmDialog(\n                context,\n                AppLocale.confirmDelete.getString(context),\n                () {\n                  final ids = chatPreviewController.selectedMessageIds.toList();\n                  if (ids.isNotEmpty) {\n                    // context\n                    //     .read<ChatMessageBloc>()\n                    //     .add(ChatMessageDeleteEvent(ids));\n\n                    showErrorMessageEnhanced(context, AppLocale.operateSuccess.getString(context));\n\n                    chatPreviewController.exitSelectMode();\n                  }\n                },\n                danger: true,\n              );\n            },\n            icon: Icon(Icons.delete, color: customColors.linkColor),\n            label: Text(\n              AppLocale.delete.getString(context),\n              style: TextStyle(color: customColors.linkColor),\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n\n  /// 构建聊天设置下拉菜单\n  Widget buildChatMoreMenu(\n    BuildContext context,\n    int chatRoomId, {\n    bool useLocalContext = true,\n    bool withSetting = true,\n  }) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return EnhancedPopupMenu(\n      items: [\n        EnhancedPopupMenuItem(\n          title: AppLocale.newChat.getString(context),\n          icon: Icons.post_add,\n          iconColor: Colors.blue,\n          onTap: (ctx) {\n            handleResetContext(useLocalContext ? ctx : context);\n          },\n        ),\n        EnhancedPopupMenuItem(\n          title: AppLocale.clearChatHistory.getString(context),\n          icon: Icons.delete_forever,\n          iconColor: Colors.red,\n          onTap: (ctx) {\n            handleClearHistory(useLocalContext ? ctx : context);\n          },\n        ),\n        if (withSetting)\n          EnhancedPopupMenuItem(\n            title: AppLocale.settings.getString(context),\n            icon: Icons.settings,\n            iconColor: customColors.linkColor,\n            onTap: (_) {\n              context.push('/group-chat/$chatRoomId/edit').whenComplete(() {\n                context.read<GroupChatBloc>().add(GroupChatLoadEvent(widget.groupId, forceUpdate: true));\n              });\n            },\n          ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/chat/group/create.dart",
    "content": "import 'package:askaide/bloc/room_bloc.dart';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/image.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/random_avatar.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/model/group.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass GroupCreatePage extends StatefulWidget {\n  final SettingRepository setting;\n\n  const GroupCreatePage({super.key, required this.setting});\n\n  @override\n  State<GroupCreatePage> createState() => _GroupCreatePageState();\n}\n\nclass _GroupCreatePageState extends State<GroupCreatePage> {\n  List<Model> models = [];\n  List<Model> selectedModels = [];\n\n  Function? globalLoadingCancel;\n\n  @override\n  void initState() {\n    super.initState();\n\n    // 加载模型\n    APIServer().models().then((value) {\n      setState(() {\n        models = value.where((e) => !e.disabled).toList();\n      });\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(\n          AppLocale.createGroupChat.getString(context),\n          style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n        ),\n        centerTitle: true,\n        elevation: 0,\n        toolbarHeight: CustomSize.toolbarHeight,\n        actions: [\n          Padding(\n            padding: const EdgeInsets.only(right: 12),\n            child: EnhancedButton(\n              width: 50,\n              height: 30,\n              fontSize: 14,\n              title: AppLocale.ok.getString(context),\n              color: selectedModels.isEmpty ? customColors.weakTextColor : null,\n              backgroundColor: selectedModels.isEmpty ? customColors.weakTextColor!.withAlpha(20) : null,\n              onPressed: () {\n                onSave(context);\n              },\n            ),\n          ),\n        ],\n      ),\n      backgroundColor: customColors.backgroundColor,\n      body: BackgroundContainer(\n        setting: widget.setting,\n        enabled: false,\n        child: BlocListener<RoomBloc, RoomState>(\n          listenWhen: (previous, current) => current is GroupRoomUpdateResultState,\n          listener: (context, state) {\n            if (state is GroupRoomUpdateResultState) {\n              globalLoadingCancel?.call();\n              if (state.success) {\n                showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                context.pop();\n              } else {\n                showErrorMessageEnhanced(context, state.error ?? AppLocale.operateFailed.getString(context));\n              }\n            }\n          },\n          child: Column(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: [\n              Container(\n                padding: const EdgeInsets.only(top: 15, left: 20, bottom: 15),\n                child: Text(\n                  AppLocale.selectGroupMembers.getString(context),\n                  style: TextStyle(\n                    fontSize: 14,\n                    color: customColors.weakLinkColor,\n                  ),\n                ),\n              ),\n              Expanded(\n                child: ListView.separated(\n                  itemCount: models.length,\n                  itemBuilder: (context, i) {\n                    var item = models[i];\n                    return CheckboxListTile(\n                      controlAffinity: ListTileControlAffinity.leading,\n                      checkboxShape: const CircleBorder(),\n                      activeColor: customColors.linkColor,\n                      side: BorderSide(\n                        color: customColors.weakTextColor!.withAlpha(100),\n                      ),\n                      title: Container(\n                        alignment: Alignment.center,\n                        padding: const EdgeInsets.symmetric(vertical: 5),\n                        child: Row(\n                          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                          mainAxisSize: MainAxisSize.min,\n                          children: [\n                            _buildAvatar(avatarUrl: item.avatarUrl, size: 40),\n                            const SizedBox(width: 20),\n                            Expanded(\n                              child: Container(\n                                alignment: Alignment.centerLeft,\n                                child: Text(item.name),\n                              ),\n                            ),\n                          ],\n                        ),\n                      ),\n                      onChanged: (selected) {\n                        setState(() {\n                          if (selectedModels.contains(item)) {\n                            selectedModels.remove(item);\n                          } else {\n                            selectedModels.add(item);\n                          }\n                        });\n                      },\n                      value: selectedModels.contains(item),\n                    );\n                  },\n                  separatorBuilder: (BuildContext context, int index) {\n                    return Divider(\n                      height: 1,\n                      color: customColors.columnBlockDividerColor?.withAlpha(200),\n                    );\n                  },\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n\n  void onSave(BuildContext context) {\n    if (selectedModels.isEmpty) {\n      return;\n    }\n\n    globalLoadingCancel = BotToast.showCustomLoading(\n      toastBuilder: (cancel) {\n        return LoadingIndicator(\n          message: AppLocale.processingWait.getString(context),\n        );\n      },\n      allowClick: false,\n      duration: const Duration(seconds: 120),\n    );\n\n    try {\n      if (context.mounted) {\n        context.read<RoomBloc>().add(\n              GroupRoomCreateEvent(\n                name: selectedModels.map((e) => e.shortName).take(3).join(\"、\"),\n                members:\n                    selectedModels.map((e) => GroupMember(modelId: e.realModelId, modelName: e.shortName)).toList(),\n              ),\n            );\n      }\n    } catch (e) {\n      globalLoadingCancel?.call();\n      // ignore: use_build_context_synchronously\n      showErrorMessageEnhanced(context, e);\n    }\n  }\n\n  Widget _buildAvatar({String? avatarUrl, int? id, int size = 30}) {\n    if (avatarUrl != null && avatarUrl.startsWith('http')) {\n      return RemoteAvatar(\n        avatarUrl: imageURL(avatarUrl, qiniuImageTypeAvatar),\n        size: size,\n      );\n    }\n\n    return RandomAvatar(\n      id: id ?? 0,\n      size: size,\n      usage: Ability().isUserLogon() ? AvatarUsage.room : AvatarUsage.legacy,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/chat/group/edit.dart",
    "content": "import 'package:askaide/bloc/group_chat_bloc.dart';\nimport 'package:askaide/repo/model/group.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'dart:io';\nimport 'dart:math';\n\nimport 'package:askaide/bloc/room_bloc.dart';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/image.dart';\nimport 'package:askaide/helper/upload.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/avatar_selector.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/enhanced_input.dart';\nimport 'package:askaide/page/component/enhanced_textfield.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/multi_item_selector.dart';\nimport 'package:askaide/page/component/random_avatar.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass ModelWithMemberId {\n  final Model model;\n  final int? memberId;\n\n  ModelWithMemberId(this.model, this.memberId);\n\n  @override\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is ModelWithMemberId &&\n          runtimeType == other.runtimeType &&\n          model == other.model &&\n          memberId == other.memberId;\n\n  @override\n  int get hashCode => model.hashCode ^ memberId.hashCode;\n}\n\nclass GroupEditPage extends StatefulWidget {\n  final SettingRepository setting;\n  final int groupId;\n\n  const GroupEditPage({\n    super.key,\n    required this.groupId,\n    required this.setting,\n  });\n\n  @override\n  State<GroupEditPage> createState() => _GroupEditPageState();\n}\n\nclass _GroupEditPageState extends State<GroupEditPage> {\n  final _nameController = TextEditingController(text: '');\n\n  String? _avatarUrl;\n  List<String> avatarPresets = [];\n\n  final randomSeed = Random().nextInt(10000);\n\n  List<Model> models = [];\n  List<ModelWithMemberId> selectedModels = [];\n\n  Function? globalLoadingCancel;\n\n  @override\n  void initState() {\n    super.initState();\n\n    // 加载预定义头像\n    APIServer().avatars().then((value) {\n      avatarPresets = value;\n    });\n\n    // 加载模型\n    APIServer().models().then((value) {\n      setState(() {\n        models = value;\n      });\n      context.read<GroupChatBloc>().add(GroupChatLoadEvent(widget.groupId, forceUpdate: true));\n    });\n  }\n\n  @override\n  void dispose() {\n    _nameController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(\n          AppLocale.roomSetting.getString(context),\n          style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n        ),\n        centerTitle: true,\n        elevation: 0,\n        toolbarHeight: CustomSize.toolbarHeight,\n      ),\n      backgroundColor: customColors.backgroundColor,\n      body: BackgroundContainer(\n        setting: widget.setting,\n        enabled: false,\n        child: BlocListener<RoomBloc, RoomState>(\n          listenWhen: (previous, current) => current is GroupRoomUpdateResultState,\n          listener: (context, state) {\n            if (state is GroupRoomUpdateResultState) {\n              globalLoadingCancel?.call();\n              if (state.success) {\n                showSuccessMessage(AppLocale.operateSuccess.getString(context));\n              } else {\n                showErrorMessageEnhanced(context, state.error ?? AppLocale.operateFailed.getString(context));\n              }\n\n              context.read<GroupChatBloc>().add(GroupChatLoadEvent(widget.groupId, forceUpdate: true));\n            }\n          },\n          child: BlocConsumer<GroupChatBloc, GroupChatState>(\n            listenWhen: (previous, current) => current is GroupChatLoaded,\n            listener: (context, state) {\n              if (state is GroupChatLoaded) {\n                _nameController.text = state.group.group.name;\n                _avatarUrl = state.group.group.avatarUrl;\n                selectedModels = state.group.members\n                    .where((e) => e.status != 2)\n                    .map((e) {\n                      final mod = models.where((em) => e.modelId == em.realModelId).firstOrNull;\n                      if (mod == null) {\n                        return null;\n                      }\n\n                      return ModelWithMemberId(mod, e.id);\n                    })\n                    .where((e) => e != null)\n                    .map((e) => e!)\n                    .toList();\n\n                final selectedModelIds = selectedModels.map((e) => e.model.realModelId).toList();\n\n                models = models.where((e) => !e.disabled || selectedModelIds.contains(e.realModelId)).toList();\n              }\n            },\n            builder: (context, state) {\n              return SingleChildScrollView(\n                padding: const EdgeInsets.symmetric(horizontal: 10),\n                child: Column(\n                  crossAxisAlignment: CrossAxisAlignment.start,\n                  children: [\n                    const SizedBox(height: 10),\n                    ColumnBlock(\n                      children: [\n                        // 名称\n                        EnhancedTextField(\n                          customColors: customColors,\n                          controller: _nameController,\n                          maxLength: 50,\n                          maxLines: 1,\n                          showCounter: false,\n                          labelText: AppLocale.roomName.getString(context),\n                          labelPosition: LabelPosition.left,\n                          hintText: AppLocale.required.getString(context),\n                          textDirection: TextDirection.rtl,\n                        ),\n                        EnhancedInput(\n                          padding: const EdgeInsets.only(top: 10, bottom: 5),\n                          title: Text(\n                            AppLocale.avatar.getString(context),\n                            style: TextStyle(\n                              color: customColors.textfieldLabelColor,\n                              fontSize: 16,\n                            ),\n                          ),\n                          value: Row(\n                            mainAxisAlignment: MainAxisAlignment.end,\n                            mainAxisSize: MainAxisSize.min,\n                            children: [\n                              Container(\n                                width: 45,\n                                height: 45,\n                                decoration: BoxDecoration(\n                                  borderRadius: CustomSize.borderRadius,\n                                  image: _avatarUrl == null\n                                      ? null\n                                      : DecorationImage(\n                                          image: (_avatarUrl!.startsWith('http')\n                                              ? CachedNetworkImageProviderEnhanced(_avatarUrl!)\n                                              : FileImage(File(_avatarUrl!))) as ImageProvider,\n                                          fit: BoxFit.cover,\n                                        ),\n                                ),\n                                child: _avatarUrl == null\n                                    ? const Center(\n                                        child: Icon(\n                                          Icons.interests,\n                                          color: Colors.grey,\n                                        ),\n                                      )\n                                    : const SizedBox(),\n                              ),\n                            ],\n                          ),\n                          onPressed: () {\n                            openModalBottomSheet(\n                              context,\n                              (context) {\n                                return AvatarSelector(\n                                  onSelected: (selected) {\n                                    setState(() {\n                                      _avatarUrl = selected.url;\n                                    });\n                                    context.pop();\n                                  },\n                                  usage: AvatarUsage.room,\n                                  defaultAvatarUrl: _avatarUrl,\n                                  externalAvatarUrls: [\n                                    ...avatarPresets,\n                                  ],\n                                );\n                              },\n                              heightFactor: 0.8,\n                            );\n                          },\n                        ),\n                      ],\n                    ),\n                    ColumnBlock(\n                      children: [\n                        // 成员\n                        EnhancedInput(\n                          padding: const EdgeInsets.only(top: 10, bottom: 5),\n                          title: Text(\n                            AppLocale.members.getString(context),\n                            style: TextStyle(\n                              color: customColors.textfieldLabelColor,\n                              fontSize: 16,\n                            ),\n                          ),\n                          value: Row(\n                            mainAxisAlignment: MainAxisAlignment.end,\n                            mainAxisSize: MainAxisSize.min,\n                            children: [\n                              Stack(\n                                children: [\n                                  Container(\n                                    width: resolveSelectedModelsPreviewWidth(context),\n                                    height: 45,\n                                    decoration: BoxDecoration(borderRadius: CustomSize.borderRadius),\n                                    alignment: Alignment.center,\n                                    clipBehavior: Clip.hardEdge,\n                                    child: buildSelectedModelsPreview(),\n                                  ),\n                                  if (selectedModels.isNotEmpty)\n                                    Positioned(\n                                      right: 0,\n                                      top: 0,\n                                      child: Container(\n                                        padding: const EdgeInsets.all(3),\n                                        decoration: BoxDecoration(\n                                          color: customColors.tagsBackground,\n                                          borderRadius: CustomSize.borderRadius,\n                                        ),\n                                        child: Text(\n                                          'x${selectedModels.length}',\n                                          style: TextStyle(\n                                            fontSize: 8,\n                                            color: customColors.weakTextColor,\n                                          ),\n                                        ),\n                                      ),\n                                    )\n                                ],\n                              ),\n                            ],\n                          ),\n                          onPressed: () {\n                            openModalBottomSheet(\n                              context,\n                              (context) {\n                                return MultiItemSelector(\n                                  itemBuilder: (item) {\n                                    return Text(item.model.name);\n                                  },\n                                  items: models\n                                      .map((e) => ModelWithMemberId(\n                                          e, selectedModels.where((se) => se.model.id == e.id).firstOrNull?.memberId))\n                                      .toList(),\n                                  onChanged: (selected) {\n                                    setState(() {\n                                      selectedModels = selected;\n                                    });\n                                  },\n                                  itemAvatarBuilder: (item) {\n                                    return _buildAvatar(\n                                      avatarUrl: item.model.avatarUrl,\n                                      size: 30,\n                                    );\n                                  },\n                                  selectedItems: selectedModels,\n                                );\n                              },\n                              heightFactor: 0.8,\n                            );\n                          },\n                        ),\n                      ],\n                    ),\n                    const SizedBox(height: 10),\n                    Row(\n                      children: [\n                        Expanded(\n                          child: EnhancedButton(\n                            title: AppLocale.save.getString(context),\n                            color: canSubmit() ? null : customColors.weakTextColor,\n                            backgroundColor: canSubmit() ? null : customColors.weakTextColor!.withAlpha(20),\n                            onPressed: () async {\n                              if (!canSubmit()) {\n                                return;\n                              }\n\n                              globalLoadingCancel = BotToast.showCustomLoading(\n                                toastBuilder: (cancel) {\n                                  return LoadingIndicator(\n                                    message: AppLocale.processingWait.getString(context),\n                                  );\n                                },\n                                allowClick: false,\n                                duration: const Duration(seconds: 120),\n                              );\n\n                              final name = _nameController.text.trim();\n                              if (name == '') {\n                                globalLoadingCancel?.call();\n                                showErrorMessage('请输入群组名称');\n                                return;\n                              }\n\n                              try {\n                                if (_avatarUrl != null) {\n                                  if (!(_avatarUrl!.startsWith('http://') || _avatarUrl!.startsWith('https://'))) {\n                                    // 上传文件，获取 URL\n                                    final cancel = BotToast.showCustomLoading(\n                                      toastBuilder: (cancel) {\n                                        return LoadingIndicator(\n                                          message: AppLocale.imageUploading.getString(context),\n                                        );\n                                      },\n                                      allowClick: false,\n                                    );\n\n                                    final uploadRes = await ImageUploader(widget.setting)\n                                        .upload(_avatarUrl!, usage: 'avatar')\n                                        .whenComplete(() => cancel());\n                                    _avatarUrl = uploadRes.url;\n                                  }\n                                }\n\n                                if (context.mounted) {\n                                  context.read<RoomBloc>().add(\n                                        GroupRoomUpdateEvent(\n                                          groupId: widget.groupId,\n                                          name: name,\n                                          avatarUrl: _avatarUrl,\n                                          members: selectedModels\n                                              .map((e) => GroupMember(\n                                                  modelId: e.model.realModelId,\n                                                  modelName: e.model.shortName,\n                                                  id: e.memberId))\n                                              .toList(),\n                                        ),\n                                      );\n                                }\n                              } catch (e) {\n                                globalLoadingCancel?.call();\n                                // ignore: use_build_context_synchronously\n                                showErrorMessageEnhanced(context, e);\n                              }\n                            },\n                          ),\n                        ),\n                      ],\n                    ),\n                  ],\n                ),\n              );\n            },\n          ),\n        ),\n      ),\n    );\n  }\n\n  bool canSubmit() {\n    if (selectedModels.isEmpty) {\n      return false;\n    }\n\n    if (_nameController.text.trim() == '') {\n      return false;\n    }\n\n    return true;\n  }\n\n  Widget buildSelectedModelsPreview() {\n    if (selectedModels.isEmpty) {\n      return const Center(\n        child: Icon(\n          Icons.group,\n          color: Colors.grey,\n        ),\n      );\n    }\n\n    return Stack(\n      clipBehavior: Clip.none,\n      children: [\n        for (var i = 0; i < selectedModels.length; i++)\n          i == 0\n              ? _buildAvatar(\n                  avatarUrl: selectedModels.first.model.avatarUrl,\n                  size: 30,\n                )\n              : Positioned(\n                  left: i * 15.0,\n                  child: _buildAvatar(\n                    avatarUrl: selectedModels[i].model.avatarUrl,\n                    size: 30,\n                  ),\n                ),\n      ],\n    );\n  }\n\n  Widget _buildAvatar({String? avatarUrl, int? id, int size = 30}) {\n    if (avatarUrl != null && avatarUrl.startsWith('http')) {\n      return RemoteAvatar(\n        avatarUrl: imageURL(avatarUrl, qiniuImageTypeAvatar),\n        size: size,\n      );\n    }\n\n    return RandomAvatar(\n      id: id ?? 0,\n      size: size,\n      usage: Ability().isUserLogon() ? AvatarUsage.room : AvatarUsage.legacy,\n    );\n  }\n\n  double resolveSelectedModelsPreviewWidth(BuildContext context) {\n    final maxSize = MediaQuery.of(context).size.width - 180;\n    final expectSize = 45.0 + selectedModels.length * 15;\n\n    return expectSize > maxSize ? maxSize : expectSize;\n  }\n}\n"
  },
  {
    "path": "lib/page/chat/home.dart",
    "content": "import 'dart:io';\nimport 'dart:math';\n\nimport 'package:askaide/bloc/chat_chat_bloc.dart';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/color.dart';\nimport 'package:askaide/helper/global_store.dart';\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/helper/cache.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/chat/empty.dart';\nimport 'package:askaide/page/component/chat/file_upload.dart';\nimport 'package:askaide/page/component/chat/voice_record.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/enhanced_textfield.dart';\nimport 'package:askaide/page/component/global_alert.dart';\nimport 'package:askaide/page/component/model_indicator.dart';\nimport 'package:askaide/page/component/notify_message.dart';\nimport 'package:askaide/page/component/sliver_component.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api/model.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/model/chat_history.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:custom_sliding_segmented_control/custom_sliding_segmented_control.dart';\nimport 'package:file_picker/file_picker.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:flutter_slidable/flutter_slidable.dart';\nimport 'package:flutter_sticky_header/flutter_sticky_header.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:url_launcher/url_launcher_string.dart';\n\nclass HomePage extends StatefulWidget {\n  final SettingRepository setting;\n  const HomePage({\n    super.key,\n    required this.setting,\n  });\n\n  @override\n  State<HomePage> createState() => _HomePageState();\n}\n\nclass ChatModel {\n  String id;\n  String name;\n  Color backgroundColor;\n  String backgroundImage;\n\n  ChatModel({\n    required this.id,\n    required this.name,\n    required this.backgroundColor,\n    required this.backgroundImage,\n  });\n}\n\nclass _HomePageState extends State<HomePage> {\n  final TextEditingController _textController = TextEditingController();\n\n  ModelIndicator? currentModel;\n\n  List<HomeModelV2> models = [\n    HomeModelV2(\n      modelId: \"openai:gpt-3.5-turbo\",\n      modelName: 'Chat-3.5',\n      type: 'model',\n      id: 'openai:gpt-3.5-turbo',\n      supportVision: false,\n      name: 'Chat-3.5',\n      avatarUrl: 'https://ssl.aicode.cc/ai-server/assets/avatar/gpt35.png',\n    ),\n    HomeModelV2(\n      modelId: \"openai:gpt-4\",\n      modelName: 'Chat-4',\n      type: 'model',\n      id: 'openai:gpt-4',\n      supportVision: false,\n      name: 'Chat-4',\n      avatarUrl: 'https://ssl.aicode.cc/ai-server/assets/avatar/gpt4-preview.png',\n    ),\n  ];\n\n  /// 是否显示提示消息对话框\n  bool showFreeModelNotifyMessage = false;\n\n  List<FileUpload> selectedImageFiles = [];\n\n  /// 促销事件\n  PromotionEvent? promotionEvent;\n\n  /// Maximum height of the chat input box\n  int inputMaxLines = 6;\n\n  /// 用于监听键盘事件，实现回车发送消息，Shift+Enter换行\n  late final FocusNode _focusNode = FocusNode(\n    onKeyEvent: (node, event) {\n      if (!HardwareKeyboard.instance.isShiftPressed && event.logicalKey.keyLabel == 'Enter') {\n        if (event is KeyDownEvent) {\n          onSubmit(context, _textController.text.trim());\n        }\n\n        return KeyEventResult.handled;\n      } else {\n        return KeyEventResult.ignored;\n      }\n    },\n  );\n\n  @override\n  void dispose() {\n    _textController.dispose();\n    super.dispose();\n  }\n\n  @override\n  void initState() {\n    context.read<ChatChatBloc>().add(ChatChatLoadRecentHistories());\n\n    if (Ability().homeModels.isNotEmpty) {\n      models = Ability().homeModels;\n    }\n\n    APIServer().capabilities().then((cap) {\n      Ability().updateCapabilities(cap);\n\n      if (cap.homeModels.isNotEmpty) {\n        models = cap.homeModels;\n\n        if (mounted) {\n          setState(() {});\n        }\n      }\n    });\n\n    // 是否显示免费模型提示消息\n    Cache().boolGet(key: 'show_home_free_model_message').then((show) async {\n      if (show) {\n        final promotions = await APIServer().notificationPromotionEvents();\n        if (promotions['chat_page'] == null || promotions['chat_page']!.isEmpty) {\n          return;\n        }\n\n        // 多个促销事件，则随机选择一个\n        promotionEvent = promotions['chat_page']![Random().nextInt(promotions['chat_page']!.length)];\n      }\n\n      setState(() {\n        showFreeModelNotifyMessage = show;\n      });\n    });\n\n    _textController.addListener(() {\n      setState(() {});\n    });\n\n    setState(() {\n      currentModel = ModelIndicator(\n        model: models[0],\n        iconAndColor: iconAndColors[0],\n      );\n    });\n\n    super.initState();\n  }\n\n  Map<String, ModelIndicator> buildModelIndicators() {\n    Map<String, ModelIndicator> map = {};\n\n    for (var i = 0; i < models.length; i++) {\n      var model = models[i];\n      map[model.id] = ModelIndicator(\n        model: model,\n        selected: model.id == currentModel?.model.id,\n        iconAndColor: iconAndColors[i],\n        itemCount: models.length,\n      );\n    }\n\n    return map;\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n    return WindowFrameWidget(\n      child: BackgroundContainer(\n        setting: widget.setting,\n        child: Scaffold(\n          backgroundColor: Colors.transparent,\n          body: BlocBuilder<ChatChatBloc, ChatChatState>(\n            buildWhen: (previous, current) => current is ChatChatRecentHistoriesLoaded,\n            builder: (context, state) {\n              if (state is ChatChatRecentHistoriesLoaded) {\n                return SliverSingleComponent(\n                  title: Text(\n                    AppLocale.chatAnywhere.getString(context),\n                    style: TextStyle(\n                      fontSize: CustomSize.appBarTitleSize,\n                      color: customColors.backgroundInvertedColor,\n                    ),\n                  ),\n                  actions: [\n                    IconButton(\n                      icon: const Icon(Icons.history),\n                      onPressed: () {\n                        context.push('/chat-chat/history').whenComplete(() {\n                          context.read<ChatChatBloc>().add(ChatChatLoadRecentHistories());\n                        });\n                      },\n                    ),\n                  ],\n                  appBarExtraWidgets: () {\n                    return [\n                      SliverStickyHeader(\n                        header: SafeArea(\n                          top: false,\n                          child: buildChatComponents(customColors, context, state),\n                        ),\n                        sliver: SliverList(\n                          delegate: SliverChildBuilderDelegate(\n                            (context, index) {\n                              if (index == 0) {\n                                return SafeArea(\n                                  top: false,\n                                  bottom: false,\n                                  child: Container(\n                                    margin: const EdgeInsets.only(top: 10, left: 15),\n                                    child: Text(\n                                      AppLocale.histories.getString(context),\n                                      style: TextStyle(\n                                        color: customColors.weakTextColor?.withAlpha(100),\n                                        fontSize: 13,\n                                      ),\n                                    ),\n                                  ),\n                                );\n                              }\n\n                              if (index == state.histories.length && index > 3) {\n                                return SafeArea(\n                                  top: false,\n                                  bottom: false,\n                                  child: GestureDetector(\n                                    onTap: () {\n                                      context.push('/chat-chat/history').whenComplete(() {\n                                        context.read<ChatChatBloc>().add(ChatChatLoadRecentHistories());\n                                      });\n                                    },\n                                    child: Container(\n                                      alignment: Alignment.center,\n                                      margin: const EdgeInsets.only(top: 5, bottom: 15),\n                                      child: Row(\n                                        mainAxisAlignment: MainAxisAlignment.center,\n                                        children: [\n                                          Icon(\n                                            Icons.keyboard_double_arrow_left,\n                                            size: 12,\n                                            color: customColors.weakTextColor!.withAlpha(120),\n                                          ),\n                                          Text(\n                                            \"查看更多\",\n                                            style: TextStyle(\n                                              fontSize: 12,\n                                              color: customColors.weakTextColor!.withAlpha(120),\n                                            ),\n                                          ),\n                                          Icon(\n                                            Icons.keyboard_double_arrow_right,\n                                            size: 12,\n                                            color: customColors.weakTextColor!.withAlpha(120),\n                                          ),\n                                        ],\n                                      ),\n                                    ),\n                                  ),\n                                );\n                              }\n\n                              return SafeArea(\n                                top: false,\n                                bottom: false,\n                                child: ChatHistoryItem(\n                                  history: state.histories[index - 1],\n                                  customColors: customColors,\n                                  onTap: () {\n                                    context\n                                        .push(\n                                            '/chat-anywhere?chat_id=${state.histories[index - 1].id}&model=${state.histories[index - 1].model}&title=${state.histories[index - 1].title}')\n                                        .whenComplete(() {\n                                      FocusScope.of(context).requestFocus(FocusNode());\n                                      context.read<ChatChatBloc>().add(ChatChatLoadRecentHistories());\n                                    });\n                                  },\n                                ),\n                              );\n                            },\n                            childCount: state.histories.isNotEmpty ? state.histories.length + 1 : 0,\n                          ),\n                        ),\n                      ),\n                    ];\n                  },\n                );\n              } else {\n                return const SizedBox();\n              }\n            },\n          ),\n        ),\n      ),\n    );\n  }\n\n  Container buildChatComponents(\n    CustomColors customColors,\n    BuildContext context,\n    ChatChatRecentHistoriesLoaded state,\n  ) {\n    final indicators = buildModelIndicators();\n    return Container(\n      color: customColors.backgroundContainerColor,\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: [\n          if (Ability().showGlobalAlert) const GlobalAlert(pageKey: 'home'),\n          if (showFreeModelNotifyMessage && promotionEvent != null)\n            // 首页通知消息组件\n            buildNotifyMessageWidget(context),\n          // 模型选择\n          Container(\n            margin: const EdgeInsets.only(\n              left: 10,\n              right: 10,\n            ),\n            padding: const EdgeInsets.only(\n              left: 5,\n              right: 5,\n              top: 10,\n            ),\n            child: CustomSlidingSegmentedControl<String>(\n              children: indicators,\n              padding: 0,\n              isStretch: true,\n              height: Ability().showHomeModelDescription ? 60 : 45,\n              innerPadding: const EdgeInsets.all(0),\n              decoration: BoxDecoration(\n                color: customColors.columnBlockBackgroundColor?.withAlpha(150),\n                borderRadius: CustomSize.borderRadius,\n              ),\n              thumbDecoration: BoxDecoration(\n                color: currentModel?.iconAndColor.color,\n                borderRadius: CustomSize.borderRadius,\n              ),\n              duration: const Duration(milliseconds: 300),\n              curve: Curves.easeInToLinear,\n              onValueChanged: (value) {\n                setState(() {\n                  currentModel = indicators[value];\n                });\n              },\n            ),\n          ),\n          // 聊天内容输入框\n          Padding(\n            padding: const EdgeInsets.only(\n              left: 10,\n              right: 10,\n              top: 10,\n            ),\n            child: ColumnBlock(\n              padding: const EdgeInsets.only(\n                top: 5,\n                bottom: 5,\n                left: 15,\n                right: 15,\n              ),\n              children: [\n                Column(\n                  crossAxisAlignment: CrossAxisAlignment.start,\n                  children: [\n                    // 聊天问题输入框\n                    Row(\n                      crossAxisAlignment: CrossAxisAlignment.start,\n                      children: [\n                        if (currentModel != null)\n                          Padding(\n                            padding: const EdgeInsets.only(\n                              top: 12,\n                              right: 4,\n                            ),\n                            child: Icon(\n                              Icons.circle,\n                              color: currentModel?.iconAndColor.color,\n                              size: 10,\n                            ),\n                          ),\n                        Expanded(\n                          child: EnhancedTextField(\n                            onFocusChange: (hasFocus) {\n                              if (hasFocus) {\n                                setState(() {\n                                  inputMaxLines = 15;\n                                });\n                              } else {\n                                setState(() {\n                                  inputMaxLines = 6;\n                                });\n                              }\n                            },\n                            focusNode: _focusNode,\n                            controller: _textController,\n                            customColors: customColors,\n                            maxLines: inputMaxLines,\n                            minLines: 6,\n                            hintText: AppLocale.askMeAnyQuestion.getString(context),\n                            maxLength: 150000,\n                            showCounter: false,\n                            hintColor: customColors.textfieldHintDeepColor,\n                            hintTextSize: 15,\n                          ),\n                        ),\n                      ],\n                    ),\n                    // 聊天控制工具栏\n                    Container(\n                      padding: const EdgeInsets.only(bottom: 5),\n                      child: _buildSendOrVoiceButton(\n                        context,\n                        customColors,\n                      ),\n                    ),\n                    if (selectedImageFiles.isNotEmpty && currentModel != null && currentModel!.model.supportVision)\n                      SizedBox(\n                        height: 110,\n                        child: ListView(\n                          scrollDirection: Axis.horizontal,\n                          children: selectedImageFiles\n                              .map(\n                                (e) => Container(\n                                  margin: const EdgeInsets.only(right: 8),\n                                  padding: const EdgeInsets.all(5),\n                                  child: Stack(\n                                    children: [\n                                      ClipRRect(\n                                        borderRadius: CustomSize.borderRadius,\n                                        child: e.file.bytes != null\n                                            ? Image.memory(\n                                                e.file.bytes!,\n                                                fit: BoxFit.cover,\n                                                width: 100,\n                                                height: 100,\n                                              )\n                                            : Image.file(\n                                                File(e.file.path!),\n                                                fit: BoxFit.cover,\n                                                width: 100,\n                                                height: 100,\n                                              ),\n                                      ),\n                                      Positioned(\n                                        right: 5,\n                                        top: 5,\n                                        child: InkWell(\n                                          onTap: () {\n                                            setState(() {\n                                              selectedImageFiles.remove(e);\n                                            });\n                                          },\n                                          child: Container(\n                                            padding: const EdgeInsets.all(3),\n                                            decoration: BoxDecoration(\n                                              borderRadius: CustomSize.borderRadius,\n                                              color: customColors.chatRoomBackground,\n                                            ),\n                                            child: Icon(\n                                              Icons.close,\n                                              size: 10,\n                                              color: customColors.weakTextColor,\n                                            ),\n                                          ),\n                                        ),\n                                      ),\n                                    ],\n                                  ),\n                                ),\n                              )\n                              .toList(),\n                        ),\n                      )\n                  ],\n                )\n              ],\n            ),\n          ),\n          // 问题示例\n          if (state.examples != null && state.examples!.isNotEmpty && state.histories.isEmpty)\n            Container(\n              decoration: BoxDecoration(borderRadius: CustomSize.borderRadius),\n              padding: const EdgeInsets.only(top: 5, left: 10, right: 10, bottom: 3),\n              margin: const EdgeInsets.all(10),\n              height: 260,\n              child: Column(\n                mainAxisSize: MainAxisSize.min,\n                crossAxisAlignment: CrossAxisAlignment.start,\n                children: [\n                  Row(\n                    children: [\n                      Image.asset(\n                        'assets/app-256-transparent.png',\n                        width: 20,\n                        height: 20,\n                      ),\n                      const SizedBox(width: 5),\n                      Text(\n                        AppLocale.askMeLikeThis.getString(context),\n                        style: TextStyle(\n                          fontSize: 16,\n                          fontWeight: FontWeight.bold,\n                          color: customColors.textfieldHintDeepColor,\n                        ),\n                      ),\n                    ],\n                  ),\n                  const SizedBox(height: 20),\n                  Expanded(\n                    child: ListView.separated(\n                      padding: const EdgeInsets.all(0),\n                      itemCount: state.examples!.length > 4 ? 4 : state.examples!.length,\n                      physics: const NeverScrollableScrollPhysics(),\n                      itemBuilder: (context, index) {\n                        return ListTextItem(\n                          title: state.examples![index].title,\n                          onTap: () {\n                            onSubmit(\n                              context,\n                              state.examples![index].text,\n                            );\n                          },\n                          customColors: customColors,\n                        );\n                      },\n                      separatorBuilder: (BuildContext context, int index) {\n                        return Divider(\n                          color: customColors.chatExampleItemText?.withAlpha(20),\n                        );\n                      },\n                    ),\n                  ),\n                  Align(\n                    alignment: Alignment.centerRight,\n                    child: TextButton(\n                      style: ButtonStyle(\n                        overlayColor: WidgetStateProperty.all(Colors.transparent),\n                      ),\n                      onPressed: () {\n                        setState(() {\n                          state.examples!.shuffle();\n                        });\n                      },\n                      child: Row(\n                        mainAxisAlignment: MainAxisAlignment.end,\n                        children: [\n                          Icon(\n                            Icons.refresh,\n                            color: customColors.weakTextColor,\n                            size: 16,\n                          ),\n                          const SizedBox(width: 3),\n                          Text(\n                            AppLocale.refresh.getString(context),\n                            style: TextStyle(\n                              color: customColors.weakTextColor,\n                            ),\n                            textScaler: const TextScaler.linear(0.9),\n                          ),\n                        ],\n                      ),\n                    ),\n                  )\n                ],\n              ),\n            ),\n        ],\n      ),\n    );\n  }\n\n  NotifyMessageWidget buildNotifyMessageWidget(BuildContext context) {\n    return NotifyMessageWidget(\n      title: promotionEvent!.title != null\n          ? Text(\n              promotionEvent!.title!,\n              style: TextStyle(\n                color: stringToColor(promotionEvent!.textColor ?? 'FFFFFFFF'),\n                fontWeight: FontWeight.bold,\n              ),\n            )\n          : null,\n      backgroundImageUrl: promotionEvent!.backgroundImage,\n      height: 85,\n      closeable: promotionEvent!.closeable,\n      onClose: () {\n        setState(() {\n          showFreeModelNotifyMessage = false;\n        });\n\n        Cache().setBool(\n          key: 'show_home_free_model_message',\n          value: false,\n          duration: Duration(days: promotionEvent!.maxCloseDurationInDays ?? 7),\n        );\n      },\n      child: Row(\n        mainAxisAlignment: MainAxisAlignment.spaceBetween,\n        children: [\n          Expanded(\n            child: Text(\n              promotionEvent!.content,\n              style: TextStyle(\n                color: stringToColor(promotionEvent!.textColor ?? 'FFFFFFFF'),\n                fontSize: 14,\n                overflow: TextOverflow.ellipsis,\n              ),\n              maxLines: 2,\n            ),\n          ),\n          if (promotionEvent!.clickButtonType != PromotionEventClickButtonType.none &&\n              promotionEvent!.clickValue != null &&\n              promotionEvent!.clickValue!.isNotEmpty)\n            InkWell(\n              onTap: () {\n                switch (promotionEvent!.clickButtonType) {\n                  case PromotionEventClickButtonType.url:\n                    if (promotionEvent!.clickValue != null && promotionEvent!.clickValue!.isNotEmpty) {\n                      launchUrlString(promotionEvent!.clickValue!, mode: LaunchMode.externalApplication);\n                    }\n                    break;\n                  case PromotionEventClickButtonType.inAppRoute:\n                    if (promotionEvent!.clickValue != null && promotionEvent!.clickValue!.isNotEmpty) {\n                      context.push(promotionEvent!.clickValue!);\n                    }\n\n                    break;\n                  case PromotionEventClickButtonType.none:\n                }\n              },\n              child: Row(\n                children: [\n                  Text(\n                    '详情',\n                    style: TextStyle(\n                      color: stringToColor(promotionEvent!.clickButtonColor ?? 'FFFFFFFF'),\n                      fontSize: 14,\n                    ),\n                  ),\n                  const SizedBox(width: 5),\n                  Icon(\n                    Icons.keyboard_double_arrow_right,\n                    size: 16,\n                    color: stringToColor(promotionEvent!.clickButtonColor ?? 'FFFFFFFF'),\n                  ),\n                ],\n              ),\n            ),\n        ],\n      ),\n    );\n  }\n\n  /// 构建发送或者语音按钮\n  Widget _buildSendOrVoiceButton(\n    BuildContext context,\n    CustomColors customColors,\n  ) {\n    return Row(\n      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n      crossAxisAlignment: CrossAxisAlignment.center,\n      children: [\n        Row(\n          children: [\n            InkWell(\n              onTap: () {\n                HapticFeedbackHelper.mediumImpact();\n\n                openModalBottomSheet(\n                  context,\n                  (context) {\n                    return VoiceRecord(\n                      onFinished: (text) {\n                        _textController.text = _textController.text + text;\n                        Navigator.pop(context);\n                      },\n                      onStart: () {},\n                    );\n                  },\n                  isScrollControlled: false,\n                  heightFactor: 0.8,\n                );\n              },\n              child: Icon(\n                Icons.mic,\n                color: customColors.chatInputPanelText,\n                size: 28,\n              ),\n            ),\n            const SizedBox(width: 10),\n            if (currentModel != null && currentModel!.model.supportVision)\n              InkWell(\n                onTap: () async {\n                  // 上传图片\n                  HapticFeedbackHelper.mediumImpact();\n                  if (selectedImageFiles.length >= 4) {\n                    showSuccessMessage(AppLocale.uploadImageLimit4.getString(context));\n                    return;\n                  }\n\n                  FilePickerResult? result = await FilePicker.platform.pickFiles(\n                    type: FileType.image,\n                    allowMultiple: true,\n                  );\n                  if (result != null && result.files.isNotEmpty) {\n                    final files = selectedImageFiles;\n                    files.addAll(result.files.map((e) => FileUpload(file: e)).toList());\n                    setState(() {\n                      selectedImageFiles = files.sublist(0, files.length > 4 ? 4 : files.length);\n                    });\n                  }\n                },\n                child: Icon(\n                  Icons.camera_alt,\n                  color: customColors.chatInputPanelText,\n                  size: 28,\n                ),\n              ),\n          ],\n        ),\n        const SizedBox(),\n        InkWell(\n          onTap: () {\n            onSubmit(context, _textController.text.trim());\n          },\n          child: Icon(\n            Icons.send,\n            color: _textController.text.trim().isNotEmpty\n                ? customColors.linkColor ?? const Color.fromARGB(255, 70, 165, 73)\n                : customColors.chatInputPanelText,\n            size: 26,\n          ),\n        )\n      ],\n    );\n  }\n\n  void onSubmit(BuildContext context, String text) {\n    if (text.trim().isEmpty) {\n      return;\n    }\n\n    if (currentModel != null && currentModel!.model.supportVision) {\n      GlobalStore().uploadedFiles = selectedImageFiles;\n    }\n\n    selectedImageFiles = [];\n\n    context\n        .push(Uri(path: '/chat-anywhere', queryParameters: {\n      'init_message': text,\n      'model': 'v2@${currentModel?.model.uniqueKey}',\n    }).toString())\n        .whenComplete(() {\n      _textController.clear();\n      GlobalStore().uploadedFiles.clear();\n\n      FocusScope.of(context).requestFocus(FocusNode());\n      context.read<ChatChatBloc>().add(ChatChatLoadRecentHistories());\n    });\n  }\n}\n\nclass ChatHistoryItem extends StatelessWidget {\n  const ChatHistoryItem({\n    super.key,\n    required this.history,\n    required this.customColors,\n    required this.onTap,\n  });\n\n  final ChatHistory history;\n  final CustomColors customColors;\n  final VoidCallback onTap;\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      margin: const EdgeInsets.symmetric(\n        horizontal: 15,\n        vertical: 5,\n      ),\n      decoration: BoxDecoration(borderRadius: CustomSize.borderRadius),\n      child: Slidable(\n        endActionPane: ActionPane(\n          motion: const ScrollMotion(),\n          children: [\n            const SizedBox(width: 10),\n            SlidableAction(\n              label: AppLocale.delete.getString(context),\n              borderRadius: CustomSize.borderRadiusAll,\n              backgroundColor: Colors.red,\n              icon: Icons.delete,\n              onPressed: (_) {\n                openConfirmDialog(\n                  context,\n                  AppLocale.confirmDelete.getString(context),\n                  () {\n                    context.read<ChatChatBloc>().add(ChatChatDeleteHistory(history.id!));\n                  },\n                  danger: true,\n                );\n              },\n            ),\n          ],\n        ),\n        child: Container(\n          decoration: BoxDecoration(\n            borderRadius: CustomSize.borderRadius,\n            color: customColors.listTileBackgroundColor,\n          ),\n          child: ListTile(\n            contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),\n            shape: RoundedRectangleBorder(borderRadius: CustomSize.borderRadius),\n            title: Row(\n              mainAxisAlignment: MainAxisAlignment.spaceBetween,\n              children: [\n                Expanded(\n                  child: Text(\n                    (history.title ?? '未命名').trim(),\n                    overflow: TextOverflow.ellipsis,\n                    style: const TextStyle(fontSize: 15),\n                    maxLines: 1,\n                  ),\n                ),\n                // Text(\n                //   humanTime(history.updatedAt),\n                //   style: TextStyle(\n                //     color: customColors.weakTextColor,\n                //     fontSize: 12,\n                //   ),\n                // ),\n              ],\n            ),\n            dense: true,\n            subtitle: Padding(\n              padding: const EdgeInsets.only(top: 5),\n              child: Text(\n                (history.lastMessage ?? '暂无内容').trim().replaceAll(\"\\n\", \" \"),\n                maxLines: 1,\n                overflow: TextOverflow.ellipsis,\n                style: TextStyle(\n                  color: customColors.weakTextColor?.withAlpha(150),\n                  fontSize: 12,\n                  overflow: TextOverflow.ellipsis,\n                ),\n              ),\n            ),\n            onTap: () {\n              HapticFeedbackHelper.lightImpact();\n              onTap();\n            },\n            tileColor: Colors.transparent,\n            hoverColor: Colors.transparent,\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/chat/home_chat.dart",
    "content": "import 'package:askaide/bloc/chat_message_bloc.dart';\nimport 'package:askaide/bloc/notify_bloc.dart';\nimport 'package:askaide/bloc/room_bloc.dart';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/cache.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/global_store.dart';\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/helper/model.dart';\nimport 'package:askaide/helper/upload.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/chat/component/model_switcher.dart';\nimport 'package:askaide/page/chat/component/stop_button.dart';\nimport 'package:askaide/page/chat/character_chat.dart';\nimport 'package:askaide/page/component/audio_player.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/chat/chat_input.dart';\nimport 'package:askaide/page/component/chat/chat_input_button.dart';\nimport 'package:askaide/page/component/chat/chat_preview.dart';\nimport 'package:askaide/page/component/chat/empty.dart';\nimport 'package:askaide/page/component/chat/file_upload.dart';\nimport 'package:askaide/page/component/chat/help_tips.dart';\nimport 'package:askaide/page/component/chat/message_state_manager.dart';\nimport 'package:askaide/page/component/chat/role_avatar.dart';\nimport 'package:askaide/page/component/enhanced_error.dart';\nimport 'package:askaide/page/component/global_alert.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/select_mode_toolbar.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api/model.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/model/message.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:askaide/repo/model/model.dart' as mm;\n\nclass HomeChatPage extends StatefulWidget {\n  /// 聊天内容窗口状态管理器\n  final MessageStateManager stateManager;\n\n  /// 设置仓库\n  final SettingRepository setting;\n\n  /// 当前聊天 ID\n  final int? chatId;\n\n  /// 初始消息，该消息会在进入页面时自动发送到服务端\n  final String? initialMessage;\n\n  /// 当前聊天所使用的模型，对于\n  /// - v1 版本，该值为模型 ID\n  /// - v2 版本，该值为 homeModel 的 ID，格式为 v2@type|id\n  final String? model;\n\n  /// 当前页面的标题\n  final String? title;\n\n  const HomeChatPage({\n    super.key,\n    required this.stateManager,\n    required this.setting,\n    this.chatId,\n    this.initialMessage,\n    this.model,\n    this.title,\n  });\n\n  @override\n  State<HomeChatPage> createState() => _HomeChatPageState();\n}\n\nclass _HomeChatPageState extends State<HomeChatPage> {\n  // 聊天内容界面控制器\n  final ChatPreviewController chatPreviewController = ChatPreviewController();\n  // 聊天内容滚动控制器\n  final ScrollController scrollController = ScrollController();\n  // 输入框是否可编辑\n  final ValueNotifier<bool> enableInput = ValueNotifier(true);\n  // 音频播放器控制器\n  final AudioPlayerController audioPlayerController = AudioPlayerController(useRemoteAPI: true);\n\n  // 聊天室 ID，当没有值时，会在第一个聊天消息发送后自动设置新值\n  int? chatId;\n  // 当前选择的图片文件\n  List<FileUpload> selectedImageFiles = [];\n\n  // 是否显示音频播放器\n  bool showAudioPlayer = false;\n  // 是否显示音频播放器加载中\n  bool audioLoadding = false;\n\n  // 全量模型列表（v1 使用）\n  List<mm.Model> supportModels = [];\n  // 全量模型列表（v2 使用）\n  List<HomeModelV2> supportModelsV2 = [];\n  // 当前聊天所使用的模型（v2）\n  HomeModelV2? currentModelV2;\n\n  /// 当前选择的模型\n  mm.Model? selectedModel;\n\n  /// 是否启用搜索\n  bool enableSearch = false;\n\n  /// 是否启用推理\n  bool enableReasoning = false;\n\n  @override\n  void initState() {\n    // 设置当前聊天 ID，当没有值时，会在第一个聊天消息发送后自动设置新值\n    chatId = widget.chatId;\n    // 加载当前聊天室信息\n    context.read<RoomBloc>().add(RoomLoadEvent(\n          chatAnywhereRoomId,\n          chatHistoryId: chatId,\n          cascading: true,\n        ));\n    // 查询最近聊天记录\n    context.read<ChatMessageBloc>().add(ChatMessageGetRecentEvent(chatHistoryId: widget.chatId));\n\n    chatPreviewController.addListener(() {\n      setState(() {});\n    });\n\n    audioPlayerController.onPlayStopped = () {\n      setState(() {\n        showAudioPlayer = false;\n      });\n    };\n    audioPlayerController.onPlayAudioStarted = () {\n      setState(() {\n        showAudioPlayer = true;\n      });\n    };\n    audioPlayerController.onPlayAudioLoading = (loading) {\n      setState(() {\n        audioLoadding = loading;\n      });\n    };\n\n    // 加载模型列表，用于查询模型名称\n    ModelAggregate.models().then((value) {\n      setState(() {\n        supportModels = value;\n      });\n\n      if (widget.model != null) {\n        selectedModel = supportModels.where((e) => e.id == widget.model).firstOrNull;\n      }\n\n      if (selectedModel == null) {\n        Cache().stringGet(key: 'last_selected_model').then((value) {\n          final selected = supportModels.where((e) => e.id == value).firstOrNull;\n          if (selected != null) {\n            setState(() {\n              selectedModel = selected;\n            });\n          }\n        });\n      }\n    });\n\n    if (widget.model != null) {\n      loadCurrentModel(widget.model!);\n    }\n\n    // 当参数 initialMessage 不为空时，延迟 500 毫秒后发送初始消息\n    if (widget.initialMessage != null) {\n      WidgetsBinding.instance.addPostFrameCallback((_) {\n        Future.delayed(const Duration(milliseconds: 500), () {\n          selectedImageFiles = GlobalStore().uploadedFiles;\n          handleSubmit(widget.initialMessage!);\n        });\n      });\n    }\n\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    scrollController.dispose();\n    chatPreviewController.dispose();\n    audioPlayerController.dispose();\n    super.dispose();\n  }\n\n  Future<void> loadCurrentModel(String model) async {\n    if (!model.startsWith('v2@') || currentModelV2 != null) {\n      return;\n    }\n\n    currentModelV2 = await APIServer().customHomeModelsItemV2(\n      uniqueKey: model.split('v2@').last,\n    );\n\n    setState(() {});\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return WindowFrameWidget(\n      child: Scaffold(\n        // AppBar\n        appBar: buildAppBar(context, customColors),\n        backgroundColor: customColors.backgroundContainerColor,\n        // 聊天内容窗口\n        body: BackgroundContainer(\n          setting: widget.setting,\n          // maxWidth: double.infinity,\n          child: BlocConsumer<RoomBloc, RoomState>(\n            listenWhen: (previous, current) => current is RoomLoaded,\n            listener: (context, state) async {\n              if (state is RoomLoaded && currentModelV2 == null) {\n                await loadCurrentModel(state.room.model);\n              }\n            },\n            buildWhen: (previous, current) => current is RoomLoaded,\n            builder: (context, room) {\n              // 加载聊天室\n              if (room is RoomLoaded) {\n                if (room.error != null) {\n                  return EnhancedErrorWidget(error: room.error);\n                }\n\n                return buildChatComponents(\n                  customColors,\n                  context,\n                  room,\n                );\n              } else {\n                return Container();\n              }\n            },\n          ),\n        ),\n      ),\n    );\n  }\n\n  /// 构建 AppBar\n  AppBar buildAppBar(BuildContext context, CustomColors customColors) {\n    if (chatPreviewController.selectMode) {\n      return AppBar(\n        title: Text(AppLocale.select.getString(context)),\n        backgroundColor: Colors.transparent,\n        centerTitle: true,\n        leadingWidth: 80,\n        leading: TextButton(\n          onPressed: () {\n            chatPreviewController.exitSelectMode();\n          },\n          child: Text(\n            AppLocale.cancel.getString(context),\n            style: TextStyle(color: customColors.linkColor),\n          ),\n        ),\n      );\n    }\n\n    return AppBar(\n      centerTitle: true,\n      elevation: 0,\n      toolbarHeight: CustomSize.toolbarHeight,\n      title: BlocBuilder<ChatMessageBloc, ChatMessageState>(\n        buildWhen: (previous, current) => current is ChatMessagesLoaded,\n        builder: (context, state) {\n          if (state is ChatMessagesLoaded) {\n            return GestureDetector(\n              onTap: () {\n                ModelSwitcher.openActionDialog(\n                  context: context,\n                  onSelected: (selected) {\n                    setState(() {\n                      selectedModel = selected;\n                    });\n                  },\n                  initValue: selectedModel,\n                );\n              },\n              child: Text(\n                widget.title ?? AppLocale.chatAnywhere.getString(context),\n                overflow: TextOverflow.ellipsis,\n                maxLines: 1,\n                style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n              ),\n            );\n          }\n\n          return const SizedBox();\n        },\n      ),\n    );\n  }\n\n  /// 构建聊天室窗口\n  Widget buildChatComponents(\n    CustomColors customColors,\n    BuildContext context,\n    RoomLoaded room,\n  ) {\n    return Column(\n      children: [\n        if (Ability().showGlobalAlert) const GlobalAlert(pageKey: 'chat'),\n        if (showAudioPlayer)\n          EnhancedAudioPlayer(\n            controller: audioPlayerController,\n            loading: audioLoadding,\n          ),\n        // 聊天内容窗口\n        Expanded(\n          child: Stack(\n            fit: StackFit.expand,\n            children: [\n              BlocConsumer<ChatMessageBloc, ChatMessageState>(\n                listener: (context, state) {\n                  if (state is ChatHistoryInited) {\n                    setState(() {\n                      chatId = state.chatId;\n                    });\n                  }\n\n                  if (state is ChatMessagesLoaded && state.error == null) {\n                    setState(() {\n                      selectedImageFiles = [];\n                    });\n                  }\n                  // 显示错误提示\n                  else if (state is ChatMessagesLoaded && state.error != null) {\n                    showErrorMessageEnhanced(context, state.error);\n                  } else if (state is ChatMessageUpdated) {\n                    // 聊天内容窗口滚动到底部\n                    if (!state.processing && scrollController.hasClients) {\n                      scrollController.animateTo(\n                        0,\n                        duration: const Duration(milliseconds: 500),\n                        curve: Curves.easeOut,\n                      );\n                    }\n\n                    if (state.processing && enableInput.value) {\n                      // 聊天回复中时，禁止输入框编辑\n                      setState(() {\n                        enableInput.value = false;\n                      });\n                    } else if (!state.processing && !enableInput.value) {\n                      // 聊天回复完成时，取消输入框的禁止编辑状态\n                      setState(() {\n                        enableInput.value = true;\n                      });\n                    }\n                  }\n                },\n                buildWhen: (prv, cur) => cur is ChatMessagesLoaded,\n                builder: (context, state) {\n                  if (state is ChatMessagesLoaded) {\n                    return buildChatPreviewArea(\n                      state,\n                      room.examples ?? [],\n                      room,\n                      customColors,\n                      chatPreviewController.selectMode,\n                    );\n                  }\n                  return const Center(child: CircularProgressIndicator());\n                },\n              ),\n              if (!enableInput.value)\n                Positioned(\n                  bottom: 10,\n                  width: CustomSize.adaptiveMaxWindowWidth(context),\n                  child: Center(\n                    child: StopButton(\n                      label: AppLocale.stopOutput.getString(context),\n                      onPressed: () {\n                        HapticFeedbackHelper.mediumImpact();\n                        context.read<ChatMessageBloc>().add(ChatMessageStopEvent());\n                      },\n                    ),\n                  ),\n                ),\n            ],\n          ),\n        ),\n\n        // 聊天输入窗口\n        if (!chatPreviewController.selectMode)\n          Container(\n            decoration: BoxDecoration(\n              borderRadius: const BorderRadius.only(topLeft: CustomSize.radius, topRight: CustomSize.radius),\n              color: customColors.chatInputPanelBackground,\n            ),\n            child: BlocBuilder<ChatMessageBloc, ChatMessageState>(\n              buildWhen: (previous, current) => current is ChatMessagesLoaded,\n              builder: (context, state) {\n                var enableImageUpload = false;\n                var showSearch = false;\n                var showReasoning = false;\n                if (state is ChatMessagesLoaded) {\n                  if (currentModelV2 != null) {\n                    enableImageUpload = currentModelV2?.supportVision ?? false;\n                    showSearch = currentModelV2?.supportSearch ?? false;\n                    showReasoning = currentModelV2?.supportReasoning ?? false;\n                  } else {\n                    var model = state.chatHistory?.model ?? room.room.model;\n                    final cur = supportModels.where((e) => e.id == model).firstOrNull;\n                    enableImageUpload = cur?.supportVision ?? false;\n                    showSearch = cur?.supportSearch ?? false;\n                    showReasoning = cur?.supportReasoning ?? false;\n                  }\n                }\n\n                return ChatInput(\n                  enableNotifier: enableInput,\n                  onSubmit: (value) {\n                    handleSubmit(value);\n                    FocusManager.instance.primaryFocus?.unfocus();\n                  },\n                  enableImageUpload:\n                      selectedModel == null ? enableImageUpload : (selectedModel?.supportVision ?? false),\n                  onImageSelected: (files) {\n                    setState(() {\n                      selectedImageFiles = files;\n                    });\n                  },\n                  selectedImageFiles: enableImageUpload ? selectedImageFiles : [],\n                  hintText: AppLocale.askMeAnyQuestion.getString(context),\n                  onVoiceRecordTappedEvent: () {\n                    audioPlayerController.stop();\n                  },\n                  onStopGenerate: () {\n                    context.read<ChatMessageBloc>().add(ChatMessageStopEvent());\n                  },\n                  toolsBuilder: () {\n                    return [\n                      if (showReasoning)\n                        ChatInputButton(\n                          text: AppLocale.reasoning.getString(context),\n                          icon: Icons.tips_and_updates_outlined,\n                          onPressed: () {\n                            setState(() {\n                              enableReasoning = !enableReasoning;\n                            });\n                          },\n                          isActive: enableReasoning,\n                        ),\n                      if (showSearch)\n                        ChatInputButton(\n                          text: AppLocale.onlineSearch.getString(context),\n                          icon: Icons.language_outlined,\n                          onPressed: () {\n                            setState(() {\n                              enableSearch = !enableSearch;\n                            });\n                          },\n                          isActive: enableSearch,\n                        ),\n                    ];\n                  },\n                );\n              },\n            ),\n          ),\n\n        // 选择模式工具栏\n        if (chatPreviewController.selectMode) SelectModeToolbar(chatPreviewController: chatPreviewController),\n      ],\n    );\n  }\n\n  /// 构建聊天内容窗口\n  Widget buildChatPreviewArea(\n    ChatMessagesLoaded loadedState,\n    List<ChatExample> examples,\n    RoomLoaded room,\n    CustomColors customColors,\n    bool selectMode,\n  ) {\n    final loadedMessages = loadedState.messages as List<Message>;\n    if (room.room.initMessage != null && room.room.initMessage != '' && loadedMessages.isEmpty) {\n      loadedMessages.add(\n        Message(\n          Role.receiver,\n          room.room.initMessage!,\n          type: MessageType.initMessage,\n        ),\n      );\n    }\n\n    // 聊天内容为空时，显示示例页面\n    if (loadedMessages.isEmpty) {\n      return EmptyPreview(\n        examples: examples,\n        onSubmit: handleSubmit,\n      );\n    }\n\n    final messages = loadedMessages.map((e) {\n      if (e.model != null && !e.model!.startsWith('v2@')) {\n        final mod = supportModels.where((m) => m.id == e.model).firstOrNull;\n        if (mod != null) {\n          e.senderName = mod.shortName;\n          e.avatarUrl = mod.avatarUrl;\n        }\n      }\n\n      if (e.avatarUrl == null || e.senderName == null) {\n        if (loadedState.chatHistory != null && loadedState.chatHistory!.model != null) {\n          if (currentModelV2 != null) {\n            e.senderName = currentModelV2!.name;\n            e.avatarUrl = currentModelV2!.avatarUrl;\n          } else {\n            final mod = supportModels.where((e) => e.id == loadedState.chatHistory!.model!).firstOrNull;\n            if (mod != null) {\n              e.senderName = mod.shortName;\n              e.avatarUrl = mod.avatarUrl;\n            }\n          }\n        }\n      }\n\n      final stateMessage = room.states[widget.stateManager.getKey(e.roomId ?? 0, e.id ?? 0)] ?? MessageState();\n      return MessageWithState(e, stateMessage);\n    }).toList();\n\n    chatPreviewController.setAllMessageIds(messages);\n\n    return ChatPreview(\n      padding: enableInput.value ? null : const EdgeInsets.only(bottom: 35),\n      messages: messages,\n      scrollController: scrollController,\n      controller: chatPreviewController,\n      stateManager: widget.stateManager,\n      robotAvatar: selectMode\n          ? null\n          : RoleAvatar(\n              avatarUrl: room.room.avatarUrl,\n              his: loadedState.chatHistory,\n              alternativeAvatarUrl: currentModelV2?.avatarUrl,\n            ),\n      senderNameBuilder: (message) {\n        if (message.senderName == null) {\n          return null;\n        }\n\n        final robotName = room.room.id != null && room.room.id! > 1 ? room.room.name : message.senderName!;\n        String? robotNameAlias;\n        if (message.model != null && room.room.modelName() != message.model && robotName != message.model) {\n          robotNameAlias = _searchModelName(message.model!);\n        }\n\n        return Container(\n          margin: const EdgeInsets.fromLTRB(0, 0, 10, 7),\n          padding: const EdgeInsets.symmetric(horizontal: 10),\n          child: Row(\n            children: [\n              Text(\n                robotName,\n                style: TextStyle(\n                  color: customColors.weakTextColor,\n                  fontSize: 12,\n                ),\n              ),\n              if (robotNameAlias != null)\n                Container(\n                  margin: const EdgeInsets.only(left: 5),\n                  child: Text(\n                    '($robotNameAlias)',\n                    style: TextStyle(color: customColors.weakTextColorLess, fontSize: 10),\n                  ),\n                ),\n            ],\n          ),\n        );\n      },\n      onDeleteMessage: (id) {\n        handleDeleteMessage(context, id, chatHistoryId: chatId);\n      },\n      onResentEvent: (message, index) {\n        scrollController.animateTo(0, duration: const Duration(milliseconds: 500), curve: Curves.easeOut);\n\n        handleSubmit(message.text, messagetType: message.type, index: index, isResent: true);\n      },\n      onSpeakEvent: (message) {\n        audioPlayerController.playAudio(message.text);\n      },\n      helpWidgets: loadedState.processing || loadedMessages.last.isInitMessage()\n          ? null\n          : [HelpTips(onSubmitMessage: handleSubmit)],\n    );\n  }\n\n  String _searchModelName(String model) {\n    final mod = supportModels.where((e) => e.id == model).firstOrNull;\n    if (mod != null) {\n      return mod.shortName ?? mod.name;\n    }\n\n    return model;\n  }\n\n  /// 提交新消息\n  void handleSubmit(\n    String text, {\n    messagetType = MessageType.text,\n    int? index,\n    bool isResent = false,\n  }) async {\n    setState(() {\n      enableInput.value = false;\n    });\n\n    if (selectedImageFiles.isNotEmpty) {\n      final cancel = BotToast.showCustomLoading(\n        toastBuilder: (cancel) {\n          return LoadingIndicator(\n            message: AppLocale.imageUploading.getString(context),\n          );\n        },\n        allowClick: false,\n      );\n\n      try {\n        final uploader = ImageUploader(widget.setting);\n\n        for (var file in selectedImageFiles) {\n          if (file.uploaded) {\n            continue;\n          }\n\n          if (file.file.bytes != null) {\n            final res = await uploader.base64(\n              imageData: file.file.bytes,\n              maxSize: 1024 * 1024,\n              compressWidth: 512,\n              compressHeight: 512,\n            );\n            file.setUrl(res);\n          } else {\n            final res = await uploader.base64(\n              path: file.file.path!,\n              maxSize: 1024 * 1024,\n              compressWidth: 512,\n              compressHeight: 512,\n            );\n            file.setUrl(res);\n          }\n        }\n      } catch (e) {\n        // ignore: use_build_context_synchronously\n        showErrorMessageEnhanced(context, e);\n        return;\n      } finally {\n        cancel();\n      }\n    }\n\n    // ignore: use_build_context_synchronously\n    context.read<ChatMessageBloc>().add(\n          ChatMessageSendEvent(\n            Message(\n              Role.sender,\n              text,\n              user: 'me',\n              ts: DateTime.now(),\n              model: selectedModel?.id ?? widget.model,\n              type: messagetType,\n              chatHistoryId: chatId,\n              images: selectedImageFiles.where((e) => e.uploaded).map((e) => e.url!).toList(),\n              flags: [\n                if (enableSearch) 'search',\n                if (enableReasoning) 'reasoning',\n              ],\n            ),\n            index: index,\n            isResent: isResent,\n          ),\n        );\n\n    // ignore: use_build_context_synchronously\n    context.read<NotifyBloc>().add(NotifyResetEvent());\n  }\n}\n"
  },
  {
    "path": "lib/page/chat/home_chat_history.dart",
    "content": "import 'package:askaide/bloc/chat_chat_bloc.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/chat/home.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/page/data/chat_history_datasource.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/chat_message_repo.dart';\nimport 'package:askaide/repo/model/chat_history.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:loading_more_list/loading_more_list.dart';\n\nclass HomeChatHistoryPage extends StatefulWidget {\n  final SettingRepository setting;\n  final ChatMessageRepository chatMessageRepo;\n\n  const HomeChatHistoryPage({super.key, required this.setting, required this.chatMessageRepo});\n\n  @override\n  State<HomeChatHistoryPage> createState() => _HomeChatHistoryPageState();\n}\n\nclass _HomeChatHistoryPageState extends State<HomeChatHistoryPage> {\n  late final ChatHistoryDatasource datasource;\n\n  String? keyword;\n\n  @override\n  void initState() {\n    datasource = ChatHistoryDatasource(widget.chatMessageRepo);\n\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          title: Text(\n            AppLocale.histories.getString(context),\n            style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          toolbarHeight: CustomSize.toolbarHeight,\n          centerTitle: true,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          child: SafeArea(\n            top: false,\n            left: false,\n            right: false,\n            child: Column(\n              children: [\n                Container(\n                  margin: const EdgeInsets.only(bottom: 10, left: 15, right: 15),\n                  decoration: BoxDecoration(\n                    color: customColors.textfieldBackgroundColor,\n                    borderRadius: CustomSize.borderRadius,\n                  ),\n                  child: TextField(\n                    textAlignVertical: TextAlignVertical.center,\n                    style: TextStyle(color: customColors.dialogDefaultTextColor),\n                    decoration: InputDecoration(\n                      hintText: AppLocale.search.getString(context),\n                      hintStyle: TextStyle(\n                        color: customColors.dialogDefaultTextColor,\n                      ),\n                      prefixIcon: Icon(\n                        Icons.search,\n                        color: customColors.dialogDefaultTextColor,\n                      ),\n                      isDense: true,\n                      border: InputBorder.none,\n                    ),\n                    onChanged: (value) {\n                      setState(() {\n                        keyword = value;\n                      });\n\n                      datasource.refresh(false, keyword);\n                    },\n                  ),\n                ),\n                Expanded(\n                  child: BlocListener<ChatChatBloc, ChatChatState>(\n                    listenWhen: (previous, current) => current is ChatChatRecentHistoriesLoaded,\n                    listener: (context, state) {\n                      if (state is ChatChatRecentHistoriesLoaded) {\n                        datasource.refresh(false, keyword);\n                      }\n                    },\n                    child: RefreshIndicator(\n                      color: customColors.linkColor,\n                      displacement: 20,\n                      onRefresh: () {\n                        return datasource.refresh(false, keyword);\n                      },\n                      child: LoadingMoreList(\n                        ListConfig<ChatHistory>(\n                          itemBuilder: (context, item, index) {\n                            // Get previous item to check if we need a header\n                            final prevItem = index > 0 ? datasource[index - 1] : null;\n\n                            final now = DateTime.now();\n                            final itemDate = DateTime.fromMillisecondsSinceEpoch(\n                                (item.createdAt ?? DateTime.now()).millisecondsSinceEpoch);\n                            final prevDate = prevItem != null\n                                ? DateTime.fromMillisecondsSinceEpoch(\n                                    (prevItem.createdAt ?? DateTime.now()).millisecondsSinceEpoch)\n                                : null;\n\n                            // Helper function to get time group\n                            String getTimeGroup(DateTime date) {\n                              final difference = now.difference(date);\n\n                              if (difference.inDays < 4) {\n                                return AppLocale.recently.getString(context);\n                              } else if (difference.inDays < 7) {\n                                return '4 ${AppLocale.daysAgo.getString(context)}';\n                              } else if (difference.inDays < 14) {\n                                return AppLocale.lastWeek.getString(context);\n                              } else if (difference.inDays < 30) {\n                                final weeks = (difference.inDays / 7).floor();\n                                return '$weeks ${AppLocale.weeksAgo.getString(context)}';\n                              } else if (difference.inDays < 365) {\n                                if (difference.inDays < 60) {\n                                  return AppLocale.lastMonth.getString(context);\n                                }\n                                final months = (difference.inDays / 30).floor();\n                                return '$months ${AppLocale.monthsAgo.getString(context)}';\n                              } else if (difference.inDays < 730) {\n                                return AppLocale.lastYear.getString(context);\n                              } else {\n                                return AppLocale.longTimeAgo.getString(context);\n                              }\n                            }\n\n                            final currentGroup = getTimeGroup(itemDate);\n                            final prevGroup = prevDate != null ? getTimeGroup(prevDate) : null;\n\n                            // Show header if group changed\n                            final showHeader = currentGroup.isNotEmpty && currentGroup != prevGroup;\n\n                            return Column(\n                              crossAxisAlignment: CrossAxisAlignment.start,\n                              children: [\n                                if (showHeader)\n                                  Padding(\n                                    padding: const EdgeInsets.only(left: 15, top: 10, bottom: 5),\n                                    child: Text(\n                                      currentGroup,\n                                      style: TextStyle(\n                                        fontSize: 12,\n                                        color: customColors.weakTextColor,\n                                      ),\n                                    ),\n                                  ),\n                                ChatHistoryItem(\n                                  history: item,\n                                  customColors: customColors,\n                                  onTap: () {\n                                    context\n                                        .push(\n                                            '/chat-anywhere?chat_id=${item.id}&model=${item.model}&title=${item.title}')\n                                        .whenComplete(() {\n                                      FocusScope.of(context).requestFocus(FocusNode());\n                                      context.read<ChatChatBloc>().add(ChatChatLoadRecentHistories());\n                                    });\n                                  },\n                                ),\n                              ],\n                            );\n                          },\n                          sourceList: datasource,\n                          indicatorBuilder: (context, status) {\n                            String msg = '';\n                            switch (status) {\n                              case IndicatorStatus.noMoreLoad:\n                                msg = '';\n                                break;\n                              case IndicatorStatus.loadingMoreBusying:\n                                msg = 'Loading...';\n                                break;\n                              case IndicatorStatus.error:\n                                msg = 'Failed to load, please try again later';\n                                break;\n                              case IndicatorStatus.empty:\n                                msg = 'No data';\n                                break;\n                              default:\n                                return const Center(child: LoadingIndicator());\n                            }\n                            return Container(\n                              padding: const EdgeInsets.all(15),\n                              alignment: Alignment.center,\n                              child: Text(\n                                msg,\n                                style: TextStyle(\n                                  color: customColors.weakTextColor,\n                                  fontSize: 14,\n                                ),\n                              ),\n                            );\n                          },\n                        ),\n                      ),\n                    ),\n                  ),\n                )\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/account_quota_card.dart",
    "content": "import 'package:askaide/helper/ability.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/credit.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/api/user.dart';\nimport 'package:auto_size_text/auto_size_text.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:url_launcher/url_launcher.dart';\n\nclass AccountQuotaCard extends StatelessWidget {\n  final UserInfo? userInfo;\n  final VoidCallback? onPaymentReturn;\n  final bool noBorder;\n  const AccountQuotaCard({super.key, this.userInfo, this.onPaymentReturn, this.noBorder = false});\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return Container(\n      margin: noBorder ? null : const EdgeInsets.symmetric(horizontal: 15, vertical: 10),\n      height: 120,\n      child: Container(\n        padding: noBorder\n            ? const EdgeInsets.only()\n            : const EdgeInsets.symmetric(\n                horizontal: 20,\n                vertical: 30,\n              ),\n        child: userInfo != null\n            ? Row(\n                crossAxisAlignment: CrossAxisAlignment.start,\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: [\n                  Expanded(\n                    child: Column(\n                      crossAxisAlignment: CrossAxisAlignment.start,\n                      children: [\n                        Row(\n                          children: [\n                            Text(\n                              AppLocale.usage.getString(context),\n                              style: TextStyle(\n                                fontSize: 16,\n                                color: customColors.backgroundInvertedColor,\n                              ),\n                            ),\n                            const SizedBox(width: 5),\n                            InkWell(\n                              onTap: () {\n                                launchUrl(\n                                  Uri.parse('https://ai.aicode.cc/zhihuiguo.html'),\n                                );\n                              },\n                              child: Icon(\n                                Icons.help,\n                                size: 12,\n                                color: customColors.weakTextColor?.withAlpha(150),\n                              ),\n                            ),\n                          ],\n                        ),\n                        const SizedBox(height: 5),\n                        Row(\n                          children: [\n                            InkWell(\n                              onTap: () {\n                                context.push('/quota-usage-statistics');\n                              },\n                              borderRadius: CustomSize.borderRadiusAll,\n                              child: Credit(\n                                count: userInfo!.quota.quotaRemain(),\n                                color: customColors.backgroundInvertedColor,\n                                fontSize: 16,\n                                fontWeight: FontWeight.normal,\n                              ),\n                            ),\n                            const SizedBox(width: 5),\n                            if (Ability().enablePayment)\n                              TextButton(\n                                onPressed: () {\n                                  context.push('/payment').whenComplete(() {\n                                    if (onPaymentReturn != null) {\n                                      onPaymentReturn!();\n                                    }\n                                  });\n                                },\n                                child: Text(\n                                  AppLocale.buy.getString(context),\n                                  style: const TextStyle(\n                                    fontSize: 14,\n                                  ),\n                                ),\n                              ),\n                          ],\n                        )\n                      ],\n                    ),\n                  ),\n                ],\n              )\n            : Row(\n                mainAxisAlignment: MainAxisAlignment.spaceAround,\n                children: [\n                  IconButton(\n                    alignment: Alignment.centerLeft,\n                    icon: Row(\n                      mainAxisSize: MainAxisSize.min,\n                      children: [\n                        const Icon(Icons.account_circle),\n                        const SizedBox(width: 5),\n                        AutoSizeText(AppLocale.signInAccount.getString(context), maxLines: 1),\n                      ],\n                    ),\n                    onPressed: () {\n                      context.go('/login');\n                    },\n                  ),\n                  const SizedBox(width: 10),\n                ],\n              ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/advanced_button.dart",
    "content": "import 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\n\nclass AdvancedButton extends StatelessWidget {\n  final bool showAdvancedOptions;\n  final Function(bool) onPressed;\n  const AdvancedButton({super.key, required this.showAdvancedOptions, required this.onPressed});\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return Center(\n      child: EnhancedButton(\n        title:\n            showAdvancedOptions ? AppLocale.collapseOptions.getString(context) : AppLocale.advanced.getString(context),\n        width: 100,\n        backgroundColor: Colors.transparent,\n        color: customColors.weakTextColorLess,\n        fontSize: 12,\n        icon: Icon(\n          showAdvancedOptions ? Icons.unfold_less : Icons.unfold_more,\n          color: customColors.weakTextColorLess,\n          size: 12,\n        ),\n        onPressed: () {\n          onPressed(!showAdvancedOptions);\n        },\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/animated_cursor.dart",
    "content": "import 'dart:async';\n\nimport 'package:flutter/material.dart';\n\nclass Cursor extends CustomPainter {\n  final Color? color;\n\n  const Cursor({this.color});\n\n  @override\n  void paint(Canvas canvas, Size size) {\n    Paint paint = Paint()..color = color ?? Colors.black;\n    canvas.drawCircle(const Offset(0, 10), 3.0, paint);\n  }\n\n  @override\n  bool shouldRepaint(CustomPainter oldDelegate) {\n    return true;\n  }\n}\n\nclass AnimatedCursor extends StatefulWidget {\n  final double width;\n  final double height;\n  final Color? color;\n\n  const AnimatedCursor({\n    super.key,\n    this.width = 2,\n    this.height = 20,\n    this.color,\n  });\n\n  @override\n  State<AnimatedCursor> createState() => _AnimatedCursorState();\n}\n\nclass _AnimatedCursorState extends State<AnimatedCursor>\n    with SingleTickerProviderStateMixin {\n  late Timer _timer;\n  bool _showCursor = false;\n\n  @override\n  void initState() {\n    super.initState();\n    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {\n      setState(() {\n        _showCursor = !_showCursor;\n      });\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return _showCursor\n        ? CustomPaint(\n            size: Size(widget.width, widget.height),\n            painter: Cursor(color: widget.color),\n          )\n        : SizedBox(width: widget.width, height: widget.height);\n  }\n\n  @override\n  void dispose() {\n    _timer.cancel();\n    super.dispose();\n  }\n}\n"
  },
  {
    "path": "lib/page/component/attached_button_panel.dart",
    "content": "import 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:flutter/material.dart';\n\nclass AttachedButtonPanel extends StatelessWidget {\n  final List<TextButton> buttons;\n\n  const AttachedButtonPanel({super.key, required this.buttons});\n\n  @override\n  Widget build(BuildContext context) {\n    final columns = <Widget>[];\n    final itemPerRow = buttons.length > 4 ? 3 : 4;\n    for (var i = 0; i < buttons.length; i += itemPerRow) {\n      final row = <Widget>[];\n      for (var j = 0; j < itemPerRow; j++) {\n        if (i + j < buttons.length) {\n          row.add(buttons[i + j]);\n        }\n      }\n      if (i > 0) {\n        columns.add(const SizedBox(height: 10));\n      }\n      columns.add(Row(\n        mainAxisSize: MainAxisSize.min,\n        mainAxisAlignment: MainAxisAlignment.start,\n        children: row,\n      ));\n    }\n    return Card(\n      child: Container(\n        padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 10),\n        decoration: BoxDecoration(\n          borderRadius: CustomSize.borderRadius,\n          color: const Color.fromARGB(223, 0, 0, 0),\n        ),\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: columns,\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/audio_player.dart",
    "content": "import 'package:askaide/helper/logger.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:audioplayers/audioplayers.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_tts/flutter_tts.dart' as tts;\nimport 'package:loading_animation_widget/loading_animation_widget.dart';\n\nclass AudioPlayerController {\n  List<String> audioSources = [];\n  int currentAudioIndex = 0;\n\n  late AudioPlayer player;\n  late tts.FlutterTts flutterTts;\n\n  Function()? onPlayStopped;\n  Function()? onPlayAudioStarted;\n  Function(bool loading)? onPlayAudioLoading;\n\n  final bool useRemoteAPI;\n\n  AudioPlayerController({required this.useRemoteAPI}) {\n    if (useRemoteAPI) {\n      player = AudioPlayer();\n      player.onPlayerComplete.listen((event) {\n        if (currentAudioIndex < audioSources.length) {\n          playNextAudioForPlayer();\n        } else {\n          if (onPlayStopped != null) {\n            onPlayStopped!();\n          }\n        }\n      });\n    } else {\n      flutterTts = tts.FlutterTts();\n      if (PlatformTool.isIOS()) {\n        flutterTts.setSharedInstance(true);\n        flutterTts.setIosAudioCategory(\n          tts.IosTextToSpeechAudioCategory.playback,\n          [\n            tts.IosTextToSpeechAudioCategoryOptions.allowBluetooth,\n            tts.IosTextToSpeechAudioCategoryOptions.allowBluetoothA2DP,\n            tts.IosTextToSpeechAudioCategoryOptions.mixWithOthers,\n          ],\n        );\n      }\n\n      flutterTts.setStartHandler(() {\n        if (onPlayAudioStarted != null) {\n          onPlayAudioStarted!();\n        }\n      });\n\n      flutterTts.setErrorHandler((msg) {\n        Logger.instance.e('TTS error: $msg');\n      });\n\n      flutterTts.setCancelHandler(() {\n        if (onPlayStopped != null) {\n          onPlayStopped!();\n        }\n      });\n\n      flutterTts.completionHandler = () {\n        if (onPlayStopped != null) {\n          onPlayStopped!();\n        }\n      };\n    }\n  }\n\n  void dispose() {\n    if (useRemoteAPI) {\n      player.dispose();\n    } else {\n      flutterTts.stop();\n    }\n  }\n\n  Future<void> playAudio(String text) async {\n    await stop();\n\n    if (text.isEmpty) {\n      return;\n    }\n\n    if (useRemoteAPI) {\n      if (onPlayAudioStarted != null) {\n        onPlayAudioStarted!();\n      }\n      onPlayAudioLoading?.call(true);\n      resetAudioSourcesForPlayer(await APIServer().textToVoice(text: text));\n      onPlayAudioLoading?.call(false);\n      playNextAudioForPlayer();\n    } else {\n      flutterTts.speak(text);\n    }\n  }\n\n  Future<void> stop() async {\n    if (useRemoteAPI) {\n      await player.stop();\n      if (onPlayStopped != null) {\n        onPlayStopped!();\n      }\n    } else {\n      await flutterTts.stop();\n    }\n  }\n\n  Future<void> playNextAudioForPlayer() async {\n    if (audioSources.isEmpty) {\n      return;\n    }\n\n    if (currentAudioIndex >= audioSources.length) {\n      return;\n    }\n\n    await player.play(UrlSource(audioSources[currentAudioIndex]));\n    currentAudioIndex++;\n  }\n\n  void resetAudioSourcesForPlayer(List<String> sources) {\n    audioSources = sources;\n    currentAudioIndex = 0;\n  }\n}\n\nclass EnhancedAudioPlayer extends StatelessWidget {\n  final AudioPlayerController controller;\n  final bool loading;\n  const EnhancedAudioPlayer(\n      {super.key, required this.controller, this.loading = false});\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return Row(\n      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n      children: [\n        const SizedBox(width: 100),\n        loading\n            ? Row(\n                children: [\n                  Text(\n                    '语音合成中，请稍候',\n                    style: TextStyle(\n                      color: customColors.weakTextColor,\n                      fontSize: 12,\n                    ),\n                  ),\n                  const SizedBox(width: 10),\n                  LoadingAnimationWidget.fourRotatingDots(\n                    color: const Color.fromARGB(255, 254, 170, 74),\n                    size: 12,\n                  ),\n                ],\n              )\n            : LoadingAnimationWidget.staggeredDotsWave(\n                color: const Color.fromARGB(255, 254, 170, 74),\n                size: 25,\n              ),\n        if (!loading)\n          SizedBox(\n            width: 100,\n            child: TextButton.icon(\n              onPressed: () {\n                controller.stop();\n              },\n              icon: Icon(\n                Icons.stop_circle_outlined,\n                color: customColors.weakLinkColor,\n              ),\n              label: Text(\n                '停止',\n                style: TextStyle(\n                  color: customColors.weakLinkColor,\n                  fontSize: 12,\n                ),\n              ),\n            ),\n          )\n        else\n          const SizedBox(width: 100),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/avatar_selector.dart",
    "content": "import 'dart:io';\n\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/random_avatar.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:file_picker/file_picker.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\n\nenum AvatarType {\n  localFile,\n  network,\n  random,\n}\n\nclass Avatar {\n  final AvatarType type;\n  final String? url;\n  final int? id;\n\n  const Avatar({\n    required this.type,\n    this.url,\n    this.id,\n  });\n}\n\nclass AvatarSelector extends StatefulWidget {\n  final Function(Avatar selected) onSelected;\n\n  final int? defaultAvatarId;\n  final String? defaultAvatarUrl;\n\n  final List<int> externalAvatarIds;\n  final List<String> externalAvatarUrls;\n\n  final AvatarUsage usage;\n\n  const AvatarSelector({\n    super.key,\n    required this.onSelected,\n    this.defaultAvatarId,\n    this.defaultAvatarUrl,\n    required this.usage,\n    this.externalAvatarIds = const [],\n    this.externalAvatarUrls = const [],\n  });\n\n  @override\n  State<AvatarSelector> createState() => _AvatarSelectorState();\n}\n\nclass _AvatarSelectorState extends State<AvatarSelector> {\n  String? _avatarUrl;\n  int? _avatarId;\n\n  @override\n  void initState() {\n    super.initState();\n    _avatarId = widget.defaultAvatarId;\n    _avatarUrl = widget.defaultAvatarUrl;\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return Column(\n      children: [\n        Expanded(\n          child: GridView.count(\n            crossAxisCount: 3,\n            crossAxisSpacing: 15,\n            mainAxisSpacing: 15,\n            padding: const EdgeInsets.only(\n              top: 15,\n              left: 8,\n              right: 8,\n              bottom: 10,\n            ),\n            children: [\n              buildAvatarSelectBox(customColors, context),\n              for (String url in widget.externalAvatarUrls)\n                _buildAvatarButton(\n                  customColors,\n                  Avatar(\n                    type: AvatarType.network,\n                    url: url,\n                  ),\n                ),\n              for (int id in widget.externalAvatarIds)\n                _buildAvatarButton(\n                  customColors,\n                  Avatar(type: AvatarType.random, id: id),\n                ),\n              // ...List.generate(\n              //   200 -\n              //       widget.externalAvatarIds.length -\n              //       widget.externalAvatarUrls.length,\n              //   (index) {\n              //     return _buildAvatarButton(\n              //         customColors,\n              //         Avatar(\n              //           type: AvatarType.random,\n              //           id: widget.randomSeed + index,\n              //         ));\n              //   },\n              // ),\n            ],\n          ),\n        ),\n      ],\n    );\n  }\n\n  Widget buildAvatarSelectBox(CustomColors customColors, BuildContext context) {\n    return Material(\n      borderRadius: CustomSize.borderRadius,\n      child: InkWell(\n        borderRadius: CustomSize.borderRadiusAll,\n        onTap: () async {\n          HapticFeedbackHelper.mediumImpact();\n          FilePickerResult? result = await FilePicker.platform.pickFiles(type: FileType.image);\n          if (result != null && result.files.isNotEmpty) {\n            setState(() {\n              _avatarUrl = result.files.first.path;\n              _avatarId = null;\n            });\n\n            widget.onSelected(Avatar(\n              type: _avatarUrl!.startsWith('http') ? AvatarType.network : AvatarType.localFile,\n              url: _avatarUrl,\n            ));\n          }\n        },\n        child: Container(\n          decoration: BoxDecoration(\n            borderRadius: CustomSize.borderRadius,\n          ),\n          child: _avatarId == null && _avatarUrl == null\n              ? ClipRRect(\n                  borderRadius: CustomSize.borderRadius,\n                  child: SizedBox(\n                    height: 100,\n                    width: 100,\n                    child: Column(\n                      mainAxisAlignment: MainAxisAlignment.center,\n                      children: [\n                        Icon(\n                          Icons.camera_alt,\n                          size: 30,\n                          color: customColors.chatInputPanelText,\n                        ),\n                        const SizedBox(width: 10),\n                        Text(\n                          AppLocale.custom.getString(context),\n                          style: TextStyle(\n                            fontSize: 10,\n                            fontWeight: FontWeight.w500,\n                            color: customColors.chatInputPanelText?.withOpacity(0.8),\n                          ),\n                        ),\n                      ],\n                    ),\n                  ),\n                )\n              : Stack(\n                  children: [\n                    SizedBox(\n                      width: 100,\n                      height: 100,\n                      child: ClipRRect(\n                        borderRadius: CustomSize.borderRadius,\n                        child: _avatarUrl!.startsWith('http')\n                            ? CachedNetworkImageEnhanced(\n                                imageUrl: _avatarUrl!,\n                              )\n                            : Image.file(File(_avatarUrl!)),\n                      ),\n                    ),\n                    Positioned(\n                      bottom: 0,\n                      left: 0,\n                      right: 0,\n                      child: ClipRRect(\n                        borderRadius:\n                            const BorderRadius.only(bottomLeft: CustomSize.radius, bottomRight: CustomSize.radius),\n                        child: Container(\n                          color: const Color.fromARGB(82, 0, 0, 0),\n                          height: 25,\n                          alignment: Alignment.center,\n                          child: Text(\n                            AppLocale.custom.getString(context),\n                            textAlign: TextAlign.center,\n                            style: const TextStyle(\n                              fontSize: 12,\n                              fontWeight: FontWeight.w500,\n                              color: Colors.white,\n                            ),\n                          ),\n                        ),\n                      ),\n                    ),\n                  ],\n                ),\n        ),\n      ),\n    );\n  }\n\n  Widget _buildAvatarButton(CustomColors customColors, Avatar avatar) {\n    return InkWell(\n      onTap: () {\n        setState(() {\n          _avatarUrl = avatar.url;\n          _avatarId = avatar.id;\n        });\n\n        widget.onSelected(avatar);\n      },\n      child: avatar.type == AvatarType.random\n          ? RandomAvatar(id: avatar.id ?? 0, size: 80, usage: widget.usage)\n          : ClipRRect(\n              borderRadius: CustomSize.borderRadius,\n              child: CachedNetworkImageEnhanced(\n                imageUrl: avatar.url!,\n                fit: BoxFit.fill,\n              ),\n            ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/background_container.dart",
    "content": "import 'dart:ui';\n\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\n\nclass BackgroundContainer extends StatefulWidget {\n  final Widget child;\n  final bool? useGradient;\n  final SettingRepository setting;\n  final bool enabled;\n  final bool pureColorMode;\n  final double maxWidth;\n  final bool clickGrapFocus;\n  final Color? backgroundColor;\n\n  const BackgroundContainer({\n    super.key,\n    required this.child,\n    this.useGradient,\n    required this.setting,\n    this.enabled = true,\n    this.pureColorMode = false,\n    this.maxWidth = CustomSize.maxWindowSize,\n    this.clickGrapFocus = true,\n    this.backgroundColor,\n  });\n\n  @override\n  State<BackgroundContainer> createState() => _BackgroundContainerState();\n}\n\nclass _BackgroundContainerState extends State<BackgroundContainer> {\n  final int opacity = 180;\n  String? imageUrl;\n  double? blur;\n\n  @override\n  void initState() {\n    super.initState();\n\n    if (widget.enabled) {\n      if ((widget.useGradient == null || widget.useGradient == false) && imageUrl == null) {\n        imageUrl = widget.setting.get(settingBackgroundImage);\n        blur = double.tryParse(\n          widget.setting.get(settingBackgroundImageBlur) ?? '15.0',\n        );\n      }\n\n      widget.setting.listen((settings, key, value) {\n        if (key == settingBackgroundImage) {\n          if (mounted) {\n            setState(() {\n              imageUrl = value;\n            });\n          }\n        }\n\n        if (key == settingBackgroundImageBlur) {\n          if (mounted) {\n            setState(() {\n              blur = double.tryParse(value);\n            });\n          }\n        }\n      });\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return GestureDetector(\n      onTap: widget.clickGrapFocus\n          ? () {\n              FocusScope.of(context).requestFocus(FocusNode());\n            }\n          : null,\n      onHorizontalDragUpdate: (details) {\n        // Only the mobile app supports horizontal swiping to go back to the previous page.\n        if (PlatformTool.isAndroid() || PlatformTool.isIOS()) {\n          int sensitivity = 15;\n          if (details.delta.dx > sensitivity) {\n            if (Navigator.of(context).canPop()) {\n              Navigator.of(context).pop();\n            }\n          }\n        }\n      },\n      child: Container(\n        color: widget.backgroundColor ?? customColors.backgroundContainerColor,\n        child: Align(\n          alignment: Alignment.topCenter,\n          child: ConstrainedBox(\n            constraints: BoxConstraints(\n              maxWidth: widget.maxWidth > 0 ? widget.maxWidth : double.infinity,\n            ),\n            child: _buildChild(customColors),\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget _buildChild(CustomColors customColors) {\n    if (widget.pureColorMode) {\n      return Container(\n        height: double.infinity,\n        decoration: _createPureColorDecoration(customColors),\n        child: widget.child,\n      );\n    }\n\n    if (!widget.enabled) {\n      return Container(\n        height: double.infinity,\n        decoration: _createTransportDecoration(),\n        child: widget.child,\n      );\n    }\n\n    if (widget.enabled && ((imageUrl != null && imageUrl != '') || widget.useGradient == true)) {\n      return Container(\n        height: double.infinity,\n        decoration: widget.useGradient == true\n            ? _createLinearGradientDecoration()\n            : BoxDecoration(\n                image: _resolveImage(),\n              ),\n        child: BackdropFilter(\n          filter: ImageFilter.blur(\n            sigmaX: blur ?? 15.0,\n            sigmaY: blur ?? 15.0,\n          ),\n          child: widget.child,\n        ),\n      );\n    }\n\n    return Container(\n      decoration: _createPureColorDecoration(customColors),\n      height: double.infinity,\n      child: widget.child,\n    );\n  }\n\n  DecorationImage _resolveImage() {\n    return DecorationImage(\n      image: resolveImageProvider(imageUrl!),\n      fit: BoxFit.cover,\n    );\n  }\n\n  BoxDecoration _createPureColorDecoration(CustomColors customColors) {\n    return BoxDecoration(\n      color: customColors.backgroundContainerColor,\n    );\n  }\n\n  BoxDecoration _createLinearGradientDecoration() {\n    return BoxDecoration(\n      gradient: LinearGradient(\n          begin: Alignment.topLeft,\n          end: Alignment.bottomRight,\n          colors: [\n            Color.fromARGB(opacity, 90, 218, 196),\n            Color.fromARGB(opacity, 230, 153, 38),\n            Color.fromARGB(opacity, 242, 7, 213),\n          ],\n          transform: const GradientRotation(0.5)),\n    );\n  }\n\n  BoxDecoration _createTransportDecoration() {\n    return const BoxDecoration(\n      color: Colors.transparent,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/bottom_sheet_box.dart",
    "content": "import 'package:askaide/helper/platform.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\n\nclass BottomSheetBox extends StatelessWidget {\n  final Widget child;\n  const BottomSheetBox({super.key, required this.child});\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return Padding(\n      padding: MediaQuery.of(context).viewInsets,\n      child: Container(\n        padding: EdgeInsets.only(\n            left: 10,\n            right: 10,\n            bottom: PlatformTool.isDesktop() ? 10 : (MediaQuery.of(context).viewInsets.bottom > 0 ? 10 : 0)),\n        child: SafeArea(\n          bottom: false,\n          child: Container(\n            width: double.infinity,\n            decoration: BoxDecoration(\n              borderRadius: const BorderRadius.vertical(top: CustomSize.radius, bottom: CustomSize.radius),\n              color: customColors.backgroundColor,\n            ),\n            padding: const EdgeInsets.only(top: 0, left: 10, right: 10),\n            child: SafeArea(\n              child: child,\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/button.dart",
    "content": "import 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:flutter/material.dart';\n\nclass Button extends StatelessWidget {\n  final String title;\n  final Function() onPressed;\n  final ButtonSize size;\n\n  final Color? backgroundColor;\n  final Color? color;\n  final Widget? icon;\n\n  const Button({\n    super.key,\n    required this.title,\n    required this.onPressed,\n    this.size = const ButtonSize.small(),\n    this.backgroundColor,\n    this.color,\n    this.icon,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return EnhancedButton(\n      width: size.width,\n      height: size.height,\n      fontSize: size.fontSize,\n      title: title,\n      onPressed: onPressed,\n      backgroundColor: backgroundColor,\n      color: color,\n      icon: icon,\n    );\n  }\n}\n\nclass ButtonSize {\n  final double width;\n  final double height;\n  final double fontSize;\n\n  const ButtonSize({\n    required this.width,\n    required this.height,\n    required this.fontSize,\n  });\n\n  const ButtonSize.full()\n      : width = double.infinity,\n        height = 42,\n        fontSize = 16;\n\n  const ButtonSize.small()\n      : width = 80,\n        height = 35,\n        fontSize = 14;\n}\n"
  },
  {
    "path": "lib/page/component/chat/chat_bubble.dart",
    "content": "import 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:flutter/material.dart';\n\n/// 来源于 https://github.com/prahack/chat_bubbles/blob/master/lib/bubbles/bubble_special_one.dart\nclass SpecialChatBubbleOne extends CustomPainter {\n  final Color color;\n  final Alignment alignment;\n  final bool tail;\n\n  SpecialChatBubbleOne({\n    required this.color,\n    required this.alignment,\n    required this.tail,\n  });\n\n  final double _radius = CustomSize.radiusValue;\n  final double _x = 5.0;\n\n  @override\n  void paint(Canvas canvas, Size size) {\n    if (alignment == Alignment.topRight) {\n      if (tail) {\n        canvas.drawRRect(\n            RRect.fromLTRBAndCorners(\n              0,\n              0,\n              size.width - _x,\n              size.height,\n              bottomLeft: Radius.circular(_radius),\n              bottomRight: Radius.circular(_radius),\n              topLeft: Radius.circular(_radius),\n            ),\n            Paint()\n              ..color = color\n              ..style = PaintingStyle.fill);\n        var path = Path();\n        path.moveTo(size.width - _x, 0);\n        path.lineTo(size.width - _x, 10);\n        path.lineTo(size.width, 0);\n        canvas.clipPath(path);\n        canvas.drawRRect(\n            RRect.fromLTRBAndCorners(\n              size.width - _x,\n              0.0,\n              size.width,\n              size.height,\n              topRight: const Radius.circular(3),\n            ),\n            Paint()\n              ..color = color\n              ..style = PaintingStyle.fill);\n      } else {\n        canvas.drawRRect(\n            RRect.fromLTRBAndCorners(\n              0,\n              0,\n              size.width - _x,\n              size.height,\n              bottomLeft: Radius.circular(_radius),\n              bottomRight: Radius.circular(_radius),\n              topLeft: Radius.circular(_radius),\n              topRight: Radius.circular(_radius),\n            ),\n            Paint()\n              ..color = color\n              ..style = PaintingStyle.fill);\n      }\n    } else {\n      if (tail) {\n        canvas.drawRRect(\n            RRect.fromLTRBAndCorners(\n              _x,\n              0,\n              size.width,\n              size.height,\n              bottomRight: Radius.circular(_radius),\n              topRight: Radius.circular(_radius),\n              bottomLeft: Radius.circular(_radius),\n            ),\n            Paint()\n              ..color = color\n              ..style = PaintingStyle.fill);\n        var path = Path();\n        path.moveTo(_x, 0);\n        path.lineTo(_x, 10);\n        path.lineTo(0, 0);\n        canvas.clipPath(path);\n        canvas.drawRRect(\n            RRect.fromLTRBAndCorners(\n              0,\n              0.0,\n              _x,\n              size.height,\n              topLeft: const Radius.circular(3),\n            ),\n            Paint()\n              ..color = color\n              ..style = PaintingStyle.fill);\n      } else {\n        canvas.drawRRect(\n            RRect.fromLTRBAndCorners(\n              _x,\n              0,\n              size.width,\n              size.height,\n              bottomRight: Radius.circular(_radius),\n              topRight: Radius.circular(_radius),\n              bottomLeft: Radius.circular(_radius),\n              topLeft: Radius.circular(_radius),\n            ),\n            Paint()\n              ..color = color\n              ..style = PaintingStyle.fill);\n      }\n    }\n  }\n\n  @override\n  bool shouldRepaint(CustomPainter oldDelegate) {\n    return true;\n  }\n}\n\nclass BubbleSpecialThree extends StatelessWidget {\n  final bool isSender;\n  final String text;\n  final bool tail;\n  final Color color;\n  final bool sent;\n  final bool delivered;\n  final bool seen;\n  final TextStyle textStyle;\n\n  const BubbleSpecialThree({\n    Key? key,\n    this.isSender = true,\n    required this.text,\n    this.color = Colors.white70,\n    this.tail = true,\n    this.sent = false,\n    this.delivered = false,\n    this.seen = false,\n    this.textStyle = const TextStyle(\n      color: Colors.black87,\n      fontSize: 16,\n    ),\n  }) : super(key: key);\n\n  ///chat bubble builder method\n  @override\n  Widget build(BuildContext context) {\n    bool stateTick = false;\n    Icon? stateIcon;\n    if (sent) {\n      stateTick = true;\n      stateIcon = const Icon(\n        Icons.done,\n        size: 18,\n        color: Color(0xFF97AD8E),\n      );\n    }\n    if (delivered) {\n      stateTick = true;\n      stateIcon = const Icon(\n        Icons.done_all,\n        size: 18,\n        color: Color(0xFF97AD8E),\n      );\n    }\n    if (seen) {\n      stateTick = true;\n      stateIcon = const Icon(\n        Icons.done_all,\n        size: 18,\n        color: Color(0xFF92DEDA),\n      );\n    }\n\n    return Align(\n      alignment: isSender ? Alignment.topRight : Alignment.topLeft,\n      child: Padding(\n        padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),\n        child: CustomPaint(\n          painter: SpecialChatBubbleThree(\n              color: color, alignment: isSender ? Alignment.topRight : Alignment.topLeft, tail: tail),\n          child: Container(\n            constraints: BoxConstraints(\n              maxWidth: MediaQuery.of(context).size.width * .7,\n            ),\n            margin: isSender\n                ? stateTick\n                    ? const EdgeInsets.fromLTRB(7, 7, 14, 7)\n                    : const EdgeInsets.fromLTRB(7, 7, 17, 7)\n                : const EdgeInsets.fromLTRB(17, 7, 7, 7),\n            child: Stack(\n              children: <Widget>[\n                Padding(\n                  padding:\n                      stateTick ? const EdgeInsets.only(left: 4, right: 20) : const EdgeInsets.only(left: 4, right: 4),\n                  child: Text(\n                    text,\n                    style: textStyle,\n                    textAlign: TextAlign.left,\n                  ),\n                ),\n                stateIcon != null && stateTick\n                    ? Positioned(\n                        bottom: 0,\n                        right: 0,\n                        child: stateIcon,\n                      )\n                    : const SizedBox(\n                        width: 1,\n                      ),\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n\n///custom painter use to create the shape of the chat bubble\n///\n/// [color],[alignment] and [tail] can be changed\nclass SpecialChatBubbleThree extends CustomPainter {\n  final Color color;\n  final Alignment alignment;\n  final bool tail;\n\n  SpecialChatBubbleThree({\n    required this.color,\n    required this.alignment,\n    required this.tail,\n  });\n\n  final double _radius = CustomSize.radiusValue;\n\n  @override\n  void paint(Canvas canvas, Size size) {\n    var h = size.height;\n    var w = size.width;\n    if (alignment == Alignment.topRight) {\n      if (tail) {\n        var path = Path();\n\n        /// starting point\n        path.moveTo(_radius * 2, 0);\n\n        /// top-left corner\n        path.quadraticBezierTo(0, 0, 0, _radius * 1.5);\n\n        /// left line\n        path.lineTo(0, h - _radius * 1.5);\n\n        /// bottom-left corner\n        path.quadraticBezierTo(0, h, _radius * 2, h);\n\n        /// bottom line\n        path.lineTo(w - _radius * 3, h);\n\n        /// bottom-right bubble curve\n        path.quadraticBezierTo(w - _radius * 1.5, h, w - _radius * 1.5, h - _radius * 0.6);\n\n        /// bottom-right tail curve 1\n        path.quadraticBezierTo(w - _radius * 1, h, w, h);\n\n        /// bottom-right tail curve 2\n        path.quadraticBezierTo(w - _radius * 0.8, h, w - _radius, h - _radius * 1.5);\n\n        /// right line\n        path.lineTo(w - _radius, _radius * 1.5);\n\n        /// top-right curve\n        path.quadraticBezierTo(w - _radius, 0, w - _radius * 3, 0);\n\n        canvas.clipPath(path);\n        canvas.drawRRect(\n            RRect.fromLTRBR(0, 0, w, h, Radius.zero),\n            Paint()\n              ..color = color\n              ..style = PaintingStyle.fill);\n      } else {\n        var path = Path();\n\n        /// starting point\n        path.moveTo(_radius * 2, 0);\n\n        /// top-left corner\n        path.quadraticBezierTo(0, 0, 0, _radius * 1.5);\n\n        /// left line\n        path.lineTo(0, h - _radius * 1.5);\n\n        /// bottom-left corner\n        path.quadraticBezierTo(0, h, _radius * 2, h);\n\n        /// bottom line\n        path.lineTo(w - _radius * 3, h);\n\n        /// bottom-right curve\n        path.quadraticBezierTo(w - _radius, h, w - _radius, h - _radius * 1.5);\n\n        /// right line\n        path.lineTo(w - _radius, _radius * 1.5);\n\n        /// top-right curve\n        path.quadraticBezierTo(w - _radius, 0, w - _radius * 3, 0);\n\n        canvas.clipPath(path);\n        canvas.drawRRect(\n            RRect.fromLTRBR(0, 0, w, h, Radius.zero),\n            Paint()\n              ..color = color\n              ..style = PaintingStyle.fill);\n      }\n    } else {\n      if (tail) {\n        var path = Path();\n\n        /// starting point\n        path.moveTo(_radius * 3, 0);\n\n        /// top-left corner\n        path.quadraticBezierTo(_radius, 0, _radius, _radius * 1.5);\n\n        /// left line\n        path.lineTo(_radius, h - _radius * 1.5);\n        // bottom-right tail curve 1\n        path.quadraticBezierTo(_radius * .8, h, 0, h);\n\n        /// bottom-right tail curve 2\n        path.quadraticBezierTo(_radius * 1, h, _radius * 1.5, h - _radius * 0.6);\n\n        /// bottom-left bubble curve\n        path.quadraticBezierTo(_radius * 1.5, h, _radius * 3, h);\n\n        /// bottom line\n        path.lineTo(w - _radius * 2, h);\n\n        /// bottom-right curve\n        path.quadraticBezierTo(w, h, w, h - _radius * 1.5);\n\n        /// right line\n        path.lineTo(w, _radius * 1.5);\n\n        /// top-right curve\n        path.quadraticBezierTo(w, 0, w - _radius * 2, 0);\n        canvas.clipPath(path);\n        canvas.drawRRect(\n            RRect.fromLTRBR(0, 0, w, h, Radius.zero),\n            Paint()\n              ..color = color\n              ..style = PaintingStyle.fill);\n      } else {\n        var path = Path();\n\n        /// starting point\n        path.moveTo(_radius * 3, 0);\n\n        /// top-left corner\n        path.quadraticBezierTo(_radius, 0, _radius, _radius * 1.5);\n\n        /// left line\n        path.lineTo(_radius, h - _radius * 1.5);\n\n        /// bottom-left curve\n        path.quadraticBezierTo(_radius, h, _radius * 3, h);\n\n        /// bottom line\n        path.lineTo(w - _radius * 2, h);\n\n        /// bottom-right curve\n        path.quadraticBezierTo(w, h, w, h - _radius * 1.5);\n\n        /// right line\n        path.lineTo(w, _radius * 1.5);\n\n        /// top-right curve\n        path.quadraticBezierTo(w, 0, w - _radius * 2, 0);\n        canvas.clipPath(path);\n        canvas.drawRRect(\n            RRect.fromLTRBR(0, 0, w, h, Radius.zero),\n            Paint()\n              ..color = color\n              ..style = PaintingStyle.fill);\n      }\n    }\n  }\n\n  @override\n  bool shouldRepaint(CustomPainter oldDelegate) {\n    return true;\n  }\n}\n\nclass BubbleSpecialTwo extends StatelessWidget {\n  final bool isSender;\n  final String text;\n  final bool tail;\n  final Color color;\n  final bool sent;\n  final bool delivered;\n  final bool seen;\n  final TextStyle textStyle;\n\n  const BubbleSpecialTwo({\n    Key? key,\n    this.isSender = true,\n    required this.text,\n    this.color = Colors.white70,\n    this.tail = true,\n    this.sent = false,\n    this.delivered = false,\n    this.seen = false,\n    this.textStyle = const TextStyle(\n      color: Colors.black87,\n      fontSize: 16,\n    ),\n  }) : super(key: key);\n\n  ///chat bubble builder method\n  @override\n  Widget build(BuildContext context) {\n    bool stateTick = false;\n    Icon? stateIcon;\n    if (sent) {\n      stateTick = true;\n      stateIcon = const Icon(\n        Icons.done,\n        size: 18,\n        color: Color(0xFF97AD8E),\n      );\n    }\n    if (delivered) {\n      stateTick = true;\n      stateIcon = const Icon(\n        Icons.done_all,\n        size: 18,\n        color: Color(0xFF97AD8E),\n      );\n    }\n    if (seen) {\n      stateTick = true;\n      stateIcon = const Icon(\n        Icons.done_all,\n        size: 18,\n        color: Color(0xFF92DEDA),\n      );\n    }\n\n    return Align(\n      alignment: isSender ? Alignment.topRight : Alignment.topLeft,\n      child: Padding(\n        padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),\n        child: CustomPaint(\n          painter: SpecialChatBubbleTwo(\n              color: color, alignment: isSender ? Alignment.topRight : Alignment.topLeft, tail: tail),\n          child: Container(\n            constraints: BoxConstraints(\n              maxWidth: MediaQuery.of(context).size.width * .8,\n            ),\n            margin: isSender\n                ? stateTick\n                    ? const EdgeInsets.fromLTRB(7, 7, 14, 7)\n                    : const EdgeInsets.fromLTRB(7, 7, 17, 7)\n                : const EdgeInsets.fromLTRB(17, 7, 7, 7),\n            child: Stack(\n              children: <Widget>[\n                Padding(\n                  padding: stateTick\n                      ? const EdgeInsets.only(right: 20)\n                      : const EdgeInsets.symmetric(vertical: 0, horizontal: 0),\n                  child: Text(\n                    text,\n                    style: textStyle,\n                    textAlign: TextAlign.left,\n                  ),\n                ),\n                stateIcon != null && stateTick\n                    ? Positioned(\n                        bottom: 0,\n                        right: 0,\n                        child: stateIcon,\n                      )\n                    : const SizedBox(\n                        width: 1,\n                      ),\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n\n///custom painter use to create the shape of the chat bubble\n///\n/// [color],[alignment] and [tail] can be changed\nclass SpecialChatBubbleTwo extends CustomPainter {\n  final Color color;\n  final Alignment alignment;\n  final bool tail;\n\n  SpecialChatBubbleTwo({\n    required this.color,\n    required this.alignment,\n    required this.tail,\n  });\n\n  final double _radius = CustomSize.radiusValue;\n  final double _x = 10.0;\n\n  @override\n  void paint(Canvas canvas, Size size) {\n    if (alignment == Alignment.topRight) {\n      if (tail) {\n        canvas.drawRRect(\n            RRect.fromLTRBAndCorners(\n              0,\n              0,\n              size.width - 8,\n              size.height,\n              bottomLeft: Radius.circular(_radius),\n              topRight: Radius.circular(_radius),\n              topLeft: Radius.circular(_radius),\n              bottomRight: Radius.circular(_radius),\n            ),\n            Paint()\n              ..color = color\n              ..style = PaintingStyle.fill);\n        var path = Path();\n        path.moveTo(size.width - _x, 4);\n        path.lineTo(size.width - _x, size.height - 5);\n        path.lineTo(size.width, size.height);\n        canvas.clipPath(path);\n        canvas.drawRRect(\n            RRect.fromLTRBAndCorners(\n              size.width - _x,\n              0.0,\n              size.width,\n              size.height,\n            ),\n            Paint()\n              ..color = color\n              ..style = PaintingStyle.fill);\n      } else {\n        canvas.drawRRect(\n            RRect.fromLTRBAndCorners(\n              0,\n              0,\n              size.width - 8,\n              size.height,\n              bottomLeft: Radius.circular(_radius),\n              topRight: Radius.circular(_radius),\n              topLeft: Radius.circular(_radius),\n              bottomRight: Radius.circular(_radius),\n            ),\n            Paint()\n              ..color = color\n              ..style = PaintingStyle.fill);\n      }\n    } else {\n      if (tail) {\n        canvas.drawRRect(\n            RRect.fromLTRBAndCorners(\n              8,\n              0,\n              size.width,\n              size.height,\n              bottomRight: Radius.circular(_radius),\n              topRight: Radius.circular(_radius),\n              topLeft: Radius.circular(_radius),\n              bottomLeft: Radius.circular(_radius),\n            ),\n            Paint()\n              ..color = color\n              ..style = PaintingStyle.fill);\n        var path = Path();\n        path.moveTo(_x, 4);\n        path.lineTo(0, size.height);\n        path.lineTo(_x, size.height - 5);\n        canvas.clipPath(path);\n        canvas.drawRRect(\n            RRect.fromLTRBAndCorners(\n              0,\n              0.0,\n              _x,\n              size.height,\n              topRight: Radius.circular(_radius),\n            ),\n            Paint()\n              ..color = color\n              ..style = PaintingStyle.fill);\n      } else {\n        canvas.drawRRect(\n            RRect.fromLTRBAndCorners(\n              8,\n              0,\n              size.width,\n              size.height,\n              bottomRight: Radius.circular(_radius),\n              topRight: Radius.circular(_radius),\n              topLeft: Radius.circular(_radius),\n              bottomLeft: Radius.circular(_radius),\n            ),\n            Paint()\n              ..color = color\n              ..style = PaintingStyle.fill);\n      }\n    }\n  }\n\n  @override\n  bool shouldRepaint(CustomPainter oldDelegate) {\n    return true;\n  }\n}\n"
  },
  {
    "path": "lib/page/component/chat/chat_input.dart",
    "content": "import 'dart:io';\n\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/chat/chat_input_button.dart';\nimport 'package:askaide/page/component/chat/file_upload.dart';\nimport 'package:askaide/page/component/chat/voice_record.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/file_preview.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:camera/camera.dart';\nimport 'package:camerawesome/camerawesome_plugin.dart';\nimport 'package:file_picker/file_picker.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:loading_animation_widget/loading_animation_widget.dart';\n\nclass ChatInput extends StatefulWidget {\n  final Function(String value) onSubmit;\n  final ValueNotifier<bool> enableNotifier;\n  final bool enableImageUpload;\n  final Function(List<FileUpload> files)? onImageSelected;\n  final List<FileUpload>? selectedImageFiles;\n  final Function()? onNewChat;\n  final String hintText;\n  final Function()? onVoiceRecordTappedEvent;\n  final List<Widget> Function()? toolsBuilder;\n  final Function()? onStopGenerate;\n  final Function(bool hasFocus)? onFocusChange;\n\n  // Whether to enable file uploading\n  final bool enableFileUpload;\n  // Selected file for uploading\n  final FileUpload? selectedFile;\n  final Function(FileUpload? file)? onFileSelected;\n\n  const ChatInput({\n    super.key,\n    required this.onSubmit,\n    required this.enableNotifier,\n    this.enableImageUpload = true,\n    this.onNewChat,\n    this.hintText = '',\n    this.onVoiceRecordTappedEvent,\n    this.toolsBuilder,\n    this.onImageSelected,\n    this.selectedImageFiles,\n    this.onStopGenerate,\n    this.onFocusChange,\n    this.enableFileUpload = false,\n    this.selectedFile,\n    this.onFileSelected,\n  });\n\n  @override\n  State<ChatInput> createState() => _ChatInputState();\n}\n\nclass _ChatInputState extends State<ChatInput> with TickerProviderStateMixin {\n  final TextEditingController _textController = TextEditingController();\n\n  /// 用于监听键盘事件，实现回车发送消息，Shift+Enter换行\n  late final FocusNode _focusNode = FocusNode(\n    onKeyEvent: (node, event) {\n      if (!HardwareKeyboard.instance.isShiftPressed && event.logicalKey.keyLabel == 'Enter') {\n        if (event is KeyDownEvent && widget.enableNotifier.value) {\n          _handleSubmited(_textController.text.trim());\n        }\n\n        return KeyEventResult.handled;\n      } else {\n        return KeyEventResult.ignored;\n      }\n    },\n  );\n\n  final maxLength = 150000;\n  var hasCamera = false;\n  var showExtensionButtons = false;\n\n  // Whether to display the bottom tool bar\n  var showBottomTools = false;\n  // Whether the input box is focused\n  var inputFocused = false;\n\n  @override\n  void initState() {\n    super.initState();\n\n    if (!PlatformTool.isDesktopAndWeb()) {\n      availableCameras().then((cameras) {\n        setState(() {\n          hasCamera = cameras.isNotEmpty;\n        });\n      });\n    }\n\n    _textController.addListener(() {\n      setState(() {});\n    });\n\n    // 机器人回复完成后自动输入框自动获取焦点\n    if (!PlatformTool.isAndroid() && !PlatformTool.isIOS()) {\n      widget.enableNotifier.addListener(() {\n        if (widget.enableNotifier.value) {\n          _focusNode.requestFocus();\n        }\n      });\n    }\n  }\n\n  @override\n  void dispose() {\n    _textController.dispose();\n    super.dispose();\n  }\n\n  // Whether the user can upload images\n  bool get canUploadImage =>\n      widget.enableImageUpload &&\n      Ability().supportImageUploader &&\n      widget.onImageSelected != null &&\n      Ability().supportWebSocket;\n\n  // Whether the user can upload files\n  bool get canUploadFile => widget.enableFileUpload && Ability().supportWebSocket;\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return GestureDetector(\n      onTap: () {\n        _focusNode.requestFocus();\n      },\n      child: SafeArea(\n        bottom: false,\n        child: Container(\n          margin: PlatformTool.isDesktopAndWeb() ? const EdgeInsets.all(8) : null,\n          padding: const EdgeInsets.only(left: 16, right: 16, top: 8),\n          decoration: BoxDecoration(\n            color: customColors.chatInputAreaBackground,\n            borderRadius: PlatformTool.isDesktopAndWeb()\n                ? BorderRadius.circular(CustomSize.radiusValue)\n                : const BorderRadius.only(\n                    topLeft: Radius.circular(CustomSize.radiusValue * 2),\n                    topRight: Radius.circular(CustomSize.radiusValue * 2),\n                  ),\n            boxShadow: [\n              BoxShadow(\n                color: Colors.black.withOpacity(0.08),\n                offset: const Offset(-1, -1),\n                blurRadius: CustomSize.radiusValue,\n              ),\n              if (PlatformTool.isDesktopAndWeb())\n                BoxShadow(\n                  color: Colors.black.withOpacity(0.08),\n                  offset: const Offset(-1, -1),\n                  blurRadius: CustomSize.radiusValue,\n                ),\n            ],\n          ),\n          child: Builder(builder: (context) {\n            final setting = context.read<SettingRepository>();\n            return SafeArea(\n              child: Column(\n                children: [\n                  // 选中的图片预览\n                  if (widget.selectedImageFiles != null && widget.selectedImageFiles!.isNotEmpty)\n                    buildSelectedImagePreview(customColors),\n                  // 选中文件预览\n                  if (widget.selectedFile != null) buildSelectedFilePreview(customColors),\n                  // 聊天内容输入栏\n                  Column(\n                    crossAxisAlignment: CrossAxisAlignment.start,\n                    children: [\n                      Focus(\n                        onFocusChange: (hasFocus) {\n                          setState(() {\n                            inputFocused = hasFocus;\n                          });\n\n                          widget.onFocusChange?.call(hasFocus);\n                        },\n                        child: TextFormField(\n                          keyboardType: TextInputType.multiline,\n                          textInputAction: TextInputAction.newline,\n                          maxLines: inputFocused ? 10 : 3,\n                          minLines: 1,\n                          maxLength: maxLength,\n                          focusNode: _focusNode,\n                          controller: _textController,\n                          style: const TextStyle(fontSize: CustomSize.defaultHintTextSize),\n                          decoration: InputDecoration(\n                            hintText: widget.hintText,\n                            hintStyle: TextStyle(\n                              fontSize: CustomSize.defaultHintTextSize,\n                              color: customColors.textfieldHintColor,\n                            ),\n                            border: InputBorder.none,\n                            counterText: '',\n                          ),\n                        ),\n                      ),\n                      Row(\n                        mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                        children: [\n                          Row(\n                            mainAxisAlignment: MainAxisAlignment.start,\n                            children: [\n                              if (widget.enableNotifier.value && (canUploadImage || canUploadFile))\n                                buildUploadButtons(context, setting, customColors),\n                              if (widget.toolsBuilder != null) ...widget.toolsBuilder!(),\n                            ],\n                          ),\n                          _buildSendOrVoiceButton(context, customColors),\n                        ],\n                      ),\n                      AnimatedContainer(\n                        duration: const Duration(milliseconds: 200),\n                        height: showExtensionButtons && widget.enableNotifier.value ? 80 : 0,\n                        child: SingleChildScrollView(\n                          child: Row(\n                            children: [\n                              if (canUploadImage && hasCamera)\n                                ChatInputSquareButton(\n                                  icon: Icons.camera_alt,\n                                  onPressed: () {\n                                    onTakePhotoButtonPressed(context, customColors);\n                                  },\n                                  text: AppLocale.takePhoto.getString(context),\n                                ),\n                              if (canUploadImage)\n                                ChatInputSquareButton(\n                                  icon: Icons.photo_library,\n                                  onPressed: () {\n                                    onImageUploadButtonPressed();\n                                  },\n                                  text: AppLocale.photoLibrary.getString(context),\n                                ),\n                              if (canUploadFile)\n                                ChatInputSquareButton(\n                                  icon: Icons.upload_file_sharp,\n                                  onPressed: () {\n                                    onFileUploadButtonPressed();\n                                  },\n                                  text: AppLocale.fileLibrary.getString(context),\n                                ),\n                            ],\n                          ),\n                        ),\n                      ),\n                      SizedBox(height: PlatformTool.isMobile() && inputFocused ? 8 : 6),\n                    ],\n                  ),\n                ],\n              ),\n            );\n          }),\n        ),\n      ),\n    );\n  }\n\n  Widget buildSelectedFilePreview(CustomColors customColors) {\n    var maxWidth = MediaQuery.of(context).size.width * 0.8;\n    if (maxWidth > 300) {\n      maxWidth = 300;\n    }\n\n    return SizedBox(\n      height: 30,\n      child: ListView(\n        scrollDirection: Axis.horizontal,\n        children: [\n          Container(\n            margin: const EdgeInsets.only(right: 8),\n            padding: const EdgeInsets.all(5),\n            child: Stack(\n              children: [\n                FilePreview(\n                  fileType: widget.selectedFile!.file.extension ?? '',\n                  maxWidth: maxWidth,\n                  filename: widget.selectedFile!.file.name,\n                ),\n                if (widget.enableNotifier.value)\n                  Positioned(\n                    right: 5,\n                    top: 5,\n                    child: InkWell(\n                      onTap: () {\n                        setState(() {\n                          widget.onFileSelected?.call(null);\n                        });\n                      },\n                      child: Container(\n                        padding: const EdgeInsets.all(3),\n                        decoration: BoxDecoration(\n                          borderRadius: CustomSize.borderRadius,\n                          color: customColors.chatRoomBackground,\n                        ),\n                        child: Icon(\n                          Icons.close,\n                          size: 10,\n                          color: customColors.weakTextColor,\n                        ),\n                      ),\n                    ),\n                  ),\n              ],\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n\n  Widget buildSelectedImagePreview(CustomColors customColors) {\n    return SizedBox(\n      height: 110,\n      child: ListView(\n        scrollDirection: Axis.horizontal,\n        children: widget.selectedImageFiles!\n            .map(\n              (e) => Container(\n                margin: const EdgeInsets.only(right: 8),\n                padding: const EdgeInsets.all(5),\n                child: Stack(\n                  children: [\n                    ClipRRect(\n                      borderRadius: CustomSize.borderRadius,\n                      child: e.file.bytes != null\n                          ? Image.memory(\n                              e.file.bytes!,\n                              fit: BoxFit.cover,\n                              width: 100,\n                              height: 100,\n                            )\n                          : Image.file(\n                              File(e.file.path!),\n                              fit: BoxFit.cover,\n                              width: 100,\n                              height: 100,\n                            ),\n                    ),\n                    if (widget.enableNotifier.value)\n                      Positioned(\n                        right: 5,\n                        top: 5,\n                        child: InkWell(\n                          onTap: () {\n                            setState(() {\n                              widget.selectedImageFiles!.remove(e);\n                              widget.onImageSelected?.call(widget.selectedImageFiles!);\n                            });\n                          },\n                          child: Container(\n                            padding: const EdgeInsets.all(3),\n                            decoration: BoxDecoration(\n                              borderRadius: CustomSize.borderRadius * 3,\n                              color: customColors.chatRoomBackground,\n                              border: Border.all(\n                                color: customColors.weakTextColor ?? Colors.white,\n                                width: 1,\n                              ),\n                            ),\n                            child: Icon(\n                              Icons.close,\n                              size: 14,\n                              color: customColors.weakTextColor,\n                            ),\n                          ),\n                        ),\n                      ),\n                  ],\n                ),\n              ),\n            )\n            .toList(),\n      ),\n    );\n  }\n\n  /// 构建发送或者语音按钮\n  Widget _buildSendOrVoiceButton(\n    BuildContext context,\n    CustomColors customColors,\n  ) {\n    if (!widget.enableNotifier.value) {\n      return InkWell(\n        onTap: () {\n          if (widget.onStopGenerate != null) {\n            openConfirmDialog(\n              context,\n              AppLocale.confirmStopOutput.getString(context),\n              () {\n                widget.onStopGenerate!();\n                HapticFeedbackHelper.heavyImpact();\n              },\n              danger: true,\n            );\n          }\n        },\n        child: LoadingAnimationWidget.beat(\n          color: customColors.linkColor ?? Colors.green,\n          size: 20,\n        ),\n      );\n    }\n\n    return _textController.text == '' && Ability().supportVoiceChat\n        ? IconButton(\n            onPressed: () {\n              HapticFeedbackHelper.mediumImpact();\n\n              openModalBottomSheet(\n                context,\n                (context) {\n                  return VoiceRecord(\n                    onFinished: (text) {\n                      _textController.text = text;\n                      Navigator.pop(context);\n                    },\n                    onStart: () {\n                      widget.onVoiceRecordTappedEvent?.call();\n                    },\n                  );\n                },\n                isScrollControlled: false,\n                heightFactor: 0.8,\n              );\n            },\n            icon: Icon(\n              Icons.mic_none_outlined,\n              color: customColors.chatInputPanelText,\n            ),\n            splashRadius: 20,\n            color: customColors.chatInputPanelText,\n          )\n        : IconButton(\n            onPressed: () => _handleSubmited(_textController.text.trim()),\n            icon: Icon(\n              Icons.send,\n              color: customColors.chatInputPanelText,\n            ),\n            splashRadius: 20,\n            color: customColors.chatInputPanelText,\n          );\n  }\n\n  // Image upload button event\n  void onImageUploadButtonPressed() async {\n    HapticFeedbackHelper.mediumImpact();\n    if (widget.selectedImageFiles != null && widget.selectedImageFiles!.length >= 4) {\n      showSuccessMessage(AppLocale.uploadImageLimit4.getString(context));\n      return;\n    }\n\n    FilePickerResult? result = await FilePicker.platform.pickFiles(\n      type: FileType.image,\n      allowMultiple: true,\n      allowCompression: true,\n    );\n    if (result != null && result.files.isNotEmpty) {\n      final files = widget.selectedImageFiles ?? [];\n      files.addAll(result.files.map((e) => FileUpload(file: e)).toList());\n      widget.onImageSelected?.call(files.sublist(0, files.length > 4 ? 4 : files.length));\n    }\n  }\n\n  // File upload button event\n  void onFileUploadButtonPressed() async {\n    HapticFeedbackHelper.mediumImpact();\n\n    FilePickerResult? result = await FilePicker.platform.pickFiles(\n      type: FileType.custom,\n      allowedExtensions: ['pdf', 'docx', 'txt', 'md'],\n      allowMultiple: false,\n    );\n    if (result != null && result.files.isNotEmpty) {\n      if (widget.onFileSelected != null) {\n        widget.onFileSelected?.call(FileUpload(file: result.files.first));\n      }\n    }\n  }\n\n  /// Build image or file upload button\n  Widget buildUploadButtons(\n    BuildContext context,\n    SettingRepository setting,\n    CustomColors customColors,\n  ) {\n    return InkWell(\n      onTap: () {\n        setState(() {\n          showExtensionButtons = !showExtensionButtons;\n        });\n      },\n      child: Container(\n        decoration: BoxDecoration(\n          color: customColors.tagsBackground,\n          shape: BoxShape.circle,\n        ),\n        padding: const EdgeInsets.all(4),\n        child: AnimatedRotation(\n          turns: showExtensionButtons ? 0.125 : 0,\n          duration: const Duration(milliseconds: 200),\n          child: Icon(\n            Icons.add,\n            color: customColors.chatInputPanelText,\n            size: 18,\n          ),\n        ),\n      ),\n    );\n  }\n\n  // Take a photo\n  void onTakePhotoButtonPressed(BuildContext context, CustomColors customColors) {\n    HapticFeedbackHelper.mediumImpact();\n    Navigator.push(\n      context,\n      MaterialPageRoute(\n        builder: (context) => Scaffold(\n          appBar: AppBar(\n            title: Text(AppLocale.takePhoto.getString(context)),\n            backgroundColor: customColors.backgroundColor,\n          ),\n          body: CameraAwesomeBuilder.awesome(\n            saveConfig: SaveConfig.photo(),\n            enablePhysicalButton: true,\n            onMediaCaptureEvent: (mediaCapture) async {\n              if (mediaCapture.status == MediaCaptureStatus.success) {\n                final file = FileUpload(\n                    file: PlatformFile(\n                  path: mediaCapture.captureRequest.path!,\n                  name: mediaCapture.captureRequest.path!.split('/').last,\n                  size: await File(mediaCapture.captureRequest.path!).length(),\n                ));\n\n                final files = widget.selectedImageFiles ?? [];\n                files.add(file);\n                widget.onImageSelected?.call(files.sublist(0, files.length > 4 ? 4 : files.length));\n\n                if (context.mounted) {\n                  context.pop();\n                }\n              }\n            },\n          ),\n        ),\n      ),\n    );\n  }\n\n  /// 处理输入框提交\n  void _handleSubmited(String text, {bool notSend = false}) {\n    if (notSend) {\n      var cursorPos = _textController.selection.base.offset;\n      if (cursorPos < 0) {\n        _textController.text = text;\n      } else {\n        String suffixText = _textController.text.substring(cursorPos);\n        String prefixText = _textController.text.substring(0, cursorPos);\n        _textController.text = prefixText + text + suffixText;\n        _textController.selection = TextSelection(\n          baseOffset: cursorPos + text.length,\n          extentOffset: cursorPos + text.length,\n        );\n      }\n\n      _focusNode.requestFocus();\n\n      return;\n    }\n\n    if (text != '') {\n      widget.onSubmit(text);\n      _textController.clear();\n    }\n  }\n}\n"
  },
  {
    "path": "lib/page/component/chat/chat_input_button.dart",
    "content": "import 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\n\nclass ChatInputButton extends StatefulWidget {\n  final String text;\n  final IconData icon;\n  final VoidCallback onPressed;\n  final bool isActive;\n  const ChatInputButton(\n      {super.key, required this.text, required this.icon, required this.onPressed, this.isActive = false});\n\n  @override\n  State<ChatInputButton> createState() => _ChatInputButtonState();\n}\n\nclass _ChatInputButtonState extends State<ChatInputButton> {\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return IconButton(\n      onPressed: widget.onPressed,\n      icon: Container(\n        decoration: BoxDecoration(\n          color: widget.isActive ? customColors.linkColor?.withAlpha(100) : null,\n          borderRadius: BorderRadius.circular(CustomSize.radiusValue * 2),\n        ),\n        padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),\n        child: Row(\n          children: [\n            Icon(\n              widget.icon,\n              color: widget.isActive ? customColors.linkColor : customColors.chatInputPanelText,\n              size: 18,\n            ),\n            const SizedBox(width: 4),\n            Text(\n              widget.text,\n              style: TextStyle(\n                fontSize: 14,\n                fontWeight: FontWeight.bold,\n                color: widget.isActive ? customColors.linkColor : customColors.chatInputPanelText,\n              ),\n            ),\n          ],\n        ),\n      ),\n      style: ButtonStyle(\n        overlayColor: WidgetStateProperty.all(Colors.transparent),\n      ),\n    );\n  }\n}\n\nclass ChatInputSquareButton extends StatelessWidget {\n  final IconData icon;\n  final VoidCallback onPressed;\n  final bool isActive;\n  final String text;\n\n  const ChatInputSquareButton(\n      {super.key, required this.icon, required this.onPressed, this.isActive = false, required this.text});\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return Container(\n      margin: const EdgeInsets.only(right: 8),\n      child: IconButton(\n        onPressed: onPressed,\n        icon: Container(\n          decoration: BoxDecoration(\n            color: isActive ? customColors.linkColor?.withAlpha(100) : null,\n            borderRadius: BorderRadius.circular(CustomSize.radiusValue * 2),\n          ),\n          padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),\n          child: Column(\n            children: [\n              Icon(\n                icon,\n                color: isActive ? customColors.linkColor : customColors.chatInputPanelText,\n                size: 30,\n              ),\n              const SizedBox(height: 4),\n              Text(\n                text,\n                style: TextStyle(\n                  fontSize: 12,\n                  fontWeight: FontWeight.bold,\n                  color: isActive ? customColors.linkColor : customColors.chatInputPanelText,\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/chat/chat_preview.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\n\nimport 'package:askaide/bloc/chat_message_bloc.dart';\nimport 'package:askaide/bloc/room_bloc.dart';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/attached_button_panel.dart';\nimport 'package:askaide/page/component/chat/chat_share.dart';\nimport 'package:askaide/page/component/chat/enhanced_selectable_text.dart';\nimport 'package:askaide/page/component/chat/file_upload.dart';\nimport 'package:askaide/page/component/chat/message_state_manager.dart';\nimport 'package:askaide/page/component/chat/search_result.dart';\nimport 'package:askaide/page/component/chat/thinking_card.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/file_preview.dart';\nimport 'package:askaide/page/component/random_avatar.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/chat/markdown.dart';\nimport 'package:askaide/repo/model/message.dart';\nimport 'package:clipboard/clipboard.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:loading_animation_widget/loading_animation_widget.dart';\nimport 'package:quickalert/models/quickalert_type.dart';\nimport 'package:url_launcher/url_launcher_string.dart';\n\nclass ChatPreview extends StatefulWidget {\n  final List<MessageWithState> messages;\n  final ScrollController? scrollController;\n  final void Function(int id)? onDeleteMessage;\n  final void Function()? onResetContext;\n  final ChatPreviewController controller;\n  final MessageStateManager? stateManager;\n  final List<Widget>? helpWidgets;\n  final Widget? robotAvatar;\n  final Widget? Function(Message message)? avatarBuilder;\n  final Widget? Function(Message message)? senderNameBuilder;\n  final bool supportBloc;\n  final void Function(Message message)? onSpeakEvent;\n  final void Function(Message message, int index)? onResentEvent;\n  final EdgeInsetsGeometry? padding;\n  final Widget Function(Message message)? messageFooterBuilder;\n\n  const ChatPreview({\n    super.key,\n    required this.messages,\n    this.scrollController,\n    this.onDeleteMessage,\n    this.onResetContext,\n    required this.controller,\n    this.stateManager,\n    this.robotAvatar,\n    this.avatarBuilder,\n    this.senderNameBuilder,\n    this.helpWidgets,\n    this.onSpeakEvent,\n    this.onResentEvent,\n    this.supportBloc = true,\n    this.padding,\n    this.messageFooterBuilder,\n  });\n\n  @override\n  State<ChatPreview> createState() => _ChatPreviewState();\n}\n\nclass _ChatPreviewState extends State<ChatPreview> {\n  @override\n  void initState() {\n    widget.controller.addListener(() {\n      setState(() {});\n    });\n\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    var messages = widget.messages.reversed.toList();\n\n    return ListView.builder(\n      controller: widget.scrollController,\n      itemCount: messages.length,\n      shrinkWrap: true,\n      reverse: true,\n      physics: const AlwaysScrollableScrollPhysics(),\n      padding: widget.padding,\n      cacheExtent: MediaQuery.of(context).size.height * 10,\n      itemBuilder: (context, index) {\n        final message = messages[index];\n\n        return Column(\n          children: [\n            // 消息类型为 hide，不展示\n            if (message.message.type == MessageType.hide) Container(),\n\n            if (message.message.type != MessageType.hide)\n              Row(\n                crossAxisAlignment: CrossAxisAlignment.center,\n                children: [\n                  // 消息选择模式，显示选择框\n                  if (widget.controller.selectMode && !message.message.isSystem())\n                    Checkbox(\n                      value: widget.controller.isMessageSelected(message.message.id!),\n                      activeColor: customColors.linkColor,\n                      onChanged: (value) {\n                        if (value != null && value) {\n                          widget.controller.selectMessage(message.message.id!);\n                        } else {\n                          widget.controller.unSelectMessage(message.message.id!);\n                        }\n                      },\n                    ),\n\n                  // 消息主体部分\n                  Expanded(\n                    child: widget.supportBloc\n                        ? BlocBuilder<ChatMessageBloc, ChatMessageState>(\n                            buildWhen: (previous, current) =>\n                                (current is ChatMessageUpdated && current.message.id == message.message.id),\n                            builder: (context, state) {\n                              return Container(\n                                padding: const EdgeInsets.all(5),\n                                child: _buildMessageBox(\n                                  context,\n                                  customColors,\n                                  _resolveMessage(state, message),\n                                  message.state,\n                                  index,\n                                ),\n                              );\n                            },\n                          )\n                        : Container(\n                            padding: const EdgeInsets.all(5),\n                            child: _buildMessageBox(\n                              context,\n                              customColors,\n                              message.message,\n                              message.state,\n                              index,\n                            ),\n                          ),\n                  ),\n                ],\n              ),\n\n            if (index == 0 && widget.helpWidgets != null && !message.message.isSystem())\n              for (var widget in widget.helpWidgets!) widget,\n          ],\n        );\n      },\n    );\n  }\n\n  Message _resolveMessage(ChatMessageState state, MessageWithState message) {\n    if (state is ChatMessageUpdated && state.message.id == message.message.id) {\n      return state.message;\n    }\n\n    return message.message;\n  }\n\n  final Map<int, bool> _displayThinkingProcess = {};\n\n  /// 构建消息框\n  Widget _buildMessageBox(\n    BuildContext context,\n    CustomColors customColors,\n    Message message,\n    MessageState state,\n    int index,\n  ) {\n    // 系统消息\n    if (message.isSystem()) {\n      return Align(\n        alignment: Alignment.center,\n        child: Container(\n          padding: const EdgeInsets.symmetric(\n            horizontal: 16,\n            vertical: 5,\n          ),\n          child: Text(\n            message.isTimeline() ? message.friendlyTime() : message.text.getString(context),\n            style: Theme.of(context).textTheme.bodySmall,\n          ),\n        ),\n      );\n    }\n\n    final showTranslate = state.showTranslate && state.translateText != null && state.translateText != '';\n\n    final extra = message.decodeExtra();\n    final extraInfo = index == 0 && extra != null ? extra['info'] ?? '' : '';\n    final reasoning = extra != null ? extra['reasoning'] ?? '' : '';\n    final states = extra != null ? extra['states'] ?? [] : [];\n\n    var referenceDocuments = <ReferenceDocument>[];\n    try {\n      final referenceDocumentsData = extra != null ? extra['reference-documents'] ?? '[]' : '[]';\n      final List<dynamic> decodedDocs = jsonDecode(referenceDocumentsData);\n      referenceDocuments = decodedDocs.map((e) => ReferenceDocument.fromJson(e)).toList().cast<ReferenceDocument>();\n    } catch (e) {\n      print('------------> $e <-----------');\n      referenceDocuments = [];\n    }\n\n    var searchResults = <ReferenceDocument>[];\n    try {\n      final searchResultsData = extra != null ? extra['search-results'] ?? '[]' : '[]';\n      final List<dynamic> decodedDocs = jsonDecode(searchResultsData);\n      searchResults = decodedDocs.map((e) => ReferenceDocument.fromJson(e)).toList().cast<ReferenceDocument>();\n    } catch (e) {\n      print('------------> $e <-----------');\n    }\n\n    final stateWidgets = <Widget>[];\n    if (states.isNotEmpty) {\n      final lastState = states[states.length - 1];\n      switch (lastState) {\n        case 'searching':\n          if (index == 0) {\n            stateWidgets.add(const SearchResult(searchResults: [], isSearching: true));\n          }\n          break;\n        case 'thinking':\n          if (index == 0) {\n            if (reasoning != '') {\n              stateWidgets.add(ThinkingCard(\n                content: reasoning,\n                title: AppLocale.thinkingProcess.getString(context),\n                isExpanded: true,\n                onTap: (displayThinkingProcess) {},\n              ));\n            } else {\n              stateWidgets.add(Row(\n                children: [\n                  Text(\n                    AppLocale.robotIsThinkingMessage.getString(context),\n                    style: TextStyle(\n                      fontSize: 14,\n                      color: customColors.weakTextColorLess!,\n                    ),\n                  ),\n                  const SizedBox(width: 10),\n                  LoadingAnimationWidget.waveDots(\n                    color: customColors.weakTextColorLess!,\n                    size: 16,\n                  ),\n                ],\n              ));\n            }\n          }\n          break;\n        case 'thinking-done':\n          if (reasoning != '') {\n            final timeConsumed = extra != null ? extra['thinking_time_consumed'] ?? 0.0 : 0.0;\n            stateWidgets.add(ThinkingCard(\n              content: reasoning,\n              title: AppLocale.thinkingProcess.getString(context),\n              timeConsumed: timeConsumed.toDouble(),\n              isExpanded: _displayThinkingProcess[message.id ?? -1] ?? false,\n              onTap: (displayThinkingProcess) {\n                setState(() {\n                  _displayThinkingProcess[message.id ?? -1] = displayThinkingProcess;\n                });\n              },\n            ));\n          }\n          break;\n        default:\n      }\n    }\n\n    // 普通消息\n    return Align(\n      alignment: message.role == Role.sender ? Alignment.topRight : Alignment.topLeft,\n      child: ConstrainedBox(\n        constraints: BoxConstraints(maxWidth: _chatBoxMaxWidth(context)),\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          crossAxisAlignment: CrossAxisAlignment.end,\n          children: [\n            // 文件\n            if (message.file != null)\n              Container(\n                margin: message.role == Role.sender\n                    ? const EdgeInsets.fromLTRB(0, 0, 10, 7)\n                    : const EdgeInsets.fromLTRB(10, 0, 0, 7),\n                padding: const EdgeInsets.only(bottom: 5, left: 5),\n                constraints: BoxConstraints(\n                  maxWidth: _chatBoxFilePreviewWidth(context),\n                ),\n                child: Builder(builder: (context) {\n                  try {\n                    final file = jsonDecode(message.file!);\n                    final filename = file['name'];\n                    // final fileUrl = file['url'];\n\n                    return FilePreview(\n                      filename: filename,\n                      fileType: filename.split('.').last,\n                      mainAxisAlignment: MainAxisAlignment.end,\n                    );\n                  } catch (e) {\n                    return FilePreview(\n                      fileType: '',\n                      filename: AppLocale.unknownFile.getString(context),\n                      mainAxisAlignment: MainAxisAlignment.end,\n                    );\n                  }\n                }),\n              ),\n            // 图片\n            if (message.images != null && message.images!.isNotEmpty)\n              Container(\n                margin: message.role == Role.sender\n                    ? const EdgeInsets.fromLTRB(0, 0, 10, 7)\n                    : const EdgeInsets.fromLTRB(10, 0, 0, 7),\n                constraints: BoxConstraints(\n                  maxWidth: _chatBoxImagePreviewWidth(\n                    context,\n                    (message.images ?? []).length,\n                  ),\n                ),\n                child: FileUploadPreview(images: message.images ?? []),\n              ),\n            // 消息主体\n            Column(\n              mainAxisSize: MainAxisSize.min,\n              crossAxisAlignment: CrossAxisAlignment.start,\n              children: [\n                // 消息头像\n                Container(\n                  margin: const EdgeInsets.only(left: 10),\n                  child: Row(\n                    mainAxisSize: MainAxisSize.min,\n                    crossAxisAlignment: CrossAxisAlignment.end,\n                    children: [\n                      buildAvatar(message),\n                      // 发送人名称\n                      if (message.role == Role.receiver && widget.senderNameBuilder != null)\n                        widget.senderNameBuilder!(message) ?? const SizedBox(),\n                    ],\n                  ),\n                ),\n                // 消息内容部分\n                ConstrainedBox(\n                  constraints: BoxConstraints(\n                    maxWidth: _chatBoxMaxWidth(context) - 30,\n                  ),\n                  child: Column(\n                    mainAxisSize: MainAxisSize.min,\n                    crossAxisAlignment: CrossAxisAlignment.start,\n                    children: [\n                      Wrap(\n                        crossAxisAlignment: WrapCrossAlignment.end,\n                        children: [\n                          // 错误指示器\n                          if (message.role == Role.sender && message.statusIsFailed())\n                            buildErrorIndicator(message, state, context, index),\n\n                          // 搜索结果\n                          if (searchResults.isNotEmpty)\n                            Container(\n                              margin: const EdgeInsets.only(left: 10, top: 10),\n                              padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),\n                              child: SearchResult(searchResults: searchResults),\n                            ),\n\n                          // 消息过程状态\n                          if (states.isNotEmpty)\n                            Container(\n                              margin: EdgeInsets.only(left: 10, top: searchResults.isEmpty ? 10 : 0),\n                              padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),\n                              child: Column(\n                                mainAxisSize: MainAxisSize.min,\n                                crossAxisAlignment: CrossAxisAlignment.start,\n                                children: stateWidgets,\n                              ),\n                            ),\n\n                          // 消息主体\n                          GestureDetector(\n                            // 选择模式下，单击切换选择与否\n                            // 非选择模式下，单击隐藏键盘\n                            onTap: () {\n                              if (widget.controller.selectMode) {\n                                widget.controller.toggleMessageSelected(message.id!);\n                              }\n                              FocusScope.of(context).requestFocus(FocusNode());\n                            },\n                            // 长按或者双击显示上下文菜单\n                            onLongPressStart: (detail) {\n                              if (PlatformTool.isDesktop()) {\n                                return;\n                              }\n\n                              _handleMessageTapControl(\n                                context,\n                                detail.globalPosition,\n                                message,\n                                state,\n                                index,\n                              );\n                            },\n                            onDoubleTapDown: (details) {\n                              if (PlatformTool.isDesktop()) {\n                                return;\n                              }\n\n                              _handleMessageTapControl(\n                                context,\n                                details.globalPosition,\n                                message,\n                                state,\n                                index,\n                              );\n                            },\n                            onSecondaryTapDown: (details) {\n                              _handleMessageTapControl(\n                                context,\n                                details.globalPosition,\n                                message,\n                                state,\n                                index,\n                              );\n                            },\n                            child: Stack(\n                              children: [\n                                Container(\n                                  margin: message.role == Role.sender\n                                      ? const EdgeInsets.fromLTRB(0, 0, 10, 7)\n                                      : const EdgeInsets.fromLTRB(10, 0, 0, 7),\n                                  decoration: BoxDecoration(\n                                    borderRadius: CustomSize.borderRadius,\n                                    color: message.role == Role.receiver\n                                        ? customColors.chatRoomReplyBackground\n                                        : customColors.chatRoomSenderBackground,\n                                  ),\n                                  padding: const EdgeInsets.symmetric(\n                                    horizontal: 10,\n                                    vertical: 8,\n                                  ),\n                                  child: Builder(\n                                    builder: (context) {\n                                      var text = message.text;\n                                      if (!message.isReady && text != '') {\n                                        text += ' ▌';\n                                      }\n                                      return Column(\n                                        mainAxisSize: MainAxisSize.min,\n                                        crossAxisAlignment: CrossAxisAlignment.start,\n                                        children: [\n                                          state.showMarkdown\n                                              ? Markdown(\n                                                  data: text.trim(),\n                                                  onUrlTap: (value) => onMarkdownUrlTap(value),\n                                                  citations: searchResults.map((e) => e.source).toList(),\n                                                )\n                                              : SelectableText(\n                                                  text,\n                                                  style: TextStyle(\n                                                    color: customColors.chatRoomSenderText,\n                                                  ),\n                                                ),\n                                        ],\n                                      );\n                                    },\n                                  ),\n                                ),\n                                if (extraInfo.isNotEmpty)\n                                  Positioned(\n                                    top: 0,\n                                    right: 0,\n                                    child: InkWell(\n                                      onTap: () {\n                                        showCustomBeautyDialog(\n                                          context,\n                                          type: QuickAlertType.warning,\n                                          confirmBtnText: AppLocale.gotIt.getString(context),\n                                          showCancelBtn: false,\n                                          title: AppLocale.goodTips.getString(context),\n                                          child: Markdown(\n                                            data: extraInfo,\n                                            onUrlTap: (value) {\n                                              onMarkdownUrlTap(value);\n                                              context.pop();\n                                            },\n                                            textStyle: TextStyle(\n                                              fontSize: 14,\n                                              color: customColors.dialogDefaultTextColor,\n                                            ),\n                                          ),\n                                        );\n                                      },\n                                      child: Icon(\n                                        Icons.info_outline,\n                                        size: 16,\n                                        color: customColors.weakLinkColor?.withAlpha(50),\n                                      ),\n                                    ),\n                                  ),\n                              ],\n                            ),\n                          ),\n                        ],\n                      ),\n                      if (showTranslate)\n                        Container(\n                          margin: message.role == Role.sender\n                              ? const EdgeInsets.fromLTRB(7, 10, 14, 7)\n                              : const EdgeInsets.fromLTRB(10, 10, 0, 7),\n                          decoration: BoxDecoration(\n                            borderRadius: CustomSize.borderRadius,\n                            color: message.role == Role.receiver\n                                ? customColors.chatRoomReplyBackgroundSecondary\n                                : customColors.chatRoomSenderBackgroundSecondary,\n                          ),\n                          padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),\n                          child: Builder(\n                            builder: (context) {\n                              return Column(\n                                crossAxisAlignment: CrossAxisAlignment.start,\n                                children: [\n                                  state.showMarkdown\n                                      ? Markdown(data: state.translateText!)\n                                      : SelectableText(\n                                          state.translateText!,\n                                          style: TextStyle(\n                                            color: customColors.chatRoomSenderText,\n                                          ),\n                                        ),\n                                  Row(\n                                    mainAxisSize: MainAxisSize.min,\n                                    children: [\n                                      const Icon(\n                                        Icons.check_circle,\n                                        size: 12,\n                                        color: Colors.green,\n                                      ),\n                                      const SizedBox(width: 5),\n                                      Text(\n                                        AppLocale.translateFinished.getString(context),\n                                        style: const TextStyle(\n                                          fontSize: 12,\n                                          color: Color.fromARGB(255, 145, 145, 145),\n                                        ),\n                                      ),\n                                    ],\n                                  ),\n                                ],\n                              );\n                            },\n                          ),\n                        ),\n                      if (referenceDocuments.isNotEmpty)\n                        Container(\n                          margin: const EdgeInsets.only(left: 20),\n                          child: ReferenceDocumentWidget(referenceDocuments: referenceDocuments),\n                        ),\n                      if (widget.messageFooterBuilder != null) widget.messageFooterBuilder!(message),\n                    ],\n                  ),\n                ),\n              ],\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n\n  Widget buildErrorIndicator(\n    Message message,\n    MessageState state,\n    BuildContext context,\n    int index,\n  ) {\n    return Container(\n      margin: const EdgeInsets.only(right: 5, bottom: 10),\n      child: GestureDetector(\n        onTapUp: (details) {\n          if (widget.controller.selectMode || message.isSystem()) {\n            return;\n          }\n\n          HapticFeedbackHelper.mediumImpact();\n\n          var confirmMessage = '';\n          if (message.extra != null && message.extra!.isNotEmpty) {\n            try {\n              final extra = jsonDecode(message.extra!);\n              if (extra['error'] != null && extra['error'] != '') {\n                var e1 = extra['error'];\n                try {\n                  e1 = (e1 as String).getString(context);\n                  // ignore: empty_catches\n                } catch (ignored) {}\n                confirmMessage = e1;\n              }\n              // ignore: empty_catches\n            } catch (ignored) {}\n          }\n\n          openConfirmDialog(\n            context,\n            confirmMessage,\n            () {\n              widget.onResentEvent!(message, index);\n            },\n            title: Text(AppLocale.robotHasSomeError.getString(context)),\n            confirmText: AppLocale.sendRetry.getString(context),\n          );\n        },\n        child: const Icon(Icons.error, color: Colors.red, size: 20),\n      ),\n    );\n  }\n\n  void onMarkdownUrlTap(value) {\n    if (value.startsWith(\"aidea-app://\")) {\n      var route = value.substring('aidea-app://'.length);\n      context.push(route);\n    } else if (value.startsWith(\"aidea-command://\")) {\n      var command = value.substring('aidea-command://'.length);\n      switch (command) {\n        case \"reset-context\":\n          if (widget.onResetContext != null) {\n            widget.onResetContext!();\n          }\n          break;\n      }\n    } else {\n      launchUrlString(value);\n    }\n  }\n\n  Widget avatarWrap(Widget avatar) {\n    return avatar;\n  }\n\n  Widget buildAvatar(Message message) {\n    if (widget.avatarBuilder != null) {\n      final avatar = widget.avatarBuilder!(message);\n      if (avatar != null) {\n        return avatarWrap(avatar);\n      }\n    }\n\n    if (widget.robotAvatar != null) {\n      if (message.role == Role.receiver && message.avatarUrl != null && (message.roomId ?? 1) <= 1) {\n        return avatarWrap(RemoteAvatar(\n          avatarUrl: message.avatarUrl!,\n          size: 30,\n        ));\n      }\n\n      if (message.role == Role.receiver) {\n        return avatarWrap(widget.robotAvatar!);\n      }\n    }\n\n    return const SizedBox();\n  }\n\n  /// 点击消息后控制操作弹窗菜单\n  void _handleMessageTapControl(\n    BuildContext context,\n    Offset? offset,\n    Message message,\n    MessageState state,\n    int index,\n  ) {\n    if (widget.controller.selectMode || message.isSystem()) {\n      return;\n    }\n\n    HapticFeedbackHelper.mediumImpact();\n\n    final showTranslate = state.showTranslate && state.translateText != null && state.translateText != '';\n\n    BotToast.showAttachedWidget(\n      target: offset,\n      duration: const Duration(seconds: 8),\n      animationDuration: const Duration(milliseconds: 200),\n      animationReverseDuration: const Duration(milliseconds: 200),\n      preferDirection: PreferDirection.topCenter,\n      ignoreContentClick: false,\n      onlyOne: true,\n      allowClick: true,\n      enableSafeArea: true,\n      attachedBuilder: (cancel) => AttachedButtonPanel(\n        buttons: [\n          // 文本、Markdown 模式切换\n          TextButton.icon(\n            onPressed: () {\n              openFullscreenDialog(\n                context,\n                child: Container(\n                  margin: const EdgeInsets.only(top: 15, bottom: 30),\n                  child: EnhancedSelectableText(\n                    text: message.text,\n                  ),\n                ),\n                title: AppLocale.selectText.getString(context),\n              );\n\n              cancel();\n            },\n            label: const Text(''),\n            icon: Column(\n              mainAxisSize: MainAxisSize.min,\n              children: [\n                Icon(\n                  state.showMarkdown ? Icons.text_format : Icons.preview,\n                  color: const Color.fromARGB(255, 255, 255, 255),\n                  size: 14,\n                ),\n                Text(\n                  AppLocale.text.getString(context),\n                  style: const TextStyle(fontSize: 12, color: Colors.white),\n                ),\n              ],\n            ),\n          ),\n          // 复制文本\n          TextButton.icon(\n            onPressed: () {\n              FlutterClipboard.copy(message.text).then((value) {\n                showSuccessMessage(AppLocale.textCopied.getString(context));\n              });\n              cancel();\n            },\n            label: const Text(''),\n            icon: Column(\n              mainAxisSize: MainAxisSize.min,\n              children: [\n                const Icon(\n                  Icons.copy,\n                  color: Color.fromARGB(255, 255, 255, 255),\n                  size: 14,\n                ),\n                Text(\n                  AppLocale.copy.getString(context),\n                  style: const TextStyle(fontSize: 12, color: Colors.white),\n                ),\n              ],\n            ),\n          ),\n          // 翻译\n          if (Ability().supportTranslate && widget.stateManager != null)\n            TextButton.icon(\n                onPressed: () {\n                  cancel();\n\n                  if (showTranslate) {\n                    widget.stateManager!\n                        .setState(message.roomId!, message.id!, state..showTranslate = false)\n                        .then((value) {\n                      setState(() {});\n                      context.read<RoomBloc>().add(RoomLoadEvent(message.roomId!, cascading: false));\n                    });\n                  } else {\n                    if (state.translateText != null && state.translateText != '') {\n                      widget.stateManager!\n                          .setState(message.roomId!, message.id!, state..showTranslate = true)\n                          .then((value) {\n                        setState(() {});\n                        context.read<RoomBloc>().add(RoomLoadEvent(message.roomId!, cascading: false));\n                      });\n                      return;\n                    }\n\n                    APIServer().translate(message.text).then((value) {\n                      widget.stateManager!\n                          .setState(\n                        message.roomId!,\n                        message.id!,\n                        state\n                          ..translateText = value.result!\n                          ..showTranslate = true,\n                      )\n                          .then((value) {\n                        setState(() {});\n                        context.read<RoomBloc>().add(RoomLoadEvent(message.roomId!, cascading: false));\n                      });\n                    }).onError((error, stackTrace) {\n                      showErrorMessage(resolveError(context, error!));\n                    });\n                  }\n                },\n                label: const Text(''),\n                icon: Column(\n                  mainAxisSize: MainAxisSize.min,\n                  children: [\n                    const Icon(\n                      Icons.translate,\n                      color: Color.fromARGB(255, 255, 255, 255),\n                      size: 14,\n                    ),\n                    Text(\n                      showTranslate ? AppLocale.hide.getString(context) : AppLocale.translate.getString(context),\n                      style: const TextStyle(fontSize: 12, color: Colors.white),\n                    )\n                  ],\n                )),\n          // 分享\n          TextButton.icon(\n              onPressed: () async {\n                cancel();\n                var messages = <ChatShareMessage>[];\n\n                if (message.role == Role.receiver) {\n                  final questions = widget.messages.where((e) => e.message.id == message.refId).toList();\n                  if (questions.isNotEmpty) {\n                    var q = questions.first;\n                    messages.add(ChatShareMessage(\n                      content: q.message.text,\n                      images: q.message.images,\n                      leftSide: false,\n                    ));\n                  }\n                }\n\n                messages.add(ChatShareMessage(\n                  content: message.text,\n                  images: message.images,\n                  leftSide: message.role == Role.receiver,\n                  avatarURL: message.avatarUrl,\n                  username: message.senderName,\n                ));\n\n                if (message.role == Role.sender) {\n                  final answers = widget.messages.where((e) => e.message.refId == message.id).toList();\n                  if (answers.isNotEmpty) {\n                    for (var a in answers) {\n                      messages.add(ChatShareMessage(\n                        content: a.message.text,\n                        images: a.message.images,\n                        leftSide: true,\n                        avatarURL: a.message.avatarUrl,\n                        username: a.message.senderName,\n                      ));\n                    }\n                  }\n                }\n\n                Navigator.push(\n                  context,\n                  MaterialPageRoute(\n                    fullscreenDialog: true,\n                    builder: (context) => ChatShareScreen(messages: messages),\n                  ),\n                );\n\n                // await shareTo(context, content: message.text, title: '聊天记录');\n              },\n              label: const Text(''),\n              icon: Column(\n                mainAxisSize: MainAxisSize.min,\n                children: [\n                  const Icon(\n                    Icons.share,\n                    color: Color.fromARGB(255, 255, 255, 255),\n                    size: 14,\n                  ),\n                  Text(\n                    AppLocale.share.getString(context),\n                    style: const TextStyle(fontSize: 12, color: Colors.white),\n                  )\n                ],\n              )),\n          // 选择\n          TextButton.icon(\n              onPressed: () {\n                widget.controller.enterSelectMode();\n                cancel();\n              },\n              label: const Text(''),\n              icon: Column(\n                mainAxisSize: MainAxisSize.min,\n                children: [\n                  const Icon(\n                    Icons.select_all,\n                    color: Color.fromARGB(255, 255, 255, 255),\n                    size: 14,\n                  ),\n                  Text(\n                    AppLocale.select.getString(context),\n                    style: const TextStyle(fontSize: 12, color: Colors.white),\n                  )\n                ],\n              )),\n          // 删除\n          if (widget.onDeleteMessage != null)\n            TextButton.icon(\n              onPressed: () {\n                widget.onDeleteMessage!(message.id!);\n                cancel();\n              },\n              label: const Text(''),\n              icon: Column(\n                mainAxisSize: MainAxisSize.min,\n                children: [\n                  const Icon(\n                    Icons.delete_outline,\n                    color: Color.fromARGB(255, 255, 255, 255),\n                    size: 14,\n                  ),\n                  Text(\n                    AppLocale.delete.getString(context),\n                    style: const TextStyle(fontSize: 12, color: Colors.white),\n                  )\n                ],\n              ),\n            ),\n          // 文本转语音\n          if (Ability().supportSpeak && widget.onSpeakEvent != null)\n            TextButton.icon(\n              onPressed: () {\n                cancel();\n                widget.onSpeakEvent!(message);\n              },\n              label: const Text(''),\n              icon: Column(\n                mainAxisSize: MainAxisSize.min,\n                children: [\n                  const Icon(\n                    Icons.record_voice_over_outlined,\n                    color: Color.fromARGB(255, 255, 255, 255),\n                    size: 14,\n                  ),\n                  Text(\n                    AppLocale.readByVoice.getString(context),\n                    style: const TextStyle(fontSize: 12, color: Colors.white),\n                  )\n                ],\n              ),\n            ),\n          // 重发\n          if (message.role == Role.sender && widget.onResentEvent != null)\n            TextButton.icon(\n              onPressed: () {\n                widget.onResentEvent!(message, index);\n                cancel();\n              },\n              label: const Text(''),\n              icon: Column(\n                mainAxisSize: MainAxisSize.min,\n                children: [\n                  const Icon(\n                    Icons.restore,\n                    color: Color.fromARGB(255, 255, 255, 255),\n                    size: 14,\n                  ),\n                  Text(\n                    AppLocale.sendRetryS.getString(context),\n                    style: const TextStyle(fontSize: 12, color: Colors.white),\n                  ),\n                ],\n              ),\n            ),\n          // 信息\n          if (message.quotaConsumed != null && message.quotaConsumed! > 0)\n            TextButton.icon(\n              onPressed: () {\n                showBeautyDialog(\n                  context,\n                  type: QuickAlertType.info,\n                  text: '本轮对话共 ${message.tokenConsumed} 个 Token， 消耗 ${message.quotaConsumed} 个智慧果。',\n                  confirmBtnText: AppLocale.gotIt.getString(context),\n                  showCancelBtn: false,\n                );\n                cancel();\n              },\n              label: const Text(''),\n              icon: Column(\n                mainAxisSize: MainAxisSize.min,\n                children: [\n                  const Icon(\n                    Icons.info_outline,\n                    color: Color.fromARGB(255, 255, 255, 255),\n                    size: 14,\n                  ),\n                  Text(\n                    AppLocale.info.getString(context),\n                    style: const TextStyle(fontSize: 12, color: Colors.white),\n                  ),\n                ],\n              ),\n            )\n        ],\n      ),\n    );\n  }\n\n  /// 获取聊天框的最大宽度\n  double _chatBoxMaxWidth(BuildContext context) {\n    var screenWidth = MediaQuery.of(context).size.width;\n    if (screenWidth >= CustomSize.maxWindowSize) {\n      return CustomSize.maxWindowSize;\n    }\n\n    return screenWidth;\n  }\n\n  /// 获取图片预览的最大宽度\n  double _chatBoxImagePreviewWidth(BuildContext context, int imageCount) {\n    final expect = _chatBoxMaxWidth(context) / 1.3;\n    final max = imageCount > 1 ? 600.0 : 400.0;\n    return expect > max ? max : expect;\n  }\n\n  // 获取文件预览的最大宽度\n  double _chatBoxFilePreviewWidth(BuildContext context) {\n    var maxWidth = MediaQuery.of(context).size.width * 0.8;\n    if (maxWidth > 300) {\n      maxWidth = 300;\n    }\n\n    return maxWidth;\n  }\n}\n\n/// ChatPreview 控制器\nclass ChatPreviewController extends ChangeNotifier {\n  /// 是否处于多选模式\n  bool _selectMode = false;\n\n  /// 选中的消息ID\n  final _selectedMessageIds = <int>{};\n\n  /// 所有消息\n  List<MessageWithState>? _allMessages;\n\n  bool get selectMode => _selectMode;\n  Set<int> get selectedMessageIds => _selectedMessageIds;\n\n  /// 获取选中的消息\n  List<MessageWithState> selectedMessages() {\n    if (_allMessages == null || _allMessages!.isEmpty) {\n      return [];\n    }\n\n    return _allMessages!.where((element) => _selectedMessageIds.contains(element.message.id)).toList();\n  }\n\n  /// 设置所有消息\n  void setAllMessageIds(List<MessageWithState> messages) {\n    _allMessages = messages.where((e) => !e.message.isSystem()).toList();\n  }\n\n  void toggleSelectMode() {\n    _selectMode = !_selectMode;\n    notifyListeners();\n  }\n\n  void exitSelectMode() {\n    _selectMode = false;\n    _selectedMessageIds.clear();\n    notifyListeners();\n  }\n\n  void enterSelectMode() {\n    _selectMode = true;\n    _selectedMessageIds.clear();\n    notifyListeners();\n  }\n\n  void toggleMessageSelected(int messageId) {\n    if (_selectedMessageIds.contains(messageId)) {\n      _selectedMessageIds.remove(messageId);\n    } else {\n      _selectedMessageIds.add(messageId);\n    }\n    notifyListeners();\n  }\n\n  void selectAllMessage() {\n    if (_allMessages == null || _allMessages!.isEmpty) {\n      return;\n    }\n\n    if (_selectedMessageIds.length == _allMessages!.length) {\n      _selectedMessageIds.clear();\n      notifyListeners();\n      return;\n    }\n\n    _selectedMessageIds.clear();\n    for (var msg in _allMessages!) {\n      _selectedMessageIds.add(msg.message.id!);\n    }\n\n    notifyListeners();\n  }\n\n  void selectMessage(int id) {\n    _selectedMessageIds.add(id);\n    notifyListeners();\n  }\n\n  void unSelectMessage(int id) {\n    _selectedMessageIds.remove(id);\n    notifyListeners();\n  }\n\n  bool isMessageSelected(int id) {\n    return _selectedMessageIds.contains(id);\n  }\n}\n\nclass ReferenceDocument {\n  final String title;\n  final String source;\n  final String content;\n  final String media;\n  final String icon;\n  final String index;\n\n  ReferenceDocument(\n      {required this.title,\n      required this.source,\n      required this.content,\n      required this.media,\n      required this.icon,\n      required this.index});\n\n  static fromJson(Map<String, dynamic> json) {\n    return ReferenceDocument(\n      title: json['title'] ?? '',\n      source: json['source'] ?? '',\n      content: json['content'] ?? '',\n      media: json['media'] ?? '',\n      icon: json['icon'] ?? '',\n      index: json['index'] ?? '',\n    );\n  }\n}\n\nclass ReferenceDocumentWidget extends StatelessWidget {\n  const ReferenceDocumentWidget({super.key, required this.referenceDocuments});\n\n  final List<ReferenceDocument> referenceDocuments;\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return Column(\n      mainAxisSize: MainAxisSize.min,\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: [\n        Text(\n          AppLocale.referenceDocuments.getString(context),\n          style: TextStyle(\n            fontSize: 14,\n            color: customColors.weakTextColorLess,\n          ),\n        ),\n        const SizedBox(height: 10),\n        ListView.builder(\n          shrinkWrap: true,\n          physics: const NeverScrollableScrollPhysics(),\n          itemCount: referenceDocuments.length,\n          itemBuilder: (context, index) {\n            return Container(\n              padding: const EdgeInsets.only(left: 15, bottom: 8),\n              child: Row(\n                children: [\n                  Flexible(\n                    child: MouseRegion(\n                      cursor: SystemMouseCursors.click,\n                      child: GestureDetector(\n                        onTap: () {\n                          launchUrlString(referenceDocuments[index].source);\n                        },\n                        child: Text(\n                          '${index + 1}. ${referenceDocuments[index].title}',\n                          style: TextStyle(\n                            fontSize: 14,\n                            color: customColors.weakTextColorLess,\n                          ),\n                        ),\n                      ),\n                    ),\n                  ),\n                ],\n              ),\n            );\n          },\n        ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/chat/chat_share.dart",
    "content": "import 'dart:io';\n\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/helper/image.dart';\nimport 'package:askaide/helper/logger.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/chat/file_upload.dart';\nimport 'package:askaide/page/component/chat/markdown.dart';\nimport 'package:askaide/page/component/enhanced_popup_menu.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/random_avatar.dart';\nimport 'package:askaide/page/component/share.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:file_saver/file_saver.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:image_gallery_saver/image_gallery_saver.dart';\nimport 'package:widgets_to_image/widgets_to_image.dart';\n\nclass ChatShareMessage {\n  final String? username;\n  final String content;\n  final String? avatarURL;\n  final bool leftSide;\n  final List<String>? images;\n\n  const ChatShareMessage({\n    this.username,\n    required this.content,\n    this.avatarURL,\n    this.leftSide = true,\n    this.images,\n  });\n}\n\nclass ChatShareScreen extends StatefulWidget {\n  final List<ChatShareMessage> messages;\n  const ChatShareScreen({\n    super.key,\n    required this.messages,\n  });\n\n  @override\n  State<ChatShareScreen> createState() => _ChatShareScreenState();\n}\n\nclass _ChatShareScreenState extends State<ChatShareScreen> {\n  final WidgetsToImageController controller = WidgetsToImageController();\n\n  bool showQRCode = true;\n  bool usingChatStyle = true;\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return WindowFrameWidget(\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          actions: [\n            if (!PlatformTool.isWeb())\n              TextButton(\n                onPressed: () async {\n                  final cancel = BotToast.showCustomLoading(\n                    toastBuilder: (cancel) {\n                      return LoadingIndicator(\n                        message: AppLocale.processingWait.getString(context),\n                      );\n                    },\n                    allowClick: false,\n                    duration: const Duration(seconds: 15),\n                  );\n\n                  try {\n                    final data = await controller.capture();\n                    if (data != null) {\n                      final file = await writeTempFile('share-image.png', data);\n                      cancel();\n                      await shareTo(\n                        // ignore: use_build_context_synchronously\n                        context,\n                        content: 'images',\n                        images: [\n                          file.path,\n                        ],\n                      );\n                    }\n                  } finally {\n                    cancel();\n                  }\n                },\n                child: Row(\n                  children: [\n                    Icon(Icons.share, size: 14, color: customColors.weakLinkColor),\n                    const SizedBox(width: 5),\n                    Text(\n                      AppLocale.share.getString(context),\n                      style: TextStyle(color: customColors.weakLinkColor, fontSize: 14),\n                    ),\n                  ],\n                ),\n              ),\n            EnhancedPopupMenu(\n              items: [\n                EnhancedPopupMenuItem(\n                  title: AppLocale.saveToLocal.getString(context),\n                  icon: Icons.save,\n                  onTap: (ctx) async {\n                    final cancel = BotToast.showCustomLoading(\n                      toastBuilder: (cancel) {\n                        return LoadingIndicator(\n                          message: AppLocale.processingWait.getString(context),\n                        );\n                      },\n                      allowClick: false,\n                      duration: const Duration(seconds: 15),\n                    );\n\n                    try {\n                      final data = await controller.capture();\n                      if (data != null) {\n                        cancel();\n                        // ignore: use_build_context_synchronously\n\n                        if (PlatformTool.isIOS() || PlatformTool.isAndroid()) {\n                          await ImageGallerySaver.saveImage(data, quality: 100);\n\n                          showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                        } else {\n                          if (PlatformTool.isWindows()) {\n                            FileSaver.instance\n                                .saveAs(\n                              name: randomId(),\n                              bytes: data,\n                              ext: '.png',\n                              mimeType: MimeType.png,\n                            )\n                                .then((value) async {\n                              if (value == null) {\n                                return;\n                              }\n\n                              await File(value).writeAsBytes(data);\n\n                              Logger.instance.d('File saved successfully: $value');\n                              showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                            });\n                          } else {\n                            FileSaver.instance\n                                .saveFile(\n                              name: randomId(),\n                              bytes: data,\n                              ext: 'png',\n                              mimeType: MimeType.png,\n                            )\n                                .then((value) {\n                              showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                            });\n                          }\n                        }\n                      }\n                    } finally {\n                      cancel();\n                    }\n                  },\n                ),\n                EnhancedPopupMenuItem(\n                  title: showQRCode\n                      ? AppLocale.dontShowInviteCode.getString(context)\n                      : AppLocale.showInviteCode.getString(context),\n                  icon: showQRCode ? Icons.visibility_off : Icons.visibility,\n                  onTap: (ctx) {\n                    setState(() {\n                      showQRCode = !showQRCode;\n                    });\n                  },\n                ),\n                EnhancedPopupMenuItem(\n                  title: usingChatStyle ? '使用列表风格' : '使用聊天风格',\n                  icon: usingChatStyle ? Icons.list : Icons.chat,\n                  onTap: (ctx) {\n                    setState(() {\n                      usingChatStyle = !usingChatStyle;\n                    });\n                  },\n                ),\n              ],\n            ),\n          ],\n        ),\n        backgroundColor: customColors.backgroundContainerColor,\n        body: Align(\n          alignment: Alignment.topCenter,\n          child: ConstrainedBox(\n            constraints: const BoxConstraints(\n              maxWidth: CustomSize.maxWindowSize,\n            ),\n            child: SafeArea(\n              child: SingleChildScrollView(\n                child: FutureBuilder(\n                    future: APIServer().shareInfo(),\n                    builder: (context, snapshot) {\n                      if (snapshot.hasError) {\n                        return Center(\n                          child: Text(resolveError(context, snapshot.error!)),\n                        );\n                      }\n\n                      if (snapshot.hasData) {\n                        return buildShareWindow(customColors, context, snapshot);\n                      }\n\n                      return const Center(\n                        child: Text('Loading ...'),\n                      );\n                    }),\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget buildShareWindow(CustomColors customColors, BuildContext context, AsyncSnapshot<ShareInfo> snapshot) {\n    return WidgetsToImage(\n      controller: controller,\n      child: Container(\n        color: customColors.backgroundContainerColor,\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          crossAxisAlignment: CrossAxisAlignment.center,\n          children: [\n            usingChatStyle ? buildChatPreview(context, customColors) : buildListPreview(context, customColors),\n            if (showQRCode) buildQRCodePanel(customColors, snapshot),\n          ],\n        ),\n      ),\n    );\n  }\n\n  Widget buildQRCodePanel(CustomColors customColors, AsyncSnapshot<ShareInfo> snapshot) {\n    return Container(\n      color: customColors.backgroundColor,\n      child: Padding(\n        padding: const EdgeInsets.symmetric(\n          horizontal: 15,\n          vertical: 20,\n        ),\n        child: Row(\n          children: [\n            ClipRRect(\n              borderRadius: CustomSize.borderRadius,\n              child: CachedNetworkImageEnhanced(\n                imageUrl: snapshot.data!.qrCode,\n                width: 100,\n                height: 100,\n              ),\n            ),\n            const SizedBox(width: 10),\n            Expanded(\n              child: Text(\n                snapshot.data!.message,\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n\n  Widget buildListPreview(BuildContext context, CustomColors customColors) {\n    return Container(\n      padding: const EdgeInsets.symmetric(\n        horizontal: 10,\n        vertical: 10,\n      ),\n      child: Column(\n        children: widget.messages.map((message) {\n          return Container(\n            padding: const EdgeInsets.symmetric(\n              horizontal: 10,\n              vertical: 10,\n            ),\n            child: Align(\n              alignment: Alignment.topLeft,\n              child: Column(\n                mainAxisSize: MainAxisSize.min,\n                crossAxisAlignment: CrossAxisAlignment.start,\n                children: [\n                  Row(\n                    mainAxisSize: MainAxisSize.min,\n                    crossAxisAlignment: CrossAxisAlignment.end,\n                    children: [\n                      if (message.avatarURL != null && message.leftSide) _buildAvatar(avatarUrl: message.avatarURL),\n                      if (message.username != null && message.leftSide)\n                        Container(\n                          margin: const EdgeInsets.fromLTRB(0, 0, 10, 7),\n                          padding: const EdgeInsets.symmetric(horizontal: 13),\n                          child: Text(\n                            message.username!,\n                            style: TextStyle(\n                              color: customColors.weakTextColor,\n                              fontSize: 12,\n                            ),\n                          ),\n                        ),\n                    ],\n                  ),\n                  if (message.images != null && message.images!.isNotEmpty)\n                    Container(\n                      margin: const EdgeInsets.fromLTRB(0, 10, 10, 0),\n                      child: ConstrainedBox(\n                        constraints:\n                            BoxConstraints(maxWidth: _chatBoxImagePreviewWidth(context, (message.images ?? []).length)),\n                        child: FileUploadPreview(images: message.images ?? []),\n                      ),\n                    ),\n                  ConstrainedBox(\n                    constraints: BoxConstraints(\n                      maxWidth: _chatBoxMaxWidth(context),\n                    ),\n                    child: Container(\n                      margin: const EdgeInsets.fromLTRB(0, 10, 10, 7),\n                      decoration: BoxDecoration(\n                        borderRadius: CustomSize.borderRadius,\n                        color: message.leftSide\n                            ? customColors.chatRoomReplyBackground\n                            : customColors.chatRoomSenderBackground,\n                      ),\n                      padding: const EdgeInsets.symmetric(\n                        horizontal: 10,\n                        vertical: 8,\n                      ),\n                      child: Builder(\n                        builder: (context) {\n                          return Markdown(data: message.content);\n                        },\n                      ),\n                    ),\n                  ),\n                ],\n              ),\n            ),\n          );\n        }).toList(),\n      ),\n    );\n  }\n\n  Widget buildChatPreview(BuildContext context, CustomColors customColors) {\n    return Container(\n      padding: const EdgeInsets.symmetric(\n        horizontal: 10,\n        vertical: 10,\n      ),\n      child: Column(\n        children: widget.messages.map((message) {\n          return Container(\n            padding: const EdgeInsets.symmetric(\n              horizontal: 10,\n              vertical: 10,\n            ),\n            child: Align(\n              alignment: message.leftSide ? Alignment.topLeft : Alignment.topRight,\n              child: ConstrainedBox(\n                constraints: BoxConstraints(maxWidth: _chatBoxMaxWidth(context)),\n                child: Column(\n                  mainAxisSize: MainAxisSize.min,\n                  crossAxisAlignment: CrossAxisAlignment.start,\n                  children: [\n                    Row(\n                      mainAxisSize: MainAxisSize.min,\n                      crossAxisAlignment: CrossAxisAlignment.end,\n                      children: [\n                        if (message.avatarURL != null && message.leftSide) _buildAvatar(avatarUrl: message.avatarURL),\n                        if (message.username != null && message.leftSide)\n                          Container(\n                            margin: const EdgeInsets.fromLTRB(0, 0, 10, 7),\n                            padding: const EdgeInsets.symmetric(horizontal: 13),\n                            child: Text(\n                              message.username!,\n                              style: TextStyle(\n                                color: customColors.weakTextColor,\n                                fontSize: 12,\n                              ),\n                            ),\n                          ),\n                      ],\n                    ),\n                    const SizedBox(height: 10),\n                    ConstrainedBox(\n                      constraints: BoxConstraints(\n                        maxWidth: _chatBoxMaxWidth(context) - 30,\n                      ),\n                      child: Column(\n                        mainAxisSize: MainAxisSize.min,\n                        crossAxisAlignment: message.leftSide ? CrossAxisAlignment.start : CrossAxisAlignment.end,\n                        children: [\n                          if (message.images != null && message.images!.isNotEmpty)\n                            Container(\n                              margin: const EdgeInsets.fromLTRB(0, 0, 10, 7),\n                              child: ConstrainedBox(\n                                constraints: BoxConstraints(\n                                    maxWidth: _chatBoxImagePreviewWidth(context, (message.images ?? []).length)),\n                                child: FileUploadPreview(images: message.images ?? []),\n                              ),\n                            ),\n                          Container(\n                            margin: message.leftSide\n                                ? const EdgeInsets.fromLTRB(0, 0, 0, 7)\n                                : const EdgeInsets.fromLTRB(0, 0, 10, 7),\n                            decoration: BoxDecoration(\n                              borderRadius: CustomSize.borderRadius,\n                              color: message.leftSide\n                                  ? customColors.chatRoomReplyBackground\n                                  : customColors.chatRoomSenderBackground,\n                            ),\n                            padding: const EdgeInsets.symmetric(\n                              horizontal: 10,\n                              vertical: 8,\n                            ),\n                            child: Builder(\n                              builder: (context) {\n                                return Markdown(data: message.content);\n                              },\n                            ),\n                          ),\n                        ],\n                      ),\n                    ),\n                  ],\n                ),\n              ),\n            ),\n          );\n        }).toList(),\n      ),\n    );\n  }\n\n  /// 获取聊天框的最大宽度\n  double _chatBoxMaxWidth(BuildContext context) {\n    var screenWidth = MediaQuery.of(context).size.width;\n    if (screenWidth >= CustomSize.maxWindowSize) {\n      return CustomSize.maxWindowSize;\n    }\n\n    return screenWidth;\n  }\n\n  /// 获取图片预览的最大宽度\n  double _chatBoxImagePreviewWidth(BuildContext context, int imageCount) {\n    final expect = _chatBoxMaxWidth(context) / 1.3;\n    final max = imageCount > 1 ? 500.0 : 300.0;\n    return expect > max ? max : expect;\n  }\n\n  Widget _buildAvatar({String? avatarUrl, int? id, int size = 30}) {\n    if (avatarUrl != null && avatarUrl.startsWith('http')) {\n      return RemoteAvatar(\n        avatarUrl: imageURL(avatarUrl, qiniuImageTypeAvatar),\n        size: size,\n      );\n    }\n\n    return RandomAvatar(\n      id: id ?? 0,\n      size: size,\n      usage: Ability().isUserLogon() ? AvatarUsage.room : AvatarUsage.legacy,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/chat/empty.dart",
    "content": "import 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:auto_size_text/auto_size_text.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:lottie/lottie.dart';\n\nclass EmptyPreview extends StatefulWidget {\n  final List<ChatExample> examples;\n  final Function(String message) onSubmit;\n  final bool cardMode;\n\n  const EmptyPreview({\n    super.key,\n    required this.examples,\n    required this.onSubmit,\n    this.cardMode = false,\n  });\n\n  @override\n  State<EmptyPreview> createState() => _EmptyPreviewState();\n}\n\nclass _EmptyPreviewState extends State<EmptyPreview> {\n  final ScrollController _scrollController = ScrollController();\n\n  final displayCount = 6;\n\n  @override\n  void dispose() {\n    _scrollController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    if (widget.examples.isEmpty) {\n      return Container();\n    }\n\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    if (widget.cardMode) {\n      return Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        mainAxisAlignment: MainAxisAlignment.start,\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          Expanded(\n            child: Container(\n              alignment: Alignment.center,\n              child: Column(\n                mainAxisAlignment: MainAxisAlignment.center,\n                children: [\n                  Opacity(\n                    opacity: 0.3,\n                    child: Lottie.asset('assets/lottie/empty_status.json', height: 150),\n                  ),\n                  // Text(\n                  //   AppLocale.welcomeToAskMe.getString(context),\n                  //   style: TextStyle(\n                  //     fontSize: 14,\n                  //     color: customColors.weakTextColor?.withOpacity(0.4),\n                  //   ),\n                  // ),\n                ],\n              ),\n            ),\n          ),\n          Container(\n            height: 60,\n            alignment: Alignment.center,\n            child: ListView.separated(\n              controller: _scrollController,\n              itemCount: (widget.examples.length > displayCount ? displayCount : widget.examples.length) + 1,\n              scrollDirection: Axis.horizontal,\n              itemBuilder: (context, index) {\n                if (index == (widget.examples.length > displayCount ? displayCount : widget.examples.length)) {\n                  return Container(\n                    width: 60,\n                    padding: const EdgeInsets.all(10),\n                    margin: const EdgeInsets.only(left: 10, right: 15),\n                    alignment: Alignment.center,\n                    child: InkWell(\n                      borderRadius: CustomSize.borderRadiusAll,\n                      onTap: () {\n                        setState(() {\n                          widget.examples.shuffle();\n                        });\n                        _scrollController.animateTo(\n                          0.0,\n                          duration: const Duration(milliseconds: 300),\n                          curve: Curves.easeOut,\n                        );\n                      },\n                      child: Container(\n                        padding: const EdgeInsets.all(5),\n                        alignment: Alignment.center,\n                        child: Icon(Icons.refresh, color: customColors.chatInputPanelText),\n                      ),\n                    ),\n                  );\n                }\n                return Container(\n                  margin: const EdgeInsets.only(left: 10, right: 5),\n                  child: InkWell(\n                    borderRadius: CustomSize.borderRadiusAll,\n                    onTap: () {\n                      widget.onSubmit(widget.examples[index].text);\n                    },\n                    child: Container(\n                      width: 150,\n                      padding: const EdgeInsets.all(10),\n                      decoration: BoxDecoration(\n                        color: customColors.backgroundColor,\n                        borderRadius: CustomSize.borderRadius,\n                      ),\n                      alignment: Alignment.center,\n                      child: AutoSizeText(\n                        widget.examples[index].title,\n                        maxLines: 1,\n                        overflow: TextOverflow.ellipsis,\n                        style: TextStyle(\n                          fontSize: 13,\n                          color: customColors.chatInputPanelText,\n                        ),\n                      ),\n                    ),\n                  ),\n                );\n              },\n              separatorBuilder: (BuildContext context, int index) {\n                return Divider(\n                  color: customColors.chatExampleItemText?.withAlpha(20),\n                );\n              },\n            ),\n          ),\n          const SizedBox(height: 10),\n        ],\n      );\n    }\n\n    return Center(\n      child: Column(\n        mainAxisAlignment: MainAxisAlignment.start,\n        children: [\n          const SizedBox(height: 30),\n          // 示例内容区域\n          Container(\n            decoration: BoxDecoration(\n              // color: customColors.backgroundColor?.withAlpha(200),\n              borderRadius: CustomSize.borderRadius,\n            ),\n            padding: const EdgeInsets.only(top: 20, left: 15, right: 10, bottom: 3),\n            height: _resolveTipHeight(context),\n            width: _resolveTipWidth(context),\n            child: Column(\n              mainAxisSize: MainAxisSize.min,\n              crossAxisAlignment: CrossAxisAlignment.start,\n              children: [\n                Row(\n                  children: [\n                    Image.asset('assets/app-256-transparent.png', width: 20, height: 20),\n                    const SizedBox(width: 5),\n                    Text(\n                      AppLocale.askMeLikeThis.getString(context),\n                      style: const TextStyle(\n                        fontSize: 16,\n                        fontWeight: FontWeight.bold,\n                      ),\n                    ),\n                  ],\n                ),\n                const SizedBox(height: 20),\n                Expanded(\n                  child: ListView.separated(\n                    itemCount: widget.examples.length > displayCount ? displayCount : widget.examples.length,\n                    physics: const NeverScrollableScrollPhysics(),\n                    itemBuilder: (context, index) {\n                      return ListTextItem(\n                        title: widget.examples[index].title,\n                        onTap: () {\n                          widget.onSubmit(widget.examples[index].text);\n                        },\n                        customColors: customColors,\n                      );\n                    },\n                    separatorBuilder: (BuildContext context, int index) {\n                      return Divider(\n                        color: customColors.chatExampleItemText?.withAlpha(20),\n                      );\n                    },\n                  ),\n                ),\n                Align(\n                  alignment: Alignment.centerRight,\n                  child: TextButton(\n                    style: ButtonStyle(\n                      overlayColor: WidgetStateProperty.all(Colors.transparent),\n                    ),\n                    onPressed: () {\n                      setState(() {\n                        widget.examples.shuffle();\n                      });\n                    },\n                    child: Row(\n                      mainAxisAlignment: MainAxisAlignment.end,\n                      children: [\n                        Icon(\n                          Icons.refresh,\n                          color: customColors.chatExampleItemText,\n                          size: 16,\n                        ),\n                        const SizedBox(width: 3),\n                        Text(\n                          AppLocale.refresh.getString(context),\n                          style: TextStyle(\n                            color: customColors.chatExampleItemText,\n                          ),\n                          textScaler: const TextScaler.linear(0.9),\n                        ),\n                      ],\n                    ),\n                  ),\n                )\n              ],\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n\n  double _resolveTipWidth(BuildContext context) {\n    final screenWidth = MediaQuery.of(context).size.width;\n    if (screenWidth < 400) {\n      return screenWidth / 1.15;\n    }\n\n    return 348;\n  }\n\n  double _resolveTipHeight(BuildContext context) {\n    final halfScreenHeight = MediaQuery.of(context).size.height / 2;\n    if (halfScreenHeight > 260) {\n      return 260;\n    }\n\n    return halfScreenHeight;\n  }\n}\n\nclass ListTextItem extends StatefulWidget {\n  final String title;\n  final Function() onTap;\n  final CustomColors customColors;\n\n  const ListTextItem({\n    super.key,\n    required this.title,\n    required this.onTap,\n    required this.customColors,\n  });\n\n  @override\n  State<ListTextItem> createState() => _ListTextItemState();\n}\n\nclass _ListTextItemState extends State<ListTextItem> {\n  @override\n  Widget build(BuildContext context) {\n    return InkWell(\n      onTap: widget.onTap,\n      child: Container(\n        padding: const EdgeInsets.only(left: 5, right: 10),\n        child: Row(\n          mainAxisSize: MainAxisSize.min,\n          children: [\n            Icon(\n              Icons.arrow_right,\n              color: widget.customColors.chatExampleItemText?.withAlpha(120),\n            ),\n            Expanded(\n              child: Text(\n                widget.title,\n                textAlign: TextAlign.left,\n                overflow: TextOverflow.ellipsis,\n                style: TextStyle(\n                  color: widget.customColors.weakTextColor,\n                ),\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/chat/enhanced_selectable_text.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass EnhancedSelectableText extends StatefulWidget {\n  final String text;\n  const EnhancedSelectableText({super.key, required this.text});\n\n  @override\n  State<EnhancedSelectableText> createState() => _EnhancedSelectableTextState();\n}\n\nclass _EnhancedSelectableTextState extends State<EnhancedSelectableText> {\n  @override\n  Widget build(BuildContext context) {\n    return SelectionArea(\n      child: SingleChildScrollView(\n        child: Container(\n          padding: const EdgeInsets.symmetric(horizontal: 30),\n          child: Text(\n            widget.text,\n            style: const TextStyle(\n              fontSize: 14,\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/chat/file_upload.dart",
    "content": "import 'dart:convert';\n\nimport 'package:askaide/page/component/image_preview.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:file_picker/file_picker.dart';\nimport 'package:flutter/widgets.dart';\n\nclass FileUpload {\n  final PlatformFile file;\n  String? url;\n\n  FileUpload({required this.file, this.url});\n\n  bool get uploaded => url != null;\n\n  setUrl(String url) {\n    this.url = url;\n  }\n}\n\nclass FileUploadPreview extends StatelessWidget {\n  final List<String> images;\n  const FileUploadPreview({super.key, required this.images});\n\n  @override\n  Widget build(BuildContext context) {\n    final children = images\n        .map((e) {\n          if (e.startsWith('http://') || e.startsWith('https://')) {\n            return NetworkImagePreviewer(\n              url: e,\n              hidePreviewButton: true,\n            );\n          }\n\n          if (e.startsWith('data:')) {\n            return ImageProviderPreviewer(\n              borderRadius: CustomSize.borderRadiusAll,\n              imageProvider: MemoryImage(\n                const Base64Decoder().convert(e.split(',')[1]),\n              ),\n            );\n          }\n          return const SizedBox();\n        })\n        .map((e) => Padding(\n              padding: const EdgeInsets.only(bottom: 5, left: 5),\n              child: e,\n            ))\n        .toList();\n    if (children.length > 1) {\n      if (children.length % 2 == 1) {\n        return Column(\n          children: [\n            GridView.count(\n              crossAxisCount: 2,\n              shrinkWrap: true,\n              physics: const NeverScrollableScrollPhysics(),\n              children: children.sublist(0, children.length - 1),\n            ),\n            children.last,\n          ],\n        );\n      }\n\n      return GridView.count(\n        crossAxisCount: 2,\n        shrinkWrap: true,\n        physics: const NeverScrollableScrollPhysics(),\n        children: children,\n      );\n    }\n\n    return ListView(\n      shrinkWrap: true,\n      physics: const NeverScrollableScrollPhysics(),\n      children: children,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/chat/help_tips.dart",
    "content": "import 'dart:math';\n\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/gestures.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\n\nclass HelpTips extends StatelessWidget {\n  final Function(String text)? onSubmitMessage;\n  final Function()? onNewChat;\n  const HelpTips({super.key, this.onSubmitMessage, this.onNewChat});\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    List<Builder> children = [\n      if (onNewChat != null && onSubmitMessage != null)\n        Builder(\n          builder: (context) => _buildNewChatActionTip(customColors, context, (text) => onSubmitMessage!(text)),\n        ),\n      if (onSubmitMessage != null)\n        Builder(builder: (context) => _buildContinueActionTip(customColors, context, onSubmitMessage!))\n    ];\n\n    // 随机取一个 builder\n    return Container(\n      padding: const EdgeInsets.symmetric(vertical: 20),\n      child: children[Random().nextInt(children.length)],\n    );\n  }\n\n  RichText _buildNewChatActionTip(CustomColors customColors, BuildContext context, Function(String text) onSubmit) {\n    return RichText(\n        text: TextSpan(\n      children: [\n        TextSpan(\n          text: AppLocale.startNewChatTips.getString(context),\n          style: TextStyle(\n            color: customColors.dialogDefaultTextColor,\n            fontSize: 12,\n          ),\n        ),\n        const TextSpan(text: ' '),\n        TextSpan(\n            text: AppLocale.newChat.getString(context),\n            style: TextStyle(\n              color: customColors.linkColor,\n              fontSize: 12,\n            ),\n            recognizer: TapGestureRecognizer()..onTap = onNewChat),\n      ],\n    ));\n  }\n\n  RichText _buildContinueActionTip(CustomColors customColors, BuildContext context, Function(String text) onSubmit) {\n    return RichText(\n        text: TextSpan(\n      children: [\n        TextSpan(\n          text: AppLocale.wantMoreContentTips.getString(context),\n          style: TextStyle(\n            color: customColors.dialogDefaultTextColor,\n            fontSize: 12,\n          ),\n        ),\n        const TextSpan(text: ' '),\n        TextSpan(\n            text: AppLocale.continueMessage.getString(context),\n            style: TextStyle(\n              color: customColors.linkColor,\n              fontSize: 12,\n            ),\n            recognizer: TapGestureRecognizer()\n              ..onTap = () {\n                onSubmit(AppLocale.continueMessage.getString(context));\n              }),\n      ],\n    ));\n  }\n}\n"
  },
  {
    "path": "lib/page/component/chat/markdown/citation.dart",
    "content": "import 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_markdown/flutter_markdown.dart';\nimport 'package:markdown/markdown.dart' as md;\n\nclass CitationSyntax extends md.InlineSyntax {\n  final List<String> citations;\n\n  CitationSyntax({required this.citations}) : super('(?:\\\\[|【)\\\\s*citation\\\\s*:\\\\s*(\\\\d+)\\\\s*(?:\\\\]|】)');\n\n  @override\n  bool onMatch(md.InlineParser parser, Match match) {\n    final num = match[1]!;\n    final node = md.Text(num);\n\n    try {\n      final el = md.Element('citation', [node]);\n      el.attributes['href'] = citations[int.parse(num) - 1];\n      parser.addNode(el);\n    } catch (e) {\n      parser.addNode(md.Element('citation', [node]));\n    }\n\n    return true;\n  }\n}\n\nclass CitationBuilder extends MarkdownElementBuilder {\n  final citationPattern = RegExp(r'^citation:\\d+$');\n\n  final Function(String href)? onTap;\n\n  CitationBuilder({this.onTap});\n\n  @override\n  Widget visitElementAfterWithContext(\n    BuildContext context,\n    md.Element element,\n    TextStyle? preferredStyle,\n    TextStyle? parentStyle,\n  ) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    final String text = element.textContent;\n    if (text.isEmpty) {\n      return const SizedBox();\n    }\n\n    final href = element.attributes['href'];\n    return RichText(\n      text: TextSpan(\n        children: [\n          WidgetSpan(\n            child: MouseRegion(\n              cursor: SystemMouseCursors.click,\n              child: GestureDetector(\n                onTap: () {\n                  if (href != null) {\n                    onTap?.call(href);\n                  }\n                },\n                child: Container(\n                  margin: const EdgeInsets.only(left: 4),\n                  decoration: BoxDecoration(\n                    color: customColors.weakTextColorLess,\n                    borderRadius: BorderRadius.circular(CustomSize.radiusValue * 2),\n                  ),\n                  padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),\n                  child: Text(\n                    text,\n                    style: const TextStyle(\n                      color: Colors.white,\n                      fontSize: 10,\n                    ),\n                  ),\n                ),\n              ),\n            ),\n          )\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/chat/markdown/code.dart",
    "content": "import 'package:askaide/helper/ability.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:clipboard/clipboard.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_highlight/flutter_highlight.dart';\nimport 'package:flutter_highlight/themes/tomorrow-night.dart';\nimport 'package:flutter_highlight/themes/tomorrow.dart';\nimport 'package:flutter_markdown/flutter_markdown.dart';\nimport 'package:markdown/markdown.dart' as md;\n\nMap<String, TextStyle> codeTheme() {\n  var theme = Map<String, TextStyle>.from(Ability().themeMode != 'dark' ? tomorrowTheme : tomorrowNightTheme);\n  theme['root'] = TextStyle(\n    backgroundColor: Colors.transparent,\n    color: theme['root']?.color,\n  );\n\n  return theme;\n}\n\nclass CodeElementBuilder extends MarkdownElementBuilder {\n  final CustomColors customColors;\n\n  CodeElementBuilder(this.customColors);\n\n  @override\n  Widget? visitElementAfter(md.Element element, TextStyle? preferredStyle) {\n    var language = '';\n\n    if (element.attributes['class'] != null) {\n      String lg = element.attributes['class'] as String;\n      language = lg.substring(9);\n    }\n\n    final multiLine = element.textContent.trim().split(\"\\n\").length > 1;\n\n    final child = RichText(\n      text: TextSpan(\n        children: [\n          WidgetSpan(\n            child: HighlightView(\n              // The original code to be highlighted\n              element.textContent,\n\n              // Specify language\n              // It is recommended to give it a value for performance\n              language: language,\n\n              // Specify highlight theme\n              // All available themes are listed in `themes` folder\n              theme: codeTheme(),\n\n              // Specify padding\n              padding: multiLine\n                  ? const EdgeInsets.only(\n                      top: 30,\n                      bottom: 10,\n                      left: 10,\n                      right: 10,\n                    )\n                  : const EdgeInsets.symmetric(horizontal: 5, vertical: 2),\n\n              textStyle: TextStyle(\n                fontSize: multiLine ? CustomSize.markdownCodeSize : CustomSize.markdownTextSize,\n                height: 1.5,\n                wordSpacing: 3,\n              ),\n            ),\n          )\n        ],\n      ),\n    );\n\n    if (multiLine) {\n      return Card(\n        elevation: 0,\n        color: customColors.markdownPreColor,\n        shape: RoundedRectangleBorder(\n          borderRadius: CustomSize.borderRadius,\n        ),\n        child: Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            Container(\n              padding: const EdgeInsets.symmetric(horizontal: 8),\n              decoration: BoxDecoration(\n                color: customColors.listTileBackgroundColor,\n                borderRadius: const BorderRadius.only(topLeft: CustomSize.radius, topRight: CustomSize.radius),\n              ),\n              child: Row(\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: [\n                  Text(\n                    language,\n                    style: TextStyle(\n                      fontSize: 12,\n                      color: customColors.weakTextColor,\n                    ),\n                  ),\n                  TextButton.icon(\n                    icon: Icon(\n                      Icons.copy,\n                      size: 14,\n                      color: customColors.weakTextColorLess,\n                    ),\n                    label: Text(\n                      'Copy',\n                      style: TextStyle(\n                        fontSize: 12,\n                        color: customColors.weakTextColorLess,\n                      ),\n                    ),\n                    onPressed: () {\n                      FlutterClipboard.copy(element.textContent).then((value) {\n                        showSuccessMessage('Copied to clipboard');\n                      });\n                    },\n                    style: ButtonStyle(\n                      overlayColor: WidgetStateProperty.all(Colors.transparent),\n                    ),\n                  ),\n                ],\n              ),\n            ),\n            child,\n          ],\n        ),\n      );\n    }\n\n    return child;\n  }\n}\n"
  },
  {
    "path": "lib/page/component/chat/markdown/latex/latex_block_syntax.dart",
    "content": "import 'package:markdown/markdown.dart';\n\nclass LatexBlockSyntax extends BlockSyntax {\n  @override\n  RegExp get pattern => RegExp(\n        r'^(?:(\\${1,2})(?:\\n|$))|(?:(?:\\\\\\[(.+)\\\\\\])(?:\\n|$))',\n        multiLine: true,\n      );\n\n  LatexBlockSyntax() : super();\n\n  @override\n  List<Line> parseChildLines(BlockParser parser) {\n    final m = pattern.firstMatch(parser.current.content);\n    if (m?[2] != null) {\n      parser.advance();\n      return [Line(m?[2] ?? '')];\n    }\n\n    final childLines = <Line>[];\n    parser.advance();\n\n    while (!parser.isDone) {\n      final match = pattern.hasMatch(parser.current.content);\n      if (!match) {\n        childLines.add(parser.current);\n        parser.advance();\n      } else {\n        parser.advance();\n        break;\n      }\n    }\n\n    return childLines;\n  }\n\n  @override\n  Node parse(BlockParser parser) {\n    final lines = parseChildLines(parser);\n    final content = lines.map((e) => e.content).join('\\n').trim();\n    final textElement = Element.text('latex', content);\n    textElement.attributes['MathStyle'] = 'display';\n\n    return Element('p', [textElement]);\n  }\n}\n"
  },
  {
    "path": "lib/page/component/chat/markdown/latex/latex_element_builder.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter_markdown/flutter_markdown.dart';\nimport 'package:flutter_math_fork/flutter_math.dart';\nimport 'package:markdown/markdown.dart' as md;\n\nclass LatexElementBuilder extends MarkdownElementBuilder {\n  LatexElementBuilder({\n    this.textStyle,\n    this.textScaleFactor,\n  });\n\n  /// The style to apply to the text.\n  final TextStyle? textStyle;\n\n  /// The text scale factor to apply to the text.\n  final double? textScaleFactor;\n\n  @override\n  Widget visitElementAfterWithContext(\n    BuildContext context,\n    md.Element element,\n    TextStyle? preferredStyle,\n    TextStyle? parentStyle,\n  ) {\n    final String text = element.textContent;\n    if (text.isEmpty) {\n      return const SizedBox();\n    }\n\n    MathStyle mathStyle;\n    switch (element.attributes['MathStyle']) {\n      case 'text':\n        mathStyle = MathStyle.text;\n      case 'display':\n        mathStyle = MathStyle.display;\n      default:\n        mathStyle = MathStyle.text;\n    }\n\n    return SingleChildScrollView(\n      scrollDirection: Axis.horizontal,\n      clipBehavior: Clip.antiAlias,\n      child: Math.tex(\n        text,\n        textStyle: textStyle,\n        mathStyle: mathStyle,\n        textScaleFactor: textScaleFactor,\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/chat/markdown/latex/latex_inline_syntax.dart",
    "content": "import 'package:markdown/markdown.dart';\n\nfinal List<Map<String, dynamic>> delimiterList = [\n  {'left': r'$$', 'right': r'$$', 'display': true},\n  {'left': r'$', 'right': r'$', 'display': false},\n  {'left': r'\\pu{', 'right': '}', 'display': false},\n  {'left': r'\\ce{', 'right': '}', 'display': false},\n  {'left': r'\\(', 'right': r'\\)', 'display': false},\n  {'left': '( ', 'right': ' )', 'display': false},\n  {'left': r'\\[', 'right': r'\\]', 'display': true},\n  {'left': '[ ', 'right': ' ]', 'display': true},\n];\n\nList<String> inlinePatterns = [];\nList<String> blockPatterns = [];\n\nString escapeRegex(String string) {\n  return string.replaceAllMapped(RegExp(r'[-\\/\\\\^$*+?.()|[\\]{}]'), (match) {\n    return '\\\\${match.group(0)}';\n  });\n}\n\nString generateRegexRules(List<Map<String, dynamic>> delimiters) {\n  for (var delimiter in delimiters) {\n    String left = delimiter['left'];\n    String right = delimiter['right'];\n    // Ensure regex-safe delimiters\n    String escapedLeft = escapeRegex(left);\n    String escapedRight = escapeRegex(right);\n\n    // Inline pattern\n    inlinePatterns.add('$escapedLeft((?:\\\\\\\\.|[^\\\\\\\\\\\\n])*?(?:\\\\\\\\.|[^\\\\\\\\\\\\n]|(?!$escapedRight)))$escapedRight');\n    // Block pattern\n    blockPatterns.add('$escapedLeft\\\\n((?:\\\\\\\\[^]|[^\\\\\\\\])+?)\\\\n$escapedRight');\n  }\n\n  return '(${inlinePatterns.join(\"|\")})(?=[\\\\s?!.,:？！。，：]|\\$)';\n}\n\nfinal _latexPattern = generateRegexRules(delimiterList);\n\nclass LatexInlineSyntax extends InlineSyntax {\n  LatexInlineSyntax() : super(_latexPattern);\n\n  @override\n  bool onMatch(InlineParser parser, Match match) {\n    String raw = match.group(0) ?? '';\n\n    int delimiterLength = 1;\n    String mathStyle = 'text';\n    // check delimiter\n    for (var delimiter in delimiterList) {\n      if (raw.startsWith(delimiter['left']) && raw.endsWith(delimiter['right'])) {\n        mathStyle = delimiter['display'] ? 'display' : 'text';\n        delimiterLength = delimiter['left'].length;\n        break;\n      }\n    }\n\n    final equation = raw.substring(delimiterLength, raw.length - delimiterLength);\n\n    final element = Element.text('latex', equation);\n    element.attributes['MathStyle'] = mathStyle;\n    parser.addNode(element);\n\n    return true;\n  }\n}\n"
  },
  {
    "path": "lib/page/component/chat/markdown/latex.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:markdown_widget/markdown_widget.dart';\nimport 'package:flutter_math_fork/flutter_math.dart';\nimport 'package:markdown/markdown.dart' as m;\n\nSpanNodeGeneratorWithTag latexGenerator = SpanNodeGeneratorWithTag(\n    tag: _latexTag,\n    generator: (e, config, visitor) =>\n        LatexNode(e.attributes, e.textContent, config));\n\nconst _latexTag = 'latex';\n\nclass LatexSyntax extends m.InlineSyntax {\n  LatexSyntax() : super(r'(\\$\\$[\\s\\S]+\\$\\$)|(\\$.+?\\$)');\n\n  @override\n  bool onMatch(m.InlineParser parser, Match match) {\n    final input = match.input;\n    final matchValue = input.substring(match.start, match.end);\n    String content = '';\n    bool isInline = true;\n    const blockSyntax = '\\$\\$';\n    const inlineSyntax = '\\$';\n    if (matchValue.startsWith(blockSyntax) &&\n        matchValue.endsWith(blockSyntax) &&\n        (matchValue != blockSyntax)) {\n      content = matchValue.substring(2, matchValue.length - 2);\n      isInline = false;\n    } else if (matchValue.startsWith(inlineSyntax) &&\n        matchValue.endsWith(inlineSyntax) &&\n        matchValue != inlineSyntax) {\n      content = matchValue.substring(1, matchValue.length - 1);\n    }\n    m.Element el = m.Element.text(_latexTag, matchValue);\n    el.attributes['content'] = content;\n    el.attributes['isInline'] = '$isInline';\n    parser.addNode(el);\n    return true;\n  }\n}\n\nclass LatexNode extends SpanNode {\n  final Map<String, String> attributes;\n  final String textContent;\n  final MarkdownConfig config;\n\n  LatexNode(this.attributes, this.textContent, this.config);\n\n  @override\n  InlineSpan build() {\n    final content = attributes['content'] ?? '';\n    final isInline = attributes['isInline'] == 'true';\n    final style = parentStyle ?? config.p.textStyle;\n    if (content.isEmpty) return TextSpan(style: style, text: textContent);\n    final latex = Math.tex(\n      content,\n      mathStyle: MathStyle.text,\n      textScaleFactor: 1,\n      onErrorFallback: (error) {\n        return Text(\n          textContent,\n          style: style.copyWith(color: Colors.red),\n        );\n      },\n    );\n    return WidgetSpan(\n        alignment: PlaceholderAlignment.middle,\n        child: !isInline\n            ? Container(\n                width: double.infinity,\n                margin: const EdgeInsets.symmetric(vertical: 16),\n                child: Center(child: latex),\n              )\n            : latex);\n  }\n}\n"
  },
  {
    "path": "lib/page/component/chat/markdown.dart",
    "content": "// import 'dart:convert';\n// import 'package:askaide/helper/platform.dart';\n// import 'package:askaide/page/component/chat/markdown/latex.dart';\n// import 'package:askaide/page/component/dialog.dart';\n// import 'package:clipboard/clipboard.dart';\n// import 'package:markdown_widget/config/all.dart';\n// import 'package:markdown_widget/widget/all.dart';\n\nimport 'package:askaide/page/component/chat/markdown/citation.dart';\nimport 'package:askaide/page/component/chat/markdown/code.dart';\nimport 'package:askaide/page/component/chat/markdown/latex/latex_block_syntax.dart';\nimport 'package:askaide/page/component/chat/markdown/latex/latex_element_builder.dart';\nimport 'package:askaide/page/component/chat/markdown/latex/latex_inline_syntax.dart';\nimport 'package:askaide/page/component/image_preview.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_cache_manager/flutter_cache_manager.dart';\nimport 'package:flutter_markdown/flutter_markdown.dart' as md;\nimport 'package:markdown/markdown.dart' as mm;\n\nclass Markdown extends StatelessWidget {\n  final String data;\n  final Function(String value)? onUrlTap;\n  final TextStyle? textStyle;\n  final cacheManager = DefaultCacheManager();\n  final bool thinkingMode;\n\n  final List<String> citations;\n\n  Markdown({\n    super.key,\n    required this.data,\n    this.onUrlTap,\n    this.textStyle,\n    this.citations = const [],\n    this.thinkingMode = false,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    // if (!PlatformTool.isWeb()) {\n    //   return MarkdownPlus(\n    //     data: data,\n    //     onUrlTap: onUrlTap,\n    //     textStyle: textStyle,\n    //     compact: true,\n    //   );\n    // }\n\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    final style = thinkingMode\n        ? md.MarkdownStyleSheet(\n            p: TextStyle(fontSize: 14, color: customColors.weakTextColorLess, height: 1.5),\n            listBullet: TextStyle(fontSize: 14, color: customColors.weakTextColorLess, height: 1.5),\n            code: TextStyle(\n              fontSize: 14,\n              color: customColors.weakTextColorLess,\n              backgroundColor: Colors.transparent,\n            ),\n            codeblockPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),\n            codeblockDecoration: const BoxDecoration(borderRadius: CustomSize.borderRadiusAll),\n            tableBorder: TableBorder.all(color: customColors.weakTextColorLess!.withOpacity(0.5), width: 1),\n            tableColumnWidth: const FlexColumnWidth(),\n            blockquotePadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 2),\n            blockquoteDecoration: BoxDecoration(\n              border: Border(left: BorderSide(color: customColors.weakTextColorLess!.withOpacity(0.4), width: 4)),\n            ),\n            a: TextStyle(color: customColors.weakTextColorLess, decoration: TextDecoration.none),\n            h1: TextStyle(color: customColors.weakTextColorLess, height: 1.5),\n            h2: TextStyle(color: customColors.weakTextColorLess, height: 1.5),\n            h3: TextStyle(color: customColors.weakTextColorLess, height: 1.5),\n            h4: TextStyle(color: customColors.weakTextColorLess, height: 1.5),\n            h5: TextStyle(color: customColors.weakTextColorLess, height: 1.5),\n            h6: TextStyle(color: customColors.weakTextColorLess, height: 1.5),\n          )\n        : md.MarkdownStyleSheet(\n            p: textStyle ?? TextStyle(fontSize: CustomSize.markdownTextSize, height: 1.5),\n            listBullet: textStyle ?? TextStyle(fontSize: CustomSize.markdownTextSize, height: 1.5),\n            code: TextStyle(\n              fontSize: CustomSize.markdownCodeSize,\n              color: customColors.markdownCodeColor,\n              backgroundColor: Colors.transparent,\n            ),\n            codeblockPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),\n            codeblockDecoration: const BoxDecoration(borderRadius: CustomSize.borderRadiusAll),\n            tableBorder: TableBorder.all(color: customColors.weakTextColor!.withOpacity(0.5), width: 1),\n            tableColumnWidth: const FlexColumnWidth(),\n            blockquotePadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 2),\n            blockquoteDecoration: BoxDecoration(\n              border: Border(left: BorderSide(color: customColors.weakTextColor!.withOpacity(0.4), width: 4)),\n            ),\n            a: TextStyle(color: customColors.weakLinkColor, decoration: TextDecoration.none),\n          );\n\n    return md.MarkdownBody(\n      shrinkWrap: true,\n      selectable: false,\n      softLineBreak: true,\n      styleSheetTheme: md.MarkdownStyleSheetBaseTheme.material,\n      styleSheet: style,\n      onTapLink: (text, href, title) {\n        if (onUrlTap != null && href != null) onUrlTap!(href);\n      },\n      imageBuilder: (uri, title, alt) {\n        if (uri.scheme == 'http' || uri.scheme == 'https') {\n          return NetworkImagePreviewer(\n            url: uri.toString(),\n            hidePreviewButton: true,\n          );\n        }\n\n        return ClipRRect(borderRadius: CustomSize.borderRadiusAll, child: Image.network(uri.toString()));\n      },\n      extensionSet: mm.ExtensionSet(\n        [\n          ...mm.ExtensionSet.gitHubFlavored.blockSyntaxes,\n          LatexBlockSyntax(),\n        ],\n        <mm.InlineSyntax>[\n          CitationSyntax(citations: citations),\n          mm.EmojiSyntax(),\n          ...mm.ExtensionSet.gitHubFlavored.inlineSyntaxes,\n          LatexInlineSyntax(),\n        ],\n      ),\n      data: data,\n      builders: {\n        'latex': LatexElementBuilder(),\n        'code': CodeElementBuilder(customColors),\n        'citation': CitationBuilder(onTap: onUrlTap),\n      },\n    );\n  }\n}\n\n// class MarkdownPlus extends StatelessWidget {\n//   final String data;\n//   final Function(String value)? onUrlTap;\n//   final bool compact;\n//   final TextStyle? textStyle;\n//   final cacheManager = DefaultCacheManager();\n\n//   MarkdownPlus({\n//     super.key,\n//     required this.data,\n//     this.onUrlTap,\n//     this.compact = true,\n//     this.textStyle,\n//   });\n\n//   MarkdownConfig _buildMarkdownConfig(CustomColors customColors) {\n//     return MarkdownConfig(\n//       configs: [\n//         PConfig(textStyle: textStyle ?? TextStyle(fontSize: CustomSize.markdownTextSize)),\n//         // 链接配置\n//         LinkConfig(\n//           style: TextStyle(\n//             color: customColors.markdownLinkColor,\n//             decoration: TextDecoration.none,\n//           ),\n//           onTap: (value) {\n//             if (onUrlTap != null) onUrlTap!(value);\n//           },\n//         ),\n//         // 代码块配置\n//         PreConfig(\n//           theme: codeTheme(),\n//           decoration: const BoxDecoration(borderRadius: CustomSize.borderRadiusAll),\n//           margin: const EdgeInsets.symmetric(vertical: 0.0),\n//           padding: const EdgeInsets.only(top: 5, left: 10, right: 10, bottom: 10),\n//           textStyle: TextStyle(fontSize: CustomSize.markdownCodeSize),\n//           wrapper: (child, code, language) {\n//             return Card(\n//               elevation: 0,\n//               color: customColors.markdownPreColor,\n//               shape: RoundedRectangleBorder(\n//                 borderRadius: CustomSize.borderRadius,\n//               ),\n//               child: Column(\n//                 crossAxisAlignment: CrossAxisAlignment.start,\n//                 children: [\n//                   Container(\n//                     padding: const EdgeInsets.symmetric(horizontal: 8),\n//                     decoration: BoxDecoration(\n//                       color: customColors.listTileBackgroundColor,\n//                       borderRadius: const BorderRadius.only(topLeft: CustomSize.radius, topRight: CustomSize.radius),\n//                     ),\n//                     child: Row(\n//                       mainAxisAlignment: MainAxisAlignment.spaceBetween,\n//                       children: [\n//                         Text(\n//                           language,\n//                           style: TextStyle(\n//                             fontSize: 12,\n//                             color: customColors.weakTextColor,\n//                           ),\n//                         ),\n//                         TextButton.icon(\n//                           icon: Icon(\n//                             Icons.copy,\n//                             size: 14,\n//                             color: customColors.weakTextColorLess,\n//                           ),\n//                           label: Text(\n//                             'Copy',\n//                             style: TextStyle(\n//                               fontSize: 12,\n//                               color: customColors.weakTextColorLess,\n//                             ),\n//                           ),\n//                           onPressed: () {\n//                             FlutterClipboard.copy(code).then((value) {\n//                               showSuccessMessage('Copied to clipboard');\n//                             });\n//                           },\n//                           style: ButtonStyle(\n//                             overlayColor: WidgetStateProperty.all(Colors.transparent),\n//                           ),\n//                         ),\n//                       ],\n//                     ),\n//                   ),\n//                   child,\n//                 ],\n//               ),\n//             );\n//           },\n//         ),\n//         // 代码配置\n//         CodeConfig(\n//           style: TextStyle(\n//             fontSize: CustomSize.markdownCodeSize,\n//             color: customColors.markdownCodeColor,\n//           ),\n//         ),\n//         // 图片配置\n//         ImgConfig(\n//           builder: (url, attributes) {\n//             if (url.isEmpty) {\n//               return const SizedBox();\n//             }\n\n//             if (url.startsWith('data:')) {\n//               return ClipRRect(\n//                 borderRadius: CustomSize.borderRadiusAll,\n//                 child: Image.memory(\n//                   const Base64Decoder().convert(url.split(',')[1]),\n//                   fit: BoxFit.cover,\n//                 ),\n//               );\n//             }\n\n//             return NetworkImagePreviewer(\n//               url: url,\n//               hidePreviewButton: true,\n//             );\n//           },\n//         ),\n//         HrConfig(height: 1, color: customColors.weakTextColorLess?.withAlpha(100) ?? Colors.transparent),\n//       ],\n//     );\n//   }\n\n//   @override\n//   Widget build(BuildContext context) {\n//     final customColors = Theme.of(context).extension<CustomColors>()!;\n//     final markdownGenerator = MarkdownGenerator(\n//       generators: [latexGenerator],\n//       inlineSyntaxList: [LatexSyntax()],\n//     );\n\n//     if (compact) {\n//       return Column(\n//         mainAxisSize: MainAxisSize.min,\n//         mainAxisAlignment: MainAxisAlignment.start,\n//         textDirection: TextDirection.ltr,\n//         crossAxisAlignment: CrossAxisAlignment.start,\n//         children: markdownGenerator.buildWidgets(\n//           data,\n//           config: _buildMarkdownConfig(customColors),\n//         ),\n//       );\n//     }\n\n//     return MarkdownWidget(\n//       data: data,\n//       shrinkWrap: true,\n//       config: _buildMarkdownConfig(customColors),\n//       markdownGenerator: markdownGenerator,\n//     );\n//   }\n// }\n"
  },
  {
    "path": "lib/page/component/chat/message_state_manager.dart",
    "content": "import 'dart:convert';\n\nimport 'package:askaide/repo/cache_repo.dart';\nimport 'package:askaide/repo/model/message.dart';\n\nclass MessageWithState {\n  final Message message;\n  final MessageState state;\n\n  MessageWithState(this.message, this.state);\n}\n\n/// 消息状态\nclass MessageState {\n  /// 是否显示翻译\n  bool showTranslate = false;\n\n  /// 翻译文本\n  String? translateText;\n\n  /// 是否显示 Markdown\n  bool showMarkdown = true;\n\n  MessageState({\n    this.showTranslate = false,\n    this.translateText,\n    this.showMarkdown = true,\n  });\n\n  /// 是否是初始状态\n  bool isInitializeState() {\n    return !showTranslate && translateText == null && showMarkdown;\n  }\n\n  toJson() {\n    return {\n      'showTranslate': showTranslate,\n      'translateText': translateText,\n      'showMarkdown': showMarkdown,\n    };\n  }\n\n  MessageState.fromJson(Map<String, dynamic> json) {\n    showTranslate = json['showTranslate'] ?? false;\n    translateText = json['translateText'];\n    showMarkdown = json['showMarkdown'] ?? true;\n  }\n}\n\n/// 消息状态管理器\nclass MessageStateManager {\n  final CacheRepository cacheRepo;\n  MessageStateManager(this.cacheRepo);\n\n  Future<Map<String, MessageState>> loadRoomStates(int roomId) async {\n    final states = await cacheRepo.getAllInGroup('room:$roomId');\n\n    return states.map((key, value) =>\n        MapEntry(key, MessageState.fromJson(jsonDecode(value))));\n  }\n\n  String getKey(int roomId, int id) {\n    return 'msg:state:$roomId:$id';\n  }\n\n  Future<MessageState> getState(int roomId, int id) async {\n    final key = getKey(roomId, id);\n    final value = await cacheRepo.get(key);\n    if (value == null) {\n      return MessageState();\n    }\n\n    return MessageState.fromJson(jsonDecode(value));\n  }\n\n  Future<void> setState(int roomId, int id, MessageState state) async {\n    final key = getKey(roomId, id);\n    if (state.isInitializeState()) {\n      return removeState(roomId, id);\n    }\n\n    return cacheRepo.set(\n      key,\n      jsonEncode(state.toJson()),\n      const Duration(days: 7),\n      group: 'room:$roomId',\n    );\n  }\n\n  Future<void> removeState(int roomId, int id) async {\n    final key = getKey(roomId, id);\n    return cacheRepo.remove(key);\n  }\n}\n"
  },
  {
    "path": "lib/page/component/chat/role_avatar.dart",
    "content": "import 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/image.dart';\nimport 'package:askaide/helper/model.dart';\nimport 'package:askaide/page/component/random_avatar.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/repo/model/chat_history.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_initicon/flutter_initicon.dart';\n\nclass RoleAvatar extends StatefulWidget {\n  final String? avatarUrl;\n  final String? alternativeAvatarUrl;\n  final String? name;\n  final ChatHistory? his;\n  final int avatarSize;\n\n  const RoleAvatar({\n    super.key,\n    this.avatarUrl,\n    this.alternativeAvatarUrl,\n    this.his,\n    this.name,\n    this.avatarSize = 30,\n  });\n\n  @override\n  State<RoleAvatar> createState() => _RoleAvatarState();\n}\n\nclass _RoleAvatarState extends State<RoleAvatar> {\n  @override\n  Widget build(BuildContext context) {\n    return _buildAvatar(context);\n  }\n\n  Widget _buildAvatar(BuildContext context) {\n    if (widget.avatarUrl != null && widget.avatarUrl!.startsWith('http')) {\n      return RemoteAvatar(\n        avatarUrl: imageURL(widget.avatarUrl!, qiniuImageTypeAvatar),\n        size: widget.avatarSize,\n      );\n    }\n\n    if (widget.alternativeAvatarUrl != null) {\n      return RemoteAvatar(\n        avatarUrl: imageURL(widget.alternativeAvatarUrl!, qiniuImageTypeAvatar),\n        size: widget.avatarSize,\n      );\n    }\n\n    if (widget.his != null && widget.his!.model != null) {\n      return FutureBuilder(\n        future: ModelAggregate.models(),\n        builder: (context, snapshot) {\n          if (!snapshot.hasError && snapshot.hasData) {\n            var mod = snapshot.data!.where((e) => e.id == widget.his!.model!).firstOrNull;\n            if (mod != null && mod.avatarUrl != null && mod.avatarUrl != '') {\n              return RemoteAvatar(avatarUrl: mod.avatarUrl!, size: widget.avatarSize);\n            }\n          }\n\n          return LocalAvatar(assetName: 'assets/app.png', size: widget.avatarSize);\n        },\n      );\n    }\n\n    if (widget.name != null && widget.name!.isNotEmpty) {\n      return Initicon(\n        text: widget.name!.split('、').join(' '),\n        size: widget.avatarSize.toDouble(),\n        backgroundColor: Colors.grey.withAlpha(100),\n        borderRadius: CustomSize.borderRadiusAll,\n      );\n    }\n\n    return LocalAvatar(assetName: 'assets/app.png', size: widget.avatarSize);\n  }\n}\n"
  },
  {
    "path": "lib/page/component/chat/search_result.dart",
    "content": "import 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/chat/chat_preview.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/item_selector_search.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:url_launcher/url_launcher_string.dart';\n\nclass SearchResult extends StatelessWidget {\n  final List<ReferenceDocument> searchResults;\n  final bool isSearching;\n  const SearchResult({super.key, required this.searchResults, this.isSearching = false});\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return SizedBox(\n      width: double.infinity,\n      child: isSearching\n          ? Row(\n              mainAxisSize: MainAxisSize.min,\n              children: [\n                Text(\n                  AppLocale.robotIsSearchingMessage.getString(context),\n                  style: TextStyle(fontSize: 14, color: customColors.weakTextColorLess),\n                ),\n                const SizedBox(width: 5),\n                RotationTransition(\n                  turns: AnimationController(\n                    duration: const Duration(seconds: 3),\n                    vsync: Scaffold.of(context),\n                  )..repeat(),\n                  child: Icon(\n                    Icons.sync,\n                    size: 16,\n                    color: customColors.weakTextColorLess,\n                  ),\n                ),\n              ],\n            )\n          : GestureDetector(\n              onTap: () {\n                openModalBottomSheet(\n                  context,\n                  (context) {\n                    return ItemSearchSelector(\n                      innerPadding: const EdgeInsets.symmetric(horizontal: 0, vertical: 5),\n                      items: searchResults\n                          .map(\n                            (e) => SelectorItem<String>(\n                              Column(\n                                mainAxisSize: MainAxisSize.min,\n                                crossAxisAlignment: CrossAxisAlignment.start,\n                                children: [\n                                  Row(\n                                    mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                                    children: [\n                                      Row(\n                                        children: [\n                                          // Icon\n                                          if (e.icon.isNotEmpty)\n                                            ClipRRect(\n                                              borderRadius: CustomSize.borderRadius,\n                                              child: CachedNetworkImageEnhanced(\n                                                imageUrl: e.icon,\n                                                fit: BoxFit.fill,\n                                                width: 15,\n                                                height: 15,\n                                              ),\n                                            )\n                                          else\n                                            const SizedBox(width: 15),\n                                          const SizedBox(width: 10),\n                                          // 媒体名称\n                                          Text(\n                                            e.media,\n                                            textAlign: TextAlign.left,\n                                            overflow: TextOverflow.ellipsis,\n                                            maxLines: 2,\n                                            style: TextStyle(\n                                              color: customColors.weakTextColorLess,\n                                              fontSize: 12,\n                                            ),\n                                          ),\n                                        ],\n                                      ),\n                                      // 索引\n                                      Container(\n                                        decoration: BoxDecoration(\n                                          color: customColors.weakTextColorLess,\n                                          shape: BoxShape.circle,\n                                        ),\n                                        padding: const EdgeInsets.all(4),\n                                        child: Text(\n                                          e.index,\n                                          style: const TextStyle(\n                                            color: Colors.white,\n                                            fontSize: 12,\n                                          ),\n                                        ),\n                                      ),\n                                    ],\n                                  ),\n                                  const SizedBox(height: 4),\n                                  Text(\n                                    e.title,\n                                    textAlign: TextAlign.left,\n                                    overflow: TextOverflow.ellipsis,\n                                    maxLines: 2,\n                                    style: TextStyle(\n                                      color: customColors.weakTextColor,\n                                    ),\n                                  ),\n                                  const SizedBox(height: 4),\n                                  Text(\n                                    e.content,\n                                    textAlign: TextAlign.left,\n                                    overflow: TextOverflow.ellipsis,\n                                    maxLines: 2,\n                                    style: TextStyle(\n                                      color: customColors.weakTextColorLess,\n                                    ),\n                                    textScaler: const TextScaler.linear(0.8),\n                                  ),\n                                ],\n                              ),\n                              e.source,\n                              search: (keyword) =>\n                                  e.title.toLowerCase().contains(keyword.toLowerCase()) ||\n                                  e.content.contains(keyword.toLowerCase()),\n                            ),\n                          )\n                          .toList(),\n                      onSelected: (value) {\n                        launchUrlString(value.value);\n                        return false;\n                      },\n                    );\n                  },\n                  heightFactor: 0.9,\n                );\n              },\n              child: Row(\n                mainAxisSize: MainAxisSize.min,\n                children: [\n                  Text(\n                    AppLocale.searchedXWebPages.getString(context).replaceAll('%s', searchResults.length.toString()),\n                    style: TextStyle(fontSize: 14, color: customColors.weakTextColorLess),\n                  ),\n                  Icon(\n                    Icons.keyboard_arrow_right,\n                    size: 16,\n                    color: customColors.weakTextColorLess,\n                  ),\n                ],\n              ),\n            ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/chat/thinking_card.dart",
    "content": "import 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/chat/markdown.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:url_launcher/url_launcher_string.dart';\n\nclass ThinkingCard extends StatelessWidget {\n  final String content;\n  final String title;\n  final bool isExpanded;\n  final Function(bool) onTap;\n  final double timeConsumed;\n  const ThinkingCard({\n    super.key,\n    required this.content,\n    required this.title,\n    this.isExpanded = false,\n    required this.onTap,\n    this.timeConsumed = 0,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return SizedBox(\n      width: double.infinity,\n      child: Column(\n        mainAxisSize: MainAxisSize.min,\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: [\n          GestureDetector(\n            onTap: () => onTap(!isExpanded),\n            child: Row(\n              mainAxisSize: MainAxisSize.min,\n              children: [\n                Text(\n                  timeConsumed > 0\n                      ? '$title (${AppLocale.timeConsume.getString(context)} ${timeConsumed.toStringAsFixed(1)}s)'\n                      : title,\n                  style: TextStyle(fontSize: 14, color: customColors.weakTextColorLess),\n                ),\n                AnimatedRotation(\n                  duration: const Duration(milliseconds: 200),\n                  turns: isExpanded ? 0.5 : 0,\n                  child: Icon(\n                    Icons.keyboard_arrow_up,\n                    size: 16,\n                    color: customColors.weakTextColorLess,\n                  ),\n                ),\n              ],\n            ),\n          ),\n          AnimatedSize(\n            duration: const Duration(milliseconds: 200),\n            child: Container(\n              height: isExpanded ? null : 0,\n              padding: const EdgeInsets.only(top: 8),\n              alignment: Alignment.topLeft,\n              width: double.infinity,\n              child: Container(\n                padding: const EdgeInsets.only(left: 8),\n                alignment: Alignment.topLeft,\n                width: double.infinity,\n                child: IntrinsicHeight(\n                  child: Row(\n                    crossAxisAlignment: CrossAxisAlignment.stretch,\n                    children: [\n                      Container(\n                        width: 3,\n                        margin: const EdgeInsets.only(right: 8),\n                        decoration: BoxDecoration(\n                          color: customColors.weakTextColorLess?.withOpacity(0.5),\n                          borderRadius: BorderRadius.circular(1.5),\n                        ),\n                      ),\n                      Expanded(\n                        child: Markdown(\n                          data: content,\n                          onUrlTap: (value) {\n                            launchUrlString(value);\n                          },\n                          thinkingMode: true,\n                        ),\n                      ),\n                    ],\n                  ),\n                ),\n              ),\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/chat/voice_record.dart",
    "content": "import 'dart:async';\nimport 'dart:io';\n\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/helper/model_resolver.dart';\nimport 'package:askaide/helper/path.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/chat/markdown.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:loading_animation_widget/loading_animation_widget.dart';\nimport 'package:quickalert/quickalert.dart';\nimport 'package:record/record.dart';\nimport 'package:url_launcher/url_launcher_string.dart';\n\nclass VoiceRecord extends StatefulWidget {\n  final Function(String text) onFinished;\n  final Function() onStart;\n  const VoiceRecord(\n      {super.key, required this.onFinished, required this.onStart});\n\n  @override\n  State<VoiceRecord> createState() => _VoiceRecordState();\n}\n\nclass _VoiceRecordState extends State<VoiceRecord> {\n  var _voiceRecording = false;\n  final record = AudioRecorder();\n  DateTime? _voiceStartTime;\n  Timer? _timer;\n  var _millSeconds = 0;\n\n  @override\n  void initState() {\n    super.initState();\n    record.hasPermission().then((hasPermission) {\n      if (!hasPermission) {\n        showErrorMessage('请授予录音权限');\n      }\n    });\n  }\n\n  @override\n  void dispose() {\n    _timer?.cancel();\n    record.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n    return Column(\n      mainAxisSize: MainAxisSize.min,\n      mainAxisAlignment: MainAxisAlignment.center,\n      crossAxisAlignment: CrossAxisAlignment.center,\n      children: [\n        SizedBox(\n          height: 90,\n          child: _voiceRecording\n              ? Column(\n                  children: [\n                    LoadingAnimationWidget.staggeredDotsWave(\n                      color: const Color.fromARGB(255, 74, 74, 254),\n                      size: 60,\n                    ),\n                    const SizedBox(height: 10),\n                    Text('${(_millSeconds / 1000.0).toStringAsFixed(3)} s'),\n                  ],\n                )\n              : const SizedBox(),\n        ),\n        const SizedBox(height: 10),\n        GestureDetector(\n          onLongPressStart: (details) async {\n            if (PlatformTool.isWeb()) {\n              showCustomBeautyDialog(\n                context,\n                type: QuickAlertType.warning,\n                confirmBtnText: AppLocale.gotIt.getString(context),\n                showCancelBtn: false,\n                title: '温馨提示',\n                child: Markdown(\n                  data:\n                      'Web 端暂不支持语音输入，敬请期待。\\n\\n要体验完整功能，您可[点击这里下载 AIdea APP](https://aidea.aicode.cc)。',\n                  onUrlTap: (value) {\n                    launchUrlString(\n                      value,\n                      mode: LaunchMode.externalApplication,\n                    );\n                  },\n                  textStyle: TextStyle(\n                    fontSize: 14,\n                    color: customColors.dialogDefaultTextColor,\n                  ),\n                ),\n              );\n              return;\n            }\n\n            widget.onStart();\n\n            if (await record.hasPermission()) {\n              // 震动反馈\n              HapticFeedbackHelper.heavyImpact();\n\n              setState(() {\n                _voiceRecording = true;\n                _voiceStartTime = DateTime.now();\n              });\n              // Start recording\n              await record.start(\n                RecordConfig(\n                  encoder: PlatformTool.isWindows() || PlatformTool.isIOS()\n                      ? AudioEncoder.aacLc\n                      : AudioEncoder.wav,\n                ),\n                path: \"${PathHelper().getCachePath}/${randomId()}.m4a\",\n              );\n\n              setState(() {\n                _millSeconds = 0;\n              });\n              if (_timer != null) {\n                _timer!.cancel();\n                _timer = null;\n              }\n\n              _timer = Timer.periodic(const Duration(milliseconds: 100),\n                  (timer) async {\n                if (_voiceStartTime == null) {\n                  timer.cancel();\n                  return;\n                }\n\n                if (DateTime.now().difference(_voiceStartTime!).inSeconds >=\n                    60) {\n                  await onRecordStop();\n                  return;\n                }\n\n                setState(() {\n                  _millSeconds = DateTime.now()\n                      .difference(_voiceStartTime!)\n                      .inMilliseconds;\n                });\n              });\n            }\n          },\n          onLongPressEnd: (details) async {\n            if (!_voiceRecording) {\n              return;\n            }\n\n            setState(() {\n              _voiceRecording = false;\n            });\n\n            await onRecordStop();\n          },\n          child: SizedBox(\n            height: 80,\n            width: 80,\n            child: CircleAvatar(\n              backgroundColor: _voiceRecording\n                  ? customColors.linkColor\n                  : customColors.linkColor?.withAlpha(200),\n              child: const Icon(\n                Icons.mic,\n                size: 50,\n                color: Colors.white,\n              ),\n            ),\n          ),\n        ),\n        const SizedBox(height: 10),\n        Text(\n          AppLocale.longPressSpeak.getString(context),\n          style: const TextStyle(fontSize: 16),\n        ),\n        const SizedBox(height: 20),\n      ],\n    );\n  }\n\n  deleteTempFile(String path) {\n    // 删除临时文件\n    if (!path.startsWith('blob:')) {\n      try {\n        File.fromUri(Uri.parse(path)).deleteSync();\n      } catch (e) {\n        try {\n          File(path).deleteSync();\n        } catch (e) {\n          // ignore\n        }\n      }\n    }\n  }\n\n  Future onRecordStop() async {\n    _timer?.cancel();\n\n    var resPath = await record.stop();\n    if (resPath == null) {\n      showErrorMessage('语音输入失败');\n      return;\n    }\n\n    resPath = resPath.replaceAll('\\\\', '/');\n\n    final voiceDuration = DateTime.now().difference(_voiceStartTime!).inSeconds;\n    if (voiceDuration < 1) {\n      showErrorMessage('说话时间太短');\n      _voiceStartTime = null;\n      deleteTempFile(resPath);\n      return;\n    }\n\n    if (voiceDuration > 60) {\n      showErrorMessage('说话时间太长');\n      _voiceStartTime = null;\n      deleteTempFile(resPath);\n      return;\n    }\n\n    _voiceStartTime = null;\n\n    final cancel = BotToast.showCustomLoading(\n      toastBuilder: (cancel) {\n        return LoadingIndicator(\n          message: AppLocale.processingWait.getString(context),\n        );\n      },\n      allowClick: false,\n      duration: const Duration(seconds: 120),\n    );\n\n    try {\n      File audioFile;\n      try {\n        audioFile = File.fromUri(Uri.parse(resPath));\n      } catch (e) {\n        audioFile = File(resPath);\n      }\n\n      widget.onFinished(await ModelResolver.instance.audioToText(audioFile));\n    } catch (e) {\n      // ignore: use_build_context_synchronously\n      showErrorMessageEnhanced(context, e);\n    } finally {\n      cancel();\n      deleteTempFile(resPath);\n    }\n  }\n}\n"
  },
  {
    "path": "lib/page/component/chat_tools_button.dart",
    "content": "import 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:flutter/material.dart';\n\nimport 'package:askaide/page/component/theme/custom_theme.dart';\n\nclass ChatToolsButton extends StatefulWidget {\n  final String text;\n  final IconData icon;\n  final Color? iconColor;\n  final void Function()? onTap;\n\n  const ChatToolsButton({\n    super.key,\n    required this.text,\n    required this.icon,\n    this.iconColor,\n    this.onTap,\n  });\n\n  @override\n  State<ChatToolsButton> createState() => _ChatToolsButtonState();\n}\n\nclass _ChatToolsButtonState extends State<ChatToolsButton> {\n  bool _mouseHover = false;\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return GestureDetector(\n      onTap: widget.onTap,\n      child: MouseRegion(\n        onEnter: (event) {\n          setState(() {\n            _mouseHover = true;\n          });\n        },\n        onExit: (event) {\n          setState(() {\n            _mouseHover = false;\n          });\n        },\n        child: Container(\n          decoration: BoxDecoration(\n            borderRadius: CustomSize.borderRadius,\n            color: _mouseHover ? customColors.tagsBackgroundHover : customColors.tagsBackground,\n          ),\n          padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),\n          child: Row(\n            children: [\n              Icon(\n                widget.icon,\n                size: 11,\n                color: widget.iconColor,\n              ),\n              const SizedBox(width: 2),\n              Text(\n                widget.text,\n                style: TextStyle(\n                  fontSize: 11,\n                  color: customColors.tagsText,\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/column_block.dart",
    "content": "import 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\n\nclass ColumnBlock extends StatelessWidget {\n  final List<Widget> children;\n  final double? innerPanding;\n  final Color? backgroundColor;\n  final BoxBorder? border;\n  final EdgeInsets? padding;\n  final EdgeInsets? margin;\n  final double? borderRadius;\n  final bool showDivider;\n\n  const ColumnBlock({\n    super.key,\n    required this.children,\n    this.innerPanding,\n    this.backgroundColor,\n    this.border,\n    this.padding,\n    this.margin,\n    this.borderRadius,\n    this.showDivider = true,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    if (children.isEmpty) {\n      return Container();\n    }\n\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    var items = <Widget>[];\n    for (var i = 0; i < children.length; i++) {\n      items.add(children[i]);\n      items.add(Container(\n        padding: EdgeInsets.symmetric(vertical: innerPanding ?? 0),\n        child: (i < children.length - 1 && showDivider)\n            ? Divider(\n                color: customColors.columnBlockDividerColor,\n                height: 1,\n              )\n            : Container(),\n      ));\n    }\n\n    return Container(\n      width: double.infinity,\n      decoration: BoxDecoration(\n        color: backgroundColor ?? customColors.columnBlockBackgroundColor,\n        border: border,\n        borderRadius: BorderRadius.circular(borderRadius ?? CustomSize.radiusValue),\n        boxShadow: [\n          BoxShadow(\n            color: customColors.boxShadowColor!,\n            offset: const Offset(0, 3),\n            blurRadius: 5,\n          ),\n          BoxShadow(\n            color: customColors.boxShadowColor!,\n            offset: const Offset(-3, 0),\n            blurRadius: 5,\n          ),\n        ],\n      ),\n      padding: padding ?? const EdgeInsets.symmetric(horizontal: 15, vertical: 5),\n      margin: margin ?? const EdgeInsets.only(bottom: 10, left: 5, right: 5),\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        mainAxisSize: MainAxisSize.min,\n        children: items,\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/credit.dart",
    "content": "import 'package:askaide/lang/lang.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\n\nclass Credit extends StatelessWidget {\n  final int count;\n  final Color? color;\n  final FontWeight? fontWeight;\n  final double? fontSize;\n  final bool withAddPrefix;\n  const Credit({\n    super.key,\n    required this.count,\n    this.color,\n    this.fontWeight,\n    this.fontSize,\n    this.withAddPrefix = false,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return RichText(\n        text: TextSpan(\n      children: [\n        TextSpan(\n          text: '${withAddPrefix ? \"+ \" : \"\"}${AppLocale.creditUnit.getString(context)}${formatCount()}',\n          style: TextStyle(\n            fontSize: fontSize ?? 20,\n            color: color ?? Colors.white,\n            fontWeight: fontWeight ?? FontWeight.bold,\n            overflow: TextOverflow.ellipsis,\n          ),\n        ),\n        TextSpan(\n          text: count >= maxShowCount ? \" +\" : \"\",\n          style: TextStyle(\n            fontSize: fontSize != null ? (fontSize! - 7) : 12,\n            color: color ?? Colors.white.withAlpha(200),\n            fontWeight: fontWeight ?? FontWeight.bold,\n          ),\n        ),\n      ],\n    ));\n  }\n\n  final maxShowCount = 1000000;\n\n  String formatCount() {\n    if (count >= maxShowCount) {\n      return '${(count / maxShowCount).toStringAsFixed(0)} 百万';\n    }\n    return '$count';\n  }\n}\n"
  },
  {
    "path": "lib/page/component/dialog.dart",
    "content": "import 'dart:ui';\n\nimport 'package:askaide/helper/event.dart';\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/helper/logger.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/bottom_sheet_box.dart';\nimport 'package:askaide/page/component/button.dart';\nimport 'package:askaide/page/component/enhanced_textfield.dart';\nimport 'package:askaide/page/component/item_selector_search.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:quickalert/quickalert.dart';\n\nshowErrorMessageEnhanced(\n  BuildContext context,\n  Object message, {\n  Duration duration = const Duration(seconds: 5),\n}) {\n  if (message is LanguageText) {\n    switch (message.action) {\n      // 智慧果不足，支付页面\n      case 'payment':\n        showBeautyDialog(\n          context,\n          type: QuickAlertType.warning,\n          text: message.message.getString(context),\n          confirmBtnText: AppLocale.buy.getString(context),\n          showCancelBtn: true,\n          onConfirmBtnTap: () {\n            context.pop();\n            context.push('/payment');\n          },\n        );\n        return;\n      // 需要重新登录\n      case 're-signin':\n        showBeautyDialog(\n          context,\n          type: QuickAlertType.warning,\n          text: message.message.getString(context),\n          confirmBtnText: AppLocale.reSignIn.getString(context),\n          showCancelBtn: true,\n          onConfirmBtnTap: () {\n            context.pop();\n            context.push('/login');\n          },\n        );\n        return;\n      // 需要登录\n      case 'sign-in':\n        showBeautyDialog(\n          context,\n          type: QuickAlertType.warning,\n          text: AppLocale.needSigninToUse.getString(context),\n          onConfirmBtnTap: () {\n            context.pop();\n            context.push('/login');\n          },\n          showCancelBtn: true,\n          confirmBtnText: AppLocale.signinNow.getString(context),\n        );\n        return;\n    }\n\n    showErrorMessage(message.message.getString(context), duration: duration);\n    return;\n  }\n\n  showErrorMessage(message.toString(), duration: duration);\n}\n\nshowCustomBeautyDialog(\n  BuildContext context, {\n  required QuickAlertType type,\n  required Widget child,\n  String confirmBtnText = '',\n  String? cancelBtnText,\n  Function()? onConfirmBtnTap,\n  Function()? onCancelBtnTap,\n  bool showCancelBtn = false,\n  String title = '',\n}) {\n  final customColors = Theme.of(context).extension<CustomColors>()!;\n\n  QuickAlert.show(\n    context: context,\n    type: type,\n    widget: child,\n    width: MediaQuery.of(context).size.width > 600 ? 400 : null,\n    barrierDismissible: false, // 禁止点击外部关闭\n    showCancelBtn: showCancelBtn,\n    confirmBtnText: confirmBtnText == '' ? AppLocale.ok.getString(context) : confirmBtnText,\n    cancelBtnText: cancelBtnText ?? AppLocale.cancel.getString(context),\n    confirmBtnColor: customColors.linkColor!,\n    borderRadius: CustomSize.radiusValue,\n    buttonBorderRadius: CustomSize.radiusValue,\n    backgroundColor: customColors.dialogBackgroundColor!,\n    confirmBtnTextStyle: const TextStyle(\n      color: Colors.white,\n      fontWeight: FontWeight.normal,\n    ),\n    title: title,\n    titleColor: customColors.dialogDefaultTextColor!,\n    textColor: customColors.dialogDefaultTextColor!,\n    cancelBtnTextStyle: TextStyle(\n      color: customColors.dialogDefaultTextColor,\n      fontWeight: FontWeight.normal,\n    ),\n    onConfirmBtnTap: onConfirmBtnTap,\n    onCancelBtnTap: onCancelBtnTap,\n  );\n}\n\nFuture<dynamic> showBeautyDialog(\n  BuildContext context, {\n  required QuickAlertType type,\n  String? text,\n  String? title,\n  String? customAsset,\n  Widget? widget,\n  String confirmBtnText = '',\n  String? cancelBtnText,\n  Function()? onConfirmBtnTap,\n  Function()? onCancelBtnTap,\n  bool showCancelBtn = false,\n  bool barrierDismissible = false, // 禁止点击外部关闭\n}) {\n  final customColors = Theme.of(context).extension<CustomColors>()!;\n\n  return QuickAlert.show(\n    context: context,\n    type: type,\n    text: text,\n    customAsset: customAsset,\n    widget: widget,\n    width: MediaQuery.of(context).size.width > 600 ? 400 : null,\n    barrierDismissible: barrierDismissible,\n    showCancelBtn: showCancelBtn,\n    confirmBtnText: confirmBtnText == '' ? AppLocale.ok.getString(context) : confirmBtnText,\n    cancelBtnText: cancelBtnText ?? AppLocale.cancel.getString(context),\n    confirmBtnColor: customColors.linkColor!,\n    borderRadius: CustomSize.radiusValue,\n    buttonBorderRadius: CustomSize.radiusValue,\n    backgroundColor: customColors.dialogBackgroundColor!,\n    confirmBtnTextStyle: const TextStyle(\n      color: Colors.white,\n      fontWeight: FontWeight.normal,\n    ),\n    title: title ?? '',\n    titleColor: customColors.dialogDefaultTextColor!,\n    textColor: customColors.dialogDefaultTextColor!,\n    cancelBtnTextStyle: TextStyle(\n      color: customColors.dialogDefaultTextColor,\n      fontWeight: FontWeight.normal,\n    ),\n    onConfirmBtnTap: onConfirmBtnTap,\n    onCancelBtnTap: onCancelBtnTap,\n  );\n}\n\nshowErrorMessage(String message, {Duration duration = const Duration(seconds: 3)}) {\n  HapticFeedbackHelper.mediumImpact();\n  Logger.instance.e(message);\n\n  BotToast.showText(\n    text: message,\n    duration: duration,\n    textStyle: const TextStyle(\n      fontSize: 15,\n      color: Colors.white,\n    ),\n    align: Alignment.center,\n  );\n}\n\nshowSuccessMessage(String message, {Duration duration = const Duration(seconds: 3)}) async {\n  BotToast.showText(\n    text: message,\n    duration: duration,\n    textStyle: const TextStyle(\n      fontSize: 15,\n      color: Colors.white,\n    ),\n    align: Alignment.center,\n  );\n}\n\nshowImportantMessage(BuildContext context, String message) {\n  openModalBottomSheet(\n    context,\n    (context) {\n      return Container(\n        padding: const EdgeInsets.all(10),\n        child: Text(\n          message,\n          style: const TextStyle(fontSize: 16),\n        ),\n      );\n    },\n    heightFactor: 0.1,\n  );\n}\n\nFuture openModalBottomSheet(\n  BuildContext context,\n  Widget Function(BuildContext context) builder, {\n  bool useSafeArea = false,\n  isScrollControlled = true,\n  double heightFactor = 0.5,\n  String? title,\n  bool disableEvent = false,\n  bool disableCompleteEvent = false,\n  bool disableInitEvent = false,\n}) {\n  final customColors = Theme.of(context).extension<CustomColors>()!;\n\n  if (!disableEvent && !disableInitEvent) {\n    GlobalEvent().emit('hideBottomNavigatorBar');\n  }\n\n  return showModalBottomSheet(\n    context: context,\n    useSafeArea: useSafeArea,\n    isScrollControlled: isScrollControlled,\n    shape: const RoundedRectangleBorder(\n      borderRadius: BorderRadius.vertical(top: CustomSize.radius),\n    ),\n    elevation: 0,\n    backgroundColor: Colors.transparent,\n    builder: (context) {\n      return BottomSheetBox(\n        child: FractionallySizedBox(\n          heightFactor: heightFactor,\n          child: Column(\n            mainAxisSize: MainAxisSize.min,\n            children: [\n              buildBottomSheetTopBar(customColors),\n              if (title != null)\n                Text(\n                  title,\n                  style: TextStyle(\n                    fontSize: 16,\n                    fontWeight: FontWeight.bold,\n                    color: customColors.weakTextColorPlus,\n                  ),\n                ),\n              if (title != null) const SizedBox(height: 10),\n              Expanded(\n                child: builder(context),\n              ),\n            ],\n          ),\n        ),\n      );\n    },\n  ).whenComplete(() {\n    if (!disableEvent && !disableCompleteEvent) {\n      GlobalEvent().emit('showBottomNavigatorBar');\n    }\n  });\n}\n\nopenConfirmDialog(\n  BuildContext context,\n  String message,\n  Function() onConfirm, {\n  Widget? title,\n  bool danger = false,\n  String? confirmText,\n  String? cancelText,\n}) {\n  HapticFeedbackHelper.mediumImpact();\n  final customColors = Theme.of(context).extension<CustomColors>()!;\n\n  GlobalEvent().emit('hideBottomNavigatorBar');\n  showModalBottomSheet(\n    context: context,\n    elevation: 0,\n    backgroundColor: Colors.transparent,\n    builder: (context) {\n      return BottomSheetBox(\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n          children: [\n            buildBottomSheetTopBar(customColors),\n            const SizedBox(height: 10),\n            if (title != null) title,\n            if (title != null && message != '') const SizedBox(height: 10),\n            if (message != '')\n              Text(\n                message,\n                style: TextStyle(\n                  color: customColors.dialogDefaultTextColor,\n                  fontSize: title == null ? 16 : 12,\n                ),\n                textAlign: TextAlign.center,\n                maxLines: title == null ? 4 : 2,\n              ),\n            const SizedBox(height: 20),\n            Column(\n              mainAxisSize: MainAxisSize.min,\n              children: [\n                Button(\n                  title: confirmText ?? AppLocale.ok.getString(context),\n                  onPressed: () {\n                    onConfirm();\n                    context.pop();\n                  },\n                  size: const ButtonSize.full(),\n                  color: danger ? const Color.fromARGB(255, 255, 17, 0) : customColors.linkColor,\n                  backgroundColor: const Color.fromARGB(36, 222, 222, 222),\n                ),\n                const SizedBox(height: 10),\n                Button(\n                  title: cancelText ?? AppLocale.cancel.getString(context),\n                  backgroundColor: const Color.fromARGB(36, 222, 222, 222),\n                  color: customColors.dialogDefaultTextColor?.withAlpha(150),\n                  onPressed: () {\n                    context.pop();\n                  },\n                  size: const ButtonSize.full(),\n                ),\n                const SizedBox(height: 10),\n              ],\n            ),\n          ],\n        ),\n      );\n    },\n  ).whenComplete(() => GlobalEvent().emit('showBottomNavigatorBar'));\n\n  // showDialog(\n  //   context: context,\n  //   builder: (_) {\n  //     return SizedBox(\n  //       width: _calDialogWidth(context),\n  //       child: CustomDialog(\n  //         customColors: customColors,\n  //         title: title ?? Container(),\n  //         content: Text(\n  //           message,\n  //           style: TextStyle(color: customColors.dialogDefaultTextColor),\n  //         ),\n  //         actions: [\n  //           TextButton(\n  //             onPressed: () {\n  //               context.pop();\n  //             },\n  //             style: TextButton.styleFrom(\n  //               foregroundColor: Colors.grey,\n  //             ),\n  //             child: Text(\n  //               AppLocale.cancel.getString(context),\n  //               style: TextStyle(color: customColors.dialogDefaultTextColor),\n  //             ),\n  //           ),\n  //           const SizedBox(width: 10),\n  //           Button(\n  //             title: AppLocale.ok.getString(context),\n  //             onPressed: () {\n  //               onConfirm();\n  //               context.pop();\n  //             },\n  //             // backgroundColor: danger ? Colors.red : null,\n  //           ),\n  //         ],\n  //       ),\n  //     );\n  //   },\n  // );\n}\n\nCenter buildBottomSheetTopBar(CustomColors customColors) {\n  return Center(\n    child: FractionallySizedBox(\n      widthFactor: 0.25,\n      child: Container(\n        margin: const EdgeInsets.only(top: 8, bottom: 10),\n        height: 4,\n        decoration: BoxDecoration(\n          color: const Color.fromARGB(255, 192, 192, 192),\n          borderRadius: CustomSize.borderRadius,\n          border: Border.all(\n            color: Colors.black12,\n            width: 0.5,\n          ),\n        ),\n      ),\n    ),\n  );\n}\n\nFuture<T?> openDialog<T>(\n  BuildContext context, {\n  Widget? title,\n  required Builder builder,\n  required bool Function() onSubmit,\n  Function()? afterSubmit,\n  bool showCancel = true,\n  String? confirmText,\n  bool barrierDismissible = true,\n}) {\n  final customColors = Theme.of(context).extension<CustomColors>()!;\n\n  return showDialog(\n    context: context,\n    builder: (context) => CustomDialog(\n      title: title,\n      customColors: customColors,\n      content: SizedBox(\n        width: _calDialogWidth(context),\n        child: builder.build(context),\n      ),\n      actions: [\n        if (showCancel)\n          TextButton(\n            onPressed: () {\n              context.pop();\n            },\n            child: Text(\n              AppLocale.cancel.getString(context),\n              style: TextStyle(color: customColors.dialogDefaultTextColor),\n            ),\n          ),\n        Button(\n          onPressed: () {\n            if (onSubmit()) {\n              context.pop();\n            }\n\n            if (afterSubmit != null) {\n              afterSubmit();\n            }\n          },\n          title: confirmText ?? AppLocale.ok.getString(context),\n          backgroundColor: const Color.fromARGB(36, 222, 222, 222),\n          color: customColors.linkColor,\n        )\n      ],\n    ),\n    barrierDismissible: barrierDismissible,\n  );\n}\n\ndouble _calDialogWidth(BuildContext context) {\n  final windowWidth = MediaQuery.of(context).size.width * 0.8;\n  if (windowWidth > 350) {\n    return 350;\n  }\n\n  return windowWidth;\n}\n\nopenListSelectDialogWithDatasource<T>({\n  required bool Function(SelectorItem value) onSelected,\n  required BuildContext context,\n  required Future<List<T>>? future,\n  required SelectorItem Function(T value) itemBuilder,\n  double heightFactor = 0.5,\n  bool enableSearch = false,\n  String? title,\n  Object? value,\n  bool horizontal = false,\n  int horizontalCount = 4,\n  EdgeInsets? innerPadding,\n}) {\n  openModalBottomSheet(\n    context,\n    (context) {\n      return FutureBuilder(\n        future: future,\n        builder: (context, snapshot) {\n          if (!snapshot.hasData) {\n            return const Center(\n              child: CircularProgressIndicator(),\n            );\n          }\n\n          return ItemSearchSelector(\n            enableSearch: enableSearch,\n            items: (snapshot.data ?? []).map((e) => itemBuilder(e)).toList(),\n            onSelected: onSelected,\n            value: value,\n            horizontal: horizontal,\n            horizontalCount: horizontalCount,\n            innerPadding: innerPadding,\n          );\n        },\n      );\n    },\n    heightFactor: heightFactor,\n    title: title,\n  );\n}\n\nopenListSelectDialog(\n  BuildContext context,\n  List<SelectorItem> items,\n  bool Function(SelectorItem value) onSelected, {\n  double heightFactor = 0.5,\n  bool enableSearch = false,\n  String? title,\n  Object? value,\n  bool horizontal = false,\n  int horizontalCount = 4,\n  EdgeInsets? innerPadding,\n}) {\n  openModalBottomSheet(\n    context,\n    (context) {\n      return ItemSearchSelector(\n        enableSearch: enableSearch,\n        items: items,\n        onSelected: onSelected,\n        value: value,\n        horizontal: horizontal,\n        horizontalCount: horizontalCount,\n        innerPadding: innerPadding,\n      );\n    },\n    heightFactor: heightFactor,\n    title: title,\n  );\n}\n\n/// 弹出一个输入框\nopenTextFieldDialog(\n  BuildContext context, {\n  required String title,\n  String? hint,\n  String? defaultValue,\n  int? maxLine,\n  bool obscureText = false,\n  int? maxLength,\n  Icon? suffixIcon,\n  bool withSuffixIcon = false,\n  bool showCounter = false,\n  bool enableSearch = false,\n  List<SelectorItem<String>>? dataSources,\n  Future<List<SelectorItem<String>>>? futureDataSources,\n  required bool Function(String) onSubmit,\n}) {\n  final customColors = Theme.of(context).extension<CustomColors>()!;\n  final controller = TextEditingController(text: defaultValue ?? '');\n  GlobalEvent().emit('hideBottomNavigatorBar');\n  showModalBottomSheet(\n    context: context,\n    backgroundColor: Colors.transparent,\n    isScrollControlled: true,\n    builder: (context) {\n      return BottomSheetBox(\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            buildBottomSheetTopBar(customColors),\n            EnhancedTextField(\n              enableBackground: true,\n              labelPosition: LabelPosition.top,\n              labelText: title,\n              customColors: customColors,\n              controller: controller,\n              maxLines: obscureText ? 1 : maxLine,\n              obscureText: obscureText,\n              maxLength: maxLength,\n              showCounter: showCounter,\n              hintText: hint,\n              inputSelector: withSuffixIcon\n                  ? IconButton(\n                      icon: suffixIcon ??\n                          Icon(\n                            Icons.style,\n                            color: customColors.dialogDefaultTextColor,\n                            size: 16,\n                          ),\n                      onPressed: () {\n                        openModalBottomSheet(\n                          context,\n                          (context) {\n                            if (futureDataSources != null) {\n                              return FutureBuilder(\n                                future: futureDataSources,\n                                builder: (context, snapshot) {\n                                  if (snapshot.hasData) {\n                                    return ItemSearchSelector(\n                                      items: snapshot.data as List<SelectorItem>,\n                                      onSelected: (value) {\n                                        controller.text = value.value;\n                                        return true;\n                                      },\n                                      enableSearch: enableSearch,\n                                    );\n                                  }\n\n                                  return const Center(\n                                    child: CircularProgressIndicator(),\n                                  );\n                                },\n                              );\n                            }\n\n                            return ItemSearchSelector(\n                              items: dataSources ?? [],\n                              onSelected: (value) {\n                                controller.text = value.value;\n                                return true;\n                              },\n                              enableSearch: enableSearch,\n                            );\n                          },\n                          disableEvent: true,\n                        );\n                      },\n                    )\n                  : null,\n            ),\n            const SizedBox(height: 10),\n            Column(\n              mainAxisSize: MainAxisSize.min,\n              children: [\n                Button(\n                  title: AppLocale.ok.getString(context),\n                  backgroundColor: const Color.fromARGB(36, 222, 222, 222),\n                  color: customColors.linkColor,\n                  onPressed: () {\n                    if (onSubmit(controller.text)) {\n                      context.pop();\n                    }\n                  },\n                  size: const ButtonSize.full(),\n                  // backgroundColor: danger ? Colors.red : null,\n                ),\n                const SizedBox(height: 10),\n                Button(\n                  title: AppLocale.cancel.getString(context),\n                  backgroundColor: const Color.fromARGB(36, 222, 222, 222),\n                  color: customColors.dialogDefaultTextColor?.withAlpha(150),\n                  onPressed: () {\n                    context.pop();\n                  },\n                  size: const ButtonSize.full(),\n                ),\n                const SizedBox(height: 20),\n              ],\n            ),\n          ],\n        ),\n      );\n    },\n  ).whenComplete(() {\n    GlobalEvent().emit('showBottomNavigatorBar');\n    Future.delayed(const Duration(seconds: 1), () {\n      controller.dispose();\n    });\n  });\n}\n\nopenFullscreenDialog(\n  BuildContext context, {\n  required Widget child,\n  required String title,\n  List<Widget>? actions,\n}) {\n  HapticFeedbackHelper.mediumImpact();\n  Navigator.of(context).push(\n    MaterialPageRoute(\n      builder: (context) => _FullScreenDialog(\n        title: title,\n        actions: actions,\n        child: child,\n      ),\n    ),\n  );\n}\n\n@immutable\nclass _FullScreenDialog extends StatelessWidget {\n  final String title;\n  final Widget child;\n  final List<Widget>? actions;\n\n  const _FullScreenDialog({\n    Key? key,\n    required this.title,\n    required this.child,\n    this.actions,\n  }) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          title: Text(title),\n          toolbarHeight: CustomSize.toolbarHeight,\n          actions: actions,\n          leading: IconButton(\n            icon: const Icon(Icons.close),\n            onPressed: () => Navigator.of(context).pop(),\n          ),\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: SafeArea(\n          child: child,\n        ),\n      ),\n    );\n  }\n}\n\nclass CustomDialog extends StatelessWidget {\n  final List<Widget>? actions;\n  final Widget? title;\n  final Widget? content;\n  final Color? backgroundColor;\n  final bool glassEffect;\n  final CustomColors customColors;\n\n  const CustomDialog({\n    super.key,\n    required this.customColors,\n    this.actions,\n    this.title,\n    this.content,\n    this.backgroundColor,\n    this.glassEffect = false,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final dialog = AlertDialog(\n      title: title,\n      elevation: 0,\n      shape: RoundedRectangleBorder(borderRadius: CustomSize.borderRadius),\n      titleTextStyle: TextStyle(\n        color: customColors.dialogDefaultTextColor,\n        fontSize: 20,\n        fontWeight: FontWeight.bold,\n      ),\n      backgroundColor: backgroundColor ??\n          (glassEffect ? customColors.dialogBackgroundColor!.withAlpha(50) : customColors.dialogBackgroundColor),\n      content: content,\n      actions: actions,\n      actionsAlignment: MainAxisAlignment.spaceAround,\n    );\n\n    if (glassEffect) {\n      return ClipRect(\n        child: BackdropFilter(\n          filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),\n          child: dialog,\n        ),\n      );\n    }\n\n    return dialog;\n  }\n}\n"
  },
  {
    "path": "lib/page/component/effect/glass.dart",
    "content": "import 'dart:ui';\n\nimport 'package:flutter/material.dart';\n\nclass GlassEffect extends StatelessWidget {\n  final Widget child;\n  final bool enabled;\n\n  const GlassEffect({\n    super.key,\n    required this.child,\n    this.enabled = false,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    if (!enabled) {\n      return Container(\n        margin: const EdgeInsets.symmetric(vertical: 8),\n        child: child,\n      );\n    }\n\n    return ClipRRect(\n      borderRadius: const BorderRadius.only(\n        topLeft: Radius.circular(20),\n        topRight: Radius.circular(20),\n      ),\n      child: BackdropFilter(\n        filter: ImageFilter.blur(\n          sigmaX: 20.0,\n          sigmaY: 20.0,\n        ),\n        child: Container(\n          decoration: BoxDecoration(\n            color: Colors.white12,\n            borderRadius: const BorderRadius.only(\n              topLeft: Radius.circular(20),\n              topRight: Radius.circular(20),\n            ),\n            border: Border.all(\n              color: Colors.black26,\n              width: 0.5,\n            ),\n          ),\n          child: Column(\n            children: [\n              Center(\n                child: FractionallySizedBox(\n                  widthFactor: 0.25,\n                  child: Container(\n                    margin: const EdgeInsets.symmetric(\n                      vertical: 8,\n                    ),\n                    height: 4,\n                    decoration: BoxDecoration(\n                      color: Colors.white,\n                      borderRadius: BorderRadius.circular(2),\n                      border: Border.all(\n                        color: Colors.black12,\n                        width: 0.5,\n                      ),\n                    ),\n                  ),\n                ),\n              ),\n              Expanded(\n                child: Container(\n                  alignment: Alignment.center,\n                  child: child,\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/enhanced_button.dart",
    "content": "import 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\n\nclass EnhancedButton extends StatelessWidget {\n  final String title;\n  final Color? backgroundColor;\n  final Color? color;\n  final double? height;\n  final double? width;\n  final double? fontSize;\n  final Widget? icon;\n  final Function() onPressed;\n\n  const EnhancedButton({\n    super.key,\n    required this.title,\n    this.backgroundColor,\n    this.color,\n    this.height,\n    this.width,\n    this.fontSize,\n    required this.onPressed,\n    this.icon,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return Material(\n      color: backgroundColor ?? customColors.linkColor,\n      borderRadius: CustomSize.borderRadius,\n      child: InkWell(\n        borderRadius: CustomSize.borderRadiusAll,\n        onTap: onPressed,\n        child: Container(\n          height: height ?? 42,\n          width: width ?? double.infinity,\n          alignment: Alignment.center,\n          child: Row(\n            mainAxisAlignment: MainAxisAlignment.center,\n            children: [\n              if (icon != null) icon!,\n              if (icon != null) const SizedBox(width: 5),\n              Text(\n                title,\n                textAlign: TextAlign.center,\n                style: TextStyle(\n                  color: color ?? Colors.white,\n                  fontSize: fontSize ?? 17,\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/enhanced_error.dart",
    "content": "import 'package:askaide/helper/helper.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass EnhancedErrorWidget extends StatelessWidget {\n  final Object? error;\n  const EnhancedErrorWidget({super.key, required this.error});\n\n  @override\n  Widget build(BuildContext context) {\n    if (error == null) {\n      return Container();\n    }\n\n    return Scaffold(\n      appBar: AppBar(\n        toolbarHeight: CustomSize.toolbarHeight,\n      ),\n      body: Center(\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          children: [\n            RichText(\n              text: TextSpan(\n                children: [\n                  TextSpan(\n                    text: resolveError(context, error!),\n                    style: const TextStyle(\n                      color: Colors.red,\n                      fontSize: 17,\n                    ),\n                  ),\n                ],\n              ),\n            ),\n            InkWell(\n              onTap: () {\n                context.go('/login');\n              },\n              borderRadius: CustomSize.borderRadiusAll,\n              child: Padding(\n                padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),\n                child: Text(\n                  AppLocale.clickToReSignin.getString(context),\n                  textScaler: const TextScaler.linear(0.8),\n                  style: const TextStyle(\n                    color: Colors.red,\n                  ),\n                ),\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/enhanced_input.dart",
    "content": "import 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\n\nclass EnhancedInputSimple extends StatelessWidget {\n  final String title;\n  final String? value;\n  final VoidCallback onPressed;\n  final Icon? icon;\n  final Widget? description;\n  final EdgeInsets? padding;\n\n  const EnhancedInputSimple({\n    super.key,\n    required this.title,\n    required this.onPressed,\n    this.value,\n    this.icon,\n    this.description,\n    this.padding,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return EnhancedInput(\n      padding: padding,\n      title: Text(\n        title,\n        style: TextStyle(\n          color: customColors.textfieldLabelColor,\n          fontSize: 16,\n        ),\n      ),\n      value: value != null\n          ? Text(\n              value!,\n              overflow: TextOverflow.ellipsis,\n              style: TextStyle(\n                color: customColors.textfieldValueColor,\n                fontSize: 15,\n              ),\n            )\n          : null,\n      onPressed: onPressed,\n      icon: icon,\n      description: description,\n    );\n  }\n}\n\nclass EnhancedInput extends StatelessWidget {\n  final Widget title;\n  final Widget? value;\n  final VoidCallback onPressed;\n  final Icon? icon;\n  final Widget? description;\n  final EdgeInsets? padding;\n\n  const EnhancedInput({\n    super.key,\n    required this.title,\n    required this.onPressed,\n    this.value,\n    this.icon,\n    this.description,\n    this.padding,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return InkWell(\n      onTap: onPressed,\n      borderRadius: CustomSize.borderRadiusAll,\n      child: Container(\n        padding: padding ?? const EdgeInsets.symmetric(vertical: 12),\n        child: Column(\n          children: [\n            Row(\n              mainAxisAlignment: MainAxisAlignment.spaceBetween,\n              children: [\n                SizedBox(\n                  width: 85,\n                  child: title,\n                ),\n                Expanded(\n                  child: Row(\n                    mainAxisAlignment: MainAxisAlignment.end,\n                    mainAxisSize: MainAxisSize.min,\n                    children: [\n                      Flexible(child: value ?? Container()),\n                      const SizedBox(width: 5),\n                      icon ??\n                          const Icon(\n                            CupertinoIcons.chevron_forward,\n                            size: 18,\n                            color: Colors.grey,\n                          ),\n                    ],\n                  ),\n                ),\n              ],\n            ),\n            if (description != null) const SizedBox(height: 10),\n            if (description != null) description!,\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/enhanced_popup_menu.dart",
    "content": "import 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\n\nclass EnhancedPopupMenuItem {\n  final String title;\n  final IconData? icon;\n  final Color? iconColor;\n  final Function(BuildContext)? onTap;\n\n  const EnhancedPopupMenuItem({\n    required this.title,\n    this.icon,\n    this.iconColor,\n    this.onTap,\n  });\n}\n\nclass EnhancedPopupMenu extends StatelessWidget {\n  final List<EnhancedPopupMenuItem> items;\n  final IconData? icon;\n  final Color? color;\n  final String? tooltip;\n  final void Function()? onOpened;\n  final void Function()? onCanceled;\n\n  const EnhancedPopupMenu({\n    super.key,\n    required this.items,\n    this.icon,\n    this.color,\n    this.tooltip,\n    this.onOpened,\n    this.onCanceled,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return PopupMenuButton(\n      icon: Icon(icon ?? Icons.more_horiz, color: color),\n      tooltip: tooltip,\n      splashRadius: 20,\n      elevation: 0,\n      enableFeedback: true,\n      color: customColors.backgroundColor,\n      shape: RoundedRectangleBorder(borderRadius: CustomSize.borderRadius),\n      position: PopupMenuPosition.under,\n      onOpened: onOpened,\n      onCanceled: onCanceled,\n      onSelected: (value) {\n        if (value.onTap != null) {\n          value.onTap!(context);\n        }\n      },\n      itemBuilder: (context) {\n        return [\n          for (final item in items)\n            PopupMenuItem(\n              // onTap: () {\n              //   item.onTap!(context);\n              // },\n              value: item,\n              child: Row(\n                children: [\n                  if (item.icon != null) Icon(item.icon!, size: 15, color: item.iconColor),\n                  if (item.icon != null) const SizedBox(width: 10),\n                  Text(\n                    item.title,\n                    style: const TextStyle(fontSize: 14),\n                  ),\n                ],\n              ),\n            )\n        ];\n      },\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/enhanced_textfield.dart",
    "content": "import 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\n\nenum LabelPosition { top, left, inner }\n\nclass InputSelector extends StatelessWidget {\n  final Widget title;\n  final VoidCallback onTap;\n\n  const InputSelector({\n    super.key,\n    required this.title,\n    required this.onTap,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return TextButton(\n      onPressed: onTap,\n      style: ButtonStyle(\n        overlayColor: WidgetStateProperty.all(Colors.transparent),\n      ),\n      child: title,\n    );\n  }\n}\n\nclass EnhancedTextField extends StatefulWidget {\n  final int? maxLength;\n  final bool? autofocus;\n  final TextInputType? keyboardType;\n  final TextEditingController? controller;\n  final FocusNode? focusNode;\n  final TextAlignVertical? textAlignVertical;\n  final CustomColors customColors;\n  final String? labelText;\n  final double? labelFontSize;\n  final double? labelWidth;\n  final Widget? labelWidget;\n  final int? minLines;\n  final int? maxLines;\n  final bool showCounter;\n  final String? hintText;\n  final Widget? suffixIcon;\n  final bool? readOnly;\n  final bool? obscureText;\n  final LabelPosition? labelPosition;\n  final Widget? inputSelector;\n  final List<TextInputFormatter>? inputFormatters;\n  final TextDirection? textDirection;\n  final double? fieldWidth;\n  final void Function(String)? onChanged;\n  final String? initValue;\n  final bool enableBackground;\n  final Widget? bottomButtons;\n  final Widget? bottomButton;\n  final VoidCallback? bottomButtonOnPressed;\n  final double? fontSize;\n  final bool? enabled;\n\n  final Color? hintColor;\n  final double? hintTextSize;\n  final Widget? labelHelpWidget;\n\n  final Widget? middleWidget;\n\n  final Function(bool hasFocus)? onFocusChange;\n\n  const EnhancedTextField({\n    super.key,\n    required this.customColors,\n    this.maxLength,\n    this.autofocus,\n    this.labelWidget,\n    this.keyboardType,\n    this.controller,\n    this.focusNode,\n    this.textAlignVertical,\n    this.labelText,\n    this.labelFontSize,\n    this.labelWidth,\n    this.minLines,\n    this.maxLines = 1,\n    this.showCounter = true,\n    this.hintText,\n    this.suffixIcon,\n    this.readOnly,\n    this.obscureText,\n    this.labelPosition,\n    this.inputSelector,\n    this.inputFormatters,\n    this.textDirection,\n    this.fieldWidth,\n    this.onChanged,\n    this.initValue,\n    this.bottomButtons,\n    this.bottomButton,\n    this.bottomButtonOnPressed,\n    this.enableBackground = false,\n    this.fontSize,\n    this.enabled,\n    this.hintColor,\n    this.hintTextSize,\n    this.labelHelpWidget,\n    this.middleWidget,\n    this.onFocusChange,\n  });\n\n  @override\n  State<EnhancedTextField> createState() => _EnhancedTextFieldState();\n}\n\nclass _EnhancedTextFieldState extends State<EnhancedTextField> {\n  var textLength = 0;\n\n  late final Function() listener;\n\n  @override\n  void initState() {\n    super.initState();\n\n    listener = () {\n      if (mounted) {\n        setState(() {\n          textLength = widget.controller!.text.length;\n        });\n      }\n    };\n\n    if (widget.showCounter) {\n      widget.controller?.addListener(listener);\n    }\n  }\n\n  @override\n  void dispose() {\n    if (widget.showCounter) {\n      widget.controller?.removeListener(listener);\n    }\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    if ((widget.labelText != null || widget.labelWidget != null) && widget.labelPosition != LabelPosition.inner) {\n      // 上下结构\n      if (widget.labelPosition == LabelPosition.top) {\n        return Column(\n          mainAxisSize: MainAxisSize.min,\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            Row(\n              mainAxisAlignment: MainAxisAlignment.spaceBetween,\n              children: [\n                widget.labelWidget != null\n                    ? widget.labelWidget!\n                    : Row(\n                        children: [\n                          Text(\n                            widget.labelText!,\n                            overflow: TextOverflow.ellipsis,\n                            style: TextStyle(\n                              fontSize: widget.labelFontSize ?? 16,\n                              color: widget.customColors.textfieldLabelColor,\n                            ),\n                          ),\n                          const SizedBox(width: 5),\n                          if (widget.labelHelpWidget != null) widget.labelHelpWidget!,\n                        ],\n                      ),\n                if (widget.inputSelector != null) widget.inputSelector!,\n              ],\n            ),\n            const SizedBox(height: 10),\n            _buildTextField(),\n          ],\n        );\n      }\n\n      // 左右结构\n      return Row(\n        mainAxisAlignment: MainAxisAlignment.spaceBetween,\n        children: [\n          SizedBox(\n            width: widget.labelWidth ?? 80,\n            child: widget.labelWidget != null\n                ? widget.labelWidget!\n                : Row(\n                    mainAxisSize: MainAxisSize.min,\n                    children: [\n                      Expanded(\n                        child: Text(\n                          widget.labelText!,\n                          overflow: TextOverflow.ellipsis,\n                          style: TextStyle(\n                            fontSize: widget.labelFontSize ?? 16,\n                            color: widget.customColors.textfieldLabelColor,\n                          ),\n                        ),\n                      ),\n                      if (widget.labelHelpWidget != null) ...[\n                        const SizedBox(width: 5),\n                        widget.labelHelpWidget!,\n                      ]\n                    ],\n                  ),\n          ),\n          const SizedBox(width: 10),\n          widget.fieldWidth != null\n              ? SizedBox(\n                  width: widget.fieldWidth,\n                  child: _buildTextField(),\n                )\n              : Expanded(\n                  child: _buildTextField(),\n                ),\n        ],\n      );\n    }\n\n    // 无标题结构\n    return _buildTextField();\n  }\n\n  Widget _buildTextField() {\n    return Column(\n      mainAxisSize: MainAxisSize.min,\n      children: [\n        Stack(\n          children: [\n            Column(\n              mainAxisSize: MainAxisSize.min,\n              children: [\n                Focus(\n                  onFocusChange: widget.onFocusChange,\n                  child: TextFormField(\n                    initialValue: widget.initValue,\n                    readOnly: widget.readOnly ?? false,\n                    focusNode: widget.focusNode,\n                    controller: widget.controller,\n                    inputFormatters: widget.inputFormatters,\n                    textDirection: widget.textDirection,\n                    obscureText: widget.obscureText ?? false,\n                    enabled: widget.enabled ?? true,\n                    style: TextStyle(\n                      color: widget.customColors.textfieldValueColor,\n                      fontSize: widget.fontSize ?? 15,\n                    ),\n                    decoration: InputDecoration(\n                      filled: widget.enableBackground,\n                      fillColor: widget.customColors.textfieldBackgroundColor,\n                      hintText: widget.hintText,\n                      hintStyle: TextStyle(\n                        fontSize: widget.hintTextSize ?? CustomSize.defaultHintTextSize,\n                        color: widget.hintColor ?? widget.customColors.textfieldHintColor,\n                      ),\n                      hintTextDirection: widget.textDirection,\n                      counterText: \"\",\n                      border: resolveInputBorder(),\n                      enabledBorder: resolveInputBorder(),\n                      focusedBorder: resolveInputBorder(),\n                      // isDense: true,\n                      contentPadding: EdgeInsets.only(\n                        top: widget.labelPosition == LabelPosition.top ? 0 : 10,\n                        left: widget.enableBackground ? 15 : 0,\n                        right: widget.enableBackground ? 15 : 0,\n                        bottom: (widget.showCounter || widget.bottomButton != null) && widget.middleWidget == null\n                            ? 30\n                            : 10,\n                      ),\n                      labelText: widget.labelPosition == LabelPosition.inner ? widget.labelText : null,\n                      labelStyle: TextStyle(\n                        color: widget.customColors.textfieldLabelColor,\n                      ),\n                      suffixIcon: widget.suffixIcon ??\n                          (widget.labelPosition == LabelPosition.left ? widget.inputSelector : null),\n                    ),\n                    cursorRadius: CustomSize.radius,\n                    keyboardType: widget.keyboardType,\n                    autofocus: widget.autofocus ?? false,\n                    maxLength: widget.maxLength,\n                    minLines: widget.minLines,\n                    maxLines: widget.maxLines,\n                    onChanged: widget.controller == null\n                        ? (value) {\n                            setState(() {\n                              textLength = value.length;\n                            });\n\n                            if (widget.onChanged != null) {\n                              widget.onChanged!(value);\n                            }\n                          }\n                        : null,\n                  ),\n                ),\n                widget.middleWidget ?? const SizedBox(),\n              ],\n            ),\n            if (widget.showCounter)\n              Positioned(\n                right: 10,\n                bottom: 10,\n                child: Text(\n                  \"$textLength / ${widget.maxLength}\",\n                  style: TextStyle(\n                    fontSize: 12,\n                    color: widget.customColors.chatInputPanelText,\n                  ),\n                ),\n              ),\n            if (widget.bottomButtons != null)\n              Positioned(\n                right: 0,\n                bottom: 0,\n                child: widget.bottomButtons!,\n              ),\n            if (widget.bottomButton != null)\n              Positioned(\n                right: 0,\n                bottom: 0,\n                child: MaterialButton(\n                  elevation: 0,\n                  splashColor: Colors.transparent,\n                  highlightColor: Colors.transparent,\n                  padding: const EdgeInsets.all(0),\n                  minWidth: 60,\n                  shape: RoundedRectangleBorder(borderRadius: CustomSize.borderRadius),\n                  onPressed: widget.bottomButtonOnPressed,\n                  child: widget.bottomButton!,\n                ),\n              ),\n          ],\n        ),\n      ],\n    );\n  }\n\n  InputBorder resolveInputBorder() {\n    if (widget.enableBackground) {\n      return const OutlineInputBorder(borderRadius: CustomSize.borderRadiusAll, borderSide: BorderSide.none);\n    }\n\n    return InputBorder.none;\n  }\n}\n"
  },
  {
    "path": "lib/page/component/file_preview.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter/widgets.dart';\n\nclass FilePreview extends StatelessWidget {\n  final String? filename;\n  final String? fileUrl;\n  final String fileType;\n  final double maxWidth;\n  final MainAxisAlignment mainAxisAlignment;\n\n  const FilePreview({\n    super.key,\n    this.filename,\n    this.fileUrl,\n    this.maxWidth = 300,\n    this.mainAxisAlignment = MainAxisAlignment.start,\n    required this.fileType,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    var iconFilePath = 'assets/icons/file.png';\n    switch (fileType) {\n      case 'pdf':\n        iconFilePath = 'assets/icons/pdf.png';\n        break;\n      case 'docx':\n        iconFilePath = 'assets/icons/doc.png';\n        break;\n      case 'txt':\n        iconFilePath = 'assets/icons/txt.png';\n        break;\n    }\n    return ConstrainedBox(\n      constraints: BoxConstraints(\n        maxWidth: maxWidth,\n        maxHeight: 25,\n      ),\n      child: Row(\n        mainAxisAlignment: mainAxisAlignment,\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          Image.asset(\n            iconFilePath,\n            width: 20,\n            height: 20,\n          ),\n          if (filename != null && filename != '') ...[\n            const SizedBox(width: 5),\n            Flexible(\n              child: Text(\n                filename!,\n                maxLines: 1,\n                overflow: TextOverflow.ellipsis,\n                style: const TextStyle(fontSize: 12),\n              ),\n            ),\n          ],\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/gallery_item_share.dart",
    "content": "import 'dart:io';\n\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/helper/image.dart';\nimport 'package:askaide/helper/logger.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/enhanced_popup_menu.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/image_preview.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/share.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/creative_island/gallery/gallery_item.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:file_saver/file_saver.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:image_gallery_saver/image_gallery_saver.dart';\nimport 'package:widgets_to_image/widgets_to_image.dart';\n\nclass GalleryItemShareScreen extends StatefulWidget {\n  final List<String> images;\n  final String? prompt;\n  final String? negativePrompt;\n\n  const GalleryItemShareScreen({\n    super.key,\n    required this.images,\n    this.prompt,\n    this.negativePrompt,\n  });\n\n  @override\n  State<GalleryItemShareScreen> createState() => _GalleryItemShareScreenState();\n}\n\nclass _GalleryItemShareScreenState extends State<GalleryItemShareScreen> {\n  final WidgetsToImageController controller = WidgetsToImageController();\n\n  bool showQRCode = true;\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return Scaffold(\n      appBar: AppBar(\n        toolbarHeight: CustomSize.toolbarHeight,\n        actions: [\n          if (!PlatformTool.isWeb())\n            TextButton(\n              onPressed: () async {\n                final cancel = BotToast.showCustomLoading(\n                  toastBuilder: (cancel) {\n                    return LoadingIndicator(\n                      message: AppLocale.processingWait.getString(context),\n                    );\n                  },\n                  allowClick: false,\n                  duration: const Duration(seconds: 15),\n                );\n\n                try {\n                  final data = await controller.capture();\n                  if (data != null) {\n                    final file = await writeTempFile('share-image.png', data);\n                    cancel();\n                    await shareTo(\n                      // ignore: use_build_context_synchronously\n                      context,\n                      content: 'images',\n                      images: [\n                        file.path,\n                      ],\n                    );\n                  }\n                } finally {\n                  cancel();\n                }\n              },\n              child: Row(\n                children: [\n                  Icon(Icons.share, size: 14, color: customColors.weakLinkColor),\n                  const SizedBox(width: 5),\n                  Text(\n                    AppLocale.share.getString(context),\n                    style: TextStyle(color: customColors.weakLinkColor, fontSize: 14),\n                  ),\n                ],\n              ),\n            ),\n          EnhancedPopupMenu(\n            items: [\n              EnhancedPopupMenuItem(\n                title: AppLocale.saveToLocal.getString(context),\n                icon: Icons.save,\n                onTap: (ctx) async {\n                  final cancel = BotToast.showCustomLoading(\n                    toastBuilder: (cancel) {\n                      return LoadingIndicator(\n                        message: AppLocale.processingWait.getString(context),\n                      );\n                    },\n                    allowClick: false,\n                    duration: const Duration(seconds: 15),\n                  );\n\n                  try {\n                    final data = await controller.capture();\n                    if (data != null) {\n                      cancel();\n                      // ignore: use_build_context_synchronously\n\n                      if (PlatformTool.isIOS() || PlatformTool.isAndroid()) {\n                        await ImageGallerySaver.saveImage(data, quality: 100);\n\n                        showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                      } else {\n                        if (PlatformTool.isWindows()) {\n                          FileSaver.instance\n                              .saveAs(\n                            name: randomId(),\n                            bytes: data,\n                            ext: '.png',\n                            mimeType: MimeType.png,\n                          )\n                              .then((value) async {\n                            if (value == null) {\n                              return;\n                            }\n\n                            await File(value).writeAsBytes(data);\n\n                            Logger.instance.d('File saved successfully: $value');\n                            showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                          });\n                        } else {\n                          FileSaver.instance\n                              .saveFile(\n                            name: randomId(),\n                            bytes: data,\n                            ext: 'png',\n                            mimeType: MimeType.png,\n                          )\n                              .then((value) {\n                            Logger.instance.d('File saved successfully: $value');\n                            showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                          });\n                        }\n                      }\n                    }\n                  } finally {\n                    cancel();\n                  }\n                },\n              ),\n              EnhancedPopupMenuItem(\n                title: showQRCode\n                    ? AppLocale.dontShowInviteCode.getString(context)\n                    : AppLocale.showInviteCode.getString(context),\n                icon: showQRCode ? Icons.visibility_off : Icons.visibility,\n                onTap: (ctx) {\n                  setState(() {\n                    showQRCode = !showQRCode;\n                  });\n                },\n              ),\n            ],\n          )\n        ],\n      ),\n      backgroundColor: customColors.backgroundColor,\n      body: Align(\n        alignment: Alignment.topCenter,\n        child: ConstrainedBox(\n          constraints: const BoxConstraints(\n            maxWidth: CustomSize.smallWindowSize,\n          ),\n          child: SafeArea(\n            child: SingleChildScrollView(\n              child: FutureBuilder(\n                  future: APIServer().shareInfo(),\n                  builder: (context, snapshot) {\n                    if (snapshot.hasError) {\n                      return Center(\n                        child: Text(resolveError(context, snapshot.error!)),\n                      );\n                    }\n\n                    if (snapshot.hasData) {\n                      return Column(\n                        children: [\n                          WidgetsToImage(\n                            controller: controller,\n                            child: Container(\n                              color: customColors.backgroundContainerColor,\n                              child: Column(\n                                mainAxisSize: MainAxisSize.min,\n                                crossAxisAlignment: CrossAxisAlignment.center,\n                                children: [\n                                  for (var img in widget.images)\n                                    Container(\n                                      decoration: BoxDecoration(color: customColors.backgroundColor),\n                                      child: NetworkImagePreviewer(\n                                        url: img,\n                                        preview: imageURL(img, qiniuImageTypeThumb),\n                                        hidePreviewButton: true,\n                                        notClickable: true,\n                                        borderRadius: BorderRadius.zero,\n                                      ),\n                                    ),\n                                  ColumnBlock(\n                                    backgroundColor: customColors.backgroundContainerColor,\n                                    innerPanding: 10,\n                                    padding: const EdgeInsets.all(15),\n                                    margin: const EdgeInsets.all(0),\n                                    borderRadius: 0,\n                                    children: [\n                                      if (widget.prompt != null && widget.prompt!.isNotEmpty)\n                                        TextItem(\n                                          title: 'Prompt',\n                                          value: widget.prompt!,\n                                        ),\n                                      if (widget.negativePrompt != null && widget.negativePrompt!.isNotEmpty)\n                                        TextItem(\n                                          title: 'Negative Prompt',\n                                          value: widget.negativePrompt!,\n                                        ),\n                                    ],\n                                  ),\n                                  if (showQRCode)\n                                    Container(\n                                      color: customColors.backgroundContainerColor,\n                                      child: Padding(\n                                        padding: const EdgeInsets.symmetric(\n                                          horizontal: 15,\n                                          vertical: 20,\n                                        ),\n                                        child: Row(\n                                          children: [\n                                            ClipRRect(\n                                              borderRadius: CustomSize.borderRadius,\n                                              child: CachedNetworkImageEnhanced(\n                                                imageUrl: snapshot.data!.qrCode,\n                                                width: 100,\n                                                height: 100,\n                                              ),\n                                            ),\n                                            const SizedBox(width: 10),\n                                            Expanded(\n                                              child: Text(\n                                                snapshot.data!.message,\n                                              ),\n                                            ),\n                                          ],\n                                        ),\n                                      ),\n                                    ),\n                                ],\n                              ),\n                            ),\n                          ),\n                        ],\n                      );\n                    }\n\n                    return const Center(\n                      child: Text('Loading ...'),\n                    );\n                  }),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/global_alert.dart",
    "content": "import 'package:askaide/helper/event.dart';\nimport 'package:askaide/page/component/chat/markdown.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:flutter/material.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:url_launcher/url_launcher_string.dart';\n\nclass GlobalAlertEvent {\n  String id;\n  String? message;\n  String type;\n  List<String> pages;\n\n  GlobalAlertEvent({\n    required this.id,\n    this.message,\n    required this.type,\n    required this.pages,\n  });\n\n  toJSON() {\n    return {\n      'id': id,\n      'message': message,\n      'type': type,\n      'pages': pages,\n    };\n  }\n}\n\nclass GlobalAlert extends StatefulWidget {\n  final String pageKey;\n  const GlobalAlert({super.key, required this.pageKey});\n\n  @override\n  State<GlobalAlert> createState() => _GlobalAlertState();\n}\n\nclass _GlobalAlertState extends State<GlobalAlert> {\n  Function? globalAlertListener;\n\n  late GlobalAlertEvent alertEvent;\n\n  @override\n  void initState() {\n    alertEvent = APIServer().globalAlertEvent;\n\n    globalAlertListener = GlobalEvent().on('global-alert', (data) {\n      final event = data as GlobalAlertEvent;\n      if (event.pages.isNotEmpty && !event.pages.contains(widget.pageKey)) {\n        return;\n      }\n\n      if (mounted) {\n        setState(() {\n          alertEvent = data;\n        });\n      }\n    });\n\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    globalAlertListener?.call();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    if (alertEvent.id == '' || alertEvent.message == null || alertEvent.message == '') {\n      return const SizedBox();\n    }\n\n    return Container(\n      margin: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),\n      padding: const EdgeInsets.symmetric(horizontal: 10),\n      width: double.infinity,\n      decoration: BoxDecoration(color: resolveBackgroundColor(), borderRadius: CustomSize.borderRadius),\n      child: Markdown(\n        data: alertEvent.message!,\n        textStyle: const TextStyle(\n          color: Colors.white,\n        ),\n        onUrlTap: (value) {\n          if (value.startsWith(\"aidea-app://\")) {\n            var route = value.substring('aidea-app://'.length);\n            context.push(route);\n          } else {\n            launchUrlString(value);\n          }\n        },\n      ),\n    );\n  }\n\n  Color resolveBackgroundColor() {\n    switch (alertEvent.type) {\n      case 'error':\n      case 'warning':\n        return const Color.fromARGB(255, 252, 145, 79);\n      case 'info':\n        return const Color.fromARGB(255, 18, 83, 135);\n      default:\n        return Colors.green;\n    }\n  }\n}\n"
  },
  {
    "path": "lib/page/component/gradient_style.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass GradientStyle {\n  static LinearGradient warmLinear() {\n    return const LinearGradient(\n      begin: Alignment.topRight,\n      end: Alignment.bottomLeft,\n      stops: [0.0, 0.5, 1.0],\n      colors: [\n        Color.fromARGB(255, 245, 205, 93),\n        Color.fromARGB(255, 234, 146, 75),\n        Color.fromARGB(255, 211, 89, 61),\n      ],\n    );\n  }\n\n  static LinearGradient coldLinear() {\n    return const LinearGradient(\n      begin: Alignment.topRight,\n      end: Alignment.bottomLeft,\n      stops: [0.0, 0.5, 1.0],\n      colors: [\n        Color.fromARGB(255, 82, 181, 208),\n        Color.fromARGB(255, 66, 133, 191),\n        Color.fromARGB(255, 66, 87, 177),\n      ],\n    );\n  }\n\n  static LinearGradient greenLinear() {\n    return const LinearGradient(\n      begin: Alignment.topLeft,\n      end: Alignment.bottomRight,\n      stops: [0.0, 1.0],\n      colors: [\n        Color.fromARGB(200, 68, 255, 0),\n        Color.fromARGB(200, 131, 220, 99),\n      ],\n    );\n  }\n\n  static LinearGradient whiteLinear() {\n    return const LinearGradient(\n      begin: Alignment.topLeft,\n      end: Alignment.bottomRight,\n      stops: [0.0, 1.0],\n      colors: [\n        Color.fromARGB(200, 255, 255, 255),\n        Color.fromARGB(200, 224, 224, 224),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/group_list_widget.dart",
    "content": "import 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\n\nclass Group<T> {\n  final String key;\n  final List<T> items;\n\n  Group({required this.key, required this.items});\n}\n\nclass GroupListWidget<T> extends StatelessWidget {\n  final List<T> items;\n  final String Function(T item) groupKey;\n  final Widget Function(T item) itemBuilder;\n  final EdgeInsets? padding;\n  final EdgeInsets? margin;\n  final bool showTitle;\n  final ScrollPhysics? physics;\n  const GroupListWidget({\n    super.key,\n    required this.items,\n    required this.groupKey,\n    required this.itemBuilder,\n    this.padding = const EdgeInsets.symmetric(horizontal: 10, vertical: 5),\n    this.margin = const EdgeInsets.symmetric(horizontal: 5, vertical: 5),\n    this.showTitle = false,\n    this.physics,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    var groups = <String, List<T>>{};\n    for (var item in items) {\n      var key = groupKey(item);\n      groups[key] = groups[key] ?? [];\n      groups[key]!.add(item);\n    }\n\n    return ListView.separated(\n      physics: physics,\n      shrinkWrap: true,\n      itemBuilder: (context, i) {\n        var group = groups.entries.elementAt(i);\n        return Column(\n          children: [\n            if (showTitle)\n              Container(\n                margin: const EdgeInsets.only(top: 5),\n                padding: const EdgeInsets.only(left: 10, right: 10),\n                width: double.infinity,\n                child: Text(\n                  groups.keys.elementAt(i),\n                  style: TextStyle(\n                    color: customColors.weakTextColor,\n                    fontSize: 12,\n                  ),\n                ),\n              ),\n            Container(\n              margin: margin,\n              padding: padding,\n              decoration: BoxDecoration(\n                borderRadius: CustomSize.borderRadius,\n                color: customColors.backgroundForDialogListItem,\n              ),\n              child: ListView.separated(\n                shrinkWrap: true,\n                physics: const NeverScrollableScrollPhysics(),\n                itemCount: group.value.length,\n                itemBuilder: (BuildContext context, int index) {\n                  return itemBuilder(group.value.elementAt(index));\n                },\n                separatorBuilder: (BuildContext context, int index) {\n                  return Padding(\n                    padding: const EdgeInsets.symmetric(vertical: 1.5),\n                    child: Divider(\n                      height: 1,\n                      color: customColors.columnBlockDividerColor,\n                    ),\n                  );\n                },\n              ),\n            ),\n          ],\n        );\n      },\n      separatorBuilder: (BuildContext context, int index) {\n        return const Padding(padding: EdgeInsets.symmetric(horizontal: 15, vertical: 5));\n      },\n      itemCount: groups.length,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/icon_box.dart",
    "content": "import 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:flutter/material.dart';\n\nclass IconBox extends StatelessWidget {\n  final Icon icon;\n  final Widget title;\n  final Function()? onTap;\n  const IconBox({\n    super.key,\n    required this.icon,\n    required this.title,\n    this.onTap,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return MaterialButton(\n      padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),\n      shape: RoundedRectangleBorder(borderRadius: CustomSize.borderRadius),\n      onPressed: onTap,\n      child: Column(\n        children: [\n          icon,\n          const SizedBox(height: 10),\n          title,\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/icon_box_button.dart",
    "content": "import 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:flutter/material.dart';\n\nclass IconBoxButton extends StatelessWidget {\n  final double? width;\n  final String title;\n  final IconData icon;\n  final IconData? smallIcon;\n  final Function()? onTap;\n\n  const IconBoxButton({\n    super.key,\n    this.width,\n    required this.title,\n    required this.icon,\n    this.smallIcon,\n    this.onTap,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return InkWell(\n      borderRadius: CustomSize.borderRadiusAll,\n      onTap: () => onTap?.call(),\n      child: Container(\n        height: 75,\n        width: width,\n        padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),\n        decoration: BoxDecoration(\n          color: Colors.grey.withAlpha(20),\n          border: Border.all(\n            color: Colors.grey.withAlpha(50),\n          ),\n          borderRadius: CustomSize.borderRadius,\n        ),\n        child: Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            Icon(icon),\n            const SizedBox(height: 5),\n            Row(\n              mainAxisAlignment: MainAxisAlignment.spaceBetween,\n              children: [\n                Text(title),\n                Icon(smallIcon ?? Icons.keyboard_arrow_right),\n              ],\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/image.dart",
    "content": "import 'dart:io';\n\nimport 'package:askaide/helper/image.dart';\nimport 'package:cached_network_image/cached_network_image.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_cache_manager/flutter_cache_manager.dart';\n\nImageProvider resolveImageProvider(\n  String imageUrl, {\n  bool useThumbnail = true,\n}) {\n  if (imageUrl.startsWith('http://') || imageUrl.startsWith('https://')) {\n    if (useThumbnail) {\n      imageUrl = imageURL(imageUrl, 'thumb');\n    }\n\n    return CachedNetworkImageProviderEnhanced(imageUrl);\n  }\n\n  return FileImage(File(imageUrl));\n}\n\nclass CachedNetworkImageEnhanced extends StatelessWidget {\n  final String imageUrl;\n  final BoxFit? fit;\n  final double? width;\n  final double? height;\n  final ProgressIndicatorBuilder? progressIndicatorBuilder;\n  final LoadingErrorWidgetBuilder? errorWidget;\n  final ImageWidgetBuilder? imageBuilder;\n  final BaseCacheManager? cacheManager;\n\n  CachedNetworkImageEnhanced({\n    super.key,\n    required this.imageUrl,\n    this.fit,\n    this.width,\n    this.height,\n    this.progressIndicatorBuilder,\n    this.errorWidget,\n    this.imageBuilder,\n    this.cacheManager,\n  }) {\n    // Logger.instance.d('load image: $imageUrl');\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return CachedNetworkImage(\n      imageUrl: imageUrl,\n      fit: fit,\n      width: width,\n      height: height,\n      progressIndicatorBuilder: progressIndicatorBuilder,\n      errorWidget: errorWidget,\n      imageBuilder: imageBuilder,\n      cacheManager: cacheManager,\n    );\n  }\n}\n\nclass CachedNetworkImageProviderEnhanced extends CachedNetworkImageProvider {\n  CachedNetworkImageProviderEnhanced(\n    super.url, {\n    super.maxHeight,\n    super.maxWidth,\n    super.scale = 1.0,\n    super.errorListener,\n    super.headers,\n    super.cacheManager,\n    super.cacheKey,\n  }) {\n    // Logger.instance.d('load image: $url');\n  }\n}\n"
  },
  {
    "path": "lib/page/component/image_action.dart",
    "content": "import 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/button.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/api/creative.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nFuture<dynamic> openImageWorkflowActionDialog(\n  BuildContext context,\n  CustomColors customColors,\n  String imageUrl,\n) {\n  return openModalBottomSheet(\n    context,\n    (context) {\n      return Column(\n        children: [\n          Text(\n            AppLocale.selectShortcutAction.getString(context),\n            style: TextStyle(\n              color: customColors.weakTextColorPlus,\n            ),\n          ),\n          Expanded(\n            child: FutureBuilder(\n              future: APIServer().creativeIslandItemsV2(),\n              builder: (context, snapshot) {\n                if (snapshot.hasData) {\n                  Map<String, CreativeIslandItemV2> itemsMap = {};\n                  for (var item in snapshot.data!) {\n                    itemsMap[item.id] = item;\n                  }\n\n                  return actionsBuilder(itemsMap, customColors, context, imageUrl);\n                }\n\n                return const LoadingIndicator(\n                  message: 'Loading, please wait...',\n                );\n              },\n            ),\n          ),\n        ],\n      );\n    },\n    heightFactor: 0.5,\n  );\n}\n\nWidget actionsBuilder(\n  Map<String, CreativeIslandItemV2> itemsMap,\n  CustomColors customColors,\n  BuildContext context,\n  String imageUrl,\n) {\n  return Column(\n    mainAxisSize: MainAxisSize.min,\n    mainAxisAlignment: MainAxisAlignment.spaceBetween,\n    children: [\n      const SizedBox(height: 20),\n      Expanded(\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          children: [\n            if (itemsMap.containsKey('image-to-image'))\n              Button(\n                title: AppLocale.imageToImage.getString(context),\n                icon: Icon(\n                  Icons.collections_outlined,\n                  size: 16,\n                  color: customColors.weakLinkColor,\n                ),\n                onPressed: () {\n                  context.pop();\n\n                  context.push(Uri(\n                    path: '/creative-draw/create',\n                    queryParameters: {\n                      'id': 'image-to-image',\n                      'mode': 'image-to-image',\n                      'note': itemsMap['image-to-image']!.note,\n                      'init_image': imageUrl,\n                    },\n                  ).toString());\n                },\n                size: const ButtonSize.full(),\n                color: customColors.weakLinkColor,\n                backgroundColor: const Color.fromARGB(34, 183, 183, 183),\n              ),\n            if (itemsMap.containsKey('image-to-image')) const SizedBox(height: 10),\n            if (itemsMap.containsKey('image-to-video'))\n              Button(\n                title: AppLocale.imageToVideo.getString(context),\n                icon: Icon(\n                  Icons.video_camera_back_outlined,\n                  size: 16,\n                  color: customColors.weakLinkColor,\n                ),\n                onPressed: () {\n                  context.pop();\n\n                  context.push(Uri(\n                    path: '/creative-draw/create-video',\n                    queryParameters: {\n                      'note': itemsMap['image-to-video']!.note,\n                      'init_image': imageUrl,\n                    },\n                  ).toString());\n                },\n                size: const ButtonSize.full(),\n                color: customColors.weakLinkColor,\n                backgroundColor: const Color.fromARGB(34, 183, 183, 183),\n              ),\n            if (itemsMap.containsKey('image-to-video')) const SizedBox(height: 10),\n            if (itemsMap.containsKey('image-upscale'))\n              Button(\n                title: AppLocale.hdRestoration.getString(context),\n                icon: Icon(\n                  Icons.hd_outlined,\n                  size: 16,\n                  color: customColors.weakLinkColor,\n                ),\n                onPressed: () {\n                  context.pop();\n\n                  context.push(Uri(\n                    path: '/creative-draw/create-upscale',\n                    queryParameters: {\n                      'note': itemsMap['image-upscale']!.note,\n                      'init_image': imageUrl,\n                    },\n                  ).toString());\n                },\n                size: const ButtonSize.full(),\n                color: customColors.weakLinkColor,\n                backgroundColor: const Color.fromARGB(34, 183, 183, 183),\n              ),\n            if (itemsMap.containsKey('image-upscale')) const SizedBox(height: 10),\n            if (itemsMap.containsKey('image-colorize'))\n              Button(\n                title: AppLocale.colorizeImage.getString(context),\n                icon: Icon(\n                  Icons.palette_outlined,\n                  size: 16,\n                  color: customColors.weakLinkColor,\n                ),\n                onPressed: () {\n                  context.pop();\n                  context.push(Uri(\n                    path: '/creative-draw/create-colorize',\n                    queryParameters: {\n                      'note': itemsMap['image-colorize']!.note,\n                      'init_image': imageUrl,\n                    },\n                  ).toString());\n                },\n                size: const ButtonSize.full(),\n                color: customColors.weakLinkColor,\n                backgroundColor: const Color.fromARGB(34, 183, 183, 183),\n              ),\n          ],\n        ),\n      ),\n      Container(\n        margin: const EdgeInsets.only(bottom: 20),\n        child: Button(\n          title: AppLocale.cancel.getString(context),\n          backgroundColor: const Color.fromARGB(36, 222, 222, 222),\n          color: customColors.dialogDefaultTextColor?.withAlpha(150),\n          onPressed: () {\n            context.pop();\n          },\n          size: const ButtonSize.full(),\n        ),\n      ),\n    ],\n  );\n}\n"
  },
  {
    "path": "lib/page/component/image_preview.dart",
    "content": "import 'dart:io';\n\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/helper/image.dart';\nimport 'package:askaide/helper/logger.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/gallery_item_share.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/image_action.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:before_after/before_after.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:file_saver/file_saver.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_cache_manager/flutter_cache_manager.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:image_gallery_saver/image_gallery_saver.dart';\nimport 'package:photo_view/photo_view.dart';\n\nclass NetworkImagePreviewer extends StatefulWidget {\n  final String url;\n  final String? preview;\n  final String? original;\n  final String? description;\n  final bool hidePreviewButton;\n  final bool notClickable;\n  final BorderRadius? borderRadius;\n\n  const NetworkImagePreviewer({\n    super.key,\n    required this.url,\n    this.preview,\n    this.description,\n    this.original,\n    this.hidePreviewButton = false,\n    this.notClickable = false,\n    this.borderRadius,\n  });\n\n  @override\n  State<NetworkImagePreviewer> createState() => _NetworkImagePreviewerState();\n}\n\nclass _NetworkImagePreviewerState extends State<NetworkImagePreviewer> {\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    if (widget.hidePreviewButton) {\n      return ClipRRect(\n        borderRadius: widget.borderRadius ?? CustomSize.borderRadius,\n        child: widget.original == null\n            ? _buildImage(widget.borderRadius)\n            : BeforeAfter(\n                beforeImage:\n                    Image(image: CachedNetworkImageProviderEnhanced(imageURL(widget.original!, qiniuImageTypeThumb))),\n                afterImage: Image(\n                    image: CachedNetworkImageProviderEnhanced(\n                        imageURL(widget.preview ?? widget.url, qiniuImageTypeThumb))),\n                thumbWidth: 1.0,\n              ),\n      );\n    }\n\n    return Container(\n      decoration: BoxDecoration(color: customColors.columnBlockBackgroundColor, borderRadius: CustomSize.borderRadius),\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.end,\n        children: [\n          widget.original == null\n              ? _buildImage(const BorderRadius.only(topLeft: CustomSize.radius, topRight: CustomSize.radius))\n              : ClipRRect(\n                  borderRadius: const BorderRadius.only(topLeft: CustomSize.radius, topRight: CustomSize.radius),\n                  child: BeforeAfter(\n                    imageCornerRadius: 0,\n                    beforeImage: Image(\n                        image: CachedNetworkImageProviderEnhanced(imageURL(widget.original!, qiniuImageTypeThumb))),\n                    afterImage: Image(\n                        image: CachedNetworkImageProviderEnhanced(\n                            imageURL(widget.preview ?? widget.url, qiniuImageTypeThumb))),\n                    thumbWidth: 0.5,\n                    thumbRadius: 3,\n                  ),\n                ),\n          Padding(\n            padding: const EdgeInsets.only(right: 10),\n            child: Row(\n              mainAxisAlignment: MainAxisAlignment.end,\n              children: [\n                IconButton(\n                  splashColor: Colors.transparent,\n                  highlightColor: Colors.transparent,\n                  hoverColor: Colors.transparent,\n                  icon: Row(\n                    mainAxisSize: MainAxisSize.min,\n                    children: [\n                      Icon(\n                        Icons.share,\n                        size: 14,\n                        color: customColors.weakLinkColor,\n                      ),\n                      const SizedBox(width: 5),\n                      Text(\n                        AppLocale.share.getString(context),\n                        style: TextStyle(\n                          fontSize: 12,\n                          color: customColors.weakLinkColor,\n                        ),\n                      ),\n                    ],\n                  ),\n                  onPressed: () {\n                    Navigator.push(\n                      context,\n                      MaterialPageRoute(\n                        fullscreenDialog: true,\n                        builder: (context) => GalleryItemShareScreen(\n                          images: [widget.url],\n                        ),\n                      ),\n                    );\n                  },\n                ),\n                IconButton(\n                  splashColor: Colors.transparent,\n                  highlightColor: Colors.transparent,\n                  hoverColor: Colors.transparent,\n                  icon: Row(\n                    mainAxisSize: MainAxisSize.min,\n                    children: [\n                      Icon(\n                        Icons.webhook,\n                        size: 14,\n                        color: customColors.weakLinkColor,\n                      ),\n                      const SizedBox(width: 5),\n                      Text(\n                        AppLocale.shortcut.getString(context),\n                        style: TextStyle(\n                          fontSize: 12,\n                          color: customColors.weakLinkColor,\n                        ),\n                      ),\n                    ],\n                  ),\n                  onPressed: () {\n                    openImageWorkflowActionDialog(\n                      context,\n                      customColors,\n                      widget.url,\n                    );\n                  },\n                ),\n                IconButton(\n                  splashColor: Colors.transparent,\n                  highlightColor: Colors.transparent,\n                  hoverColor: Colors.transparent,\n                  icon: Row(\n                    mainAxisSize: MainAxisSize.min,\n                    children: [\n                      Icon(\n                        Icons.open_in_new,\n                        size: 14,\n                        color: customColors.weakLinkColor,\n                      ),\n                      const SizedBox(width: 5),\n                      Text(\n                        AppLocale.preview.getString(context),\n                        style: TextStyle(\n                          fontSize: 12,\n                          color: customColors.weakLinkColor,\n                        ),\n                      ),\n                    ],\n                  ),\n                  onPressed: () {\n                    try {\n                      openImagePreviewDialog(\n                        context,\n                        customColors,\n                        imageProvider: CachedNetworkImageProviderEnhanced(widget.url),\n                        imageUrl: widget.url,\n                      );\n                    } catch (e) {\n                      showErrorMessageEnhanced(context, 'Image loading failed, please try again later');\n                    }\n                  },\n                ),\n              ],\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n\n  Widget _buildImage(BorderRadius? borderRadius) {\n    return CachedNetworkImageEnhanced(\n      imageUrl: widget.preview ?? widget.url,\n      cacheManager: DefaultCacheManager(),\n      imageBuilder: (context, imageProvider) {\n        if (widget.notClickable) {\n          return Image(image: imageProvider, fit: BoxFit.cover);\n        }\n\n        return ImageFilePreviewer(\n          borderRadius: borderRadius,\n          imageProvider: imageProvider,\n          imageUrl: widget.preview ?? widget.url,\n          description: widget.description,\n          originalURL: widget.preview != null ? widget.url : null,\n        );\n      },\n      progressIndicatorBuilder: (context, url, downloadProgress) => Container(\n        padding: const EdgeInsets.all(5),\n        child: ConstrainedBox(\n          constraints: const BoxConstraints(\n            minWidth: 200,\n            minHeight: 200,\n          ),\n          child: Center(\n            child: LoadingIndicator(\n              message: AppLocale.processingWait.getString(context),\n            ),\n          ),\n        ),\n      ),\n      errorWidget: (context, url, error) => _buildImageBrokenWidget(context),\n    );\n  }\n\n  Widget _buildImageBrokenWidget(BuildContext context) {\n    return Center(\n      child: Image.asset(\n        'assets/image-broken.png',\n        fit: BoxFit.cover,\n        width: 200,\n        color: Theme.of(context).cardColor,\n      ),\n    );\n  }\n}\n\nclass ImageFilePreviewer extends StatelessWidget {\n  final ImageProvider imageProvider;\n  final String? description;\n  final String? originalURL;\n  final String imageUrl;\n  final BorderRadius? borderRadius;\n  const ImageFilePreviewer({\n    super.key,\n    required this.imageProvider,\n    this.description,\n    this.originalURL,\n    required this.imageUrl,\n    this.borderRadius,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return ClipRRect(\n      borderRadius: borderRadius ?? CustomSize.borderRadius,\n      child: InkWell(\n        borderRadius: borderRadius ?? CustomSize.borderRadiusAll,\n        child: Image(image: imageProvider, fit: BoxFit.cover),\n        onTap: () {\n          openImagePreviewDialog(\n            context,\n            customColors,\n            imageProvider: imageProvider,\n            imageUrl: imageUrl,\n            originalURL: originalURL,\n            description: description,\n          );\n        },\n      ),\n    );\n  }\n}\n\nvoid openImagePreviewDialog(\n  BuildContext context,\n  CustomColors customColors, {\n  required ImageProvider imageProvider,\n  String? imageUrl,\n  String? originalURL,\n  String? description,\n}) {\n  final downloadUrl = originalURL ?? imageUrl;\n\n  Navigator.of(context).push(\n    MaterialPageRoute(\n      fullscreenDialog: true,\n      builder: (context) => Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          leading: IconButton(\n            icon: const Icon(Icons.close),\n            onPressed: () {\n              Navigator.of(context).pop();\n            },\n          ),\n          actions: [\n            if (imageUrl != null)\n              IconButton(\n                onPressed: () {\n                  Navigator.push(\n                    context,\n                    MaterialPageRoute(\n                      fullscreenDialog: true,\n                      builder: (context) => GalleryItemShareScreen(\n                        images: [imageUrl],\n                      ),\n                    ),\n                  );\n                },\n                icon: Icon(\n                  Icons.share,\n                  size: 16,\n                  color: customColors.weakLinkColor,\n                ),\n              ),\n            if (downloadUrl != null)\n              IconButton(\n                onPressed: () async {\n                  final cancel = BotToast.showCustomLoading(\n                    toastBuilder: (cancel) {\n                      return const LoadingIndicator(\n                        message: 'Downloading, please wait...',\n                      );\n                    },\n                    allowClick: false,\n                    duration: const Duration(seconds: 120),\n                  );\n\n                  try {\n                    final saveFile = await DefaultCacheManager().getSingleFile(downloadUrl);\n\n                    if (PlatformTool.isIOS() || PlatformTool.isAndroid()) {\n                      await ImageGallerySaver.saveImage(\n                        saveFile.readAsBytesSync(),\n                        quality: 100,\n                      );\n\n                      showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                    } else {\n                      var ext = saveFile.path.toLowerCase().split('.').last;\n                      MimeType mimeType;\n                      switch (ext) {\n                        case 'jpg':\n                        case 'jpeg':\n                          mimeType = MimeType.jpeg;\n                          break;\n                        case 'png':\n                          mimeType = MimeType.png;\n                          break;\n                        case 'gif':\n                          mimeType = MimeType.gif;\n                          break;\n                        default:\n                          mimeType = MimeType.other;\n                      }\n\n                      if (PlatformTool.isWindows()) {\n                        FileSaver.instance\n                            .saveAs(\n                          name: filenameWithoutExt(saveFile.path.split('/').last),\n                          filePath: saveFile.path,\n                          ext: '.$ext',\n                          mimeType: mimeType,\n                        )\n                            .then((value) async {\n                          if (value == null) {\n                            return;\n                          }\n\n                          await File(value).writeAsBytes(await saveFile.readAsBytes());\n\n                          Logger.instance.d('File saved successfully: $value');\n                          showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                        });\n                      } else {\n                        FileSaver.instance\n                            .saveFile(\n                          name: filenameWithoutExt(saveFile.path.split('/').last),\n                          filePath: saveFile.path,\n                          ext: ext,\n                          mimeType: mimeType,\n                        )\n                            .then((value) {\n                          Logger.instance.d('File saved successfully: $value');\n                          showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                        });\n                      }\n                    }\n                  } catch (e) {\n                    // ignore: use_build_context_synchronously\n                    showErrorMessageEnhanced(context, 'Image save failed, please try again later');\n                    Logger.instance.e('Failed to download the original image', error: e);\n                  } finally {\n                    cancel();\n                  }\n                },\n                icon: Icon(\n                  Icons.download_sharp,\n                  size: 16,\n                  color: customColors.weakLinkColor,\n                ),\n              ),\n            if (downloadUrl == null && (PlatformTool.isIOS() || PlatformTool.isAndroid()))\n              IconButton(\n                onPressed: () async {\n                  final cancel = BotToast.showCustomLoading(\n                    toastBuilder: (cancel) {\n                      return const LoadingIndicator(\n                        message: 'Downloading, please wait...',\n                      );\n                    },\n                    allowClick: false,\n                    duration: const Duration(seconds: 120),\n                  );\n\n                  try {\n                    await ImageGallerySaver.saveImage(\n                      (imageProvider as MemoryImage).bytes,\n                      quality: 100,\n                    );\n\n                    showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                  } catch (e) {\n                    // ignore: use_build_context_synchronously\n                    showErrorMessageEnhanced(context, 'Image save failed, please try again later.');\n                    Logger.instance.e('Failed to download the original image', error: e);\n                  } finally {\n                    cancel();\n                  }\n                },\n                icon: Icon(\n                  Icons.download_sharp,\n                  size: 16,\n                  color: customColors.weakLinkColor,\n                ),\n              )\n          ],\n        ),\n        backgroundColor: customColors.backgroundContainerColor,\n        body: PhotoView(\n          imageProvider: imageProvider,\n          enableRotation: true,\n          backgroundDecoration: BoxDecoration(\n            color: customColors.backgroundContainerColor,\n          ),\n        ),\n      ),\n    ),\n  );\n}\n\nclass ImageProviderPreviewer extends StatelessWidget {\n  final ImageProvider imageProvider;\n  final BorderRadius? borderRadius;\n  const ImageProviderPreviewer({\n    super.key,\n    required this.imageProvider,\n    this.borderRadius,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return ClipRRect(\n      borderRadius: borderRadius ?? CustomSize.borderRadius,\n      child: InkWell(\n        borderRadius: borderRadius ?? CustomSize.borderRadiusAll,\n        child: Image(image: imageProvider, fit: BoxFit.cover),\n        onTap: () {\n          openImagePreviewDialog(\n            context,\n            customColors,\n            imageProvider: imageProvider,\n          );\n        },\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/invite_card.dart",
    "content": "import 'package:askaide/helper/color.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/share.dart';\nimport 'package:askaide/repo/api/user.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\n\nclass InviteCard extends StatelessWidget {\n  final UserInfo userInfo;\n  const InviteCard({super.key, required this.userInfo});\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      margin: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),\n      decoration: BoxDecoration(\n        borderRadius: BorderRadius.circular(15), // Maintain consistency with Settings Card\n        boxShadow: [\n          BoxShadow(\n            color: Colors.white.withOpacity(0.1),\n            blurRadius: 10,\n            offset: const Offset(0, 5),\n          ),\n        ],\n        image: DecorationImage(\n          // opacity: 0.83,\n          image: CachedNetworkImageProviderEnhanced(\n              userInfo.control.inviteCardBg ?? 'https://ssl.aicode.cc/ai-server/assets/invite-card-bg.webp-thumb1000'),\n          fit: BoxFit.cover,\n        ),\n        // gradient: const LinearGradient(\n        //   begin: Alignment.topLeft,\n        //   end: Alignment.bottomRight,\n        //   colors: [\n        //     Color.fromARGB(255, 255, 255, 255),\n        //     // Color.fromARGB(255, 230, 153, 38),\n        //     Color.fromARGB(255, 250, 213, 246),\n        //   ],\n        //   transform: GradientRotation(0.5),\n        // ),\n      ),\n      height: 150,\n      child: Container(\n        padding: const EdgeInsets.symmetric(\n          horizontal: 20,\n          vertical: 20,\n        ),\n        child: Row(\n          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n          children: [\n            Expanded(\n              child: Column(\n                crossAxisAlignment: CrossAxisAlignment.start,\n                children: [\n                  Text(\n                    '邀新有礼',\n                    style: TextStyle(\n                      fontSize: 17,\n                      fontWeight: FontWeight.bold,\n                      color: userInfo.control.inviteCardColor != null\n                          ? stringToColor(userInfo.control.inviteCardColor!)\n                          : Colors.black,\n                    ),\n                  ),\n                  const SizedBox(height: 10),\n                  Text(\n                    userInfo.control.inviteCardSlogan ?? AppLocale.inviteSlogan.getString(context),\n                    strutStyle: const StrutStyle(height: 1.3),\n                    overflow: TextOverflow.ellipsis,\n                    maxLines: 4,\n                    style: TextStyle(\n                      fontSize: 13,\n                      color: userInfo.control.inviteCardColor != null\n                          ? stringToColor(userInfo.control.inviteCardColor!)\n                          : Colors.black,\n                    ),\n                  ),\n                ],\n              ),\n            ),\n            EnhancedButton(\n              title: AppLocale.inviteNow.getString(context),\n              fontSize: 14,\n              height: 35,\n              width: 80,\n              backgroundColor: const Color.fromARGB(255, 230, 173, 58),\n              onPressed: () {\n                shareTo(\n                  context,\n                  content: userInfo.control.inviteMessage ??\n                      '${AppLocale.inviteCode.getString(context)} ${userInfo.user.inviteCode}',\n                  title: AppLocale.inviteCodeShare.getString(context),\n                );\n              },\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/item_selector.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:settings_ui/settings_ui.dart';\n\nclass ItemSelector extends StatelessWidget {\n  final List<String> data;\n  final String? selected;\n  final String title;\n\n  const ItemSelector(\n      {super.key, required this.title, required this.data, this.selected});\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: Text(title)),\n      body: data.isEmpty\n          ? const Center(child: CircularProgressIndicator())\n          : SettingsList(\n              sections: [\n                SettingsSection(\n                  tiles: data\n                      .map(\n                        (e) => SettingsTile(\n                          title: Text(e),\n                          leading: e == selected\n                              ? const Icon(\n                                  Icons.check_rounded,\n                                  color: Colors.green,\n                                )\n                              : Icon(\n                                  Icons.check_rounded,\n                                  color: Colors.grey[200],\n                                ),\n                          onPressed: (context) {\n                            context.pop(e);\n                          },\n                        ),\n                      )\n                      .toList(),\n                ),\n              ],\n            ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/item_selector_search.dart",
    "content": "import 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/group_list_widget.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\n/// 带搜索框的列表选择器\nclass ItemSearchSelector extends StatefulWidget {\n  final List<SelectorItem> items;\n  final bool Function(SelectorItem item) onSelected;\n  final bool enableSearch;\n  final bool horizontal;\n  final int horizontalCount;\n  final Object? value;\n  final EdgeInsets? innerPadding;\n\n  const ItemSearchSelector({\n    super.key,\n    required this.items,\n    required this.onSelected,\n    this.enableSearch = true,\n    this.horizontal = false,\n    this.value,\n    this.horizontalCount = 4,\n    this.innerPadding,\n  });\n\n  @override\n  State<ItemSearchSelector> createState() => _ItemSearchSelectorState();\n}\n\nclass _ItemSearchSelectorState extends State<ItemSearchSelector> {\n  final _searchController = TextEditingController();\n\n  @override\n  void initState() {\n    _searchController.addListener(() {\n      setState(() {});\n    });\n\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    _searchController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    final items = widget.items.where((item) {\n      if (_searchController.text.isEmpty) return true;\n      if (item.search != null) {\n        return item.search!(_searchController.text.trim());\n      }\n\n      return false;\n    }).toList();\n\n    if (widget.horizontal) {\n      return GridView.count(\n        crossAxisCount: widget.horizontalCount,\n        children: items\n            .map(\n              (item) => ListTile(\n                title: Container(\n                  alignment: Alignment.center,\n                  padding: widget.innerPadding ?? const EdgeInsets.symmetric(vertical: 5),\n                  child: widget.value != null\n                      ? Column(\n                          mainAxisAlignment: MainAxisAlignment.spaceAround,\n                          mainAxisSize: MainAxisSize.min,\n                          children: [\n                            const SizedBox(height: 5),\n                            ConstrainedBox(\n                              constraints: const BoxConstraints(\n                                maxWidth: 50,\n                                maxHeight: 50,\n                              ),\n                              child: item.title,\n                            ),\n                            SizedBox(\n                              height: 10,\n                              child: Icon(\n                                Icons.check,\n                                color: (widget.value != null && widget.value == item.value)\n                                    ? customColors.linkColor\n                                    : Colors.transparent,\n                              ),\n                            ),\n                          ],\n                        )\n                      : item.title,\n                ),\n                onTap: () {\n                  if (widget.onSelected(item)) context.pop();\n                },\n              ),\n            )\n            .toList(),\n      );\n    }\n\n    return Column(\n      children: [\n        // 搜索框\n        if (widget.enableSearch)\n          Container(\n            margin: const EdgeInsets.only(bottom: 10, left: 5, right: 5),\n            decoration: BoxDecoration(\n              color: customColors.textfieldBackgroundColor,\n              borderRadius: CustomSize.borderRadius,\n            ),\n            child: TextField(\n              controller: _searchController,\n              textAlignVertical: TextAlignVertical.center,\n              style: TextStyle(color: customColors.textfieldHintColor),\n              decoration: InputDecoration(\n                hintText: AppLocale.search.getString(context),\n                hintStyle: TextStyle(\n                  color: customColors.weakTextColor?.withAlpha(150),\n                ),\n                prefixIcon: Icon(\n                  Icons.search,\n                  color: customColors.weakTextColor?.withAlpha(150),\n                ),\n                isDense: true,\n                border: InputBorder.none,\n              ),\n            ),\n          ),\n        // 列表部分\n        Expanded(\n          child: GroupListWidget(\n            items: items,\n            groupKey: (item) => '',\n            itemBuilder: (item) {\n              return ListTile(\n                title: Container(\n                  alignment: Alignment.center,\n                  padding: widget.innerPadding ??\n                      const EdgeInsets.symmetric(\n                        horizontal: 10,\n                        vertical: 15,\n                      ),\n                  child: widget.value != null\n                      ? Row(\n                          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                          mainAxisSize: MainAxisSize.min,\n                          children: [\n                            Expanded(\n                                child: Container(\n                              alignment: Alignment.center,\n                              child: item.title,\n                            )),\n                            SizedBox(\n                              width: 10,\n                              child: Icon(\n                                Icons.check,\n                                color: (widget.value != null && widget.value == item.value)\n                                    ? customColors.linkColor\n                                    : Colors.transparent,\n                              ),\n                            ),\n                          ],\n                        )\n                      : item.title,\n                ),\n                onTap: () {\n                  if (widget.onSelected(item)) context.pop();\n                },\n              );\n            },\n          ),\n        ),\n      ],\n    );\n  }\n}\n\nclass SelectorItem<T> {\n  Widget title;\n  T value;\n  bool Function(String keywrod)? search;\n\n  SelectorItem(this.title, this.value, {this.search});\n}\n"
  },
  {
    "path": "lib/page/component/loading.dart",
    "content": "import 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\nimport 'package:loading_animation_widget/loading_animation_widget.dart';\n\nclass LoadingIndicator extends StatelessWidget {\n  final String? message;\n  const LoadingIndicator({super.key, this.message});\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return Column(\n      mainAxisSize: MainAxisSize.min,\n      children: [\n        LoadingAnimationWidget.halfTriangleDot(\n          color: customColors.backgroundInvertedColor ?? Colors.white,\n          size: 70,\n        ),\n        const SizedBox(height: 10),\n        Text(\n          message ?? \"加载中，请稍后...\",\n          style: TextStyle(\n            color: customColors.backgroundInvertedColor ?? Colors.white,\n          ),\n        ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/message_box.dart",
    "content": "import 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:flutter/material.dart';\n\nclass MessageBox extends StatelessWidget {\n  final String message;\n  final MessageBoxType type;\n  const MessageBox({super.key, required this.message, required this.type});\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      decoration: BoxDecoration(\n        color: type.backgroundColor,\n        borderRadius: CustomSize.borderRadius,\n        border: Border.all(\n          color: type.borderColor,\n          width: 1,\n        ),\n      ),\n      margin: const EdgeInsets.symmetric(horizontal: 5),\n      padding: const EdgeInsets.symmetric(\n        vertical: 8,\n        horizontal: 16,\n      ),\n      child: Theme(\n        data: ThemeData(\n          iconTheme: IconThemeData(\n            color: type.textColor,\n          ),\n          primaryTextTheme: TextTheme(\n            bodyMedium: TextStyle(\n              color: type.textColor,\n            ),\n            bodySmall: TextStyle(\n              color: type.textColor,\n            ),\n            bodyLarge: TextStyle(\n              color: type.textColor,\n            ),\n          ),\n        ),\n        child: Row(\n          crossAxisAlignment: CrossAxisAlignment.center,\n          children: [\n            if (type.iconData != null)\n              Icon(\n                type.iconData,\n                color: type.textColor,\n                size: 16,\n              ),\n            if (type.iconData != null) const SizedBox(width: 8),\n            Expanded(\n              child: Text(\n                message,\n                style: TextStyle(\n                  color: type.textColor,\n                  fontSize: 13,\n                ),\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n\nclass MessageBoxType {\n  final Color backgroundColor;\n  final Color textColor;\n  final Color borderColor;\n  final IconData? iconData;\n\n  const MessageBoxType({\n    required this.backgroundColor,\n    required this.textColor,\n    required this.borderColor,\n    this.iconData,\n  });\n\n  static const MessageBoxType info = MessageBoxType(\n    backgroundColor: Color.fromARGB(255, 232, 245, 233),\n    textColor: Color.fromARGB(255, 72, 121, 75),\n    borderColor: Color.fromARGB(255, 46, 125, 50),\n    iconData: Icons.info,\n  );\n\n  static const MessageBoxType warning = MessageBoxType(\n    backgroundColor: Color(0xFFFFFDE7),\n    textColor: Color(0xFFE65100),\n    borderColor: Color(0xFFE65100),\n    iconData: Icons.warning,\n  );\n\n  static const MessageBoxType error = MessageBoxType(\n    backgroundColor: Color(0xFFFFEBEE),\n    textColor: Color(0xFFC62828),\n    borderColor: Color(0xFFC62828),\n    iconData: Icons.error,\n  );\n\n  static const MessageBoxType success = MessageBoxType(\n    backgroundColor: Color(0xFFE0F2F1),\n    textColor: Color(0xFF00695C),\n    borderColor: Color(0xFF00695C),\n    iconData: Icons.check_circle,\n  );\n}\n"
  },
  {
    "path": "lib/page/component/model_indicator.dart",
    "content": "import 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/api/model.dart';\nimport 'package:flutter/material.dart';\n\nclass IconAndColor {\n  final IconData icon;\n  final Color color;\n\n  IconAndColor(this.icon, this.color);\n}\n\nfinal iconAndColors = [\n  IconAndColor(Icons.bolt, Colors.green),\n  IconAndColor(Icons.auto_awesome, const Color.fromARGB(255, 120, 73, 223)),\n  IconAndColor(Icons.extension, const Color.fromARGB(255, 255, 122, 13)),\n];\n\nclass ModelIndicatorInfo {\n  IconData icon;\n  Color activeColor;\n  String modelId;\n  String modelName;\n  String description;\n  bool supportVision;\n\n  ModelIndicatorInfo({\n    required this.modelName,\n    required this.modelId,\n    required this.description,\n    required this.icon,\n    required this.activeColor,\n    this.supportVision = false,\n  });\n}\n\nclass ModelIndicator extends StatelessWidget {\n  final HomeModelV2 model;\n  final IconAndColor iconAndColor;\n  final bool selected;\n  final int itemCount;\n\n  const ModelIndicator({\n    super.key,\n    required this.model,\n    required this.iconAndColor,\n    this.selected = false,\n    this.itemCount = 2,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n    return Padding(\n      padding: EdgeInsets.symmetric(horizontal: itemCount > 2 ? 10 : 15),\n      child: Row(\n        crossAxisAlignment: CrossAxisAlignment.center,\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          Icon(\n            iconAndColor.icon,\n            color: selected ? Colors.white : customColors.weakLinkColor,\n            size: itemCount > 2 ? 16 : 20,\n          ),\n          SizedBox(width: itemCount > 2 ? 5 : 10),\n          Expanded(\n            child: Center(\n              child: Row(\n                children: [\n                  Expanded(\n                    child: Center(\n                      child: Text(\n                        model.name,\n                        maxLines: 1,\n                        style: TextStyle(\n                          fontSize: itemCount > 2 ? 14 : 15,\n                          color: selected\n                              ? Colors.white\n                              : customColors.weakLinkColor,\n                          fontWeight: FontWeight.w600,\n                          overflow: TextOverflow.ellipsis,\n                        ),\n                      ),\n                    ),\n                  ),\n                  SizedBox(width: itemCount > 2 ? 16 : 20),\n                ],\n              ),\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/model_item.dart",
    "content": "import 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/color.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/image.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/group_list_widget.dart';\nimport 'package:askaide/page/component/random_avatar.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/model/model.dart';\nimport 'package:auto_size_text/auto_size_text.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:quickalert/models/quickalert_type.dart';\n\nclass ModelItem extends StatefulWidget {\n  final List<Model> models;\n  final Function(Model? selected) onSelected;\n  final String? initValue;\n  final bool showUsing;\n\n  const ModelItem({\n    super.key,\n    required this.models,\n    required this.onSelected,\n    this.initValue,\n    this.showUsing = false,\n  });\n\n  @override\n  State<ModelItem> createState() => _ModelItemState();\n}\n\nclass _ModelItemState extends State<ModelItem> {\n  String keyword = '';\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    var tags = <Widget>[];\n\n    // Collect all unique tags from models\n    var uniqueTags = <String>{};\n    for (var model in widget.models) {\n      if (model.tag != null) {\n        uniqueTags.addAll(model.tag!.split(',').where((e) => e.isNotEmpty));\n      }\n\n      if (model.isRecommend) {\n        uniqueTags.add(AppLocale.recommendTag.getString(context));\n      }\n\n      if (model.isNew) {\n        uniqueTags.add(AppLocale.newTag.getString(context));\n      }\n\n      if (model.supportVision) {\n        uniqueTags.add(AppLocale.visionTag.getString(context));\n      }\n\n      if (model.supportReasoning) {\n        uniqueTags.add(AppLocale.reasoning.getString(context));\n      }\n\n      if (model.supportSearch) {\n        uniqueTags.add(AppLocale.search.getString(context));\n      }\n\n      if (model.modelPrice.isFree) {\n        uniqueTags.add(AppLocale.free.getString(context));\n      }\n    }\n\n    // Create tag widgets\n    tags = uniqueTags.map((tag) {\n      return InkWell(\n        onTap: () {\n          setState(() {\n            selectedTag = selectedTag == tag ? '' : tag;\n          });\n        },\n        child: Container(\n          padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),\n          child: buildTag(\n            customColors,\n            tag,\n            tagTextColor: selectedTag == tag ? customColors.linkColor : null,\n          ),\n        ),\n      );\n    }).toList();\n\n    return widget.models.isNotEmpty\n        ? Column(\n            children: [\n              // Search\n              Container(\n                margin: const EdgeInsets.only(bottom: 10, left: 5, right: 5),\n                decoration: BoxDecoration(\n                  color: customColors.textfieldBackgroundColor,\n                  borderRadius: CustomSize.borderRadius,\n                ),\n                child: TextField(\n                  textAlignVertical: TextAlignVertical.center,\n                  style: TextStyle(color: customColors.dialogDefaultTextColor),\n                  decoration: InputDecoration(\n                    hintText: AppLocale.search.getString(context),\n                    hintStyle: TextStyle(color: customColors.textfieldHintColor),\n                    prefixIcon: Icon(\n                      Icons.search,\n                      color: customColors.weakTextColor?.withAlpha(150),\n                    ),\n                    isDense: true,\n                    border: InputBorder.none,\n                  ),\n                  onChanged: (value) => setState(() => keyword = value.toLowerCase()),\n                ),\n              ),\n\n              // Tags\n              if (tags.isNotEmpty)\n                Container(\n                  padding: const EdgeInsets.only(top: 5, left: 5, right: 5, bottom: 5),\n                  child: SingleChildScrollView(\n                    scrollDirection: Axis.horizontal,\n                    child: Row(children: tags),\n                  ),\n                ),\n\n              Expanded(\n                child: Builder(builder: (context) {\n                  final models = searchModels();\n                  return Container(\n                    padding: const EdgeInsets.only(bottom: 15),\n                    child: GroupListWidget(\n                      items: models,\n                      showTitle: true,\n                      groupKey: (item) {\n                        return item.category;\n                      },\n                      itemBuilder: (item) {\n                        return buildItem(context, item, customColors);\n                      },\n                    ),\n                  );\n                }),\n              ),\n            ],\n          )\n        : const Center(\n            child: Text(\n              'No model available\\nPlease login first!',\n              textAlign: TextAlign.center,\n            ),\n          );\n  }\n\n  Widget buildItem(BuildContext context, Model item, CustomColors customColors) {\n    final modelPrice = item.modelPrice;\n\n    var tags = <Widget>[];\n    if (modelPrice.isFree) {\n      tags.add(buildTag(\n        customColors,\n        AppLocale.free.getString(context),\n        tagTextColor: widget.initValue == item.uid() ? customColors.linkColor : null,\n      ));\n    }\n    if (item.tag != null) {\n      var tt = item.tag!.split(\",\").where((e) => e.isNotEmpty).toList();\n      for (var i = 0; i < tt.length; i++) {\n        tags.add(buildTag(\n          customColors,\n          tt[i],\n          tagTextColor: widget.initValue == item.uid() ? customColors.linkColor : null,\n        ));\n      }\n    }\n\n    if (item.supportVision) {\n      tags.add(buildTag(\n        customColors,\n        AppLocale.visionTag.getString(context),\n        tagTextColor: widget.initValue == item.uid() ? customColors.linkColor : null,\n      ));\n    }\n\n    if (item.supportReasoning) {\n      tags.add(buildTag(\n        customColors,\n        AppLocale.reasoning.getString(context),\n        tagTextColor: widget.initValue == item.uid() ? customColors.linkColor : null,\n      ));\n    }\n\n    if (item.supportSearch) {\n      tags.add(buildTag(\n        customColors,\n        AppLocale.search.getString(context),\n        tagTextColor: widget.initValue == item.uid() ? customColors.linkColor : null,\n      ));\n    }\n\n    if (item.isNew) {\n      tags.add(buildTag(\n        customColors,\n        AppLocale.newTag.getString(context),\n        tagTextColor: widget.initValue == item.uid() ? customColors.linkColor : null,\n      ));\n    }\n\n    return ListTile(\n      leading: Stack(\n        children: [\n          buildAvatar(avatarUrl: item.avatarUrl, size: 48),\n          if (item.userNoPermission)\n            Container(\n              width: 48,\n              height: 48,\n              decoration: BoxDecoration(\n                color: Colors.black.withOpacity(0.4),\n                borderRadius: CustomSize.borderRadiusAll,\n              ),\n              child: const Icon(\n                Icons.lock_outline,\n                color: Colors.white,\n                size: 24,\n              ),\n            ),\n        ],\n      ),\n      contentPadding: EdgeInsets.zero,\n      title: AutoSizeText(\n        item.name,\n        minFontSize: 10,\n        maxFontSize: 15,\n        maxLines: 1,\n        style: TextStyle(\n          color: widget.initValue == item.uid()\n              ? customColors.linkColor\n              : (item.userNoPermission ? customColors.weakTextColorLess : null),\n          fontWeight: widget.initValue == item.uid() ? FontWeight.bold : null,\n        ),\n      ),\n      subtitle: Column(\n        mainAxisSize: MainAxisSize.min,\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: [\n          SingleChildScrollView(\n            scrollDirection: Axis.horizontal,\n            child: Container(\n              margin: const EdgeInsets.symmetric(vertical: 3),\n              child: Row(children: formatTags(tags)),\n            ),\n          ),\n          buildPriceBlock(customColors, item, modelPrice),\n        ],\n      ),\n      onTap: () {\n        if (item.userNoPermission) {\n          showErrorMessage(AppLocale.modelNeedSignIn.getString(context));\n          return;\n        }\n\n        widget.onSelected(item);\n      },\n      onLongPress: () {\n        if (item.description == null || item.description == '') {\n          return;\n        }\n\n        showBeautyDialog(\n          context,\n          type: QuickAlertType.info,\n          text: item.description,\n          confirmBtnText: AppLocale.gotIt.getString(context),\n          showCancelBtn: false,\n        );\n      },\n    );\n  }\n\n  String selectedTag = '';\n\n  List<Model> searchModels() {\n    var models = keyword.isEmpty\n        ? widget.models\n        : widget.models.where((e) {\n            var matchText = e.name + (e.description ?? '') + (e.shortName ?? '') + (e.tag ?? '') + (e.category);\n            if (e.supportVision) {\n              matchText += 'vision视觉看图';\n            }\n            if (e.isNew) {\n              matchText += 'new新';\n            }\n\n            if (e.isRecommend) {\n              matchText += 'recommend推荐';\n            }\n\n            if (e.supportReasoning) {\n              matchText += 'reasoning推理';\n            }\n\n            if (e.supportSearch) {\n              matchText += 'search搜索';\n            }\n\n            if (e.modelPrice.isFree) {\n              matchText += 'free免费';\n            }\n\n            return matchText.toLowerCase().contains(keyword);\n          }).toList();\n\n    if (selectedTag.isNotEmpty) {\n      models = models.where((e) {\n        var tags = [];\n        if (e.tag != null) {\n          tags = e.tag!.split(',').where((e) => e.isNotEmpty).toList();\n        }\n\n        if (e.isRecommend) {\n          tags.add(AppLocale.recommendTag.getString(context));\n        }\n\n        if (e.isNew) {\n          tags.add(AppLocale.newTag.getString(context));\n        }\n\n        if (e.supportVision) {\n          tags.add(AppLocale.visionTag.getString(context));\n        }\n\n        if (e.supportReasoning) {\n          tags.add(AppLocale.reasoning.getString(context));\n        }\n\n        if (e.supportSearch) {\n          tags.add(AppLocale.search.getString(context));\n        }\n\n        if (e.modelPrice.isFree) {\n          tags.add(AppLocale.free.getString(context));\n        }\n\n        if (e.id.startsWith('v2@rooms')) {\n          tags.add(AppLocale.character.getString(context));\n        } else {\n          tags.add(AppLocale.model.getString(context));\n        }\n\n        return tags.contains(selectedTag);\n      }).toList();\n    }\n\n    return models;\n  }\n\n  Widget buildPriceBlock(CustomColors customColors, Model model, ModelPrice item) {\n    if (item.isFree) {\n      return const SizedBox();\n    }\n\n    var priceText = '';\n    if (item.input > 0 || item.output > 0) {\n      priceText +=\n          '${AppLocale.input.getString(context)} ￠${item.input}, ${AppLocale.output.getString(context)} ￠${item.output}';\n    }\n\n    if (item.request > 0) {\n      priceText += '${priceText == '' ? '' : ', '}${AppLocale.perRequest.getString(context)} ￠${item.request}';\n    }\n\n    if (item.search > 0) {\n      priceText += '${priceText == '' ? '' : ', '}${AppLocale.perSearch.getString(context)} ￠${item.search}';\n    }\n\n    return Row(\n      children: [\n        Text(\n          priceText,\n          maxLines: 2,\n          overflow: TextOverflow.ellipsis,\n          style: TextStyle(\n            fontSize: 11,\n            color:\n                widget.initValue == model.uid() ? customColors.linkColor : customColors.weakTextColor?.withAlpha(150),\n          ),\n        ),\n        if (item.hasNote) ...[\n          const SizedBox(width: 5),\n          InkWell(\n            onTap: () {\n              showBeautyDialog(\n                context,\n                type: QuickAlertType.info,\n                text: item.note,\n                confirmBtnText: AppLocale.gotIt.getString(context),\n                showCancelBtn: false,\n              );\n            },\n            child: Icon(\n              Icons.help_outline,\n              size: 12,\n              color: customColors.weakLinkColor?.withAlpha(50),\n            ),\n          ),\n        ],\n      ],\n    );\n  }\n\n  Widget buildCategory(CustomColors customColors, String category) {\n    return Container(\n      padding: const EdgeInsets.only(left: 10, right: 10),\n      width: double.infinity,\n      child: Text(\n        category,\n        style: TextStyle(\n          color: customColors.dialogDefaultTextColor,\n          fontSize: 14,\n        ),\n      ),\n    );\n  }\n\n  Widget buildAvatar({String? avatarUrl, int? id, int size = 30}) {\n    if (avatarUrl != null && avatarUrl.startsWith('http')) {\n      return RemoteAvatar(\n        avatarUrl: imageURL(avatarUrl, qiniuImageTypeAvatar),\n        size: size,\n      );\n    }\n\n    return RandomAvatar(\n      id: id ?? 0,\n      size: size,\n      usage: Ability().isUserLogon() ? AvatarUsage.room : AvatarUsage.legacy,\n    );\n  }\n\n  Widget buildTag(\n    CustomColors customColors,\n    String tag, {\n    double? tagFontSize,\n    Color? tagTextColor,\n  }) {\n    return Container(\n      padding: const EdgeInsets.only(right: 5),\n      child: Text(\n        \"#$tag\",\n        style: TextStyle(\n          fontSize: tagFontSize ?? 11,\n          color: tagTextColor ?? customColors.weakTextColor?.withAlpha(150),\n        ),\n      ),\n    );\n  }\n}\n\nString modelTagColorSeq(int index) {\n  var colors = <Color>{\n    Colors.grey,\n    Colors.purple,\n    Colors.orange,\n    Colors.pink,\n    Colors.deepPurple,\n    Colors.indigo,\n    Colors.cyan,\n  };\n  return colorToString(colors.elementAt(index % colors.length));\n}\n\nList<Widget> formatTags(List<Widget> tags) {\n  var widgets = <Widget>[];\n\n  for (var i = 0; i < tags.length; i++) {\n    widgets.add(tags[i]);\n  }\n\n  return widgets;\n}\n"
  },
  {
    "path": "lib/page/component/multi_item_selector.dart",
    "content": "import 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass MultiItemSelector<T> extends StatefulWidget {\n  final Widget Function(T item)? itemAvatarBuilder;\n  final Widget Function(T item) itemBuilder;\n  final List<T> items;\n  final Function(List<T> selected)? onSubmit;\n  final Function(List<T> selected)? onChanged;\n  final List<T>? selectedItems;\n\n  const MultiItemSelector({\n    super.key,\n    required this.items,\n    required this.itemBuilder,\n    this.selectedItems,\n    this.onSubmit,\n    this.onChanged,\n    this.itemAvatarBuilder,\n  });\n\n  @override\n  State<MultiItemSelector<T>> createState() => _MultiItemSelectorState();\n}\n\nclass _MultiItemSelectorState<T> extends State<MultiItemSelector<T>> {\n  var selectedItems = <T>[];\n\n  @override\n  void initState() {\n    selectedItems = widget.selectedItems ?? [];\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return Container(\n      margin: const EdgeInsets.only(top: 15),\n      child: Column(\n        children: [\n          if (widget.onSubmit != null)\n            Row(\n              mainAxisAlignment: MainAxisAlignment.spaceAround,\n              children: [\n                EnhancedButton(\n                  width: 100,\n                  height: 40,\n                  backgroundColor: customColors.weakTextColor,\n                  title: AppLocale.cancel.getString(context),\n                  onPressed: () {\n                    context.pop();\n                  },\n                ),\n                EnhancedButton(\n                  width: 100,\n                  height: 40,\n                  title: AppLocale.ok.getString(context),\n                  onPressed: () {\n                    widget.onSubmit!(selectedItems);\n                  },\n                ),\n              ],\n            ),\n          Expanded(\n            child: ListView.separated(\n              itemCount: widget.items.length,\n              itemBuilder: (context, i) {\n                var item = widget.items[i];\n                return CheckboxListTile(\n                  controlAffinity: ListTileControlAffinity.leading,\n                  checkboxShape: const CircleBorder(),\n                  activeColor: customColors.linkColor,\n                  side: BorderSide(\n                    color: customColors.weakTextColor!.withAlpha(100),\n                  ),\n                  title: Container(\n                    alignment: Alignment.center,\n                    padding:\n                        const EdgeInsets.symmetric(horizontal: 10, vertical: 5),\n                    child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      mainAxisSize: MainAxisSize.min,\n                      children: [\n                        if (widget.itemAvatarBuilder != null)\n                          widget.itemAvatarBuilder!(item),\n                        const SizedBox(width: 20),\n                        Expanded(\n                          child: Container(\n                            alignment: Alignment.centerLeft,\n                            child: widget.itemBuilder(item),\n                          ),\n                        ),\n                      ],\n                    ),\n                  ),\n                  onChanged: (selected) {\n                    setState(() {\n                      if (selectedItems.contains(item)) {\n                        selectedItems.remove(item);\n                      } else {\n                        selectedItems.add(item);\n                      }\n\n                      if (widget.onChanged != null) {\n                        widget.onChanged!(selectedItems);\n                      }\n                    });\n                  },\n                  value: selectedItems.contains(item),\n                );\n              },\n              separatorBuilder: (BuildContext context, int index) {\n                return Divider(\n                  height: 1,\n                  color: customColors.columnBlockDividerColor,\n                );\n              },\n            ),\n          )\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/notify_message.dart",
    "content": "import 'package:askaide/page/component/gradient_style.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:cached_network_image/cached_network_image.dart';\nimport 'package:flutter/material.dart';\n\nclass NotifyMessageWidget extends StatelessWidget {\n  final Function()? onClose;\n  final Widget child;\n  final String? backgroundImageUrl;\n  final Color? backgroundColor;\n  final double height;\n  final Widget? title;\n  final bool closeable;\n  const NotifyMessageWidget({\n    super.key,\n    this.onClose,\n    required this.child,\n    this.backgroundImageUrl,\n    this.height = 100,\n    this.title,\n    this.closeable = true,\n    this.backgroundColor,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      margin: const EdgeInsets.only(\n        left: 10,\n        right: 10,\n      ),\n      padding: const EdgeInsets.only(\n        left: 5,\n        right: 5,\n        top: 7,\n      ),\n      child: Container(\n        width: double.infinity,\n        height: height,\n        padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),\n        decoration: BoxDecoration(\n          borderRadius: CustomSize.borderRadius,\n          // gradient: buildGradientStyle(),\n          color: backgroundColor,\n          image: backgroundImageUrl != null\n              ? DecorationImage(\n                  fit: BoxFit.cover,\n                  image: CachedNetworkImageProvider(backgroundImageUrl!),\n                )\n              : null,\n        ),\n        child: Column(\n          children: [\n            Row(\n              mainAxisAlignment: MainAxisAlignment.spaceBetween,\n              mainAxisSize: MainAxisSize.max,\n              children: [\n                if (title != null) title! else const SizedBox(),\n                if (closeable)\n                  InkWell(\n                    onTap: () {\n                      onClose?.call();\n                    },\n                    child: Container(\n                      decoration: BoxDecoration(\n                        color: const Color.fromARGB(184, 37, 37, 37),\n                        borderRadius: BorderRadius.circular(80),\n                      ),\n                      padding: const EdgeInsets.all(3),\n                      child: const Icon(\n                        Icons.close,\n                        color: Color.fromARGB(255, 255, 255, 255),\n                        size: 12,\n                      ),\n                    ),\n                  )\n                else\n                  const SizedBox(),\n              ],\n            ),\n            if (title != null) const SizedBox(height: 3),\n            Expanded(child: child),\n          ],\n        ),\n      ),\n    );\n  }\n\n  LinearGradient buildGradientStyle() {\n    return GradientStyle.warmLinear();\n  }\n}\n"
  },
  {
    "path": "lib/page/component/pagination.dart",
    "content": "/// 该文件来源于 https://github.com/created-by-varun/flutter_pagination/blob/master/lib/pagination.dart\n\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\n\nclass Pagination extends StatefulWidget {\n  const Pagination({\n    super.key,\n    required this.numOfPages,\n    required this.selectedPage,\n    this.pagesVisible = 5,\n    required this.onPageChanged,\n  });\n\n  final int numOfPages;\n  final int selectedPage;\n  final int pagesVisible;\n  final Function onPageChanged;\n\n  @override\n  State<Pagination> createState() => _PaginationState();\n}\n\nclass _PaginationState extends State<Pagination> {\n  late int _startPage;\n  late int _endPage;\n\n  @override\n  void initState() {\n    super.initState();\n    _calculateVisiblePages();\n  }\n\n  @override\n  void didUpdateWidget(Pagination oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    _calculateVisiblePages();\n  }\n\n  void _calculateVisiblePages() {\n    /// If the number of pages is less than or equal to the number of pages visible, then show all the pages\n    if (widget.numOfPages <= widget.pagesVisible) {\n      _startPage = 1;\n      _endPage = widget.numOfPages;\n    } else {\n      /// If the number of pages is greater than the number of pages visible, then show the pages visible\n      int middle = (widget.pagesVisible - 1) ~/ 2;\n      if (widget.selectedPage <= middle + 1) {\n        _startPage = 1;\n        _endPage = widget.pagesVisible;\n      } else if (widget.selectedPage >= widget.numOfPages - middle) {\n        _startPage = widget.numOfPages - (widget.pagesVisible - 1);\n        _endPage = widget.numOfPages;\n      } else {\n        _startPage = widget.selectedPage - middle;\n        _endPage = widget.selectedPage + middle;\n      }\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return Row(\n      mainAxisAlignment: MainAxisAlignment.center,\n      children: [\n        /// loop through the pages and show the page buttons\n        for (int i = _startPage; i <= _endPage; i++)\n          AnimatedContainer(\n            duration: const Duration(milliseconds: 200),\n            child: TextButton(\n              style: i == widget.selectedPage\n                  ? ButtonStyle(\n                      backgroundColor:\n                          WidgetStateProperty.all(Colors.transparent),\n                    )\n                  : ButtonStyle(\n                      backgroundColor:\n                          WidgetStateProperty.all(Colors.transparent)),\n              onPressed: () => widget.onPageChanged(i),\n              child: Text(\n                '$i',\n                style: i == widget.selectedPage\n                    ? TextStyle(\n                        color: customColors.linkColor,\n                        fontSize: 14,\n                        fontWeight: FontWeight.w700,\n                      )\n                    : TextStyle(\n                        fontSize: 12,\n                        fontWeight: FontWeight.w700,\n                        color: customColors.weakLinkColor,\n                      ),\n              ),\n            ),\n          ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/password_field.dart",
    "content": "import 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\n\nclass PasswordField extends StatefulWidget {\n  final TextEditingController? controller;\n  final ValueChanged<String>? onSubmitted;\n  final String? labelText;\n  final String? hintText;\n  final bool inColumnBlock;\n\n  const PasswordField({\n    super.key,\n    this.onSubmitted,\n    this.controller,\n    this.labelText,\n    this.hintText,\n    this.inColumnBlock = false,\n  });\n\n  @override\n  State<PasswordField> createState() => _PasswordFieldState();\n}\n\nclass _PasswordFieldState extends State<PasswordField> {\n  var obscureText = true;\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return Container(\n      padding: widget.inColumnBlock ? const EdgeInsets.all(5) : null,\n      child: Row(\n        crossAxisAlignment: CrossAxisAlignment.center,\n        children: [\n          if (widget.inColumnBlock)\n            SizedBox(\n              width: 80,\n              child: Text(\n                widget.labelText!,\n                overflow: TextOverflow.ellipsis,\n                style: TextStyle(\n                  fontSize: 16,\n                  color: customColors.textfieldLabelColor,\n                ),\n              ),\n            ),\n          if (widget.inColumnBlock) const SizedBox(width: 10),\n          Expanded(\n            child: TextField(\n              obscureText: obscureText,\n              inputFormatters: [FilteringTextInputFormatter.singleLineFormatter],\n              textAlignVertical: TextAlignVertical.center,\n              decoration: InputDecoration(\n                border: const OutlineInputBorder(),\n                enabledBorder: widget.inColumnBlock\n                    ? InputBorder.none\n                    : const OutlineInputBorder(\n                        borderSide: BorderSide(color: Color.fromARGB(200, 192, 192, 192)),\n                      ),\n                focusedBorder: widget.inColumnBlock\n                    ? InputBorder.none\n                    : OutlineInputBorder(\n                        borderSide: BorderSide(color: customColors.linkColor!),\n                      ),\n                // floatingLabelStyle: TextStyle(color: customColors.linkColor!),\n                isDense: true,\n                floatingLabelBehavior: FloatingLabelBehavior.always,\n                labelText: widget.inColumnBlock ? null : widget.labelText,\n                labelStyle: const TextStyle(fontSize: 17),\n                hintText: widget.hintText,\n                hintStyle: TextStyle(\n                  color: customColors.textfieldHintColor,\n                  fontSize: 15,\n                ),\n                suffixIcon: IconButton(\n                  icon: Icon(\n                    obscureText ? Icons.visibility_off : Icons.visibility,\n                    size: 15,\n                    color: const Color.fromARGB(150, 141, 141, 141),\n                  ),\n                  onPressed: () {\n                    setState(() {\n                      obscureText = !obscureText;\n                    });\n                  },\n                ),\n              ),\n              keyboardType: TextInputType.visiblePassword,\n              onSubmitted: widget.onSubmitted,\n              controller: widget.controller,\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/prompt_tags_selector.dart",
    "content": "import 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/weak_text_button.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:flutter/material.dart';\nimport 'package:go_router/go_router.dart';\n\nclass PromptTagsSelector extends StatefulWidget {\n  final List<PromptTag> selectedTags;\n  final Function(List<PromptTag> tags) onSubmit;\n  const PromptTagsSelector({\n    super.key,\n    this.selectedTags = const [],\n    required this.onSubmit,\n  });\n\n  @override\n  State<PromptTagsSelector> createState() => _PromptTagsSelectorState();\n}\n\nclass _PromptTagsSelectorState extends State<PromptTagsSelector> {\n  List<PromptCategory> promptCategories = [];\n  Map<String, PromptTag> selectedTags = {};\n\n  final ScrollController _scrollController = ScrollController();\n\n  @override\n  void dispose() {\n    _scrollController.dispose();\n    super.dispose();\n  }\n\n  @override\n  void initState() {\n    for (var element in widget.selectedTags) {\n      selectedTags[element.value] = element;\n    }\n\n    APIServer().drawPromptTags().then((res) {\n      setState(() {\n        promptCategories = res;\n      });\n    });\n\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return SafeArea(\n      top: false,\n      child: Column(\n        mainAxisSize: MainAxisSize.min,\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: [\n          Expanded(\n            child: Container(\n              decoration: BoxDecoration(borderRadius: CustomSize.borderRadius),\n              child: DefaultTabController(\n                length: promptCategories.length,\n                child: Column(\n                  children: [\n                    Theme(\n                      data: Theme.of(context).copyWith(\n                        colorScheme:\n                            Theme.of(context).colorScheme.copyWith(surfaceContainerHighest: Colors.transparent),\n                      ),\n                      child: TabBar(\n                        tabs: [for (var cat in promptCategories) Tab(text: cat.name)],\n                        isScrollable: true,\n                        labelPadding: const EdgeInsets.only(left: 0, right: 20),\n                        labelColor: customColors.linkColor,\n                        unselectedLabelColor: customColors.weakLinkColor,\n                        labelStyle: const TextStyle(\n                          fontSize: 16,\n                          fontWeight: FontWeight.bold,\n                        ),\n                        indicator: const BoxDecoration(),\n                        overlayColor: WidgetStateProperty.all(Colors.transparent),\n                      ),\n                    ),\n                    Expanded(\n                      child: TabBarView(\n                        children: [\n                          for (var cat in promptCategories) buildTabBarView(customColors, cat.children),\n                        ],\n                      ),\n                    ),\n                  ],\n                ),\n              ),\n            ),\n          ),\n          // Container(\n          //   margin: const EdgeInsets.symmetric(vertical: 8),\n          //   width: double.infinity,\n          //   child: ConstrainedBox(\n          //     constraints: const BoxConstraints(maxHeight: 95),\n          //     child: Column(\n          //       crossAxisAlignment: CrossAxisAlignment.start,\n          //       mainAxisSize: MainAxisSize.min,\n          //       children: [\n          //         Text(\n          //           '已选择（${selectedTags.length}）：',\n          //           style: TextStyle(\n          //             fontSize: 14,\n          //             color: customColors.weakLinkColor,\n          //             fontWeight: FontWeight.bold,\n          //           ),\n          //         ),\n          //         const SizedBox(height: 5),\n          //         Expanded(\n          //           child: SingleChildScrollView(\n          //             controller: _scrollController,\n          //             child: Wrap(\n          //               spacing: 3,\n          //               runSpacing: 3,\n          //               children: [\n          //                 for (var tag in selectedTags.values)\n          //                   Tag(\n          //                     name: tag.name,\n          //                     backgroundColor: customColors.linkColor,\n          //                     textColor: Colors.white,\n          //                     fontsize: 10,\n          //                     onDeleted: () {\n          //                       setState(() {\n          //                         selectedTags.remove(tag.value);\n          //                       });\n          //                     },\n          //                   ),\n          //               ],\n          //             ),\n          //           ),\n          //         ),\n          //       ],\n          //     ),\n          //   ),\n          // ),\n          Container(\n            margin: const EdgeInsets.symmetric(vertical: 10),\n            child: Row(\n              mainAxisSize: MainAxisSize.min,\n              children: [\n                WeakTextButton(\n                  title: '取消',\n                  onPressed: () {\n                    context.pop();\n                  },\n                ),\n                const SizedBox(width: 20),\n                Expanded(\n                  child: EnhancedButton(\n                    title: '确定',\n                    fontSize: 14,\n                    onPressed: () {\n                      widget.onSubmit(selectedTags.values.toList());\n                    },\n                  ),\n                ),\n              ],\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n\n  Widget buildTabBarView(CustomColors customColors, List<PromptCategory> subCategories) {\n    return Container(\n      margin: const EdgeInsets.symmetric(horizontal: 5),\n      padding: const EdgeInsets.only(left: 10, right: 10, top: 0, bottom: 10),\n      decoration: BoxDecoration(\n        color: customColors.backgroundContainerColor?.withAlpha(50),\n        borderRadius: CustomSize.borderRadius,\n      ),\n      child: ListView.builder(\n        itemCount: subCategories.length,\n        itemBuilder: (context, index) {\n          return Column(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: [\n              Padding(\n                padding: const EdgeInsets.only(top: 10, bottom: 10),\n                child: Text(\n                  '# ${subCategories[index].name}',\n                  style: TextStyle(\n                    fontWeight: FontWeight.bold,\n                    color: customColors.weakTextColorPlusPlus?.withAlpha(150),\n                    fontSize: 14,\n                    height: 1.5,\n                  ),\n                ),\n              ),\n              buildTagView(customColors, subCategories[index].tags),\n            ],\n          );\n        },\n      ),\n    );\n  }\n\n  Widget buildTagView(CustomColors customColors, List<PromptTag> tags) {\n    return Wrap(\n      spacing: 5,\n      runSpacing: 5,\n      children: [\n        for (var tag in tags)\n          Tag(\n            name: tag.name,\n            onTap: () {\n              if (selectedTags.containsKey(tag.value)) {\n                selectedTags.remove(tag.value);\n              } else {\n                selectedTags[tag.value] = tag;\n              }\n              setState(() {});\n              _scrollController.animateTo(\n                _scrollController.position.maxScrollExtent,\n                duration: const Duration(milliseconds: 300),\n                curve: Curves.easeOut,\n              );\n            },\n            textColor: selectedTags.containsKey(tag.value) ? Colors.white : customColors.weakTextColorPlusPlus,\n            backgroundColor: selectedTags.containsKey(tag.value)\n                ? customColors.linkColor\n                : customColors.backgroundContainerColor?.withAlpha(200),\n          ),\n      ],\n    );\n  }\n}\n\nclass Tag extends StatelessWidget {\n  final String name;\n  final Color? backgroundColor;\n  final Color? textColor;\n  final VoidCallback? onDeleted;\n  final VoidCallback? onTap;\n  final double? fontsize;\n\n  const Tag({\n    super.key,\n    required this.name,\n    this.backgroundColor,\n    this.textColor,\n    this.onDeleted,\n    this.onTap,\n    this.fontsize,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return InkWell(\n      onTap: onTap,\n      child: Chip(\n        side: BorderSide.none,\n        visualDensity: const VisualDensity(horizontal: -4.0, vertical: -4.0),\n        padding: const EdgeInsets.all(0),\n        labelPadding: EdgeInsets.only(left: 5, right: onDeleted == null ? 5 : 0),\n        elevation: 0,\n        label: Text(\n          name,\n          style: TextStyle(\n            fontSize: fontsize ?? 12,\n            color: textColor ?? Colors.white,\n          ),\n        ),\n        backgroundColor: backgroundColor ?? Colors.grey,\n        deleteIcon: Icon(\n          Icons.close,\n          color: textColor ?? Colors.white,\n          size: fontsize ?? 12,\n        ),\n        onDeleted: onDeleted,\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/random_avatar.dart",
    "content": "import 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:cached_network_image/cached_network_image.dart';\nimport 'package:flutter/material.dart';\nimport 'package:random_avatar/random_avatar.dart' as ava;\n\nenum AvatarUsage {\n  room,\n  user,\n  legacy,\n}\n\nclass RandomAvatar extends StatelessWidget {\n  final int id;\n  final int? size;\n  final AvatarUsage usage;\n  final BorderRadiusGeometry? borderRadius;\n  const RandomAvatar({\n    super.key,\n    required this.id,\n    this.size,\n    required this.usage,\n    this.borderRadius,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    if (usage == AvatarUsage.user || usage == AvatarUsage.legacy) {\n      return ava.RandomAvatar(\n        '$id',\n        width: size?.toDouble(),\n        height: size?.toDouble(),\n      );\n    }\n\n    return ConstrainedBox(\n      constraints: BoxConstraints(\n        maxWidth: size?.toDouble() ?? 500,\n        maxHeight: size?.toDouble() ?? 500,\n      ),\n      child: ClipRRect(\n        borderRadius: borderRadius ?? CustomSize.borderRadius,\n        child: CachedNetworkImage(\n          imageUrl: 'https://ai-api.aicode.cc/v1/images/random-avatar/${usage.name}/$id/${size ?? 500}',\n          fit: BoxFit.cover,\n        ),\n      ),\n    );\n  }\n}\n\nclass RemoteAvatar extends StatelessWidget {\n  final String avatarUrl;\n  final int? size;\n  final double? radius;\n  const RemoteAvatar({super.key, required this.avatarUrl, this.size, this.radius});\n\n  @override\n  Widget build(BuildContext context) {\n    return SizedBox(\n      width: size?.toDouble() ?? 60,\n      height: size?.toDouble() ?? 60,\n      child: ClipRRect(\n        borderRadius: BorderRadius.circular(radius ?? CustomSize.radiusValue),\n        child: CachedNetworkImage(\n          imageUrl: avatarUrl,\n          fit: BoxFit.fill,\n        ),\n      ),\n    );\n  }\n}\n\nclass LocalAvatar extends StatelessWidget {\n  final String assetName;\n  final int? size;\n  const LocalAvatar({super.key, required this.assetName, this.size});\n\n  @override\n  Widget build(BuildContext context) {\n    return SizedBox(\n      width: size?.toDouble() ?? 60,\n      height: size?.toDouble() ?? 60,\n      child: ClipRRect(borderRadius: CustomSize.borderRadius, child: Image.asset(assetName, fit: BoxFit.fill)),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/room_card.dart",
    "content": "import 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/image.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/weak_text_button.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/api/room_gallery.dart';\nimport 'package:flutter/material.dart';\nimport 'package:go_router/go_router.dart';\n\nclass RoomCard extends StatelessWidget {\n  final bool selected;\n  final RoomGallery item;\n  final Function(RoomGallery) onItemSelected;\n  final Function()? selectedCheck;\n  final double fontsize;\n  final bool stopAllEvents;\n  const RoomCard({\n    super.key,\n    this.selected = false,\n    required this.item,\n    required this.onItemSelected,\n    this.fontsize = 13,\n    this.selectedCheck,\n    this.stopAllEvents = false,\n  });\n\n  Future openRoomCard(BuildContext context, RoomGallery item) {\n    return openModalBottomSheet(\n      context,\n      (_) {\n        return Container(\n          padding: const EdgeInsets.only(top: 20),\n          child: GalleryRoomCard(\n            item: item,\n            selected: selected,\n            // onConfirm: () {\n            //   onItemSelected(item);\n            // },\n          ),\n        );\n      },\n      heightFactor: 0.7,\n      disableCompleteEvent: true,\n      disableEvent: stopAllEvents,\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n    return InkWell(\n      onLongPress: () async {\n        await openRoomCard(context, item);\n        selectedCheck?.call();\n      },\n      onTap: () {\n        onItemSelected(item);\n      },\n      borderRadius: CustomSize.borderRadiusAll,\n      child: Column(\n        mainAxisSize: MainAxisSize.min,\n        crossAxisAlignment: CrossAxisAlignment.center,\n        mainAxisAlignment: MainAxisAlignment.center,\n        children: [\n          Container(\n            decoration: BoxDecoration(\n              borderRadius: CustomSize.borderRadius,\n              border: selected\n                  ? Border.all(\n                      width: 2,\n                      color: customColors.linkColor ?? Colors.green,\n                    )\n                  : null,\n            ),\n            child: Stack(\n              children: [\n                ClipRRect(\n                  borderRadius: CustomSize.borderRadius,\n                  child: CachedNetworkImageEnhanced(\n                    imageUrl: imageURL(item.avatarUrl, qiniuImageTypeAvatar),\n                    fit: BoxFit.cover,\n                  ),\n                ),\n                if (selected)\n                  Positioned(\n                    right: -1,\n                    bottom: -1,\n                    child: Container(\n                      padding: const EdgeInsets.all(4),\n                      decoration: BoxDecoration(\n                        color: customColors.linkColor ?? Colors.green,\n                        borderRadius:\n                            const BorderRadius.only(topLeft: CustomSize.radius, bottomRight: CustomSize.radius),\n                      ),\n                      child: const Icon(\n                        Icons.check,\n                        color: Colors.white,\n                        size: 14,\n                      ),\n                    ),\n                  ),\n              ],\n            ),\n          ),\n          const SizedBox(height: 5),\n          Text(\n            item.name,\n            textAlign: TextAlign.center,\n            style: TextStyle(\n              color: customColors.weakTextColorPlusPlus,\n              fontSize: fontsize,\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n}\n\nclass GalleryRoomCard extends StatelessWidget {\n  final RoomGallery item;\n  final Function()? onConfirm;\n  final bool selected;\n  const GalleryRoomCard({super.key, required this.item, this.onConfirm, this.selected = false});\n\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      mainAxisSize: MainAxisSize.min,\n      children: [\n        Expanded(\n          child: SingleChildScrollView(\n            child: Column(\n              mainAxisSize: MainAxisSize.min,\n              crossAxisAlignment: CrossAxisAlignment.center,\n              children: [\n                Container(\n                  width: MediaQuery.of(context).size.width - 80,\n                  height: MediaQuery.of(context).size.width - 80,\n                  decoration: BoxDecoration(\n                    borderRadius: BorderRadius.circular(30),\n                  ),\n                  child: ClipRRect(\n                    borderRadius: CustomSize.borderRadius,\n                    child: CachedNetworkImageEnhanced(\n                      imageUrl: item.avatarUrl,\n                      fit: BoxFit.cover,\n                    ),\n                  ),\n                ),\n                Container(\n                  margin: const EdgeInsets.only(top: 10),\n                  child: Text(\n                    item.name,\n                    style: const TextStyle(\n                      fontSize: 16,\n                      fontWeight: FontWeight.w600,\n                    ),\n                  ),\n                ),\n                if (item.description != '')\n                  Container(\n                    margin: const EdgeInsets.symmetric(\n                      vertical: 10,\n                      horizontal: 20,\n                    ),\n                    padding: const EdgeInsets.all(10),\n                    width: double.infinity,\n                    child: Column(\n                      crossAxisAlignment: CrossAxisAlignment.start,\n                      children: [\n                        const Text(\n                          '简介：',\n                          style: TextStyle(\n                            fontSize: 14,\n                            fontWeight: FontWeight.w600,\n                          ),\n                        ),\n                        const SizedBox(height: 5),\n                        SelectableText(\n                          item.description,\n                          style: const TextStyle(\n                            fontSize: 14,\n                          ),\n                        ),\n                      ],\n                    ),\n                  ),\n              ],\n            ),\n          ),\n        ),\n        if (onConfirm != null)\n          Container(\n            height: 70,\n            margin: const EdgeInsets.only(top: 20),\n            padding: const EdgeInsets.symmetric(\n              horizontal: 20,\n              vertical: 10,\n            ),\n            child: Row(\n              children: [\n                WeakTextButton(\n                  title: '取消',\n                  onPressed: () {\n                    context.pop();\n                  },\n                ),\n                const SizedBox(width: 20),\n                Expanded(\n                  child: EnhancedButton(\n                      title: selected ? '移除' : '选择',\n                      onPressed: () {\n                        onConfirm!();\n                        context.pop();\n                      }),\n                )\n              ],\n            ),\n          )\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/rotating_widget.dart",
    "content": "import 'dart:math';\n\nimport 'package:flutter/material.dart';\n\nclass RotatingWidget extends StatefulWidget {\n  final Widget child;\n  const RotatingWidget({super.key, required this.child});\n\n  @override\n  State<RotatingWidget> createState() => _RotatingWidgetState();\n}\n\nclass _RotatingWidgetState extends State<RotatingWidget>\n    with SingleTickerProviderStateMixin {\n  late AnimationController _controller;\n\n  @override\n  void initState() {\n    super.initState();\n    _controller = AnimationController(\n      duration: const Duration(seconds: 1),\n      vsync: this,\n    )..repeat();\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return AnimatedBuilder(\n      animation: _controller,\n      builder: (_, child) {\n        return Transform.rotate(\n          angle: _controller.value * 2 * pi,\n          child: child,\n        );\n      },\n      child: widget.child,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/select_mode_toolbar.dart",
    "content": "import 'package:askaide/bloc/chat_message_bloc.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/chat/chat_preview.dart';\nimport 'package:askaide/page/component/chat/chat_share.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/model/message.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:provider/provider.dart';\n\nclass SelectModeToolbar extends StatefulWidget {\n  final ChatPreviewController chatPreviewController;\n  const SelectModeToolbar({super.key, required this.chatPreviewController});\n\n  @override\n  State<SelectModeToolbar> createState() => _SelectModeToolbarState();\n}\n\nclass _SelectModeToolbarState extends State<SelectModeToolbar> {\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return Container(\n      padding: const EdgeInsets.all(10),\n      decoration: BoxDecoration(\n        borderRadius: const BorderRadius.only(topLeft: CustomSize.radius, topRight: CustomSize.radius),\n        color: customColors.backgroundColor,\n      ),\n      child: SafeArea(\n        child: Row(\n          mainAxisAlignment: MainAxisAlignment.spaceAround,\n          children: [\n            TextButton.icon(\n              onPressed: () {\n                var messages = widget.chatPreviewController.selectedMessages();\n                if (messages.isEmpty) {\n                  showErrorMessageEnhanced(context, AppLocale.noMessageSelected.getString(context));\n                  return;\n                }\n\n                Navigator.push(\n                  context,\n                  MaterialPageRoute(\n                    fullscreenDialog: true,\n                    builder: (context) => ChatShareScreen(\n                      messages: messages\n                          .map((e) => ChatShareMessage(\n                                content: e.message.text,\n                                username: e.message.senderName,\n                                avatarURL: e.message.avatarUrl,\n                                leftSide: e.message.role == Role.receiver,\n                                images: e.message.images,\n                              ))\n                          .toList(),\n                    ),\n                  ),\n                );\n                // var messages = chatPreviewController.selectedMessages();\n                // if (messages.isEmpty) {\n                //   showErrorMessageEnhanced(\n                //       context, AppLocale.noMessageSelected.getString(context));\n                //   return;\n                // }\n                // var shareText = messages.map((e) {\n                //   if (e.message.role == Role.sender) {\n                //     return '我：\\n${e.message.text}';\n                //   }\n\n                //   return '助理：\\n${e.message.text}';\n                // }).join('\\n\\n');\n\n                // shareTo(\n                //   context,\n                //   content: shareText,\n                //   title: AppLocale.chatHistory.getString(context),\n                // );\n              },\n              icon: Icon(Icons.share, color: customColors.linkColor),\n              label: Text(\n                AppLocale.share.getString(context),\n                style: TextStyle(color: customColors.linkColor),\n              ),\n            ),\n            TextButton.icon(\n              onPressed: () {\n                widget.chatPreviewController.selectAllMessage();\n              },\n              icon: Icon(Icons.select_all_outlined, color: customColors.linkColor),\n              label: Text(\n                AppLocale.selectAll.getString(context),\n                style: TextStyle(color: customColors.linkColor),\n              ),\n            ),\n            TextButton.icon(\n              onPressed: () {\n                if (widget.chatPreviewController.selectedMessageIds.isEmpty) {\n                  showErrorMessageEnhanced(context, AppLocale.noMessageSelected.getString(context));\n                  return;\n                }\n\n                openConfirmDialog(\n                  context,\n                  AppLocale.confirmDelete.getString(context),\n                  () {\n                    final ids = widget.chatPreviewController.selectedMessageIds.toList();\n                    if (ids.isNotEmpty) {\n                      context.read<ChatMessageBloc>().add(ChatMessageDeleteEvent(ids));\n\n                      showErrorMessageEnhanced(context, AppLocale.operateSuccess.getString(context));\n\n                      widget.chatPreviewController.exitSelectMode();\n                    }\n                  },\n                  danger: true,\n                );\n              },\n              icon: Icon(Icons.delete, color: customColors.linkColor),\n              label: Text(\n                AppLocale.delete.getString(context),\n                style: TextStyle(color: customColors.linkColor),\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/share.dart",
    "content": "import 'dart:io';\n\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:fluwx/fluwx.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:share_plus/share_plus.dart';\n\nFuture<void> shareTo(\n  BuildContext context, {\n  required String content,\n  String? title,\n  List<String>? images,\n}) async {\n  Rect? sharePositionOrigin;\n\n  try {\n    final box = context.findRenderObject() as RenderBox?;\n    Rect? pos = box!.localToGlobal(Offset.zero) & box.size;\n    sharePositionOrigin = pos;\n    // ignore: empty_catches\n  } catch (ignored) {}\n\n  if ((PlatformTool.isIOS() || PlatformTool.isAndroid()) && await isWeChatInstalled) {\n    openModalBottomSheet(\n      // ignore: use_build_context_synchronously\n      context,\n      (context) => Column(\n        mainAxisAlignment: MainAxisAlignment.center,\n        crossAxisAlignment: CrossAxisAlignment.center,\n        children: [\n          Row(\n            mainAxisAlignment: MainAxisAlignment.spaceAround,\n            crossAxisAlignment: CrossAxisAlignment.center,\n            children: [\n              IconButton(\n                onPressed: () {\n                  final model = images == null || images.isEmpty\n                      ? WeChatShareTextModel(\n                          content,\n                          title: title,\n                          scene: WeChatScene.TIMELINE,\n                        )\n                      : WeChatShareImageModel(\n                          images.first.startsWith('http')\n                              ? WeChatImage.network(images.first)\n                              : WeChatImage.file(File(images.first)),\n                          title: title,\n                          description: content,\n                          scene: WeChatScene.TIMELINE,\n                        );\n\n                  shareToWeChat(model).whenComplete(() => context.pop());\n                },\n                icon: Column(\n                  crossAxisAlignment: CrossAxisAlignment.center,\n                  children: [\n                    Image.asset('assets/friendroom.png', width: 40),\n                    const SizedBox(height: 10),\n                    Text(\n                      AppLocale.shareToWechatQ.getString(context),\n                      style: const TextStyle(fontSize: 12),\n                    ),\n                  ],\n                ),\n              ),\n              IconButton(\n                onPressed: () {\n                  final model = images == null || images.isEmpty\n                      ? WeChatShareTextModel(\n                          content,\n                          title: title,\n                        )\n                      : WeChatShareImageModel(\n                          images.first.startsWith('http')\n                              ? WeChatImage.network(images.first)\n                              : WeChatImage.file(File(images.first)),\n                          title: title,\n                          description: content,\n                        );\n\n                  shareToWeChat(model).whenComplete(() => context.pop());\n                },\n                icon: Column(\n                  crossAxisAlignment: CrossAxisAlignment.center,\n                  children: [\n                    Image.asset('assets/wechat.png', width: 40),\n                    const SizedBox(height: 10),\n                    Text(\n                      AppLocale.shareToWechat.getString(context),\n                      style: const TextStyle(fontSize: 12),\n                    ),\n                  ],\n                ),\n              ),\n              IconButton(\n                onPressed: () {\n                  if (images != null && images.isNotEmpty) {\n                    Share.shareXFiles(\n                      [XFile(images.first)],\n                      subject: title,\n                      sharePositionOrigin: sharePositionOrigin,\n                    ).whenComplete(() => context.pop());\n                  } else {\n                    Share.share(\n                      content,\n                      subject: title,\n                      sharePositionOrigin: sharePositionOrigin,\n                    ).whenComplete(() => context.pop());\n                  }\n                },\n                icon: Column(\n                  crossAxisAlignment: CrossAxisAlignment.center,\n                  children: [\n                    Image.asset('assets/share.png', width: 40),\n                    const SizedBox(height: 10),\n                    Text(\n                      AppLocale.shareToOtherApps.getString(context),\n                      style: const TextStyle(fontSize: 12),\n                    ),\n                  ],\n                ),\n              ),\n            ],\n          ),\n        ],\n      ),\n      heightFactor: 0.25,\n    );\n  } else {\n    if (images != null && images.isNotEmpty) {\n      Share.shareXFiles(\n        [XFile(images.first)],\n        subject: title,\n        sharePositionOrigin: sharePositionOrigin,\n      );\n    } else {\n      Share.share(\n        content,\n        subject: title,\n        sharePositionOrigin: sharePositionOrigin,\n      );\n    }\n  }\n}\n"
  },
  {
    "path": "lib/page/component/sliver_component.dart",
    "content": "import 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\n\nclass SliverSingleComponent extends StatelessWidget {\n  final Widget? title;\n  final Widget? backgroundImage;\n  final List<Widget>? actions;\n  final double expendedHeight;\n  final List<Widget> Function() appBarExtraWidgets;\n  final EdgeInsets? titlePadding;\n  final bool centerTitle;\n\n  const SliverSingleComponent({\n    super.key,\n    required this.title,\n    this.backgroundImage,\n    this.actions,\n    this.expendedHeight = 80,\n    required this.appBarExtraWidgets,\n    this.titlePadding,\n    this.centerTitle = true,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return CustomScrollView(\n      slivers: [\n        SliverAppBar(\n          automaticallyImplyLeading: false,\n          expandedHeight: expendedHeight,\n          floating: false,\n          pinned: true,\n          snap: false,\n          primary: true,\n          actions: (actions ?? []).isEmpty ? null : <Widget>[...actions!, const SizedBox(width: 8)],\n          backgroundColor: customColors.backgroundContainerColor,\n          flexibleSpace: FlexibleSpaceBar(\n            title: title,\n            centerTitle: centerTitle,\n            titlePadding: titlePadding,\n            background: ShaderMask(\n              shaderCallback: (rect) {\n                return const LinearGradient(\n                  begin: Alignment.topCenter,\n                  end: Alignment.bottomCenter,\n                  colors: [Colors.black, Colors.transparent],\n                ).createShader(Rect.fromLTRB(0, 0, rect.width, rect.height));\n              },\n              blendMode: BlendMode.dstIn,\n              child: backgroundImage,\n            ),\n            expandedTitleScale: 1.1,\n          ),\n        ),\n        ...appBarExtraWidgets(),\n      ],\n    );\n  }\n}\n\nclass SliverComponent extends StatelessWidget {\n  final Widget? title;\n  final Widget? backgroundImage;\n  final List<Widget>? actions;\n  final double expendedHeight;\n  final Widget child;\n  final List<Widget> Function(bool innerBoxIsScrolled)? appBarExtraWidgets;\n  final EdgeInsets? titlePadding;\n  final bool centerTitle;\n  const SliverComponent({\n    super.key,\n    required this.title,\n    this.backgroundImage,\n    this.actions,\n    this.expendedHeight = 80,\n    required this.child,\n    this.appBarExtraWidgets,\n    this.titlePadding,\n    this.centerTitle = true,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return NestedScrollView(\n      headerSliverBuilder: (context, innerBoxIsScrolled) {\n        return [\n          SliverAppBar(\n            automaticallyImplyLeading: true,\n            toolbarHeight: CustomSize.toolbarHeight,\n            expandedHeight: expendedHeight,\n            floating: false,\n            pinned: true,\n            snap: false,\n            primary: true,\n            actions: (actions ?? []).isEmpty ? null : <Widget>[...actions!, const SizedBox(width: 8)],\n            backgroundColor: backgroundImage != null ? Colors.transparent : customColors.backgroundColor,\n            flexibleSpace: FlexibleSpaceBar(\n              title: title,\n              centerTitle: centerTitle,\n              titlePadding: titlePadding,\n              background: ShaderMask(\n                shaderCallback: (rect) {\n                  return const LinearGradient(\n                    begin: Alignment.topCenter,\n                    end: Alignment.bottomCenter,\n                    colors: [Colors.black, Colors.transparent],\n                  ).createShader(Rect.fromLTRB(0, 0, rect.width, rect.height));\n                },\n                blendMode: BlendMode.dstIn,\n                child: backgroundImage,\n              ),\n              expandedTitleScale: 1.1,\n            ),\n          ),\n          if (appBarExtraWidgets != null) ...appBarExtraWidgets!(innerBoxIsScrolled),\n        ];\n      },\n      body: child,\n    );\n  }\n}\n\nclass SliverTabComponent extends StatelessWidget {\n  final List<String> tabBarTitles;\n  final Widget? title;\n  final String? backgroundImageUrl;\n  final List<Widget>? actions;\n  final int crossAxisCount;\n  final double childAspectRatio;\n  final double expendedHeight;\n\n  final List<Widget> Function(BuildContext context, String tabName) itemsBuilder;\n\n  const SliverTabComponent({\n    super.key,\n    required this.tabBarTitles,\n    this.title,\n    this.backgroundImageUrl,\n    this.actions,\n    required this.crossAxisCount,\n    this.childAspectRatio = 1,\n    this.expendedHeight = 80,\n    required this.itemsBuilder,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return DefaultTabController(\n      length: tabBarTitles.length,\n      child: NestedScrollView(\n        headerSliverBuilder: (context, innerBoxIsScrolled) {\n          return [\n            Theme(\n              data: Theme.of(context).copyWith(\n                colorScheme: Theme.of(context).colorScheme.copyWith(surfaceContainerHighest: Colors.transparent),\n              ),\n              child: SliverOverlapAbsorber(\n                handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),\n                sliver: SliverAppBar(\n                  title: title,\n                  backgroundColor: innerBoxIsScrolled ? customColors.backgroundColor : null,\n                  centerTitle: true,\n                  pinned: true,\n                  floating: true,\n                  snap: false,\n                  // primary: false,\n                  expandedHeight: expendedHeight,\n                  elevation: 0,\n                  forceElevated: innerBoxIsScrolled,\n                  flexibleSpace: FlexibleSpaceBar(\n                    background: ShaderMask(\n                      shaderCallback: (rect) {\n                        return const LinearGradient(\n                          begin: Alignment.topCenter,\n                          end: Alignment.bottomCenter,\n                          colors: [Colors.black, Colors.transparent],\n                        ).createShader(Rect.fromLTRB(0, 0, rect.width, rect.height));\n                      },\n                      blendMode: BlendMode.dstIn,\n                      child: backgroundImageUrl != null && backgroundImageUrl!.isNotEmpty\n                          ? CachedNetworkImageEnhanced(\n                              imageUrl: backgroundImageUrl!,\n                              fit: BoxFit.cover,\n                            )\n                          : Image.asset(\n                              'assets/background.webp',\n                              fit: BoxFit.cover,\n                            ),\n                    ),\n                    expandedTitleScale: 1.2,\n                  ),\n                  actions: actions,\n                  bottom: TabBar(\n                    tabs: tabBarTitles.map((e) => Tab(text: e)).toList(),\n                    isScrollable: true,\n                    labelColor: customColors.linkColor,\n                    indicator: const BoxDecoration(),\n                    overlayColor: WidgetStateProperty.all(Colors.transparent),\n                  ),\n                ),\n              ),\n            ),\n          ];\n        },\n        body: TabBarView(\n          children: tabBarTitles.map(\n            (e) {\n              return Builder(\n                builder: (context) {\n                  final items = itemsBuilder(context, e);\n                  return CustomScrollView(\n                    key: PageStorageKey<String>(e),\n                    slivers: [\n                      SliverOverlapInjector(\n                        handle: NestedScrollView.sliverOverlapAbsorberHandleFor(\n                          context,\n                        ),\n                      ),\n                      SliverPadding(\n                        padding: const EdgeInsets.all(8),\n                        sliver: SliverGrid(\n                          delegate: SliverChildBuilderDelegate(\n                            (BuildContext context, int index) {\n                              return items[index];\n                            },\n                            childCount: items.length, //内部控件数量\n                          ),\n                          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(\n                            crossAxisCount: crossAxisCount,\n                            crossAxisSpacing: 5,\n                            mainAxisSpacing: 10,\n                            childAspectRatio: childAspectRatio,\n                          ),\n                        ),\n                      ),\n                    ],\n                  );\n                },\n              );\n            },\n          ).toList(),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/social_icon.dart",
    "content": "import 'package:askaide/helper/platform.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:settings_ui/settings_ui.dart';\nimport 'package:url_launcher/url_launcher_string.dart';\n\nclass SocialItem {\n  final String image;\n  final String name;\n  final Function? onTap;\n  final bool nonIOS;\n\n  const SocialItem({\n    required this.image,\n    required this.name,\n    required this.onTap,\n    this.nonIOS = false,\n  });\n}\n\nclass SocialIconGroup extends StatelessWidget {\n  final bool isSettingTiles;\n\n  final List<SocialItem> items = [\n    SocialItem(\n      image: 'assets/app-256-transparent.png',\n      name: '官方网站',\n      nonIOS: true,\n      onTap: () {\n        launchUrlString(\n          'https://ai.aicode.cc/social/home',\n          mode: LaunchMode.externalApplication,\n        );\n      },\n    ),\n    SocialItem(\n      image: 'assets/weibo.png',\n      name: '新浪微博',\n      onTap: () {\n        launchUrlString(\n          'https://ai.aicode.cc/social/weibo',\n          mode: LaunchMode.externalApplication,\n        );\n      },\n    ),\n    SocialItem(\n      image: 'assets/wechat.png',\n      name: '微信公众号',\n      onTap: () {\n        launchUrlString(\n          'https://ai.aicode.cc/social/wechat-platform',\n          mode: LaunchMode.externalApplication,\n        );\n      },\n    ),\n    SocialItem(\n      image: 'assets/x.png',\n      name: 'Twitter(X)',\n      onTap: () {\n        launchUrlString(\n          'https://ai.aicode.cc/social/x',\n          mode: LaunchMode.externalApplication,\n        );\n      },\n    ),\n    SocialItem(\n      image: 'assets/github.png',\n      name: 'Github',\n      onTap: () {\n        launchUrlString(\n          'https://ai.aicode.cc/social/github',\n          mode: LaunchMode.externalApplication,\n        );\n      },\n    ),\n    SocialItem(\n      image: 'assets/xiaohongshu.png',\n      name: '小红书',\n      onTap: () {\n        launchUrlString(\n          'https://ai.aicode.cc/social/xiaohongshu',\n          mode: LaunchMode.externalApplication,\n        );\n      },\n    ),\n  ];\n\n  SocialIconGroup({\n    super.key,\n    this.isSettingTiles = false,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    if (isSettingTiles) {\n      return SettingsSection(\n        title: Text(AppLocale.socialMedia.getString(context)),\n        tiles: items\n            .where((e) {\n              if (e.nonIOS) {\n                return !PlatformTool.isIOS();\n              }\n\n              return true;\n            })\n            .map(\n              (e) => SettingsTile(\n                title: Row(children: [\n                  Image.asset(e.image, width: 20, height: 20),\n                  const SizedBox(width: 10),\n                  Text(e.name),\n                ]),\n                trailing: const Icon(\n                  CupertinoIcons.chevron_forward,\n                  size: 18,\n                  color: Colors.grey,\n                ),\n                onPressed: (context) {\n                  e.onTap?.call();\n                },\n              ),\n            )\n            .toList(),\n      );\n    }\n\n    return Column(\n      mainAxisAlignment: MainAxisAlignment.start,\n      children: items.map((e) => SocialIcon(image: e.image, name: e.name, onTap: e.onTap)).toList(),\n    );\n  }\n}\n\nclass SocialIcon extends StatelessWidget {\n  final String image;\n  final String name;\n  final Function? onTap;\n  const SocialIcon({\n    super.key,\n    required this.image,\n    required this.name,\n    required this.onTap,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return GestureDetector(\n      onTap: () {\n        onTap?.call();\n      },\n      child: Column(\n        children: [\n          Image.asset(image, width: 25),\n          const SizedBox(height: 5),\n          Text(\n            name,\n            style: const TextStyle(fontSize: 8),\n          )\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/take_photo.dart",
    "content": "import 'dart:io';\n\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:flutter/material.dart';\nimport 'package:camera/camera.dart';\n\nclass TakePhoto extends StatefulWidget {\n  const TakePhoto({super.key});\n\n  @override\n  State<TakePhoto> createState() => _TakePhotoState();\n}\n\nclass _TakePhotoState extends State<TakePhoto> with WidgetsBindingObserver {\n  CameraController? _cameraController;\n  late Future<void> _initializeControllerFuture;\n\n  @override\n  void didChangeAppLifecycleState(AppLifecycleState state) {\n    final CameraController? cameraController = _cameraController;\n\n    // App state changed before we got the chance to initialize.\n    if (cameraController == null || !cameraController.value.isInitialized) {\n      return;\n    }\n\n    if (state == AppLifecycleState.inactive) {\n      cameraController.dispose();\n    } else if (state == AppLifecycleState.resumed) {\n      _initializeCameraController(cameraController.description);\n    }\n  }\n\n  Future<void> _initializeCameraController(CameraDescription cameraDescription) async {\n    final CameraController cameraController = CameraController(\n      cameraDescription,\n      PlatformTool.isWeb() ? ResolutionPreset.max : ResolutionPreset.medium,\n      enableAudio: false,\n      imageFormatGroup: ImageFormatGroup.jpeg,\n    );\n\n    _cameraController = cameraController;\n\n    // If the controller is updated then update the UI.\n    cameraController.addListener(() {\n      if (mounted) {\n        setState(() {});\n      }\n      if (cameraController.value.hasError) {\n        showErrorMessage('Camera error ${cameraController.value.errorDescription}');\n      }\n    });\n\n    try {\n      _initializeControllerFuture = cameraController.initialize();\n      await _initializeControllerFuture;\n    } on CameraException catch (e) {\n      switch (e.code) {\n        case 'CameraAccessDenied':\n          showErrorMessage('You have denied camera access.');\n        case 'CameraAccessDeniedWithoutPrompt':\n          // iOS only\n          showErrorMessage('Please go to Settings app to enable camera access.');\n        case 'CameraAccessRestricted':\n          // iOS only\n          showErrorMessage('Camera access is restricted.');\n        case 'AudioAccessDenied':\n          showErrorMessage('You have denied audio access.');\n        case 'AudioAccessDeniedWithoutPrompt':\n          // iOS only\n          showErrorMessage('Please go to Settings app to enable audio access.');\n        case 'AudioAccessRestricted':\n          // iOS only\n          showErrorMessage('Audio access is restricted.');\n        default:\n          showErrorMessage('Camera error: ${e.code} ${e.description}');\n      }\n    }\n\n    if (mounted) {\n      setState(() {});\n    }\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    WidgetsBinding.instance.addObserver(this);\n\n    availableCameras().then((cameras) {\n      if (cameras.isNotEmpty) {\n        _initializeCameraController(cameras.first);\n      }\n    });\n  }\n\n  @override\n  void dispose() {\n    WidgetsBinding.instance.removeObserver(this);\n    _cameraController?.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: const Text('Take a picture')),\n      // You must wait until the controller is initialized before displaying the\n      // camera preview. Use a FutureBuilder to display a loading spinner until the\n      // controller has finished initializing.\n      body: FutureBuilder<void>(\n        future: _initializeControllerFuture,\n        builder: (context, snapshot) {\n          if (snapshot.connectionState == ConnectionState.done) {\n            // If the Future is complete, display the preview.\n            return CameraPreview(_cameraController!);\n          } else {\n            // Otherwise, display a loading indicator.\n            return const Center(child: CircularProgressIndicator());\n          }\n        },\n      ),\n      floatingActionButton: FloatingActionButton(\n        // Provide an onPressed callback.\n        onPressed: () async {\n          // Take the Picture in a try / catch block. If anything goes wrong,\n          // catch the error.\n          try {\n            // Ensure that the camera is initialized.\n            await _initializeControllerFuture;\n\n            // Attempt to take a picture and get the file `image`\n            // where it was saved.\n            final image = await _cameraController!.takePicture();\n\n            if (!context.mounted) return;\n\n            // If the picture was taken, display it on a new screen.\n            await Navigator.of(context).push(\n              MaterialPageRoute(\n                builder: (context) => DisplayPictureScreen(\n                  // Pass the automatically generated path to\n                  // the DisplayPictureScreen widget.\n                  imagePath: image.path,\n                ),\n              ),\n            );\n          } catch (e) {\n            // If an error occurs, log the error to the console.\n            print(e);\n          }\n        },\n        child: const Icon(Icons.camera_alt),\n      ),\n    );\n  }\n}\n\n// A widget that displays the picture taken by the user.\nclass DisplayPictureScreen extends StatelessWidget {\n  final String imagePath;\n\n  const DisplayPictureScreen({super.key, required this.imagePath});\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: const Text('Display the Picture')),\n      // The image is stored as a file on the device. Use the `Image.file`\n      // constructor with the given path to display the image.\n      body: Image.file(File(imagePath)),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/theme/custom_size.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass CustomSize {\n  static const double appBarTitleSize = 16;\n  static const double defaultHintTextSize = 16;\n  static const double maxWindowSize = 1000;\n  static const double smallWindowSize = 500;\n\n  static const double radiusValue = 8.0;\n\n  static BorderRadiusGeometry borderRadius = BorderRadius.circular(radiusValue);\n  static const Radius radius = Radius.circular(radiusValue);\n  static const BorderRadius borderRadiusAll = BorderRadius.all(radius);\n\n  static double get markdownTextSize {\n    return 16;\n  }\n\n  static double get markdownCodeSize {\n    return 14;\n  }\n\n  static double get toolbarHeight {\n    return kToolbarHeight;\n  }\n\n  static double adaptiveMaxWindowWidth(BuildContext context) {\n    final windowSize = MediaQuery.of(context).size.width;\n    return windowSize > CustomSize.maxWindowSize ? CustomSize.maxWindowSize : windowSize;\n  }\n}\n"
  },
  {
    "path": "lib/page/component/theme/custom_theme.dart",
    "content": "import 'dart:ui';\n\nimport 'package:flutter/material.dart';\n\nclass CustomColors extends ThemeExtension<CustomColors> {\n  const CustomColors({\n    this.chatRoomBackground,\n    this.chatRoomReplyBackground,\n    this.chatRoomReplyBackgroundSecondary,\n    this.chatRoomReplyText,\n    this.chatRoomSenderBackground,\n    this.chatRoomSenderBackgroundSecondary,\n    this.chatRoomSenderBackgroundWarning,\n    this.chatRoomSenderText,\n    this.tagsBackground,\n    this.tagsBackgroundHover,\n    this.tagsText,\n    this.chatInputPanelBackground,\n    this.chatInputPanelText,\n    this.chatInputAreaBackground,\n    this.chatExampleItemBackground,\n    this.chatExampleItemBackgroundHover,\n    this.chatExampleItemText,\n    this.chatExampleTitleText,\n    this.markdownLinkColor,\n    this.markdownPreColor,\n    this.markdownCodeColor,\n    this.boxShadowColor,\n    this.backgroundColor,\n    this.backgroundInvertedColor,\n    this.backgroundContainerColor,\n    this.backgroundForDialogListItem,\n    this.listTileBackgroundColor,\n    this.textFieldBorderColor,\n    this.iconButtonColor,\n    this.linkColor,\n    this.weakLinkColor,\n    this.weakTextColor,\n    this.weakTextColorLess,\n    this.weakTextColorPlus,\n    this.weakTextColorPlusPlus,\n    this.dialogDefaultTextColor,\n    this.dialogBackgroundColor,\n    this.columnBlockBorderColor,\n    this.columnBlockBackgroundColor,\n    this.columnBlockDividerColor,\n    this.textfieldHintColor,\n    this.textfieldHintDeepColor,\n    this.textfieldLabelColor,\n    this.textfieldValueColor,\n    this.textfieldBackgroundColor,\n    this.textfieldSelectorColor,\n    this.paymentItemBorderColor,\n    this.paymentItemBackgroundColor,\n    this.paymentItemTitleColor,\n    this.paymentItemPriceColor,\n    this.paymentItemDateColor,\n    this.paymentItemDescriptionColor,\n    this.settingsSectionBackground,\n  });\n\n  final Color? chatRoomBackground;\n  final Color? chatRoomReplyBackground;\n  final Color? chatRoomReplyBackgroundSecondary;\n  final Color? chatRoomReplyText;\n  final Color? chatRoomSenderBackground;\n  final Color? chatRoomSenderBackgroundSecondary;\n  final Color? chatRoomSenderBackgroundWarning;\n  final Color? chatRoomSenderText;\n  final Color? tagsBackground;\n  final Color? tagsBackgroundHover;\n  final Color? tagsText;\n\n  final Color? chatInputPanelBackground;\n  final Color? chatInputPanelText;\n  final Color? chatInputAreaBackground;\n\n  final Color? chatExampleItemBackground;\n  final Color? chatExampleItemBackgroundHover;\n\n  final Color? chatExampleItemText;\n  final Color? chatExampleTitleText;\n\n  final Color? markdownLinkColor;\n  final Color? markdownPreColor;\n  final Color? markdownCodeColor;\n\n  final Color? boxShadowColor;\n  final Color? backgroundColor;\n  final Color? backgroundInvertedColor;\n  final Color? backgroundContainerColor;\n  final Color? backgroundForDialogListItem;\n\n  final Color? listTileBackgroundColor;\n\n  final Color? textFieldBorderColor;\n  final Color? iconButtonColor;\n\n  final Color? linkColor;\n  final Color? weakLinkColor;\n  final Color? weakTextColor;\n  final Color? weakTextColorLess;\n  final Color? weakTextColorPlus;\n  final Color? weakTextColorPlusPlus;\n\n  final Color? dialogDefaultTextColor;\n  final Color? dialogBackgroundColor;\n\n  final Color? columnBlockBorderColor;\n  final Color? columnBlockBackgroundColor;\n  final Color? columnBlockDividerColor;\n\n  final Color? textfieldHintColor;\n  final Color? textfieldHintDeepColor;\n  final Color? textfieldLabelColor;\n  final Color? textfieldValueColor;\n  final Color? textfieldBackgroundColor;\n  final Color? textfieldSelectorColor;\n\n  final Color? paymentItemBorderColor;\n  final Color? paymentItemBackgroundColor;\n  final Color? paymentItemTitleColor;\n  final Color? paymentItemPriceColor;\n  final Color? paymentItemDateColor;\n  final Color? paymentItemDescriptionColor;\n\n  final Color? settingsSectionBackground;\n\n  @override\n  ThemeExtension<CustomColors> lerp(\n    covariant ThemeExtension<CustomColors>? other,\n    double t,\n  ) {\n    if (other is! CustomColors) {\n      return this;\n    }\n\n    return CustomColors(\n      chatRoomBackground: Color.lerp(chatRoomBackground, other.chatRoomBackground, t),\n      chatRoomReplyBackground: Color.lerp(chatRoomReplyBackground, other.chatRoomReplyBackground, t),\n      chatRoomReplyBackgroundSecondary:\n          Color.lerp(chatRoomReplyBackgroundSecondary, other.chatRoomReplyBackgroundSecondary, t),\n      chatRoomReplyText: Color.lerp(chatRoomReplyText, other.chatRoomReplyText, t),\n      chatRoomSenderBackground: Color.lerp(chatRoomSenderBackground, other.chatRoomSenderBackground, t),\n      chatRoomSenderBackgroundSecondary:\n          Color.lerp(chatRoomSenderBackgroundSecondary, other.chatRoomSenderBackgroundSecondary, t),\n      chatRoomSenderBackgroundWarning:\n          Color.lerp(chatRoomSenderBackgroundWarning, other.chatRoomSenderBackgroundWarning, t),\n      chatRoomSenderText: Color.lerp(chatRoomSenderText, other.chatRoomSenderText, t),\n      tagsBackground: Color.lerp(tagsBackground, other.tagsBackground, t),\n      tagsBackgroundHover: Color.lerp(tagsBackgroundHover, other.tagsBackgroundHover, t),\n      tagsText: Color.lerp(tagsText, other.tagsText, t),\n      chatInputPanelBackground: Color.lerp(chatInputPanelBackground, other.chatInputPanelBackground, t),\n      chatInputPanelText: Color.lerp(chatInputPanelText, other.chatInputPanelText, t),\n      chatInputAreaBackground: Color.lerp(chatInputAreaBackground, other.chatInputAreaBackground, t),\n      chatExampleItemBackground: Color.lerp(chatExampleItemBackground, other.chatExampleItemBackground, t),\n      chatExampleItemBackgroundHover:\n          Color.lerp(chatExampleItemBackgroundHover, other.chatExampleItemBackgroundHover, t),\n      chatExampleItemText: Color.lerp(chatExampleItemText, other.chatExampleItemText, t),\n      chatExampleTitleText: Color.lerp(chatExampleTitleText, other.chatExampleTitleText, t),\n      markdownLinkColor: Color.lerp(markdownLinkColor, other.markdownLinkColor, t),\n      markdownPreColor: Color.lerp(markdownPreColor, other.markdownPreColor, t),\n      markdownCodeColor: Color.lerp(markdownCodeColor, other.markdownCodeColor, t),\n      boxShadowColor: Color.lerp(boxShadowColor, other.boxShadowColor, t),\n      backgroundColor: Color.lerp(backgroundColor, other.backgroundColor, t),\n      backgroundInvertedColor: Color.lerp(backgroundInvertedColor, other.backgroundInvertedColor, t),\n      backgroundContainerColor: Color.lerp(backgroundContainerColor, other.backgroundContainerColor, t),\n      backgroundForDialogListItem: Color.lerp(backgroundForDialogListItem, other.backgroundForDialogListItem, t),\n      listTileBackgroundColor: Color.lerp(listTileBackgroundColor, other.listTileBackgroundColor, t),\n      textFieldBorderColor: Color.lerp(textFieldBorderColor, other.textFieldBorderColor, t),\n      iconButtonColor: Color.lerp(iconButtonColor, other.iconButtonColor, t),\n      weakLinkColor: Color.lerp(weakLinkColor, other.weakLinkColor, t),\n      weakTextColor: Color.lerp(weakTextColor, other.weakTextColor, t),\n      weakTextColorLess: Color.lerp(weakTextColorLess, other.weakTextColorLess, t),\n      weakTextColorPlus: Color.lerp(weakTextColorPlus, other.weakTextColorPlus, t),\n      weakTextColorPlusPlus: Color.lerp(weakTextColorPlusPlus, other.weakTextColorPlusPlus, t),\n      dialogDefaultTextColor: Color.lerp(dialogDefaultTextColor, other.dialogDefaultTextColor, t),\n      dialogBackgroundColor: Color.lerp(dialogBackgroundColor, other.dialogBackgroundColor, t),\n      columnBlockBorderColor: Color.lerp(columnBlockBorderColor, other.columnBlockBorderColor, t),\n      columnBlockBackgroundColor: Color.lerp(columnBlockBackgroundColor, other.columnBlockBackgroundColor, t),\n      columnBlockDividerColor: Color.lerp(columnBlockDividerColor, other.columnBlockDividerColor, t),\n      textfieldHintColor: Color.lerp(textfieldHintColor, other.textfieldHintColor, t),\n      textfieldHintDeepColor: Color.lerp(textfieldHintDeepColor, other.textfieldHintDeepColor, t),\n      textfieldLabelColor: Color.lerp(textfieldLabelColor, other.textfieldLabelColor, t),\n      textfieldValueColor: Color.lerp(textfieldValueColor, other.textfieldValueColor, t),\n      textfieldBackgroundColor: Color.lerp(textfieldBackgroundColor, other.textfieldBackgroundColor, t),\n      textfieldSelectorColor: Color.lerp(textfieldSelectorColor, other.textfieldSelectorColor, t),\n      paymentItemBorderColor: Color.lerp(paymentItemBorderColor, other.paymentItemBorderColor, t),\n      paymentItemBackgroundColor: Color.lerp(paymentItemBackgroundColor, other.paymentItemBackgroundColor, t),\n      paymentItemTitleColor: Color.lerp(paymentItemTitleColor, other.paymentItemTitleColor, t),\n      paymentItemPriceColor: Color.lerp(paymentItemPriceColor, other.paymentItemPriceColor, t),\n      paymentItemDateColor: Color.lerp(paymentItemDateColor, other.paymentItemDateColor, t),\n      paymentItemDescriptionColor: Color.lerp(paymentItemDescriptionColor, other.paymentItemDescriptionColor, t),\n      settingsSectionBackground: Color.lerp(settingsSectionBackground, other.settingsSectionBackground, t),\n    );\n  }\n\n  static const light = CustomColors(\n    chatRoomBackground: Color.fromARGB(255, 239, 239, 239),\n    chatRoomReplyBackground: Colors.transparent,\n    chatRoomReplyBackgroundSecondary: Color.fromARGB(200, 255, 255, 255),\n    chatRoomReplyText: Color(0xFF000000),\n    chatRoomSenderBackground: Color.fromARGB(255, 242, 242, 242),\n    chatRoomSenderBackgroundSecondary: Color.fromARGB(255, 133, 238, 94),\n    chatRoomSenderBackgroundWarning: Color.fromARGB(255, 255, 176, 131),\n    chatRoomSenderText: Color(0xFF000000),\n    tagsBackground: Color.fromARGB(255, 238, 238, 238),\n    tagsBackgroundHover: Color.fromARGB(255, 237, 237, 237),\n    tagsText: Colors.black,\n    chatInputPanelBackground: Colors.transparent,\n    chatInputPanelText: Color.fromARGB(255, 0, 0, 0),\n    chatInputAreaBackground: Color.fromARGB(255, 255, 255, 255),\n    chatExampleItemBackground: Color.fromARGB(194, 221, 221, 221),\n    chatExampleItemBackgroundHover: Color.fromARGB(255, 223, 223, 223),\n    chatExampleItemText: Color.fromARGB(255, 255, 255, 255),\n    chatExampleTitleText: Color.fromARGB(255, 66, 66, 66),\n    markdownLinkColor: Colors.blue,\n    markdownPreColor: Color.fromARGB(255, 247, 247, 247),\n    markdownCodeColor: Color.fromARGB(255, 167, 100, 153),\n    boxShadowColor: Color.fromARGB(149, 232, 232, 232),\n    backgroundColor: Color.fromARGB(255, 242, 242, 242),\n    backgroundInvertedColor: Color.fromARGB(255, 72, 72, 72),\n    backgroundContainerColor: Color.fromARGB(255, 255, 255, 255),\n    backgroundForDialogListItem: Color.fromARGB(255, 255, 255, 255),\n    listTileBackgroundColor: Color.fromARGB(60, 217, 217, 217),\n    textFieldBorderColor: Color.fromARGB(255, 228, 228, 228),\n    iconButtonColor: Color.fromARGB(255, 117, 117, 117),\n    linkColor: Color.fromARGB(255, 9, 185, 85),\n    weakLinkColor: Color.fromARGB(255, 75, 75, 75),\n    weakTextColor: Color.fromARGB(255, 75, 75, 75),\n    weakTextColorLess: Color.fromARGB(255, 146, 146, 146),\n    weakTextColorPlus: Color.fromARGB(255, 146, 146, 146),\n    weakTextColorPlusPlus: Color.fromARGB(255, 29, 29, 29),\n    dialogDefaultTextColor: Color.fromARGB(195, 0, 0, 0),\n    dialogBackgroundColor: Colors.white,\n    columnBlockBorderColor: Color.fromARGB(255, 236, 236, 236),\n    columnBlockBackgroundColor: Color.fromARGB(255, 255, 255, 255),\n    columnBlockDividerColor: Color.fromARGB(255, 241, 241, 241),\n    textfieldHintColor: Color.fromARGB(255, 181, 181, 181),\n    textfieldHintDeepColor: Color.fromARGB(255, 94, 94, 94),\n    textfieldLabelColor: Color.fromARGB(255, 66, 66, 66),\n    textfieldValueColor: Color.fromARGB(255, 108, 108, 108),\n    textfieldBackgroundColor: Color.fromARGB(255, 230, 230, 230),\n    textfieldSelectorColor: Color.fromARGB(255, 9, 185, 85),\n    paymentItemBorderColor: Color.fromARGB(255, 228, 228, 228),\n    paymentItemBackgroundColor: Color.fromARGB(255, 245, 245, 245),\n    paymentItemTitleColor: Color.fromARGB(255, 66, 66, 66),\n    paymentItemPriceColor: Color.fromARGB(255, 66, 66, 66),\n    paymentItemDateColor: Color.fromARGB(255, 117, 117, 117),\n    paymentItemDescriptionColor: Color.fromARGB(255, 117, 117, 117),\n    settingsSectionBackground: Color.fromARGB(255, 255, 255, 255),\n  );\n\n  static const dark = CustomColors(\n    chatRoomBackground: Color.fromARGB(255, 0, 0, 0),\n    chatRoomReplyBackground: Colors.transparent,\n    chatRoomReplyBackgroundSecondary: Color.fromARGB(200, 39, 39, 39),\n    chatRoomReplyText: Color(0xFFECEFF1),\n    chatRoomSenderBackground: Color.fromARGB(255, 33, 33, 33),\n    chatRoomSenderBackgroundSecondary: Color.fromARGB(181, 36, 172, 86),\n    chatRoomSenderBackgroundWarning: Color.fromARGB(255, 255, 176, 131),\n    chatRoomSenderText: Color(0xFFECEFF1),\n    tagsBackground: Color.fromARGB(255, 69, 69, 69),\n    tagsBackgroundHover: Color.fromARGB(255, 106, 106, 106),\n    tagsText: Color.fromARGB(255, 218, 218, 218),\n    chatInputPanelBackground: Color.fromARGB(255, 0, 0, 0),\n    chatInputPanelText: Color.fromARGB(255, 255, 255, 255),\n    chatInputAreaBackground: Color.fromARGB(255, 32, 32, 32),\n    chatExampleItemBackground: Color.fromARGB(255, 80, 80, 80),\n    chatExampleItemBackgroundHover: Color.fromARGB(255, 69, 69, 69),\n    chatExampleItemText: Color.fromARGB(255, 218, 218, 218),\n    chatExampleTitleText: Color.fromARGB(255, 150, 150, 150),\n    markdownLinkColor: Color.fromARGB(255, 0, 122, 255),\n    markdownPreColor: Color.fromARGB(255, 16, 16, 16),\n    markdownCodeColor: Color.fromARGB(255, 179, 148, 173),\n    boxShadowColor: Color.fromARGB(70, 37, 37, 37),\n    backgroundColor: Color.fromARGB(255, 30, 30, 30),\n    backgroundInvertedColor: Color.fromARGB(255, 233, 233, 233),\n    backgroundContainerColor: Color.fromARGB(255, 0, 0, 0),\n    backgroundForDialogListItem: Color.fromARGB(23, 0, 0, 0),\n    listTileBackgroundColor: Color.fromARGB(25, 0, 0, 0),\n    textFieldBorderColor: Color.fromARGB(106, 107, 107, 107),\n    iconButtonColor: Color.fromARGB(255, 218, 218, 218),\n    linkColor: Color.fromARGB(255, 9, 185, 85),\n    weakLinkColor: Color.fromARGB(255, 218, 218, 218),\n    weakTextColor: Color.fromARGB(255, 130, 130, 130),\n    weakTextColorLess: Color.fromARGB(198, 146, 146, 146),\n    weakTextColorPlus: Color.fromARGB(255, 137, 137, 137),\n    weakTextColorPlusPlus: Color.fromARGB(255, 173, 173, 173),\n    dialogDefaultTextColor: Color.fromARGB(195, 255, 255, 255),\n    dialogBackgroundColor: Colors.black,\n    columnBlockBorderColor: Color.fromARGB(255, 72, 72, 72),\n    columnBlockBackgroundColor: Color.fromARGB(255, 44, 44, 46),\n    columnBlockDividerColor: Color.fromARGB(57, 60, 60, 60),\n    textfieldHintColor: Color.fromARGB(255, 105, 105, 105),\n    textfieldHintDeepColor: Color.fromARGB(255, 170, 170, 170),\n    textfieldLabelColor: Color.fromARGB(255, 218, 218, 218),\n    textfieldValueColor: Color.fromARGB(255, 207, 207, 207),\n    textfieldBackgroundColor: Color.fromARGB(255, 44, 44, 44),\n    textfieldSelectorColor: Color.fromARGB(255, 9, 185, 85),\n    paymentItemBorderColor: Color.fromARGB(255, 69, 69, 69),\n    paymentItemBackgroundColor: Color.fromARGB(255, 29, 29, 29),\n    paymentItemTitleColor: Color.fromARGB(255, 218, 218, 218),\n    paymentItemPriceColor: Color.fromARGB(255, 218, 218, 218),\n    paymentItemDateColor: Color.fromARGB(255, 218, 218, 218),\n    paymentItemDescriptionColor: Color.fromARGB(255, 218, 218, 218),\n    settingsSectionBackground: Color.fromARGB(255, 44, 44, 46),\n  );\n\n  @override\n  ThemeExtension<CustomColors> copyWith({\n    Color? chatRoomBackground,\n    Color? chatRoomReplyBackground,\n    Color? chatRoomReplyBackgroundSecondary,\n    Color? chatRoomReplyText,\n    Color? chatRoomSenderBackground,\n    Color? chatRoomSenderBackgroundSecondary,\n    Color? chatRoomSenderBackgroundWarning,\n    Color? chatRoomSenderText,\n    Color? tagsBackground,\n    Color? tagsBackgroundHover,\n    Color? tagsText,\n    Color? chatInputPanelBackground,\n    Color? chatInputPanelText,\n    Color? chatInputAreaBackground,\n    Color? chatExampleItemBackground,\n    Color? chatExampleItemBackgroundHover,\n    Color? chatExampleItemText,\n    Color? chatExampleTitleText,\n    Color? markdownLinkColor,\n    Color? markdownPreColor,\n    Color? markdownCodeColor,\n    Color? boxShadowColor,\n    Color? backgroundColor,\n    Color? backgroundInvertedColor,\n    Color? backgroundContainerColor,\n    Color? backgroundForDialogListItem,\n    Color? listTileBackgroundColor,\n    Color? textFieldBorderColor,\n    Color? iconButtonColor,\n    Color? linkColor,\n    Color? weakLinkColor,\n    Color? weakTextColor,\n    Color? weakTextColorLess,\n    Color? weakTextColorPlus,\n    Color? weakTextColorPlusPlus,\n    Color? dialogDefaultTextColor,\n    Color? dialogBackgroundColor,\n    Color? columnBlockBorderColor,\n    Color? columnBlockBackgroundColor,\n    Color? columnBlockDividerColor,\n    Color? textfieldHintColor,\n    Color? textfieldHintDeepColor,\n    Color? textfieldLabelColor,\n    Color? textfieldValueColor,\n    Color? textfieldBackgroundColor,\n    Color? textfieldSelectorColor,\n    Color? paymentItemBorderColor,\n    Color? paymentItemBackgroundColor,\n    Color? paymentItemTitleColor,\n    Color? paymentItemPriceColor,\n    Color? paymentItemDateColor,\n    Color? paymentItemDescriptionColor,\n    Color? settingsSectionBackground,\n  }) {\n    return CustomColors(\n      chatRoomBackground: chatRoomBackground ?? this.chatRoomBackground,\n      chatRoomReplyBackground: chatRoomReplyBackground ?? this.chatRoomReplyBackground,\n      chatRoomReplyBackgroundSecondary: chatRoomReplyBackgroundSecondary ?? this.chatRoomReplyBackgroundSecondary,\n      chatRoomReplyText: chatRoomReplyText ?? this.chatRoomReplyText,\n      chatRoomSenderBackground: chatRoomSenderBackground ?? this.chatRoomSenderBackground,\n      chatRoomSenderBackgroundSecondary: chatRoomSenderBackgroundSecondary ?? this.chatRoomSenderBackgroundSecondary,\n      chatRoomSenderBackgroundWarning: chatRoomSenderBackgroundWarning ?? this.chatRoomSenderBackgroundWarning,\n      chatRoomSenderText: chatRoomSenderText ?? this.chatRoomSenderText,\n      tagsBackground: tagsBackground ?? this.tagsBackground,\n      tagsBackgroundHover: tagsBackgroundHover ?? this.tagsBackgroundHover,\n      tagsText: tagsText ?? this.tagsText,\n      chatInputPanelBackground: chatInputPanelBackground ?? this.chatInputPanelBackground,\n      chatInputPanelText: chatInputPanelText ?? this.chatInputPanelText,\n      chatInputAreaBackground: chatInputAreaBackground ?? this.chatInputAreaBackground,\n      chatExampleItemBackground: chatExampleItemBackground ?? this.chatExampleItemBackground,\n      chatExampleItemBackgroundHover: chatExampleItemBackgroundHover ?? this.chatExampleItemBackgroundHover,\n      chatExampleItemText: chatExampleItemText ?? this.chatExampleItemText,\n      chatExampleTitleText: chatExampleTitleText ?? this.chatExampleTitleText,\n      markdownLinkColor: markdownLinkColor ?? this.markdownLinkColor,\n      markdownPreColor: markdownPreColor ?? this.markdownPreColor,\n      markdownCodeColor: markdownCodeColor ?? this.markdownCodeColor,\n      boxShadowColor: boxShadowColor ?? this.boxShadowColor,\n      backgroundColor: backgroundColor ?? this.backgroundColor,\n      backgroundInvertedColor: backgroundInvertedColor ?? this.backgroundInvertedColor,\n      backgroundContainerColor: backgroundContainerColor ?? this.backgroundContainerColor,\n      backgroundForDialogListItem: backgroundForDialogListItem ?? this.backgroundForDialogListItem,\n      listTileBackgroundColor: listTileBackgroundColor ?? this.listTileBackgroundColor,\n      textFieldBorderColor: textFieldBorderColor ?? this.textFieldBorderColor,\n      iconButtonColor: iconButtonColor ?? this.iconButtonColor,\n      linkColor: linkColor ?? this.linkColor,\n      weakLinkColor: weakLinkColor ?? this.weakLinkColor,\n      weakTextColor: weakTextColor ?? this.weakTextColor,\n      weakTextColorLess: weakTextColorLess ?? this.weakTextColorLess,\n      weakTextColorPlus: weakTextColorPlus ?? this.weakTextColorPlus,\n      weakTextColorPlusPlus: weakTextColorPlusPlus ?? this.weakTextColorPlusPlus,\n      dialogDefaultTextColor: dialogDefaultTextColor ?? this.dialogDefaultTextColor,\n      dialogBackgroundColor: dialogBackgroundColor ?? this.dialogBackgroundColor,\n      columnBlockBorderColor: columnBlockBorderColor ?? this.columnBlockBorderColor,\n      columnBlockBackgroundColor: columnBlockBackgroundColor ?? this.columnBlockBackgroundColor,\n      columnBlockDividerColor: columnBlockDividerColor ?? this.columnBlockDividerColor,\n      textfieldHintColor: textfieldHintColor ?? this.textfieldHintColor,\n      textfieldHintDeepColor: textfieldHintDeepColor ?? this.textfieldHintDeepColor,\n      textfieldLabelColor: textfieldLabelColor ?? this.textfieldLabelColor,\n      textfieldValueColor: textfieldValueColor ?? this.textfieldValueColor,\n      textfieldBackgroundColor: textfieldBackgroundColor ?? this.textfieldBackgroundColor,\n      textfieldSelectorColor: textfieldSelectorColor ?? this.textfieldSelectorColor,\n      paymentItemBorderColor: paymentItemBorderColor ?? this.paymentItemBorderColor,\n      paymentItemBackgroundColor: paymentItemBackgroundColor ?? this.paymentItemBackgroundColor,\n      paymentItemTitleColor: paymentItemTitleColor ?? this.paymentItemTitleColor,\n      paymentItemPriceColor: paymentItemPriceColor ?? this.paymentItemPriceColor,\n      paymentItemDateColor: paymentItemDateColor ?? this.paymentItemDateColor,\n      paymentItemDescriptionColor: paymentItemDescriptionColor ?? this.paymentItemDescriptionColor,\n      settingsSectionBackground: settingsSectionBackground ?? this.settingsSectionBackground,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/theme/theme.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass AppTheme extends ChangeNotifier {\n  ThemeMode _mode = ThemeMode.system;\n  ThemeMode get mode => _mode;\n\n  AppTheme(String mode) {\n    switch (mode) {\n      case 'light':\n        _mode = ThemeMode.light;\n        break;\n      case 'dark':\n        _mode = ThemeMode.dark;\n        break;\n      default:\n        _mode = ThemeMode.system;\n    }\n  }\n\n  static AppTheme instance = AppTheme('system');\n\n  static AppTheme get() {\n    if (!instance.mounted) {\n      instance = AppTheme('system');\n    }\n\n    return instance;\n  }\n\n  static ThemeMode themeModeFormString(String mode) {\n    switch (mode) {\n      case 'light':\n        return ThemeMode.light;\n      case 'dark':\n        return ThemeMode.dark;\n      default:\n        return ThemeMode.system;\n    }\n  }\n\n  set mode(ThemeMode mode) {\n    _mode = mode;\n    notifyListeners();\n  }\n\n  bool _mounted = false;\n  bool get mounted => _mounted;\n\n  @override\n  void dispose() {\n    super.dispose();\n    _mounted = true;\n  }\n}\n"
  },
  {
    "path": "lib/page/component/transition_resolver.dart",
    "content": "import 'package:animations/animations.dart';\nimport 'package:flutter/material.dart';\nimport 'package:go_router/go_router.dart';\n\nPage<dynamic> transitionResolver(Widget child, {bool useTransition = true}) {\n  if (useTransition) {\n    return CustomTransitionPage(\n      child: child,\n      transitionsBuilder: (context, animation, secondaryAnimation, child) {\n        return FadeThroughTransition(\n          animation: animation,\n          secondaryAnimation: secondaryAnimation,\n          child: child,\n        );\n      },\n    );\n  }\n\n  return MaterialPage(child: child);\n}\n"
  },
  {
    "path": "lib/page/component/verify_code_input.dart",
    "content": "import 'dart:async';\n\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:auto_size_text/auto_size_text.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\n\nclass VerifyCodeInput extends StatefulWidget {\n  final Future<String> Function() sendVerifyCode;\n  final Function(String) onVerifyCodeSent;\n  final bool Function() sendCheck;\n  final TextEditingController controller;\n  final bool inColumnBlock;\n  const VerifyCodeInput({\n    super.key,\n    required this.onVerifyCodeSent,\n    required this.sendVerifyCode,\n    required this.sendCheck,\n    required this.controller,\n    this.inColumnBlock = false,\n  });\n\n  @override\n  State<VerifyCodeInput> createState() => _VerifyCodeInputState();\n}\n\nclass _VerifyCodeInputState extends State<VerifyCodeInput> {\n  final phoneNumberValidator = RegExp(r\"^1[3456789]\\d{9}$\");\n\n  //  下次发送验证码等待时间\n  int verifyCodeWaitSeconds = 0;\n\n  Timer? timer;\n  DateTime? lastSendVerifyCodeTime;\n\n  @override\n  void dispose() {\n    timer?.cancel();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return Container(\n      padding: widget.inColumnBlock ? const EdgeInsets.all(5) : null,\n      child: Row(\n        children: [\n          if (widget.inColumnBlock)\n            SizedBox(\n              width: 80,\n              child: Text(\n                AppLocale.verifyCode.getString(context),\n                overflow: TextOverflow.ellipsis,\n                style: TextStyle(\n                  fontSize: 16,\n                  color: customColors.textfieldLabelColor,\n                ),\n              ),\n            ),\n          if (widget.inColumnBlock) const SizedBox(width: 10),\n          Expanded(\n            child: TextFormField(\n              controller: widget.controller,\n              inputFormatters: [\n                FilteringTextInputFormatter.singleLineFormatter,\n                FilteringTextInputFormatter.digitsOnly,\n              ],\n              maxLength: 6,\n              keyboardType: TextInputType.number,\n              decoration: InputDecoration(\n                counterText: '',\n                border: widget.inColumnBlock ? InputBorder.none : const OutlineInputBorder(),\n                enabledBorder: widget.inColumnBlock\n                    ? InputBorder.none\n                    : const OutlineInputBorder(\n                        borderSide: BorderSide(color: Color.fromARGB(200, 192, 192, 192)),\n                      ),\n                focusedBorder: widget.inColumnBlock\n                    ? InputBorder.none\n                    : OutlineInputBorder(\n                        borderSide: BorderSide(color: customColors.linkColor ?? Colors.green),\n                      ),\n                // floatingLabelStyle:\n                //     TextStyle(color: customColors.linkColor ?? Colors.green),\n                isDense: true,\n                floatingLabelBehavior: FloatingLabelBehavior.always,\n                labelText: widget.inColumnBlock ? null : AppLocale.verifyCode.getString(context),\n                labelStyle: const TextStyle(fontSize: 17),\n                hintText: AppLocale.verifyCodeInputTips.getString(context),\n                hintStyle: TextStyle(\n                  color: customColors.textfieldHintColor,\n                  fontSize: 15,\n                ),\n              ),\n            ),\n          ),\n          const SizedBox(width: 30),\n          SizedBox(\n            width: 100,\n            child: verifyCodeWaitSeconds > 0\n                ? TextButton(\n                    onPressed: null,\n                    child: AutoSizeText(\n                      '$verifyCodeWaitSeconds ${AppLocale.retryInSeconds.getString(context)}',\n                      style: TextStyle(\n                        color: customColors.weakTextColor,\n                        fontSize: 15,\n                      ),\n                      maxLines: 1,\n                    ),\n                  )\n                : TextButton(\n                    onPressed: () {\n                      if (!widget.sendCheck()) {\n                        return;\n                      }\n\n                      widget.sendVerifyCode().then((id) {\n                        widget.onVerifyCodeSent(id);\n                        setState(() {\n                          verifyCodeWaitSeconds = 60;\n                        });\n\n                        if (timer != null) {\n                          timer?.cancel();\n                          timer = null;\n                        }\n\n                        lastSendVerifyCodeTime = DateTime.now();\n                        timer = Timer.periodic(const Duration(seconds: 1), (timer) {\n                          if (verifyCodeWaitSeconds <= 0) {\n                            timer.cancel();\n                            return;\n                          }\n\n                          setState(() {\n                            verifyCodeWaitSeconds = 60 - (DateTime.now().difference(lastSendVerifyCodeTime!).inSeconds);\n                          });\n                        });\n\n                        showSuccessMessage(AppLocale.verifyCodeSendSuccess.getString(context));\n                      }).onError((error, stackTrace) {\n                        setState(() {\n                          verifyCodeWaitSeconds = 0;\n                        });\n                        timer?.cancel();\n                        showErrorMessage(resolveError(context, error!));\n                      });\n                    },\n                    child: Text(\n                      AppLocale.sendVerifyCode.getString(context),\n                      style: TextStyle(\n                        color: customColors.linkColor,\n                        fontSize: 15,\n                      ),\n                    ),\n                  ),\n          ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/video_player.dart",
    "content": "import 'dart:io';\n\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/helper/logger.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:file_saver/file_saver.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_cache_manager/flutter_cache_manager.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:image_gallery_saver/image_gallery_saver.dart';\nimport 'package:media_kit/media_kit.dart';\nimport 'package:media_kit_video/media_kit_video.dart';\n\nclass VideoPlayer extends StatefulWidget {\n  final String url;\n  final double? aspectRatio;\n  final int? width;\n  final int? height;\n\n  const VideoPlayer({super.key, required this.url, this.width, this.height, this.aspectRatio});\n\n  @override\n  State<VideoPlayer> createState() => _VideoPlayerState();\n}\n\nclass _VideoPlayerState extends State<VideoPlayer> {\n  late final player = Player();\n  late final controller = VideoController(player);\n\n  @override\n  void initState() {\n    super.initState();\n    player.setPlaylistMode(PlaylistMode.single);\n    player.open(Media(widget.url));\n  }\n\n  @override\n  void dispose() {\n    player.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return Container(\n      decoration: BoxDecoration(\n        color: customColors.columnBlockBackgroundColor,\n        borderRadius: CustomSize.borderRadius,\n      ),\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.end,\n        children: [\n          ClipRRect(\n            borderRadius: const BorderRadius.only(topLeft: CustomSize.radius, topRight: CustomSize.radius),\n            child: Center(\n              child: SizedBox(\n                width: MediaQuery.of(context).size.width,\n                height: widget.width != null && widget.height != null\n                    ? MediaQuery.of(context).size.width * widget.height! / widget.width!\n                    : MediaQuery.of(context).size.width,\n                child: Video(\n                  controller: controller,\n                  width: widget.width?.toDouble(),\n                  height: widget.height?.toDouble(),\n                  aspectRatio: widget.aspectRatio,\n                ),\n              ),\n            ),\n          ),\n          Padding(\n            padding: const EdgeInsets.only(right: 10),\n            child: Row(\n              mainAxisAlignment: MainAxisAlignment.end,\n              children: [\n                IconButton(\n                  splashColor: Colors.transparent,\n                  highlightColor: Colors.transparent,\n                  hoverColor: Colors.transparent,\n                  icon: Row(\n                    mainAxisSize: MainAxisSize.min,\n                    children: [\n                      Icon(\n                        Icons.download,\n                        size: 14,\n                        color: customColors.weakLinkColor,\n                      ),\n                      const SizedBox(width: 5),\n                      Text(\n                        AppLocale.download.getString(context),\n                        style: TextStyle(\n                          fontSize: 12,\n                          color: customColors.weakLinkColor,\n                        ),\n                      ),\n                    ],\n                  ),\n                  onPressed: () async {\n                    final cancel = BotToast.showCustomLoading(\n                      toastBuilder: (cancel) {\n                        return const LoadingIndicator(\n                          message: 'Downloading, please wait...',\n                        );\n                      },\n                      allowClick: false,\n                      duration: const Duration(seconds: 120),\n                    );\n\n                    try {\n                      final saveFile = await DefaultCacheManager().getSingleFile(widget.url);\n\n                      if (PlatformTool.isIOS() || PlatformTool.isAndroid()) {\n                        await ImageGallerySaver.saveFile(saveFile.path);\n\n                        showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                      } else {\n                        var ext = saveFile.path.toLowerCase().split('.').last;\n\n                        if (PlatformTool.isWindows()) {\n                          FileSaver.instance\n                              .saveAs(\n                            name: filenameWithoutExt(saveFile.path.split('/').last),\n                            filePath: saveFile.path,\n                            ext: '.$ext',\n                            mimeType: MimeType.mpeg,\n                          )\n                              .then((value) async {\n                            if (value == null) {\n                              return;\n                            }\n\n                            await File(value).writeAsBytes(await saveFile.readAsBytes());\n\n                            Logger.instance.d('File saved successfully: $value');\n                            showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                          });\n                        } else {\n                          FileSaver.instance\n                              .saveFile(\n                            name: filenameWithoutExt(saveFile.path.split('/').last),\n                            filePath: saveFile.path,\n                            ext: ext,\n                            mimeType: MimeType.mpeg,\n                          )\n                              .then((value) {\n                            showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                          });\n                        }\n                      }\n                    } catch (e) {\n                      // ignore: use_build_context_synchronously\n                      showErrorMessageEnhanced(context, 'Image save failed, please try again later');\n                      Logger.instance.e('Download failed', error: e);\n                    } finally {\n                      cancel();\n                    }\n                  },\n                ),\n              ],\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/weak_text_button.dart",
    "content": "import 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\n\nclass WeakTextButton extends StatelessWidget {\n  final String title;\n  final IconData? icon;\n  final VoidCallback? onPressed;\n  final double? fontSize;\n  const WeakTextButton({\n    super.key,\n    required this.title,\n    this.icon,\n    this.onPressed,\n    this.fontSize,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    final item = Row(\n      mainAxisAlignment: MainAxisAlignment.center,\n      mainAxisSize: MainAxisSize.min,\n      children: [\n        if (icon != null)\n          Icon(\n            icon,\n            color: customColors.weakLinkColor,\n            size: (fontSize ?? 15.0) + 1,\n          ),\n        if (icon != null) const SizedBox(width: 5),\n        Text(\n          title,\n          style: TextStyle(\n            color: customColors.weakLinkColor,\n            fontSize: fontSize ?? 15.0,\n          ),\n        ),\n      ],\n    );\n\n    if (onPressed == null) {\n      return item;\n    }\n\n    return TextButton(\n      onPressed: onPressed,\n      child: item,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/component/windows.dart",
    "content": "import 'package:askaide/helper/platform.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:bitsdojo_window/bitsdojo_window.dart';\nimport 'package:flutter/material.dart';\n\nclass WindowFrameWidget extends StatelessWidget {\n  final Color? backgroundColor;\n  final Widget child;\n\n  const WindowFrameWidget({super.key, required this.child, this.backgroundColor});\n\n  @override\n  Widget build(BuildContext context) {\n    if (!PlatformTool.isDesktop()) {\n      return child;\n    }\n\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return WindowBorder(\n      color: Colors.transparent,\n      width: 0,\n      child: Container(\n        decoration: BoxDecoration(\n          color: backgroundColor ?? customColors.backgroundContainerColor,\n        ),\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          mainAxisAlignment: MainAxisAlignment.start,\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            WindowTitleBarBox(\n              child: Row(\n                children: [Expanded(child: MoveWindow()), const WindowButtons()],\n              ),\n            ),\n            Expanded(child: child),\n          ],\n        ),\n      ),\n    );\n  }\n}\n\nfinal buttonColors = WindowButtonColors(\n    iconNormal: const Color.fromARGB(255, 93, 93, 93),\n    mouseOver: const Color.fromARGB(255, 90, 90, 90),\n    mouseDown: const Color.fromARGB(255, 171, 171, 171),\n    iconMouseOver: const Color.fromARGB(255, 190, 190, 190),\n    iconMouseDown: const Color.fromARGB(255, 217, 217, 217));\n\nfinal closeButtonColors = WindowButtonColors(\n    mouseOver: const Color(0xFFD32F2F),\n    mouseDown: const Color(0xFFB71C1C),\n    iconNormal: const Color(0xFF805306),\n    iconMouseOver: Colors.white);\n\nclass WindowButtons extends StatefulWidget {\n  const WindowButtons({super.key});\n\n  @override\n  State<WindowButtons> createState() => _WindowButtonsState();\n}\n\nclass _WindowButtonsState extends State<WindowButtons> {\n  void maximizeOrRestore() {\n    setState(() {\n      appWindow.maximizeOrRestore();\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Row(\n      children: [\n        MinimizeWindowButton(colors: buttonColors),\n        appWindow.isMaximized\n            ? RestoreWindowButton(\n                colors: buttonColors,\n                onPressed: maximizeOrRestore,\n              )\n            : MaximizeWindowButton(\n                colors: buttonColors,\n                onPressed: maximizeOrRestore,\n              ),\n        CloseWindowButton(colors: closeButtonColors),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/creative_island/draw/artistic_qr.dart",
    "content": "import 'dart:math';\n\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/cache.dart';\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/advanced_button.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/enhanced_input.dart';\nimport 'package:askaide/page/component/enhanced_textfield.dart';\nimport 'package:askaide/page/component/global_alert.dart';\nimport 'package:askaide/page/component/item_selector_search.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/prompt_tags_selector.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/page/creative_island/draw/components/artistic_style_selector.dart';\nimport 'package:askaide/page/creative_island/draw/components/content_preview.dart';\nimport 'package:askaide/page/creative_island/draw/draw_result.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/api/creative.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:quickalert/models/quickalert_type.dart';\n\nclass ArtisticQRScreen extends StatefulWidget {\n  final SettingRepository setting;\n  final int? galleryCopyId;\n  final String type;\n  final String id;\n  final String? note;\n\n  const ArtisticQRScreen({\n    super.key,\n    required this.id,\n    required this.setting,\n    this.galleryCopyId,\n    required this.type,\n    this.note,\n  });\n\n  @override\n  State<ArtisticQRScreen> createState() => _ArtisticQRScreenState();\n}\n\nclass _ArtisticQRScreenState extends State<ArtisticQRScreen> {\n  bool enableAIRewrite = false;\n  bool showAdvancedOptions = false;\n\n  CreativeIslandCapacity? capacity;\n\n  CreativeIslandArtisticStyle? selectedStyle;\n\n  /// 是否停止周期性查询任务执行状态\n  var stopPeriodQuery = false;\n\n  int generationImageCount = 1;\n  double? textWeight = 1.35;\n\n  TextEditingController promptController = TextEditingController();\n  TextEditingController negativePromptController = TextEditingController();\n  TextEditingController textController = TextEditingController();\n  TextEditingController seedController = TextEditingController();\n\n  @override\n  void dispose() {\n    promptController.dispose();\n    negativePromptController.dispose();\n    textController.dispose();\n    seedController.dispose();\n    super.dispose();\n  }\n\n  @override\n  void initState() {\n    APIServer().creativeIslandCapacity(mode: widget.type, id: widget.id).then((cap) {\n      setState(() {\n        capacity = cap;\n      });\n\n      if (widget.galleryCopyId != null && widget.galleryCopyId! > 0) {\n        APIServer().creativeGalleryItem(id: widget.galleryCopyId!).then((response) {\n          final gallery = response.item;\n          if (gallery.prompt != null && gallery.prompt!.isNotEmpty) {\n            promptController.text = gallery.prompt!;\n          }\n\n          if (gallery.metaMap['real_prompt'] != null && gallery.metaMap['real_prompt'] != '') {\n            promptController.text = gallery.metaMap['real_prompt']!;\n          }\n\n          if (gallery.metaMap['negative_prompt'] != null && gallery.metaMap['negative_prompt'] != '') {\n            negativePromptController.text = gallery.metaMap['negative_prompt']!;\n          }\n\n          if (gallery.metaMap['real_negative_prompt'] != null && gallery.metaMap['real_negative_prompt'] != '') {\n            negativePromptController.text = gallery.metaMap['real_negative_prompt']!;\n          }\n\n          // 创建同款时，默认关闭 AI 优化，除非该同款包含 ai_rewrite 的设定\n          enableAIRewrite = false;\n          if ((gallery.metaMap['real_prompt'] == null || gallery.metaMap['real_prompt'] == '') &&\n              gallery.metaMap['ai_rewrite'] != null &&\n              gallery.metaMap['ai_rewrite']) {\n            enableAIRewrite = gallery.metaMap['ai_rewrite'];\n          }\n\n          setState(() {});\n        });\n      }\n    });\n\n    if (widget.note != null) {\n      Cache().boolGet(key: 'creative:tutorials:${widget.type}:dialog').then((show) {\n        if (!show) {\n          return;\n        }\n\n        openDefaultTutorials(onConfirm: () {\n          Cache().setBool(\n            key: 'creative:tutorials:${widget.type}:dialog',\n            value: false,\n            duration: const Duration(days: 30),\n          );\n        });\n      });\n    }\n\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          title: Text(\n            widget.type == 'qr' ? '艺术二维码' : '图文融合',\n            style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n          leading: IconButton(\n            onPressed: () {\n              context.pop();\n            },\n            icon: const Icon(Icons.arrow_back_ios),\n          ),\n          toolbarHeight: CustomSize.toolbarHeight,\n          backgroundColor: customColors.backgroundColor,\n          actions: [\n            if (widget.note != null)\n              IconButton(\n                onPressed: () {\n                  openDefaultTutorials();\n                },\n                icon: const Icon(Icons.help_outline),\n              )\n          ],\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          maxWidth: CustomSize.smallWindowSize,\n          child: Column(\n            children: [\n              if (Ability().showGlobalAlert) const GlobalAlert(pageKey: 'creative_create'),\n              Expanded(\n                child: Container(\n                  padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),\n                  height: double.infinity,\n                  child: SingleChildScrollView(\n                    child: buildEditPanel(context, customColors),\n                  ),\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n\n  void openDefaultTutorials({Function? onConfirm}) {\n    showBeautyDialog(\n      context,\n      type: QuickAlertType.info,\n      text: '     ${widget.note!}',\n      onConfirmBtnTap: () async {\n        onConfirm?.call();\n        context.pop();\n      },\n      showCancelBtn: true,\n      confirmBtnText: AppLocale.gotIt.getString(context),\n    );\n  }\n\n  Widget buildEditPanel(BuildContext context, CustomColors customColors) {\n    return SafeArea(\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: [\n          ColumnBlock(\n            innerPanding: 10,\n            padding: const EdgeInsets.only(left: 15, right: 15, top: 15, bottom: 0),\n            children: [\n              if (capacity != null && capacity!.artisticStyles.isNotEmpty)\n                ArtisticStyleSelector(\n                  styles: capacity!.artisticStyles,\n                  onSelected: (style) {\n                    setState(() {\n                      selectedStyle = style;\n                    });\n                  },\n                  selectedStyle: selectedStyle,\n                ),\n            ],\n          ),\n          ColumnBlock(\n            innerPanding: 10,\n            padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),\n            children: [\n              EnhancedTextField(\n                labelPosition: LabelPosition.top,\n                labelText: widget.type == 'qr' ? '链接地址' : '文字内容',\n                customColors: customColors,\n                controller: textController,\n                textAlignVertical: TextAlignVertical.top,\n                hintText: widget.type == 'qr' ? '要生成的二维码链接地址。' : '要在画面中绘制的文字。',\n                maxLength: widget.type == 'qr' ? 250 : 20,\n                maxLines: 3,\n                minLines: 1,\n                showCounter: false,\n              ),\n              // 生成内容\n              ...buildPromptField(customColors),\n              // AI 优化配置\n              Row(\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: [\n                  Row(\n                    children: [\n                      Text(\n                        AppLocale.smartOptimization.getString(context),\n                        style: const TextStyle(fontSize: 16),\n                      ),\n                      const SizedBox(width: 5),\n                      InkWell(\n                        onTap: () {\n                          showBeautyDialog(\n                            context,\n                            type: QuickAlertType.info,\n                            text: AppLocale.onceEnabledSmartOptimization.getString(context),\n                            confirmBtnText: AppLocale.gotIt.getString(context),\n                            showCancelBtn: false,\n                          );\n                        },\n                        child: Icon(\n                          Icons.help_outline,\n                          size: 16,\n                          color: customColors.weakLinkColor?.withAlpha(150),\n                        ),\n                      ),\n                    ],\n                  ),\n                  CupertinoSwitch(\n                    activeColor: customColors.linkColor,\n                    value: enableAIRewrite,\n                    onChanged: (value) {\n                      setState(() {\n                        enableAIRewrite = value;\n                      });\n                    },\n                  ),\n                ],\n              ),\n            ],\n          ),\n\n          if (showAdvancedOptions)\n            ColumnBlock(\n              innerPanding: 10,\n              padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),\n              children: [\n                // 反向提示语\n                EnhancedTextField(\n                  labelPosition: LabelPosition.top,\n                  labelText: AppLocale.excludeContents.getString(context),\n                  customColors: customColors,\n                  controller: negativePromptController,\n                  textAlignVertical: TextAlignVertical.top,\n                  hintText: AppLocale.unwantedElements.getString(context),\n                  maxLength: 500,\n                  maxLines: 5,\n                  minLines: 3,\n                  showCounter: false,\n                ),\n                // 权重\n                Row(\n                  children: [\n                    Row(\n                      children: [\n                        const Text('文本权重'),\n                        const SizedBox(width: 5),\n                        InkWell(\n                          onTap: () {\n                            showBeautyDialog(\n                              context,\n                              type: QuickAlertType.info,\n                              text: '文本权重\\n\\n权重越高，图像中出现的文本痕迹越明显。',\n                              confirmBtnText: AppLocale.gotIt.getString(context),\n                              showCancelBtn: false,\n                            );\n                          },\n                          child: Icon(\n                            Icons.help_outline,\n                            size: 16,\n                            color: customColors.weakLinkColor?.withAlpha(150),\n                          ),\n                        ),\n                      ],\n                    ),\n                    const SizedBox(width: 10),\n                    Expanded(\n                      child: Slider(\n                        value: textWeight ?? 1.35,\n                        min: 0,\n                        max: 3,\n                        divisions: 60,\n                        activeColor: customColors.linkColor,\n                        onChanged: (value) {\n                          setState(() {\n                            textWeight = value;\n                          });\n                        },\n                      ),\n                    ),\n                    Text(\n                      (textWeight ?? 1.38).toStringAsFixed(2),\n                      style: TextStyle(\n                        fontSize: 12,\n                        color: customColors.weakTextColor,\n                      ),\n                    ),\n                  ],\n                ),\n                // 图片数量\n                EnhancedInput(\n                  title: Text(\n                    AppLocale.imageCount.getString(context),\n                    style: TextStyle(\n                      color: customColors.textfieldLabelColor,\n                      fontSize: 16,\n                    ),\n                  ),\n                  value: Text(generationImageCount.toString()),\n                  onPressed: () {\n                    openListSelectDialog(\n                      context,\n                      <SelectorItem>[\n                        SelectorItem(const Text('1', textAlign: TextAlign.center), 1),\n                        SelectorItem(const Text('2', textAlign: TextAlign.center), 2),\n                        SelectorItem(const Text('3', textAlign: TextAlign.center), 3),\n                        SelectorItem(const Text('4', textAlign: TextAlign.center), 4),\n                      ],\n                      (value) {\n                        setState(() {\n                          generationImageCount = value.value;\n                        });\n                        return true;\n                      },\n                      heightFactor: 0.4,\n                      value: generationImageCount,\n                    );\n                  },\n                ),\n                // Seed\n                EnhancedTextField(\n                  controller: seedController,\n                  customColors: customColors,\n                  labelText: 'Seed',\n                  labelPosition: LabelPosition.left,\n                  showCounter: false,\n                  keyboardType: TextInputType.number,\n                  inputFormatters: [\n                    FilteringTextInputFormatter.digitsOnly,\n                  ],\n                  hintText: '默认随机',\n                  textDirection: TextDirection.rtl,\n                ),\n              ],\n            ),\n          // 生成按钮\n          AdvancedButton(\n            showAdvancedOptions: showAdvancedOptions,\n            onPressed: (value) {\n              setState(() {\n                showAdvancedOptions = value;\n              });\n            },\n          ),\n          if (capacity != null) const SizedBox(height: 10),\n          EnhancedButton(\n            title: AppLocale.generate.getString(context),\n            onPressed: onGenerate,\n          ),\n          const SizedBox(height: 20),\n        ],\n      ),\n    );\n  }\n\n  List<Widget> buildPromptField(CustomColors customColors) {\n    return [\n      EnhancedTextField(\n        labelPosition: LabelPosition.top,\n        labelText: AppLocale.yourIdeas.getString(context),\n        customColors: customColors,\n        controller: promptController,\n        textAlignVertical: TextAlignVertical.top,\n        hintText: AppLocale.keywordsSeparatedByCommas.getString(context),\n        maxLines: 10,\n        minLines: 2,\n        maxLength: 460,\n        showCounter: false,\n        inputSelector: IconButton(\n          onPressed: () {\n            openModalBottomSheet(\n              context,\n              (context) {\n                return PromptTagsSelector(\n                  selectedTags: selectedTags,\n                  onSubmit: (tags) {\n                    setState(() {\n                      selectedTags = tags;\n                    });\n                    context.pop();\n                  },\n                );\n              },\n              heightFactor: 0.8,\n              useSafeArea: true,\n            );\n          },\n          icon: Icon(\n            Icons.lightbulb_outline,\n            color: customColors.linkColor,\n            size: 16,\n          ),\n        ),\n        middleWidget: Container(\n          width: double.infinity,\n          margin: const EdgeInsets.only(bottom: 30),\n          child: Wrap(\n            spacing: 3,\n            runSpacing: 3,\n            children: selectedTags\n                .map(\n                  (e) => Tag(\n                    name: e.name,\n                    backgroundColor: customColors.linkColor,\n                    textColor: Colors.white,\n                    fontsize: 10,\n                    onDeleted: () {\n                      setState(() {\n                        selectedTags.remove(e);\n                      });\n                    },\n                  ),\n                )\n                .toList(),\n          ),\n        ),\n        bottomButton: Row(\n          children: [\n            Icon(\n              Icons.shuffle,\n              size: 13,\n              color: customColors.linkColor?.withAlpha(150),\n            ),\n            const SizedBox(width: 5),\n            Text(\n              AppLocale.random.getString(context),\n              style: TextStyle(\n                color: customColors.linkColor?.withAlpha(150),\n                fontSize: 13,\n              ),\n            ),\n          ],\n        ),\n        bottomButtonOnPressed: () async {\n          final examples = await APIServer().exampleByTag('artistic-text');\n          if (examples.isEmpty) {\n            return;\n          }\n\n          // 随机选取一个例子\n          final example = examples[Random().nextInt(examples.length)];\n          promptController.text = example.text;\n        },\n      ),\n    ];\n  }\n\n  List<PromptTag> selectedTags = [];\n\n  void onGenerate() async {\n    FocusScope.of(context).requestFocus(FocusNode());\n    HapticFeedbackHelper.mediumImpact();\n\n    final prompt = promptController.text.trim();\n    if (prompt.isEmpty) {\n      showErrorMessage(AppLocale.contentIsRequired.getString(context));\n      return;\n    }\n\n    final text = textController.text.trim();\n    if (text.isEmpty) {\n      showErrorMessage('${widget.type == \"qr\" ? \"链接地址\" : \"文本内容\"}不能为空');\n      return;\n    }\n\n    final seed = int.tryParse(seedController.text);\n    if (seed != null && (seed < 0 || seed > 2147483647)) {\n      showErrorMessage('Seed 取值范围为 0 ~ 2147483647');\n      return;\n    }\n\n    var params = <String, dynamic>{\n      'prompt': prompt,\n      'prompt_tags': selectedTags.map((e) => e.value).join(','),\n      'negative_prompt': negativePromptController.text,\n      'ai_rewrite': enableAIRewrite,\n      'gallery_copy_id': widget.galleryCopyId,\n      'text': text,\n      'type': widget.type,\n      'seed': seed,\n      'image_count': generationImageCount,\n      'control_weight': textWeight,\n      'style_preset': selectedStyle?.id,\n    };\n\n    final cancel = BotToast.showCustomLoading(\n      toastBuilder: (cancel) {\n        return const LoadingIndicator(\n          message: '思考中，请稍候...',\n        );\n      },\n      allowClick: false,\n      duration: const Duration(seconds: 15),\n    );\n\n    request(int waitDuration) async {\n      try {\n        final taskId = await APIServer().creativeIslandArtisticTextCompletionsAsyncV2(params);\n\n        stopPeriodQuery = false;\n\n        cancel();\n\n        Navigator.push(\n          // ignore: use_build_context_synchronously\n          context,\n          MaterialPageRoute(\n            fullscreenDialog: true,\n            builder: (context) => DrawResultPage(\n              future: Future.delayed(const Duration(seconds: 10), () async {\n                return await queryCompletionTaskStatus(\n                  taskId: taskId,\n                  retryTimes: 0,\n                  delaySeconds: 3,\n                  params: params,\n                );\n              }),\n              waitDuration: waitDuration,\n            ),\n          ),\n        ).whenComplete(() {\n          stopPeriodQuery = true;\n        });\n      } catch (e) {\n        stopPeriodQuery = true;\n        cancel();\n        // ignore: use_build_context_synchronously\n        showErrorMessageEnhanced(context, e);\n      }\n    }\n\n    try {\n      request(30);\n    } catch (e) {\n      cancel();\n      // ignore: use_build_context_synchronously\n      showErrorMessageEnhanced(context, e);\n    }\n  }\n\n  Future<IslandResult> queryCompletionTaskStatus({\n    required String taskId,\n    required int retryTimes,\n    required int delaySeconds,\n    Map<String, dynamic>? params,\n  }) async {\n    if (retryTimes > 60) {\n      return Future.error(AppLocale.generateTimeout.getString(context));\n    }\n\n    final resp = await APIServer().asyncTaskStatus(taskId);\n    switch (resp.status) {\n      case 'success':\n        if (params != null && resp.originImage != null && resp.originImage != '') {\n          params['image'] = resp.originImage;\n        }\n        return IslandResult(\n          result: resp.resources ?? const [],\n          params: params,\n        );\n      case 'failed':\n        return Future.error(resp.errors!.join(\";\"));\n      default:\n        if (stopPeriodQuery) {\n          // ignore: use_build_context_synchronously\n          return Future.error(AppLocale.generateTimeout.getString(context));\n        }\n\n        return await Future.delayed(Duration(seconds: delaySeconds), () async {\n          return await queryCompletionTaskStatus(\n            taskId: taskId,\n            retryTimes: retryTimes + 1,\n            delaySeconds: 3,\n            params: params,\n          );\n        });\n    }\n  }\n}\n"
  },
  {
    "path": "lib/page/creative_island/draw/artistic_wordart.dart",
    "content": "import 'dart:math';\n\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/cache.dart';\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/advanced_button.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/enhanced_input.dart';\nimport 'package:askaide/page/component/enhanced_textfield.dart';\nimport 'package:askaide/page/component/global_alert.dart';\nimport 'package:askaide/page/component/item_selector_search.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/prompt_tags_selector.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/page/creative_island/draw/components/artistic_style_selector.dart';\nimport 'package:askaide/page/creative_island/draw/components/content_preview.dart';\nimport 'package:askaide/page/creative_island/draw/draw_result.dart';\nimport 'package:askaide/repo/api/creative.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:quickalert/models/quickalert_type.dart';\n\nclass ArtisticWordArtScreen extends StatefulWidget {\n  final SettingRepository setting;\n  final int? galleryCopyId;\n  final String id;\n  final String? note;\n\n  const ArtisticWordArtScreen({\n    super.key,\n    required this.id,\n    required this.setting,\n    this.galleryCopyId,\n    this.note,\n  });\n\n  @override\n  State<ArtisticWordArtScreen> createState() => _ArtisticWordArtScreenState();\n}\n\nclass _ArtisticWordArtScreenState extends State<ArtisticWordArtScreen> {\n  bool showAdvancedOptions = false;\n\n  CreativeIslandCapacity? capacity;\n\n  CreativeIslandArtisticStyle? selectedStyle;\n  CreativeIslandArtisticStyle? selectedFonts;\n\n  /// 是否停止周期性查询任务执行状态\n  var stopPeriodQuery = false;\n\n  int generationImageCount = 1;\n\n  TextEditingController promptController = TextEditingController();\n  TextEditingController textController = TextEditingController();\n\n  @override\n  void dispose() {\n    promptController.dispose();\n    textController.dispose();\n    super.dispose();\n  }\n\n  @override\n  void initState() {\n    APIServer().creativeIslandCapacity(mode: 'artistic-text', id: widget.id).then((cap) {\n      setState(() {\n        capacity = cap;\n      });\n\n      if (widget.galleryCopyId != null && widget.galleryCopyId! > 0) {\n        APIServer().creativeGalleryItem(id: widget.galleryCopyId!).then((response) {\n          final gallery = response.item;\n          if (gallery.prompt != null && gallery.prompt!.isNotEmpty) {\n            promptController.text = gallery.prompt!;\n          }\n\n          if (gallery.metaMap['real_prompt'] != null && gallery.metaMap['real_prompt'] != '') {\n            promptController.text = gallery.metaMap['real_prompt']!;\n          }\n\n          setState(() {});\n        });\n      }\n    });\n\n    if (widget.note != null) {\n      Cache().boolGet(key: 'creative:tutorials:artistic-text:dialog').then((show) {\n        if (!show) {\n          return;\n        }\n\n        openDefaultTutorials(onConfirm: () {\n          Cache().setBool(\n            key: 'creative:tutorials:artistic-text:dialog',\n            value: false,\n            duration: const Duration(days: 30),\n          );\n        });\n      });\n    }\n\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          title: const Text(\n            '艺术字',\n            style: TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n          leading: IconButton(\n            onPressed: () {\n              context.pop();\n            },\n            icon: const Icon(Icons.arrow_back_ios),\n          ),\n          toolbarHeight: CustomSize.toolbarHeight,\n          backgroundColor: customColors.backgroundColor,\n          actions: [\n            if (widget.note != null)\n              IconButton(\n                onPressed: () {\n                  openDefaultTutorials();\n                },\n                icon: const Icon(Icons.help_outline),\n              )\n          ],\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          maxWidth: CustomSize.smallWindowSize,\n          backgroundColor: customColors.backgroundColor,\n          child: Column(\n            children: [\n              if (Ability().showGlobalAlert) const GlobalAlert(pageKey: 'creative_create'),\n              Expanded(\n                child: Container(\n                  padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),\n                  height: double.infinity,\n                  child: SingleChildScrollView(\n                    child: buildEditPanel(context, customColors),\n                  ),\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n\n  void openDefaultTutorials({Function? onConfirm}) {\n    showBeautyDialog(\n      context,\n      type: QuickAlertType.info,\n      text: '     ${widget.note!}',\n      onConfirmBtnTap: () async {\n        onConfirm?.call();\n        context.pop();\n      },\n      showCancelBtn: true,\n      confirmBtnText: AppLocale.gotIt.getString(context),\n    );\n  }\n\n  Widget buildEditPanel(BuildContext context, CustomColors customColors) {\n    return SafeArea(\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: [\n          ColumnBlock(\n            innerPanding: 10,\n            padding: const EdgeInsets.only(top: 15, left: 15, right: 15, bottom: 0),\n            children: [\n              if (capacity != null && capacity!.artisticTextStyles.isNotEmpty)\n                ArtisticStyleSelector(\n                  styles: capacity!.artisticTextStyles,\n                  onSelected: (style) {\n                    setState(() {\n                      selectedStyle = style;\n                    });\n                  },\n                  selectedStyle: selectedStyle,\n                ),\n            ],\n          ),\n          ColumnBlock(\n            innerPanding: 10,\n            padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),\n            children: [\n              EnhancedTextField(\n                labelPosition: LabelPosition.top,\n                labelText: '文字内容',\n                customColors: customColors,\n                controller: textController,\n                textAlignVertical: TextAlignVertical.top,\n                hintText: '要在画面中绘制的文字。',\n                maxLength: 20,\n                maxLines: 3,\n                minLines: 1,\n                showCounter: false,\n              ),\n\n              // 生成内容\n              ...buildPromptField(customColors),\n            ],\n          ),\n\n          if (showAdvancedOptions)\n            ColumnBlock(\n              innerPanding: 10,\n              padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),\n              children: [\n                if (capacity != null && capacity!.artisticTextFonts.isNotEmpty)\n                  ArtisticStyleSelector(\n                    title: '文字字体',\n                    styles: capacity!.artisticTextFonts,\n                    onSelected: (style) {\n                      setState(() {\n                        selectedFonts = style;\n                      });\n                    },\n                    selectedStyle: selectedFonts,\n                  ),\n                // 图片数量\n                EnhancedInput(\n                  title: Text(\n                    AppLocale.imageCount.getString(context),\n                    style: TextStyle(\n                      color: customColors.textfieldLabelColor,\n                      fontSize: 16,\n                    ),\n                  ),\n                  value: Text(generationImageCount.toString()),\n                  onPressed: () {\n                    openListSelectDialog(\n                      context,\n                      <SelectorItem>[\n                        SelectorItem(const Text('1', textAlign: TextAlign.center), 1),\n                        SelectorItem(const Text('2', textAlign: TextAlign.center), 2),\n                        SelectorItem(const Text('3', textAlign: TextAlign.center), 3),\n                        SelectorItem(const Text('4', textAlign: TextAlign.center), 4),\n                      ],\n                      (value) {\n                        setState(() {\n                          generationImageCount = value.value;\n                        });\n                        return true;\n                      },\n                      heightFactor: 0.4,\n                      value: generationImageCount,\n                    );\n                  },\n                ),\n              ],\n            ),\n          // 生成按钮\n          AdvancedButton(\n            showAdvancedOptions: showAdvancedOptions,\n            onPressed: (value) {\n              setState(() {\n                showAdvancedOptions = value;\n              });\n            },\n          ),\n          if (capacity != null) const SizedBox(height: 10),\n          EnhancedButton(\n            title: AppLocale.generate.getString(context),\n            onPressed: onGenerate,\n          ),\n          const SizedBox(height: 20),\n        ],\n      ),\n    );\n  }\n\n  List<Widget> buildPromptField(CustomColors customColors) {\n    return [\n      EnhancedTextField(\n        labelPosition: LabelPosition.top,\n        labelText: AppLocale.yourIdeas.getString(context),\n        customColors: customColors,\n        controller: promptController,\n        textAlignVertical: TextAlignVertical.top,\n        hintText: AppLocale.keywordsSeparatedByCommas.getString(context),\n        maxLines: 10,\n        minLines: 2,\n        maxLength: 460,\n        showCounter: false,\n        inputSelector: IconButton(\n          onPressed: () {\n            openModalBottomSheet(\n              context,\n              (context) {\n                return PromptTagsSelector(\n                  selectedTags: selectedTags,\n                  onSubmit: (tags) {\n                    setState(() {\n                      selectedTags = tags;\n                    });\n                    context.pop();\n                  },\n                );\n              },\n              heightFactor: 0.8,\n              useSafeArea: true,\n            );\n          },\n          icon: Icon(\n            Icons.lightbulb_outline,\n            color: customColors.linkColor,\n            size: 16,\n          ),\n        ),\n        middleWidget: Container(\n          width: double.infinity,\n          margin: const EdgeInsets.only(bottom: 30),\n          child: Wrap(\n            spacing: 3,\n            runSpacing: 3,\n            children: selectedTags\n                .map(\n                  (e) => Tag(\n                    name: e.name,\n                    backgroundColor: customColors.linkColor,\n                    textColor: Colors.white,\n                    fontsize: 10,\n                    onDeleted: () {\n                      setState(() {\n                        selectedTags.remove(e);\n                      });\n                    },\n                  ),\n                )\n                .toList(),\n          ),\n        ),\n        bottomButton: Row(\n          children: [\n            Icon(\n              Icons.shuffle,\n              size: 13,\n              color: customColors.linkColor?.withAlpha(150),\n            ),\n            const SizedBox(width: 5),\n            Text(\n              AppLocale.random.getString(context),\n              style: TextStyle(\n                color: customColors.linkColor?.withAlpha(150),\n                fontSize: 13,\n              ),\n            ),\n          ],\n        ),\n        bottomButtonOnPressed: () async {\n          final examples = await APIServer().exampleByTag('artistic-wordart');\n          if (examples.isEmpty) {\n            return;\n          }\n\n          // 随机选取一个例子\n          final example = examples[Random().nextInt(examples.length)];\n          promptController.text = example.text;\n        },\n      ),\n    ];\n  }\n\n  List<PromptTag> selectedTags = [];\n\n  void onGenerate() async {\n    FocusScope.of(context).requestFocus(FocusNode());\n    HapticFeedbackHelper.mediumImpact();\n\n    final prompt = promptController.text.trim();\n    if (prompt.isEmpty) {\n      showErrorMessage(AppLocale.contentIsRequired.getString(context));\n      return;\n    }\n\n    final text = textController.text.trim();\n    if (text.isEmpty) {\n      showErrorMessage('文本内容不能为空');\n      return;\n    }\n\n    var params = <String, dynamic>{\n      'prompt': prompt,\n      'prompt_tags': selectedTags.map((e) => e.value).join(','),\n      'gallery_copy_id': widget.galleryCopyId,\n      'text': text,\n      'type': 'word_art',\n      'image_count': generationImageCount,\n      'style_preset': selectedStyle?.id,\n      'font_name': selectedFonts?.id,\n    };\n\n    final cancel = BotToast.showCustomLoading(\n      toastBuilder: (cancel) {\n        return const LoadingIndicator(\n          message: '思考中，请稍候...',\n        );\n      },\n      allowClick: false,\n      duration: const Duration(seconds: 15),\n    );\n\n    request(int waitDuration) async {\n      try {\n        final taskId = await APIServer().creativeIslandArtisticTextCompletionsAsyncV2(params);\n\n        stopPeriodQuery = false;\n\n        cancel();\n        Navigator.push(\n          // ignore: use_build_context_synchronously\n          context,\n          MaterialPageRoute(\n            fullscreenDialog: true,\n            builder: (context) => DrawResultPage(\n              future: Future.delayed(const Duration(seconds: 10), () async {\n                return await queryCompletionTaskStatus(\n                  taskId: taskId,\n                  retryTimes: 0,\n                  delaySeconds: 3,\n                  params: params,\n                );\n              }),\n              waitDuration: waitDuration,\n            ),\n          ),\n        ).whenComplete(() {\n          stopPeriodQuery = true;\n        });\n      } catch (e) {\n        stopPeriodQuery = true;\n        cancel();\n        // ignore: use_build_context_synchronously\n        showErrorMessageEnhanced(context, e);\n      }\n    }\n\n    try {\n      request(45);\n    } catch (e) {\n      cancel();\n      // ignore: use_build_context_synchronously\n      showErrorMessageEnhanced(context, e);\n    }\n  }\n\n  Future<IslandResult> queryCompletionTaskStatus({\n    required String taskId,\n    required int retryTimes,\n    required int delaySeconds,\n    Map<String, dynamic>? params,\n  }) async {\n    if (retryTimes > 60) {\n      return Future.error(AppLocale.generateTimeout.getString(context));\n    }\n\n    final resp = await APIServer().asyncTaskStatus(taskId);\n    switch (resp.status) {\n      case 'success':\n        if (params != null && resp.originImage != null && resp.originImage != '') {\n          params['image'] = resp.originImage;\n        }\n        return IslandResult(\n          result: resp.resources ?? const [],\n          params: params,\n        );\n      case 'failed':\n        return Future.error(resp.errors!.join(\";\"));\n      default:\n        if (stopPeriodQuery) {\n          // ignore: use_build_context_synchronously\n          return Future.error(AppLocale.generateTimeout.getString(context));\n        }\n\n        return await Future.delayed(Duration(seconds: delaySeconds), () async {\n          return await queryCompletionTaskStatus(\n            taskId: taskId,\n            retryTimes: retryTimes + 1,\n            delaySeconds: 3,\n            params: params,\n          );\n        });\n    }\n  }\n}\n"
  },
  {
    "path": "lib/page/creative_island/draw/components/artistic_style_selector.dart",
    "content": "import 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/enhanced_input.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/api/creative.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\n\nclass ArtisticStyleSelector extends StatelessWidget {\n  final List<CreativeIslandArtisticStyle> styles;\n  final Function(CreativeIslandArtisticStyle style) onSelected;\n  final CreativeIslandArtisticStyle? selectedStyle;\n  final String? title;\n\n  const ArtisticStyleSelector({\n    super.key,\n    required this.styles,\n    required this.onSelected,\n    this.selectedStyle,\n    this.title,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return EnhancedInput(\n      title: Text(\n        title ?? AppLocale.style.getString(context),\n        style: TextStyle(\n          color: customColors.textfieldLabelColor,\n          fontSize: 16,\n        ),\n      ),\n      value: Row(\n        mainAxisAlignment: MainAxisAlignment.end,\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          // Text(selectedStyle == null || selectedStyle!.name == ''\n          //     ? AppLocale.auto.getString(context)\n          //     : selectedStyle!.name),\n          // const SizedBox(width: 10),\n          _buildImageStyleItemPreview(\n            customColors,\n            selectedStyle == null ? CreativeIslandArtisticStyle(id: '', name: '', previewImage: '') : selectedStyle!,\n            size: 50,\n          ),\n        ],\n      ),\n      onPressed: () {\n        openModalBottomSheet(\n          context,\n          (context) {\n            return GridView.count(\n              crossAxisCount: 3,\n              crossAxisSpacing: 20,\n              mainAxisSpacing: 20,\n              padding: const EdgeInsets.only(top: 20, bottom: 20),\n              children: [\n                for (var item in [CreativeIslandArtisticStyle(id: '', name: '自动', previewImage: ''), ...styles])\n                  InkWell(\n                    onTap: () {\n                      onSelected(item);\n\n                      Navigator.pop(context);\n                    },\n                    child: Column(\n                      children: [\n                        Expanded(\n                          child: AspectRatio(\n                            aspectRatio: 1,\n                            child: _buildImageStyleItemPreview(\n                              customColors,\n                              item,\n                              showSelected: true,\n                            ),\n                          ),\n                        ),\n                        const SizedBox(height: 10),\n                        Text(\n                          item.name,\n                          style: const TextStyle(fontSize: 12),\n                        ),\n                      ],\n                    ),\n                  ),\n              ],\n            );\n          },\n          heightFactor: 0.8,\n        );\n      },\n    );\n  }\n\n  Widget _buildImageStyleItemPreview(\n    CustomColors customColors,\n    CreativeIslandArtisticStyle style, {\n    double? size,\n    bool showSelected = false,\n  }) {\n    return Container(\n        width: size,\n        height: size,\n        decoration: BoxDecoration(\n          borderRadius: CustomSize.borderRadius,\n          border: showSelected && (selectedStyle != null && style.id == selectedStyle!.id)\n              ? Border.all(\n                  color: customColors.linkColor ?? Colors.green,\n                  width: 1,\n                )\n              : null,\n          image: style.previewImage != null && style.previewImage != ''\n              ? DecorationImage(\n                  image: CachedNetworkImageProviderEnhanced(style.previewImage!),\n                  fit: BoxFit.cover,\n                )\n              : null,\n        ),\n        child: style.previewImage == ''\n            ? const Center(\n                child: Icon(\n                  Icons.interests,\n                  color: Colors.grey,\n                  size: 40,\n                ),\n              )\n            : null);\n  }\n}\n"
  },
  {
    "path": "lib/page/creative_island/draw/components/box.dart",
    "content": "import 'dart:ui';\n\nimport 'package:askaide/helper/color.dart';\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/repo/api/creative.dart';\nimport 'package:flutter/material.dart';\nimport 'package:go_router/go_router.dart';\n\n/// 创作岛列表项目\nclass CreativeIslandBox extends StatelessWidget {\n  final CreativeIslandItem item;\n  final Color? backgroundColor;\n  const CreativeIslandBox({super.key, required this.item, this.backgroundColor});\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),\n      child: Stack(\n        children: [\n          Container(\n            // width: MediaQuery.of(context).size.width / 2 - 20,\n            // height: 80,\n            decoration: BoxDecoration(\n              color: backgroundColor,\n              borderRadius: CustomSize.borderRadius,\n              image: item.bgImage != null\n                  ? DecorationImage(\n                      image: CachedNetworkImageProviderEnhanced(item.bgImage!),\n                      fit: BoxFit.cover,\n                    )\n                  : null,\n            ),\n            child: Material(\n              color: Colors.transparent,\n              child: InkWell(\n                borderRadius: CustomSize.borderRadiusAll,\n                onTap: () {\n                  HapticFeedbackHelper.lightImpact();\n                  context.push('/creative-island/${item.id}/create');\n                },\n                child: item.bgImage != null\n                    ? Column(\n                        mainAxisAlignment: MainAxisAlignment.end,\n                        children: [\n                          Container(\n                            decoration: BoxDecoration(\n                              color: Colors.white.withAlpha(60),\n                              borderRadius: const BorderRadius.only(\n                                  bottomLeft: CustomSize.radius, bottomRight: CustomSize.radius),\n                            ),\n                            child: ClipRect(\n                              child: BackdropFilter(\n                                filter: ImageFilter.blur(\n                                  sigmaX: 1.0,\n                                  sigmaY: 1.0,\n                                ),\n                                child: Center(\n                                  child: Container(\n                                    padding: const EdgeInsets.all(8),\n                                    child: Text(\n                                      item.title,\n                                      style: TextStyle(\n                                        fontSize: 18,\n                                        fontWeight: FontWeight.bold,\n                                        color: item.titleColor != null ? stringToColor(item.titleColor!) : Colors.white,\n                                        shadows: [\n                                          Shadow(\n                                            color: const Color.fromARGB(255, 161, 161, 161).withOpacity(0.5),\n                                            offset: const Offset(2, 2),\n                                            blurRadius: 5,\n                                          ),\n                                        ],\n                                      ),\n                                    ),\n                                  ),\n                                ),\n                              ),\n                            ),\n                          ),\n                        ],\n                      )\n                    : Center(\n                        child: Text(\n                          item.title,\n                          style: TextStyle(\n                            color: item.titleColor != null\n                                ? stringToColor(item.titleColor!)\n                                : Theme.of(context).textTheme.bodyMedium!.color,\n                          ),\n                        ),\n                      ),\n              ),\n            ),\n          ),\n          if (item.label != null)\n            Positioned(\n              top: 0,\n              right: 0,\n              child: Container(\n                padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),\n                decoration: BoxDecoration(\n                  borderRadius: const BorderRadius.only(topRight: CustomSize.radius, bottomLeft: CustomSize.radius),\n                  color: item.labelColor != null\n                      ? stringToColor(item.labelColor!)\n                      : const Color.fromARGB(255, 230, 173, 58),\n                ),\n                child: Text(\n                  item.label!,\n                  style: const TextStyle(\n                    fontSize: 10,\n                    color: Colors.white,\n                  ),\n                ),\n              ),\n            ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/creative_island/draw/components/content_preview.dart",
    "content": "import 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/image.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/image_preview.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/video_player.dart';\nimport 'package:askaide/repo/api/creative.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:intl/intl.dart';\n\nclass CreativeIslandContentPreview extends StatefulWidget {\n  final CustomColors customColors;\n  final CreativeItemInServer? item;\n  final String? prompt;\n  final IslandResult result;\n\n  const CreativeIslandContentPreview({\n    super.key,\n    required this.customColors,\n    this.item,\n    this.prompt,\n    required this.result,\n  });\n\n  @override\n  State<CreativeIslandContentPreview> createState() => _CreativeIslandContentPreviewState();\n}\n\nclass _CreativeIslandContentPreviewState extends State<CreativeIslandContentPreview> {\n  var currentTime = DateTime.now().add(const Duration(days: 7));\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n    final expireTime = widget.item != null\n        ? DateFormat('y-MM-dd').format(widget.item!.createdAt!.add(const Duration(days: 7)).toLocal())\n        : DateFormat('y-MM-dd').format(currentTime);\n    return widget.result.text == ''\n        ? const Center(\n            child: Text('生成结果将在这里展示'),\n          )\n        : SingleChildScrollView(\n            child: Column(\n              crossAxisAlignment: CrossAxisAlignment.start,\n              mainAxisAlignment: MainAxisAlignment.start,\n              mainAxisSize: MainAxisSize.min,\n              children: [\n                Container(\n                  margin: const EdgeInsets.only(\n                    top: 5,\n                    bottom: 5,\n                    left: 10,\n                  ),\n                  child: Row(\n                    mainAxisSize: MainAxisSize.min,\n                    crossAxisAlignment: CrossAxisAlignment.center,\n                    children: [\n                      Icon(\n                        Icons.info_outline,\n                        size: 14,\n                        color: customColors.weakTextColor,\n                      ),\n                      const SizedBox(width: 5),\n                      Text(\n                        '${AppLocale.clickToShareWithExpire.getString(context)} $expireTime',\n                        maxLines: 2,\n                        style: TextStyle(\n                          fontSize: 12,\n                          color: customColors.weakTextColor,\n                        ),\n                      ),\n                    ],\n                  ),\n                ),\n                ...(widget.result.result\n                    .map(\n                      (e) => Padding(\n                        padding: const EdgeInsets.symmetric(\n                          vertical: 5,\n                          horizontal: 10,\n                        ),\n                        child: (widget.item != null && (widget.item!.isVideoType)) || e.endsWith('.mp4')\n                            ? _buildVideoPreviewer(\n                                widget.result.params ?? {},\n                                e,\n                              )\n                            : _buildImagePreviewer(\n                                widget.result.params ?? {},\n                                e,\n                              ),\n                      ),\n                    )\n                    .toList()),\n              ],\n            ),\n          );\n  }\n\n  Widget _buildVideoPreviewer(\n    Map<String, dynamic> params,\n    String e,\n  ) {\n    int? width = params['width'] == null ? null : params['width'] as int;\n    int? height = params['height'] == null ? null : params['height'] as int;\n\n    return VideoPlayer(\n      url: e,\n      width: width,\n      height: height,\n    );\n  }\n\n  Widget _buildImagePreviewer(\n    Map<String, dynamic> params,\n    String e,\n  ) {\n    return NetworkImagePreviewer(\n      url: e,\n      preview: imageURL(e, qiniuImageTypeThumb),\n      original: params['image'] == null || params['image'] == '' ? null : params['image'] as String,\n      description: widget.prompt ?? widget.item?.prompt ?? '',\n    );\n  }\n}\n\nclass IslandResult {\n  final List<String> result;\n  final Map<String, dynamic>? params;\n\n  bool hasImageParam() {\n    return params != null && params!['image'] != null && params!['image'] != '';\n  }\n\n  String get text {\n    return result.map((e) => '![image]($e)').join(\"\\n\\n\");\n  }\n\n  IslandResult({\n    required this.result,\n    this.params,\n  });\n}\n"
  },
  {
    "path": "lib/page/creative_island/draw/components/creative_item.dart",
    "content": "import 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/prompt_tags_selector.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\n\nclass CreativeItem extends StatelessWidget {\n  final String imageURL;\n  final String title;\n  final Color? titleColor;\n  final String? tag;\n  final Function() onTap;\n  final String size;\n  const CreativeItem({\n    super.key,\n    required this.imageURL,\n    required this.title,\n    required this.onTap,\n    this.titleColor,\n    this.tag,\n    required this.size,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return Material(\n      borderRadius: CustomSize.borderRadius,\n      child: InkWell(\n        borderRadius: CustomSize.borderRadiusAll,\n        onTap: onTap,\n        child: Stack(\n          children: [\n            SizedBox(\n              width: double.infinity,\n              height: double.infinity,\n              child: ClipRRect(\n                borderRadius: CustomSize.borderRadius,\n                child: CachedNetworkImageEnhanced(\n                  imageUrl: imageURL,\n                  fit: BoxFit.cover,\n                ),\n              ),\n            ),\n            if (size == 'large')\n              Positioned(\n                left: 20,\n                top: 20,\n                child: Row(\n                  children: [\n                    Text(\n                      title,\n                      style: TextStyle(\n                        color: titleColor ?? Colors.white,\n                        fontSize: 30,\n                      ),\n                    ),\n                    const SizedBox(width: 10),\n                    if (tag != null && tag != '')\n                      Tag(\n                        name: tag!,\n                        backgroundColor: customColors.linkColor,\n                        fontsize: 10,\n                      ),\n                  ],\n                ),\n              )\n            else if (size == 'medium')\n              Positioned(\n                left: 20,\n                top: 25,\n                child: Row(\n                  children: [\n                    Text(\n                      title,\n                      style: TextStyle(\n                        color: titleColor ?? Colors.white,\n                        fontSize: 20,\n                      ),\n                    ),\n                    const SizedBox(width: 10),\n                    if (tag != null && tag != '')\n                      ScaleTransition(\n                        scale: const AlwaysStoppedAnimation(0.5),\n                        child: Tag(\n                          name: tag!,\n                          backgroundColor: customColors.linkColor,\n                          fontsize: 10,\n                        ),\n                      ),\n                  ],\n                ),\n              )\n            else\n              Center(\n                child: Row(\n                  mainAxisSize: MainAxisSize.min,\n                  children: [\n                    if (tag != null && tag != '') const SizedBox(width: 20),\n                    Text(\n                      title,\n                      style: TextStyle(\n                        color: titleColor ?? Colors.white,\n                        fontSize: 20,\n                      ),\n                    ),\n                    if (tag != null && tag != '')\n                      ScaleTransition(\n                        scale: const AlwaysStoppedAnimation(0.5),\n                        child: Tag(\n                          name: tag!,\n                          backgroundColor: customColors.linkColor,\n                          fontsize: 10,\n                        ),\n                      ),\n                  ],\n                ),\n              ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/creative_island/draw/components/image_selector.dart",
    "content": "import 'dart:typed_data';\n\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:file_picker/file_picker.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\n\nclass ImageSelector extends StatelessWidget {\n  final String? title;\n  final Widget? titleHelper;\n  final Function({String? path, Uint8List? data}) onImageSelected;\n  final String? selectedImagePath;\n  final Uint8List? selectedImageData;\n  final double? height;\n  const ImageSelector({\n    super.key,\n    this.title,\n    required this.onImageSelected,\n    this.selectedImagePath,\n    this.selectedImageData,\n    this.height,\n    this.titleHelper,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: [\n        if (title != null)\n          Row(\n            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n            children: [\n              Row(\n                children: [\n                  Text(\n                    title!,\n                    style: TextStyle(\n                      fontSize: 16,\n                      color: customColors.textfieldLabelColor,\n                    ),\n                  ),\n                  const SizedBox(width: 5),\n                  if (titleHelper != null) titleHelper!,\n                ],\n              ),\n            ],\n          ),\n        if (title != null) const SizedBox(height: 10),\n        Material(\n          borderRadius: CustomSize.borderRadius,\n          color: customColors.backgroundColor,\n          child: InkWell(\n            borderRadius: CustomSize.borderRadiusAll,\n            onTap: () async {\n              HapticFeedbackHelper.mediumImpact();\n              FilePickerResult? result = await FilePicker.platform.pickFiles(type: FileType.image);\n              if (result != null && result.files.isNotEmpty) {\n                if (PlatformTool.isWeb()) {\n                  onImageSelected(data: result.files.first.bytes!);\n                } else {\n                  onImageSelected(path: result.files.first.path!);\n                }\n              }\n            },\n            child: Container(\n                decoration: BoxDecoration(borderRadius: CustomSize.borderRadius),\n                child: ClipRRect(\n                  borderRadius: CustomSize.borderRadius,\n                  child: Stack(\n                    children: [\n                      Container(\n                        decoration: (selectedImagePath != null && selectedImagePath!.isNotEmpty) ||\n                                (selectedImageData != null && selectedImageData!.isNotEmpty)\n                            ? BoxDecoration(\n                                image: DecorationImage(\n                                  image: (selectedImagePath != null\n                                      ? resolveImageProvider(selectedImagePath!)\n                                      : (selectedImageData != null ? MemoryImage(selectedImageData!) : null))!,\n                                  fit: BoxFit.cover,\n                                ),\n                                color: customColors.backgroundContainerColor?.withAlpha(100),\n                                borderRadius: CustomSize.borderRadius,\n                              )\n                            : null,\n                        child: SizedBox(\n                          width: double.infinity,\n                          height: height ?? 200,\n                        ),\n                      ),\n                      selectedImagePath == null || selectedImagePath!.isEmpty\n                          ? SizedBox(\n                              height: height ?? 200,\n                              child: Row(\n                                mainAxisAlignment: MainAxisAlignment.center,\n                                children: [\n                                  Icon(\n                                    Icons.camera_alt,\n                                    size: 30,\n                                    color: customColors.chatInputPanelText,\n                                  ),\n                                  const SizedBox(width: 10),\n                                  Text(\n                                    AppLocale.selectImage.getString(context),\n                                    style: TextStyle(\n                                      fontSize: 14,\n                                      fontWeight: FontWeight.w500,\n                                      color: customColors.chatInputPanelText?.withOpacity(0.8),\n                                    ),\n                                  ),\n                                ],\n                              ),\n                            )\n                          : Positioned(\n                              bottom: 0,\n                              left: 0,\n                              right: 0,\n                              child: Container(\n                                color: const Color.fromARGB(80, 255, 255, 255),\n                                height: 50,\n                                child: Row(\n                                  mainAxisAlignment: MainAxisAlignment.center,\n                                  children: [\n                                    const Icon(\n                                      Icons.camera_alt,\n                                      size: 30,\n                                      color: Color.fromARGB(147, 255, 255, 255),\n                                    ),\n                                    const SizedBox(width: 10),\n                                    Text(\n                                      AppLocale.clickSwitchImage.getString(context),\n                                      style: const TextStyle(\n                                        fontSize: 14,\n                                        fontWeight: FontWeight.w500,\n                                        color: Color.fromARGB(147, 255, 255, 255),\n                                      ),\n                                    ),\n                                  ],\n                                ),\n                              ),\n                            )\n                    ],\n                  ),\n                )),\n          ),\n        ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/creative_island/draw/components/image_size.dart",
    "content": "import 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\n\nclass ImageSize extends StatelessWidget {\n  final String aspectRatio;\n  const ImageSize({super.key, required this.aspectRatio});\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    final widthFactor = int.parse(aspectRatio.split(':')[0]);\n    final heightFactor = int.parse(aspectRatio.split(':')[1]);\n\n    var width = 0.0;\n    var height = 0.0;\n\n    if (widthFactor > heightFactor) {\n      width = 40;\n      height = 40 / widthFactor * heightFactor;\n    } else {\n      height = 40;\n      width = 40 / heightFactor * widthFactor;\n    }\n\n    return Container(\n      width: width,\n      height: height,\n      decoration: BoxDecoration(\n        borderRadius: CustomSize.borderRadius,\n        color: customColors.backgroundContainerColor,\n      ),\n      alignment: Alignment.center,\n      child: Text(\n        aspectRatio,\n        style: const TextStyle(fontSize: 12, color: Colors.white),\n        textAlign: TextAlign.center,\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/creative_island/draw/components/image_style_selector.dart",
    "content": "import 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/enhanced_input.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/api/creative.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\n\nclass ImageStyleSelector extends StatelessWidget {\n  final List<CreativeIslandImageFilter> styles;\n  final Function(CreativeIslandImageFilter style) onSelected;\n  final CreativeIslandImageFilter? selectedStyle;\n\n  const ImageStyleSelector({\n    super.key,\n    required this.styles,\n    required this.onSelected,\n    this.selectedStyle,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return EnhancedInput(\n      title: Text(\n        AppLocale.style.getString(context),\n        style: TextStyle(\n          color: customColors.textfieldLabelColor,\n          fontSize: 16,\n        ),\n      ),\n      value: Row(\n        mainAxisAlignment: MainAxisAlignment.end,\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          // Text(selectedStyle == null || selectedStyle!.name == ''\n          //     ? AppLocale.auto.getString(context)\n          //     : selectedStyle!.name),\n          // const SizedBox(width: 10),\n          _buildImageStyleItemPreview(\n            customColors,\n            selectedStyle == null ? CreativeIslandImageFilter(id: 0, name: '', previewImage: '') : selectedStyle!,\n            size: 50,\n          ),\n        ],\n      ),\n      onPressed: () {\n        openModalBottomSheet(\n          context,\n          (context) {\n            return GridView.count(\n              crossAxisCount: 3,\n              crossAxisSpacing: 20,\n              mainAxisSpacing: 20,\n              padding: const EdgeInsets.only(top: 20, bottom: 20),\n              children: [\n                for (var item in [CreativeIslandImageFilter(id: 0, name: '自动', previewImage: ''), ...styles])\n                  InkWell(\n                    onTap: () {\n                      onSelected(item);\n\n                      Navigator.pop(context);\n                    },\n                    child: Column(\n                      children: [\n                        Expanded(\n                          child: AspectRatio(\n                            aspectRatio: 1,\n                            child: _buildImageStyleItemPreview(\n                              customColors,\n                              item,\n                              showSelected: true,\n                            ),\n                          ),\n                        ),\n                        const SizedBox(height: 10),\n                        Text(\n                          item.name,\n                          style: const TextStyle(fontSize: 12),\n                        ),\n                      ],\n                    ),\n                  ),\n              ],\n            );\n          },\n          heightFactor: 0.8,\n        );\n      },\n    );\n  }\n\n  Widget _buildImageStyleItemPreview(\n    CustomColors customColors,\n    CreativeIslandImageFilter style, {\n    double? size,\n    bool showSelected = false,\n  }) {\n    return Container(\n        width: size,\n        height: size,\n        decoration: BoxDecoration(\n          borderRadius: CustomSize.borderRadius,\n          border: showSelected && (selectedStyle != null && style.id == selectedStyle!.id)\n              ? Border.all(\n                  color: customColors.linkColor ?? Colors.green,\n                  width: 1,\n                )\n              : null,\n          image: style.previewImage != ''\n              ? DecorationImage(\n                  image: CachedNetworkImageProviderEnhanced(style.previewImage),\n                  fit: BoxFit.cover,\n                )\n              : null,\n        ),\n        child: style.previewImage == ''\n            ? const Center(\n                child: Icon(\n                  Icons.interests,\n                  color: Colors.grey,\n                  size: 40,\n                ),\n              )\n            : null);\n  }\n}\n"
  },
  {
    "path": "lib/page/creative_island/draw/data/draw_history_datasource.dart",
    "content": "import 'package:askaide/helper/logger.dart';\nimport 'package:askaide/repo/api/creative.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:loading_more_list/loading_more_list.dart';\n\nclass DrawHistoryDatasource extends LoadingMoreBase<CreativeItemInServer> {\n  int pageindex = 1;\n  bool _hasMore = true;\n  bool forceRefresh = false;\n\n  @override\n  bool get hasMore => (_hasMore && length < 300) || forceRefresh;\n\n  @override\n  Future<bool> loadData([bool isloadMoreAction = false]) async {\n    try {\n      final resp = await APIServer().creativeHistories(\n        mode: 'image-draw',\n        page: pageindex,\n        perPage: 30,\n      );\n      if (pageindex == 1) {\n        clear();\n      }\n\n      for (var element in resp.data) {\n        add(element);\n      }\n\n      if (resp.page == resp.lastPage) {\n        _hasMore = false;\n      }\n\n      pageindex = resp.page + 1;\n      return true;\n    } catch (e) {\n      Logger.instance.e(e);\n      return false;\n    }\n  }\n\n  @override\n  Future<bool> refresh([bool notifyStateChanged = false]) async {\n    _hasMore = true;\n    pageindex = 1;\n    //force to refresh list when you don't want clear list before request\n    //for the case, if your list already has 20 items.\n    forceRefresh = !notifyStateChanged;\n    var result = await super.refresh(notifyStateChanged);\n    forceRefresh = false;\n    return result;\n  }\n}\n"
  },
  {
    "path": "lib/page/creative_island/draw/draw_create.dart",
    "content": "import 'dart:math';\n\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/cache.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/helper/upload.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/advanced_button.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/enhanced_input.dart';\nimport 'package:askaide/page/component/enhanced_textfield.dart';\nimport 'package:askaide/page/component/global_alert.dart';\nimport 'package:askaide/page/component/item_selector_search.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/prompt_tags_selector.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/page/creative_island/draw/components/content_preview.dart';\nimport 'package:askaide/page/creative_island/draw/draw_result.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/creative_island/draw/components/image_selector.dart';\nimport 'package:askaide/page/creative_island/draw/components/image_size.dart';\nimport 'package:askaide/page/creative_island/draw/components/image_style_selector.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/api/creative.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:quickalert/models/quickalert_type.dart';\n\nclass DrawCreateScreen extends StatefulWidget {\n  final SettingRepository setting;\n  final int? galleryCopyId;\n  final String mode;\n  final String id;\n  final String? note;\n  final String? initImage;\n\n  const DrawCreateScreen({\n    super.key,\n    required this.id,\n    required this.setting,\n    this.galleryCopyId,\n    required this.mode,\n    this.note,\n    this.initImage,\n  });\n\n  @override\n  State<DrawCreateScreen> createState() => _DrawCreateScreenState();\n}\n\nclass _DrawCreateScreenState extends State<DrawCreateScreen> {\n  String? selectedImagePath;\n  Uint8List? selectedImageData;\n\n  bool enableAIRewrite = false;\n  int generationImageCount = 1;\n  CreativeIslandVendorModel? selectedModel;\n  String? upscaleBy;\n  String selectedImageSize = '1:1';\n  bool showAdvancedOptions = false;\n  CreativeIslandImageFilter? selectedStyle;\n  double? imageStrength = 0.65;\n\n  /// 是否停止周期性查询任务执行状态\n  var stopPeriodQuery = false;\n  CreativeIslandCapacity? capacity;\n\n  TextEditingController promptController = TextEditingController();\n  TextEditingController negativePromptController = TextEditingController();\n  TextEditingController seedController = TextEditingController();\n\n  /// 是否强制显示 negativePrompt\n  bool forceShowNegativePrompt = false;\n\n  @override\n  void dispose() {\n    promptController.dispose();\n    negativePromptController.dispose();\n    seedController.dispose();\n    super.dispose();\n  }\n\n  @override\n  void initState() {\n    if (widget.initImage != null) {\n      selectedImagePath = widget.initImage;\n    }\n\n    APIServer().creativeIslandCapacity(mode: widget.mode, id: widget.id).then((cap) {\n      setState(() {\n        capacity = cap;\n      });\n\n      if (widget.galleryCopyId != null && widget.galleryCopyId! > 0) {\n        APIServer().creativeGalleryItem(id: widget.galleryCopyId!).then((response) {\n          final gallery = response.item;\n          if (gallery.prompt != null && gallery.prompt!.isNotEmpty) {\n            promptController.text = gallery.prompt!;\n          }\n\n          if (gallery.negativePrompt != null && gallery.negativePrompt!.isNotEmpty) {\n            if (gallery.negativePrompt != null && gallery.negativePrompt!.isNotEmpty) {\n              forceShowNegativePrompt = true;\n            }\n\n            negativePromptController.text = gallery.negativePrompt!;\n          }\n\n          if (gallery.metaMap['model_id'] != null && gallery.metaMap['model_id'] != '') {\n            final matchedModels = capacity!.vendorModels\n                .where((e) => e.id == gallery.metaMap['model_id'] || e.id == 'model-${gallery.metaMap['model_id']}');\n            if (matchedModels.isNotEmpty) {\n              selectedModel = matchedModels.first;\n            }\n          }\n\n          if (gallery.metaMap['image_ratio'] != null && gallery.metaMap['image_ratio'] != '') {\n            selectedImageSize = gallery.metaMap['image_ratio']!;\n          }\n\n          if (gallery.metaMap['filter_id'] != null && gallery.metaMap['filter_id'] > 0) {\n            final matchedStyles = capacity!.filters.where((e) => e.id == gallery.metaMap['filter_id']);\n            if (matchedStyles.isNotEmpty) {\n              selectedStyle = matchedStyles.first;\n            }\n          }\n\n          if (gallery.metaMap['real_prompt'] != null && gallery.metaMap['real_prompt'] != '') {\n            promptController.text = gallery.metaMap['real_prompt']!;\n          }\n\n          if (gallery.metaMap['negative_prompt'] != null && gallery.metaMap['negative_prompt'] != '') {\n            negativePromptController.text = gallery.metaMap['negative_prompt']!;\n          }\n\n          if (gallery.metaMap['real_negative_prompt'] != null && gallery.metaMap['real_negative_prompt'] != '') {\n            negativePromptController.text = gallery.metaMap['real_negative_prompt']!;\n          }\n\n          // 创建同款时，默认关闭 AI 优化，除非该同款包含 ai_rewrite 的设定\n          enableAIRewrite = false;\n          if ((gallery.metaMap['real_prompt'] == null || gallery.metaMap['real_prompt'] == '') &&\n              gallery.metaMap['ai_rewrite'] != null &&\n              gallery.metaMap['ai_rewrite']) {\n            enableAIRewrite = gallery.metaMap['ai_rewrite'];\n          }\n\n          setState(() {});\n        });\n      }\n    });\n\n    if (widget.note != null) {\n      Cache().boolGet(key: 'creative:tutorials:${widget.mode}:dialog').then((show) {\n        if (!show) {\n          return;\n        }\n\n        openDefaultTutorials(onConfirm: () {\n          Cache().setBool(\n            key: 'creative:tutorials:${widget.mode}:dialog',\n            value: false,\n            duration: const Duration(days: 30),\n          );\n        });\n      });\n    }\n\n    super.initState();\n  }\n\n  void openDefaultTutorials({Function? onConfirm}) {\n    showBeautyDialog(\n      context,\n      type: QuickAlertType.info,\n      text: '     ${widget.note!}',\n      onConfirmBtnTap: () async {\n        onConfirm?.call();\n        context.pop();\n      },\n      showCancelBtn: true,\n      confirmBtnText: AppLocale.gotIt.getString(context),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          title: Text(\n            widget.mode == 'image-to-image'\n                ? AppLocale.imageToImage.getString(context)\n                : AppLocale.textToImage.getString(context),\n            style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n          leading: IconButton(\n            onPressed: () {\n              context.pop();\n            },\n            icon: const Icon(Icons.arrow_back_ios),\n          ),\n          toolbarHeight: CustomSize.toolbarHeight,\n          backgroundColor: customColors.backgroundColor,\n          actions: [\n            if (widget.note != null)\n              IconButton(\n                onPressed: () {\n                  openDefaultTutorials();\n                },\n                icon: const Icon(Icons.help_outline),\n              )\n          ],\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          maxWidth: CustomSize.smallWindowSize,\n          backgroundColor: customColors.backgroundColor,\n          child: Column(\n            children: [\n              if (Ability().showGlobalAlert) const GlobalAlert(pageKey: 'creative_create'),\n              Expanded(\n                child: Container(\n                  padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),\n                  height: double.infinity,\n                  child: SingleChildScrollView(\n                    child: buildEditPanel(context, customColors),\n                  ),\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget buildEditPanel(BuildContext context, CustomColors customColors) {\n    return SafeArea(\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: [\n          ColumnBlock(\n            innerPanding: 10,\n            padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),\n            children: [\n              // 上传图片\n              if (widget.mode == 'image-to-image')\n                ImageSelector(\n                  onImageSelected: ({path, data}) {\n                    if (path != null) {\n                      setState(() {\n                        selectedImagePath = path;\n                        selectedImageData = null;\n                      });\n                    }\n\n                    if (data != null) {\n                      setState(() {\n                        selectedImageData = data;\n                        selectedImagePath = null;\n                      });\n                    }\n                  },\n                  selectedImagePath: selectedImagePath,\n                  selectedImageData: selectedImageData,\n                  title: AppLocale.referenceImage.getString(context),\n                  height: _calImageSelectorHeight(context),\n                  titleHelper: InkWell(\n                    onTap: () {\n                      showBeautyDialog(\n                        context,\n                        type: QuickAlertType.info,\n                        text: AppLocale.referenceImageNote.getString(context),\n                        confirmBtnText: AppLocale.gotIt.getString(context),\n                        showCancelBtn: false,\n                      );\n                    },\n                    child: Icon(\n                      Icons.help_outline,\n                      size: 16,\n                      color: customColors.weakLinkColor?.withAlpha(150),\n                    ),\n                  ),\n                ),\n\n              // 图片风格\n              if (capacity != null && capacity!.showStyle && capacity!.filters.isNotEmpty)\n                ImageStyleSelector(\n                  styles: capacity!.filters,\n                  onSelected: (style) {\n                    setState(() {\n                      selectedStyle = style;\n                    });\n                  },\n                  selectedStyle: selectedStyle,\n                ),\n            ],\n          ),\n          ColumnBlock(\n            innerPanding: 10,\n            padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),\n            children: [\n              // 生成内容\n              if (widget.mode == 'text-to-image') ...buildPromptField(customColors),\n              // AI 优化配置\n              if (capacity != null && capacity!.showAIRewrite && widget.mode != 'image-to-image')\n                Row(\n                  mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                  children: [\n                    Row(\n                      children: [\n                        Text(\n                          AppLocale.smartOptimization.getString(context),\n                          style: const TextStyle(fontSize: 16),\n                        ),\n                        const SizedBox(width: 5),\n                        InkWell(\n                          onTap: () {\n                            showBeautyDialog(\n                              context,\n                              type: QuickAlertType.info,\n                              text: AppLocale.onceEnabledSmartOptimization.getString(context),\n                              confirmBtnText: AppLocale.gotIt.getString(context),\n                              showCancelBtn: false,\n                            );\n                          },\n                          child: Icon(\n                            Icons.help_outline,\n                            size: 16,\n                            color: customColors.weakLinkColor?.withAlpha(150),\n                          ),\n                        ),\n                      ],\n                    ),\n                    CupertinoSwitch(\n                      activeColor: customColors.linkColor,\n                      value: enableAIRewrite,\n                      onChanged: (value) {\n                        setState(() {\n                          enableAIRewrite = value;\n                        });\n                      },\n                    ),\n                  ],\n                ),\n            ],\n          ),\n\n          if (showAdvancedOptions)\n            ColumnBlock(\n              innerPanding: 10,\n              padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),\n              children: [\n                if (widget.mode == 'image-to-image' && capacity != null && capacity!.showPromptForImage2Image)\n                  ...buildPromptField(customColors),\n                // 反向提示语\n                if ((capacity != null && capacity!.showNegativeText) || forceShowNegativePrompt)\n                  EnhancedTextField(\n                    labelPosition: LabelPosition.top,\n                    labelText: AppLocale.excludeContents.getString(context),\n                    customColors: customColors,\n                    controller: negativePromptController,\n                    textAlignVertical: TextAlignVertical.top,\n                    hintText: AppLocale.unwantedElements.getString(context),\n                    maxLength: 500,\n                    maxLines: 5,\n                    minLines: 3,\n                    showCounter: false,\n                  ),\n                // 原图相似度\n                if (capacity != null && capacity!.showImageStrength && widget.mode == 'image-to-image')\n                  Row(\n                    children: [\n                      Row(\n                        children: [\n                          Text(AppLocale.imagination.getString(context)),\n                          const SizedBox(width: 5),\n                          InkWell(\n                            onTap: () {\n                              showBeautyDialog(\n                                context,\n                                type: QuickAlertType.info,\n                                text: '想象力\\n\\n提高想象力，得到更有创造力的内容。降低想象力，效果与参考图更相似。',\n                                confirmBtnText: AppLocale.gotIt.getString(context),\n                                showCancelBtn: false,\n                              );\n                            },\n                            child: Icon(\n                              Icons.help_outline,\n                              size: 16,\n                              color: customColors.weakLinkColor?.withAlpha(150),\n                            ),\n                          ),\n                        ],\n                      ),\n                      const SizedBox(width: 10),\n                      Expanded(\n                        child: Slider(\n                          value: imageStrength ?? 0.65,\n                          min: 0,\n                          max: 1,\n                          divisions: 20,\n                          label: imageStrengthText(),\n                          activeColor: customColors.linkColor,\n                          onChanged: (value) {\n                            setState(() {\n                              imageStrength = value;\n                            });\n                          },\n                        ),\n                      ),\n                      Text(\n                        ((imageStrength ?? 0) * 100).toStringAsFixed(0),\n                        style: TextStyle(\n                          fontSize: 12,\n                          color: customColors.weakTextColor,\n                        ),\n                      ),\n                    ],\n                  ),\n                // 图片数量\n                if (capacity != null && capacity!.showImageCount && widget.mode != 'image-to-image')\n                  EnhancedInput(\n                    title: Text(\n                      AppLocale.imageCount.getString(context),\n                      style: TextStyle(\n                        color: customColors.textfieldLabelColor,\n                        fontSize: 16,\n                      ),\n                    ),\n                    value: Text(generationImageCount.toString()),\n                    onPressed: () {\n                      openListSelectDialog(\n                        context,\n                        <SelectorItem>[\n                          SelectorItem(const Text('1', textAlign: TextAlign.center), 1),\n                          SelectorItem(const Text('2', textAlign: TextAlign.center), 2),\n                          SelectorItem(const Text('3', textAlign: TextAlign.center), 3),\n                          SelectorItem(const Text('4', textAlign: TextAlign.center), 4),\n                        ],\n                        (value) {\n                          setState(() {\n                            generationImageCount = value.value;\n                          });\n                          return true;\n                        },\n                        heightFactor: 0.4,\n                        value: generationImageCount,\n                      );\n                    },\n                  ),\n                // 图片尺寸\n                if (capacity != null && capacity!.allowRatios.isNotEmpty && widget.mode != 'image-to-image')\n                  EnhancedInput(\n                    title: Text(\n                      AppLocale.imageSize.getString(context),\n                      style: TextStyle(\n                        color: customColors.textfieldLabelColor,\n                        fontSize: 16,\n                      ),\n                    ),\n                    value: ImageSize(aspectRatio: selectedImageSize),\n                    onPressed: () {\n                      openListSelectDialog(\n                        context,\n                        capacity!.allowRatios.map((e) => SelectorItem(ImageSize(aspectRatio: e), e)).toList(),\n                        (value) {\n                          setState(() {\n                            selectedImageSize = value.value;\n                          });\n\n                          return true;\n                        },\n                        value: selectedImageSize,\n                        heightFactor: 0.3,\n                        horizontal: true,\n                        horizontalCount: capacity!.allowRatios.length > 3 ? 4 : capacity!.allowRatios.length,\n                      );\n                    },\n                  ),\n              ],\n            ),\n          if (showAdvancedOptions)\n            ColumnBlock(\n              padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),\n              children: [\n                // 模型\n                if (capacity != null && capacity!.vendorModels.isNotEmpty)\n                  EnhancedInput(\n                    title: Text(\n                      AppLocale.model.getString(context),\n                      style: TextStyle(\n                        color: customColors.textfieldLabelColor,\n                        fontSize: 16,\n                      ),\n                    ),\n                    value: Container(\n                      alignment: Alignment.centerRight,\n                      width: MediaQuery.of(context).size.width - 200,\n                      child: Text(\n                        selectedModel?.name ?? '自动',\n                        overflow: TextOverflow.ellipsis,\n                      ),\n                    ),\n                    onPressed: () {\n                      openListSelectDialog(\n                        context,\n                        [\n                          SelectorItem(const Text('自动'), null),\n                          ...capacity!.vendorModels\n                              .map((e) => SelectorItem(\n                                    Stack(\n                                      children: [\n                                        Container(\n                                          padding: const EdgeInsets.only(top: 25, bottom: 10),\n                                          alignment: Alignment.center,\n                                          child: Text(\n                                            e.name,\n                                            textAlign: TextAlign.center,\n                                            style: const TextStyle(fontSize: 14),\n                                            textWidthBasis: TextWidthBasis.longestLine,\n                                          ),\n                                        ),\n                                        if (e.vendor != null && e.vendor!.isNotEmpty)\n                                          Positioned(\n                                            left: 0,\n                                            top: 0,\n                                            child: Container(\n                                              padding: const EdgeInsets.symmetric(\n                                                horizontal: 5,\n                                                vertical: 3,\n                                              ),\n                                              decoration: BoxDecoration(\n                                                borderRadius: CustomSize.borderRadius,\n                                                color: modelTypeTagColors[e.vendor!],\n                                              ),\n                                              child: Text(\n                                                e.vendor!,\n                                                style: const TextStyle(\n                                                  fontSize: 12,\n                                                  color: Colors.white,\n                                                ),\n                                              ),\n                                            ),\n                                          ),\n                                      ],\n                                    ),\n                                    e.id,\n                                    search: (keywrod) {\n                                      return e.name.contains(keywrod) ||\n                                          (e.vendor != null && e.vendor!.contains(keywrod));\n                                    },\n                                  ))\n                              .toList(),\n                        ],\n                        (value) {\n                          setState(() {\n                            if (value.value == null) {\n                              selectedModel = null;\n                              return;\n                            }\n\n                            selectedModel = capacity!.vendorModels.firstWhere((e) => e.id == value.value);\n                          });\n                          return true;\n                        },\n                        heightFactor: 0.8,\n                        value: selectedModel?.id,\n                        enableSearch: true,\n                        innerPadding: const EdgeInsets.symmetric(\n                          horizontal: 10,\n                          vertical: 0,\n                        ),\n                      );\n                    },\n                  ),\n                if (capacity != null &&\n                    capacity!.showUpscaleBy &&\n                    capacity!.allowUpscaleBy.isNotEmpty &&\n                    (selectedModel?.upscale ?? false))\n                  EnhancedInput(\n                    title: Text(\n                      'Upscale',\n                      style: TextStyle(\n                        color: customColors.textfieldLabelColor,\n                        fontSize: 16,\n                      ),\n                    ),\n                    value: Text(upscaleBy ?? '自动'),\n                    onPressed: () {\n                      openListSelectDialog(\n                        context,\n                        [\n                          SelectorItem(const Text('自动'), null),\n                          ...capacity!.allowUpscaleBy.map((e) => SelectorItem(Text(e), e)).toList(),\n                        ],\n                        (value) {\n                          setState(() {\n                            upscaleBy = value.value;\n                          });\n                          return true;\n                        },\n                        heightFactor: 0.5,\n                        value: upscaleBy,\n                      );\n                    },\n                  ),\n\n                // Seed\n                if (capacity != null && capacity!.showSeed)\n                  EnhancedTextField(\n                    controller: seedController,\n                    customColors: customColors,\n                    labelText: 'Seed',\n                    labelPosition: LabelPosition.left,\n                    showCounter: false,\n                    keyboardType: TextInputType.number,\n                    inputFormatters: [\n                      FilteringTextInputFormatter.digitsOnly,\n                    ],\n                    hintText: '默认随机',\n                    textDirection: TextDirection.rtl,\n                  ),\n              ],\n            ),\n\n          // 生成按钮\n          AdvancedButton(\n            showAdvancedOptions: showAdvancedOptions,\n            onPressed: (value) {\n              setState(() {\n                showAdvancedOptions = value;\n              });\n            },\n          ),\n          if (capacity != null) const SizedBox(height: 10),\n          EnhancedButton(\n            title: AppLocale.generate.getString(context),\n            onPressed: onGenerate,\n          ),\n          const SizedBox(height: 20),\n        ],\n      ),\n    );\n  }\n\n  List<Widget> buildPromptField(CustomColors customColors) {\n    return [\n      EnhancedTextField(\n        labelPosition: LabelPosition.top,\n        labelText: AppLocale.yourIdeas.getString(context),\n        customColors: customColors,\n        controller: promptController,\n        textAlignVertical: TextAlignVertical.top,\n        hintText: AppLocale.keywordsSeparatedByCommas.getString(context),\n        maxLines: 10,\n        minLines: 2,\n        maxLength: 460,\n        showCounter: false,\n        inputSelector: IconButton(\n          onPressed: () {\n            openModalBottomSheet(\n              context,\n              (context) {\n                return PromptTagsSelector(\n                  selectedTags: selectedTags,\n                  onSubmit: (tags) {\n                    setState(() {\n                      selectedTags = tags;\n                    });\n                    context.pop();\n                  },\n                );\n              },\n              heightFactor: 0.8,\n              useSafeArea: true,\n            );\n          },\n          icon: Icon(\n            Icons.lightbulb_outline,\n            color: customColors.linkColor,\n            size: 16,\n          ),\n        ),\n        middleWidget: Container(\n          width: double.infinity,\n          margin: const EdgeInsets.only(bottom: 30),\n          child: Wrap(\n            spacing: 3,\n            runSpacing: 3,\n            children: selectedTags\n                .map(\n                  (e) => Tag(\n                    name: e.name,\n                    backgroundColor: customColors.linkColor,\n                    textColor: Colors.white,\n                    fontsize: 10,\n                    onDeleted: () {\n                      setState(() {\n                        selectedTags.remove(e);\n                      });\n                    },\n                  ),\n                )\n                .toList(),\n          ),\n        ),\n        bottomButton: Row(\n          children: [\n            Icon(\n              Icons.shuffle,\n              size: 13,\n              color: customColors.linkColor?.withAlpha(150),\n            ),\n            const SizedBox(width: 5),\n            Text(\n              AppLocale.random.getString(context),\n              style: TextStyle(\n                color: customColors.linkColor?.withAlpha(150),\n                fontSize: 13,\n              ),\n            ),\n          ],\n        ),\n        bottomButtonOnPressed: () async {\n          final examples = await APIServer().exampleByTag('image-generation');\n          if (examples.isEmpty) {\n            return;\n          }\n\n          // 随机选取一个例子\n          final example = examples[Random().nextInt(examples.length)];\n          promptController.text = example.text;\n        },\n      ),\n    ];\n  }\n\n  List<PromptTag> selectedTags = [];\n\n  void onGenerate() async {\n    FocusScope.of(context).requestFocus(FocusNode());\n    HapticFeedbackHelper.mediumImpact();\n\n    final prompt = promptController.text.trim();\n    if (prompt.isEmpty && widget.mode == 'text-to-image') {\n      showErrorMessage(AppLocale.contentIsRequired.getString(context));\n      return;\n    }\n\n    if (widget.mode == 'image-to-image' && selectedImagePath == null && selectedImageData == null) {\n      showErrorMessage(AppLocale.selectReferenceImage.getString(context));\n      return;\n    }\n\n    final seed = int.tryParse(seedController.text);\n    if (seed != null && (seed < 0 || seed > 2147483647)) {\n      showErrorMessage('Seed 取值范围为 0 ~ 2147483647');\n      return;\n    }\n\n    var params = <String, dynamic>{\n      'prompt': prompt,\n      'negative_prompt': negativePromptController.text,\n      'prompt_tags': selectedTags.map((e) => e.value).join(','),\n      'filter_id': selectedStyle?.id,\n      'image_ratio': selectedImageSize,\n      'image_count': generationImageCount,\n      'ai_rewrite': enableAIRewrite,\n      'gallery_copy_id': widget.galleryCopyId,\n      'upscale_by': upscaleBy,\n      'model': selectedModel?.id,\n      'image_strength': imageStrength,\n      'seed': seed,\n    };\n\n    if (selectedImagePath != null && selectedImagePath!.isNotEmpty) {\n      params['image'] = 'https://${selectedImagePath ?? 'demo'}'; // 仅用于测试消耗量，正式上传后会被替换为 URL\n    }\n\n    if (selectedImageData != null && selectedImageData!.isNotEmpty) {\n      params['image'] = \"https://fake-image-url.com\";\n    }\n\n    final cancel = BotToast.showCustomLoading(\n      toastBuilder: (cancel) {\n        return const LoadingIndicator(\n          message: '思考中，请稍候...',\n        );\n      },\n      allowClick: false,\n      duration: const Duration(seconds: 15),\n    );\n\n    request(int waitDuration) async {\n      try {\n        cancel();\n\n        if (params['image'] != null && params['image'] != '') {\n          final cancel = BotToast.showCustomLoading(\n            toastBuilder: (cancel) {\n              return LoadingIndicator(\n                message: AppLocale.imageUploading.getString(context),\n              );\n            },\n            allowClick: false,\n          );\n\n          if (selectedImagePath != null &&\n              (selectedImagePath!.startsWith('http://') || selectedImagePath!.startsWith('https://'))) {\n            params['image'] = selectedImagePath;\n            cancel();\n          } else {\n            if (selectedImagePath != null && selectedImagePath!.isNotEmpty) {\n              final uploadRes =\n                  await ImageUploader(widget.setting).upload(selectedImagePath!).whenComplete(() => cancel());\n              params['image'] = uploadRes.url;\n            } else if (selectedImageData != null && selectedImageData!.isNotEmpty) {\n              final uploadRes =\n                  await ImageUploader(widget.setting).uploadData(selectedImageData!).whenComplete(() => cancel());\n              params['image'] = uploadRes.url;\n            }\n          }\n        }\n\n        final taskId = await APIServer().creativeIslandCompletionsAsyncV2(params);\n\n        stopPeriodQuery = false;\n\n        Navigator.push(\n          // ignore: use_build_context_synchronously\n          context,\n          MaterialPageRoute(\n            fullscreenDialog: true,\n            builder: (context) => DrawResultPage(\n              future: Future.delayed(const Duration(seconds: 10), () async {\n                return await queryCompletionTaskStatus(\n                  taskId: taskId,\n                  retryTimes: 0,\n                  delaySeconds: 3,\n                  params: params,\n                );\n              }),\n              waitDuration: waitDuration,\n            ),\n          ),\n        ).whenComplete(() {\n          stopPeriodQuery = true;\n        });\n      } catch (e) {\n        stopPeriodQuery = true;\n        cancel();\n        // ignore: use_build_context_synchronously\n        showErrorMessageEnhanced(context, e);\n      }\n    }\n\n    try {\n      final res = await APIServer().creativeIslandCompletionsEvaluateV2(params);\n      if (!res.enough) {\n        if (context.mounted) {\n          showBeautyDialog(\n            // ignore: use_build_context_synchronously\n            context,\n            type: QuickAlertType.warning,\n            // ignore: use_build_context_synchronously\n            text: AppLocale.quotaExceeded.getString(context),\n            confirmBtnText: '立即购买',\n            showCancelBtn: true,\n            onConfirmBtnTap: () {\n              context.pop();\n              context.push('/payment');\n            },\n          );\n        }\n        return;\n      }\n      if (res.cost > 0) {\n        cancel();\n        openConfirmDialog(\n          // ignore: use_build_context_synchronously\n          context,\n          '本次请求预计消耗 ${res.cost} 个智慧果，是否继续操作？',\n          () => request(res.waitDuration ?? 60),\n        );\n      } else {\n        request(res.waitDuration ?? 60);\n      }\n    } catch (e) {\n      cancel();\n      // ignore: use_build_context_synchronously\n      showErrorMessageEnhanced(context, e);\n    }\n  }\n\n  String imageStrengthText() {\n    if (imageStrength == 0) {\n      return '自动';\n    }\n\n    if (imageStrength! >= 0.4 && imageStrength! <= 0.67) {\n      return '适中';\n    }\n\n    if (imageStrength! > 0.65 && imageStrength! < 0.9) {\n      return '更有创造力';\n    }\n\n    if (imageStrength! >= 0.9) {\n      return '尽情发挥创造力';\n    }\n\n    return '更接近参考图';\n  }\n\n  Future<IslandResult> queryCompletionTaskStatus({\n    required String taskId,\n    required int retryTimes,\n    required int delaySeconds,\n    Map<String, dynamic>? params,\n  }) async {\n    if (retryTimes > 60) {\n      return Future.error(AppLocale.generateTimeout.getString(context));\n    }\n\n    final resp = await APIServer().asyncTaskStatus(taskId);\n    switch (resp.status) {\n      case 'success':\n        if (params != null && resp.originImage != null && resp.originImage != '') {\n          params['image'] = resp.originImage;\n        }\n        return IslandResult(\n          result: resp.resources ?? const [],\n          params: params,\n        );\n      case 'failed':\n        return Future.error(resp.errors!.join(\";\"));\n      default:\n        if (stopPeriodQuery) {\n          // ignore: use_build_context_synchronously\n          return Future.error(AppLocale.generateTimeout.getString(context));\n        }\n\n        return await Future.delayed(Duration(seconds: delaySeconds), () async {\n          return await queryCompletionTaskStatus(\n            taskId: taskId,\n            retryTimes: retryTimes + 1,\n            delaySeconds: 3,\n            params: params,\n          );\n        });\n    }\n  }\n\n  double _calImageSelectorHeight(BuildContext context) {\n    var width = MediaQuery.of(context).size.width;\n    if (width > CustomSize.smallWindowSize) {\n      width = CustomSize.smallWindowSize;\n    }\n\n    return width - 15 * 2 - 10 * 2 - 10;\n  }\n}\n"
  },
  {
    "path": "lib/page/creative_island/draw/draw_list.dart",
    "content": "import 'package:askaide/bloc/creative_island_bloc.dart';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/color.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/sliver_component.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/page/creative_island/draw/components/creative_item.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass DrawListScreen extends StatefulWidget {\n  final SettingRepository setting;\n  const DrawListScreen({super.key, required this.setting});\n\n  @override\n  State<DrawListScreen> createState() => _DrawListScreenState();\n}\n\nclass _DrawListScreenState extends State<DrawListScreen> {\n  @override\n  void initState() {\n    if (Ability().isUserLogon()) {\n      userSignedIn = true;\n    }\n\n    context.read<CreativeIslandBloc>().add(CreativeIslandItemsV2LoadEvent(forceRefresh: false));\n\n    super.initState();\n  }\n\n  bool userSignedIn = false;\n\n  @override\n  void dispose() {\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        backgroundColor: customColors.backgroundColor,\n        body: _buildIslandItems(customColors),\n      ),\n    );\n  }\n\n  /// 创作岛列表\n  Widget _buildIslandItems(\n    CustomColors customColors,\n  ) {\n    return SliverComponent(\n      title: Text(\n        AppLocale.creativeIsland.getString(context),\n        style: TextStyle(\n          fontSize: CustomSize.appBarTitleSize,\n          color: customColors.backgroundInvertedColor,\n        ),\n      ),\n      actions: [\n        IconButton(\n          onPressed: () {\n            if (userSignedIn) {\n              context.push('/creative-island/history?mode=image-draw');\n            } else {\n              context.push('/login');\n            }\n          },\n          icon: const Icon(Icons.crop_original),\n        ),\n      ],\n      child: BackgroundContainer(\n        setting: widget.setting,\n        enabled: false,\n        backgroundColor: customColors.backgroundColor,\n        child: BlocBuilder<CreativeIslandBloc, CreativeIslandState>(\n          buildWhen: (previous, current) => current is CreativeIslandItemsV2Loaded,\n          builder: (context, state) {\n            if (state is CreativeIslandItemsV2Loaded) {\n              final items = state.items\n                  .map((e) => CreativeItem(\n                        imageURL: e.previewImage,\n                        title: e.title,\n                        titleColor: stringToColor(e.titleColor),\n                        tag: e.tag,\n                        onTap: () {\n                          var uri = Uri.tryParse(e.routeUri);\n                          if (e.note != null && e.note != '') {\n                            uri = uri!.replace(\n                                queryParameters: <String, String>{\n                              'note': e.note!,\n                            }..addAll(uri.queryParameters));\n                          }\n\n                          context.push(uri.toString());\n                        },\n                        size: e.size,\n                      ))\n                  .toList();\n              final largeItems = items.where((e) => e.size == 'large').toList();\n              final mediumItems = items.where((e) => e.size == 'medium').toList();\n              final otherItems = items.where((e) => e.size != 'large' && e.size != 'medium').toList();\n\n              return RefreshIndicator(\n                onRefresh: () async {\n                  context.read<CreativeIslandBloc>().add(CreativeIslandItemsV2LoadEvent(forceRefresh: true));\n                },\n                color: customColors.linkColor,\n                displacement: 20,\n                child: SingleChildScrollView(\n                  child: Column(\n                    children: [\n                      GridView.count(\n                        physics: const NeverScrollableScrollPhysics(),\n                        padding: const EdgeInsets.only(top: 0, left: 10, right: 10),\n                        crossAxisCount: _calCrossAxisCount(context),\n                        childAspectRatio: 2,\n                        shrinkWrap: true,\n                        children: largeItems\n                            .map((e) => Container(\n                                  padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 10),\n                                  child: e,\n                                ))\n                            .toList(),\n                      ),\n                      GridView.count(\n                        physics: const NeverScrollableScrollPhysics(),\n                        padding: const EdgeInsets.only(top: 5, left: 10, right: 10),\n                        crossAxisCount: _calCrossAxisCount(context) * 2,\n                        childAspectRatio: 1,\n                        shrinkWrap: true,\n                        children: mediumItems\n                            .map((e) => Container(\n                                  padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),\n                                  child: e,\n                                ))\n                            .toList(),\n                      ),\n                      GridView.count(\n                        physics: const NeverScrollableScrollPhysics(),\n                        padding: const EdgeInsets.only(top: 5, left: 10, right: 10),\n                        crossAxisCount: _calCrossAxisCount(context) * 2,\n                        childAspectRatio: 2,\n                        shrinkWrap: true,\n                        children: otherItems\n                            .map((e) => Container(\n                                  padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),\n                                  child: e,\n                                ))\n                            .toList(),\n                      ),\n                    ],\n                  ),\n                ),\n              );\n            }\n\n            return const Center(\n              child: CircularProgressIndicator(),\n            );\n          },\n        ),\n      ),\n    );\n  }\n\n  int _calCrossAxisCount(BuildContext context) {\n    var width = MediaQuery.of(context).size.width;\n    if (width > CustomSize.maxWindowSize) {\n      width = CustomSize.maxWindowSize;\n    }\n\n    return (width / 400).round() > 2 ? 2 : (width / 400).round();\n  }\n}\n"
  },
  {
    "path": "lib/page/creative_island/draw/draw_result.dart",
    "content": "import 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/creative_island/draw/components/content_preview.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:circular_countdown_timer/circular_countdown_timer.dart';\n\nclass DrawResultPage extends StatefulWidget {\n  final Future<IslandResult> future;\n  final int waitDuration;\n\n  const DrawResultPage({\n    super.key,\n    required this.future,\n    this.waitDuration = 30,\n  });\n\n  @override\n  State<DrawResultPage> createState() => _DrawResultPageState();\n}\n\nconst defaultCounterRestartValue = 15;\n\nclass _DrawResultPageState extends State<DrawResultPage> {\n  var loading = true;\n  var restartCounterValue = defaultCounterRestartValue;\n\n  CountDownController controller = CountDownController();\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return Container(\n      color: customColors.backgroundColor,\n      child: Align(\n        alignment: Alignment.topCenter,\n        child: ConstrainedBox(\n          constraints: const BoxConstraints(maxWidth: CustomSize.smallWindowSize),\n          child: Scaffold(\n            appBar: AppBar(\n              title: Text(\n                AppLocale.generateResult.getString(context),\n                style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n              ),\n              toolbarHeight: CustomSize.toolbarHeight,\n              centerTitle: true,\n              leading: IconButton(\n                icon: const Icon(Icons.close),\n                onPressed: () {\n                  if (loading) {\n                    openConfirmDialog(\n                      context,\n                      AppLocale.generateExitConfirm.getString(context),\n                      () => Navigator.pop(context),\n                    );\n                  } else {\n                    Navigator.pop(context);\n                  }\n                },\n              ),\n            ),\n            backgroundColor: customColors.backgroundColor,\n            body: FutureBuilder(\n              future: widget.future,\n              builder: (context, snapshot) {\n                if (snapshot.hasData || snapshot.hasError) {\n                  HapticFeedbackHelper.mediumImpact();\n                  loading = false;\n                }\n\n                if (snapshot.hasError) {\n                  return Center(\n                    child: Column(\n                      mainAxisSize: MainAxisSize.min,\n                      children: [\n                        const Icon(\n                          Icons.error_outline,\n                          size: 50,\n                          color: Colors.red,\n                        ),\n                        const SizedBox(height: 10),\n                        const Text(\n                          '创作失败',\n                          style: TextStyle(color: Colors.red),\n                        ),\n                        const SizedBox(height: 10),\n                        Padding(\n                          padding: const EdgeInsets.symmetric(horizontal: 15),\n                          child: Text(\n                            resolveError(context, snapshot.error!),\n                            style: TextStyle(\n                              color: customColors.weakTextColor,\n                              fontSize: 10,\n                            ),\n                          ),\n                        ),\n                      ],\n                    ),\n                  );\n                }\n\n                if (snapshot.hasData) {\n                  return Column(\n                    mainAxisSize: MainAxisSize.min,\n                    mainAxisAlignment: MainAxisAlignment.center,\n                    children: [\n                      Expanded(\n                        child: CreativeIslandContentPreview(\n                          result: snapshot.data!,\n                          customColors: customColors,\n                        ),\n                      ),\n                      const SizedBox(height: 30),\n                    ],\n                  );\n                }\n\n                return Center(\n                  child: Column(\n                    mainAxisSize: MainAxisSize.min,\n                    children: [\n                      CountDownProgressBar(\n                        duration: widget.waitDuration,\n                        controller: controller,\n                        onComplete: (controller) {\n                          if (!loading) {\n                            return;\n                          }\n\n                          if (restartCounterValue == defaultCounterRestartValue) {\n                            showSuccessMessage('当前排队人数较多，还需要等待一下哦');\n                          }\n\n                          controller.restart(duration: restartCounterValue);\n                          setState(() {\n                            restartCounterValue += 1;\n                          });\n                        },\n                      ),\n                      const SizedBox(height: 10),\n                      Text(\n                        '正在打造神奇...',\n                        style: TextStyle(\n                          color: customColors.backgroundInvertedColor,\n                        ),\n                      ),\n                      const SizedBox(height: 5),\n                      Text(\n                        '如队列太长，将会有数分钟等待时间',\n                        style: TextStyle(\n                          color: customColors.backgroundInvertedColor?.withAlpha(150),\n                          fontSize: 10,\n                        ),\n                      )\n                    ],\n                  ),\n                );\n              },\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nclass CountDownProgressBar extends StatefulWidget {\n  final int duration;\n  final CountDownController controller;\n  final Function(CountDownController controller)? onComplete;\n  const CountDownProgressBar({\n    super.key,\n    required this.duration,\n    required this.controller,\n    this.onComplete,\n  });\n\n  @override\n  State<CountDownProgressBar> createState() => _CountDownProgressBarState();\n}\n\nclass _CountDownProgressBarState extends State<CountDownProgressBar> {\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return CircularCountDownTimer(\n      controller: widget.controller,\n      duration: widget.duration,\n      initialDuration: 0,\n      width: MediaQuery.of(context).size.width / 3,\n      height: MediaQuery.of(context).size.height / 3,\n      ringColor: Colors.grey[300]!,\n      fillColor: customColors.linkColor!,\n      strokeWidth: 10.0,\n      strokeCap: StrokeCap.round,\n      textStyle: const TextStyle(fontSize: 33.0, fontWeight: FontWeight.bold),\n      textFormat: CountdownTextFormat.S,\n      isReverse: true,\n      isReverseAnimation: true,\n      isTimerTextShown: true,\n      autoStart: true,\n      onComplete: () {\n        widget.onComplete?.call(widget.controller);\n      },\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/creative_island/draw/image_edit_direct.dart",
    "content": "import 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/cache.dart';\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/helper/upload.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/advanced_button.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/enhanced_textfield.dart';\nimport 'package:askaide/page/component/global_alert.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/page/creative_island/draw/components/content_preview.dart';\nimport 'package:askaide/page/creative_island/draw/draw_result.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/creative_island/draw/components/image_selector.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:quickalert/quickalert.dart';\n\nclass ImageEditDirectScreen extends StatefulWidget {\n  final SettingRepository setting;\n  final String title;\n  final String apiEndpoint;\n  final String? note;\n  final int initWaitDuration;\n  final String? initImage;\n\n  const ImageEditDirectScreen({\n    super.key,\n    required this.setting,\n    required this.title,\n    required this.apiEndpoint,\n    this.note,\n    this.initWaitDuration = 30,\n    this.initImage,\n  });\n\n  @override\n  State<ImageEditDirectScreen> createState() => _ImageEditDirectScreenState();\n}\n\nclass _ImageEditDirectScreenState extends State<ImageEditDirectScreen> {\n  String? selectedImagePath;\n  Uint8List? selectedImageData;\n\n  TextEditingController seedController = TextEditingController();\n  double? cfgScale = 0.0;\n  int? motionBucketId = 0;\n\n  bool showAdvancedOptions = false;\n\n  /// 是否停止周期性查询任务执行状态\n  var stopPeriodQuery = false;\n\n  @override\n  void initState() {\n    if (widget.initImage != null && widget.initImage!.isNotEmpty) {\n      selectedImagePath = widget.initImage;\n    }\n\n    if (widget.note != null) {\n      if (widget.apiEndpoint == 'image-to-video') {\n        Cache().boolGet(key: 'creative:tutorials:${widget.apiEndpoint}:dialog').then((show) {\n          if (!show) {\n            return;\n          }\n\n          openImageToVideoTutorials(onConfirm: () {\n            Cache().setBool(\n              key: 'creative:tutorials:${widget.apiEndpoint}:dialog',\n              value: false,\n              duration: const Duration(days: 30),\n            );\n          });\n        });\n      } else {\n        Cache().boolGet(key: 'creative:tutorials:${widget.apiEndpoint}:dialog').then((show) {\n          if (!show) {\n            return;\n          }\n\n          openDefaultTutorials(onConfirm: () {\n            Cache().setBool(\n              key: 'creative:tutorials:${widget.apiEndpoint}:dialog',\n              value: false,\n              duration: const Duration(days: 30),\n            );\n          });\n        });\n      }\n    }\n\n    super.initState();\n  }\n\n  void openDefaultTutorials({Function? onConfirm}) {\n    showBeautyDialog(\n      context,\n      type: QuickAlertType.info,\n      text: '     ${widget.note!}',\n      onConfirmBtnTap: () async {\n        onConfirm?.call();\n        context.pop();\n      },\n      showCancelBtn: true,\n      confirmBtnText: AppLocale.gotIt.getString(context),\n    );\n  }\n\n  void openImageToVideoTutorials({Function? onConfirm}) {\n    showBeautyDialog(\n      context,\n      type: QuickAlertType.custom,\n      widget: Text('      ${widget.note!}'),\n      customAsset: 'assets/text-to-video.gif',\n      onConfirmBtnTap: () async {\n        onConfirm?.call();\n        context.pop();\n      },\n      showCancelBtn: true,\n      confirmBtnText: AppLocale.gotIt.getString(context),\n    );\n  }\n\n  @override\n  void dispose() {\n    seedController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          title: Text(\n            widget.title,\n            style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n          leading: IconButton(\n            onPressed: () {\n              context.pop();\n            },\n            icon: const Icon(Icons.arrow_back_ios),\n          ),\n          toolbarHeight: CustomSize.toolbarHeight,\n          backgroundColor: customColors.backgroundColor,\n          actions: [\n            if (widget.note != null && widget.apiEndpoint == 'image-to-video')\n              IconButton(\n                onPressed: () {\n                  openImageToVideoTutorials();\n                },\n                icon: const Icon(Icons.help_outline),\n              )\n            else if (widget.note != null)\n              IconButton(\n                onPressed: () {\n                  openDefaultTutorials();\n                },\n                icon: const Icon(Icons.help_outline),\n              ),\n          ],\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          maxWidth: CustomSize.smallWindowSize,\n          backgroundColor: customColors.backgroundColor,\n          child: Column(\n            children: [\n              if (Ability().showGlobalAlert) const GlobalAlert(pageKey: 'creative_create'),\n              Expanded(\n                child: Container(\n                  padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),\n                  height: double.infinity,\n                  child: SingleChildScrollView(\n                    child: buildEditPanel(context, customColors),\n                  ),\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget buildEditPanel(BuildContext context, CustomColors customColors) {\n    return SafeArea(\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: [\n          ColumnBlock(\n            innerPanding: 10,\n            padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),\n            children: [\n              // 上传图片\n              ImageSelector(\n                onImageSelected: ({path, data}) {\n                  if (path != null) {\n                    setState(() {\n                      selectedImagePath = path;\n                      selectedImageData = null;\n                    });\n                  }\n\n                  if (data != null) {\n                    setState(() {\n                      selectedImageData = data;\n                      selectedImagePath = null;\n                    });\n                  }\n                },\n                selectedImagePath: selectedImagePath,\n                selectedImageData: selectedImageData,\n                title: AppLocale.originalImage.getString(context),\n                height: _calImageSelectorHeight(context),\n              ),\n            ],\n          ),\n          if (showAdvancedOptions)\n            ColumnBlock(\n              innerPanding: 10,\n              padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),\n              children: [\n                // Cfg Scale\n                Column(\n                  children: [\n                    Row(\n                      children: [\n                        const Text('Cfg Scale'),\n                        const SizedBox(width: 5),\n                        InkWell(\n                          onTap: () {\n                            showBeautyDialog(\n                              context,\n                              type: QuickAlertType.info,\n                              text:\n                                  'How strongly the video sticks to the original image. \\nUse lower values to allow the model more freedom to make changes and higher values to correct motion distortions',\n                              confirmBtnText: AppLocale.gotIt.getString(context),\n                              showCancelBtn: false,\n                            );\n                          },\n                          child: Icon(\n                            Icons.help_outline,\n                            size: 16,\n                            color: customColors.weakLinkColor?.withAlpha(150),\n                          ),\n                        ),\n                      ],\n                    ),\n                    Row(\n                      children: [\n                        Expanded(\n                          child: Slider(\n                            value: cfgScale ?? 0.0,\n                            min: 0,\n                            max: 10,\n                            divisions: 20,\n                            label: cfgScaleText(cfgScale),\n                            activeColor: customColors.linkColor,\n                            onChanged: (value) {\n                              setState(() {\n                                if (value > 0 && value < 1) {\n                                  value = 1;\n                                }\n\n                                cfgScale = value;\n                              });\n                            },\n                          ),\n                        ),\n                        Text(\n                          cfgScaleText(cfgScale),\n                          style: TextStyle(\n                            fontSize: 12,\n                            color: customColors.weakTextColor,\n                          ),\n                        ),\n                      ],\n                    )\n                  ],\n                ),\n                // Motion Bucket ID\n                Column(\n                  mainAxisSize: MainAxisSize.min,\n                  children: [\n                    Row(\n                      children: [\n                        const Text('Motion Bucket ID'),\n                        const SizedBox(width: 5),\n                        InkWell(\n                          onTap: () {\n                            showBeautyDialog(\n                              context,\n                              type: QuickAlertType.info,\n                              text:\n                                  'Lower values generally result in less motion in the output video, \\nwhile higher values generally result in more motion',\n                              confirmBtnText: AppLocale.gotIt.getString(context),\n                              showCancelBtn: false,\n                            );\n                          },\n                          child: Icon(\n                            Icons.help_outline,\n                            size: 16,\n                            color: customColors.weakLinkColor?.withAlpha(150),\n                          ),\n                        ),\n                      ],\n                    ),\n                    Row(\n                      children: [\n                        Expanded(\n                          child: Slider(\n                            value: (motionBucketId ?? 0).toDouble(),\n                            min: 0,\n                            max: 255,\n                            divisions: 51,\n                            label: motionBucketIdText(motionBucketId),\n                            activeColor: customColors.linkColor,\n                            onChanged: (value) {\n                              setState(() {\n                                if (value > 0 && value < 1) {\n                                  value = 1;\n                                }\n\n                                motionBucketId = value.toInt();\n                              });\n                            },\n                          ),\n                        ),\n                        Text(\n                          motionBucketIdText(motionBucketId),\n                          style: TextStyle(\n                            fontSize: 12,\n                            color: customColors.weakTextColor,\n                          ),\n                        ),\n                      ],\n                    )\n                  ],\n                ),\n                // Seed\n                EnhancedTextField(\n                  controller: seedController,\n                  customColors: customColors,\n                  labelText: 'Seed',\n                  labelPosition: LabelPosition.left,\n                  showCounter: false,\n                  keyboardType: TextInputType.number,\n                  inputFormatters: [\n                    FilteringTextInputFormatter.digitsOnly,\n                  ],\n                  hintText: '默认随机',\n                  textDirection: TextDirection.rtl,\n                ),\n              ],\n            ),\n          // 生成按钮\n          if (widget.apiEndpoint == 'image-to-video')\n            AdvancedButton(\n              showAdvancedOptions: showAdvancedOptions,\n              onPressed: (value) {\n                setState(() {\n                  showAdvancedOptions = value;\n                });\n              },\n            ),\n          if (widget.apiEndpoint == 'image-to-video') const SizedBox(height: 10),\n          EnhancedButton(\n            title: AppLocale.generate.getString(context),\n            onPressed: onGenerate,\n          ),\n          const SizedBox(height: 20),\n        ],\n      ),\n    );\n  }\n\n  String cfgScaleText(double? cfgScale) {\n    cfgScale ??= 0;\n    return cfgScale == 0 ? 'Auto' : cfgScale.toStringAsFixed(1);\n  }\n\n  String motionBucketIdText(int? motionBucketId) {\n    motionBucketId ??= 0;\n    return motionBucketId == 0 ? 'Auto' : motionBucketId.toString();\n  }\n\n  void onGenerate() async {\n    FocusScope.of(context).requestFocus(FocusNode());\n    HapticFeedbackHelper.mediumImpact();\n\n    if (selectedImagePath == null && selectedImageData == null) {\n      showErrorMessage('请先选择要处理的图片');\n      return;\n    }\n\n    var params = <String, dynamic>{};\n\n    if (cfgScale != null && cfgScale! >= 1) {\n      params['cfg_scale'] = cfgScale;\n    }\n\n    if (motionBucketId != null && motionBucketId! >= 1) {\n      params['motion_bucket_id'] = motionBucketId;\n    }\n\n    final cancelOutside = BotToast.showCustomLoading(\n      toastBuilder: (cancel) {\n        return const LoadingIndicator(\n          message: '思考中，请稍候...',\n        );\n      },\n      allowClick: false,\n      duration: const Duration(seconds: 15),\n    );\n\n    request(int waitDuration) async {\n      try {\n        cancelOutside();\n\n        final cancel = BotToast.showCustomLoading(\n          toastBuilder: (cancel) {\n            return LoadingIndicator(\n              message: AppLocale.imageUploading.getString(context),\n            );\n          },\n          allowClick: false,\n        );\n\n        if (selectedImagePath != null &&\n            (selectedImagePath!.startsWith('http://') || selectedImagePath!.startsWith('https://'))) {\n          params['image'] = selectedImagePath;\n          cancel();\n        } else {\n          if (selectedImagePath != null && selectedImagePath!.isNotEmpty) {\n            final uploadRes =\n                await ImageUploader(widget.setting).upload(selectedImagePath!).whenComplete(() => cancel());\n            params['image'] = uploadRes.url;\n          } else if (selectedImageData != null && selectedImageData!.isNotEmpty) {\n            final uploadRes =\n                await ImageUploader(widget.setting).uploadData(selectedImageData!).whenComplete(() => cancel());\n            params['image'] = uploadRes.url;\n          }\n        }\n\n        final taskId = await APIServer().creativeIslandImageDirectEdit(\n          widget.apiEndpoint,\n          params,\n        );\n\n        stopPeriodQuery = false;\n\n        Navigator.push(\n          // ignore: use_build_context_synchronously\n          context,\n          MaterialPageRoute(\n            fullscreenDialog: true,\n            builder: (context) => DrawResultPage(\n              future: Future.delayed(const Duration(seconds: 10), () async {\n                return await queryCompletionTaskStatus(\n                  taskId: taskId,\n                  retryTimes: 0,\n                  delaySeconds: 3,\n                  params: params,\n                );\n              }),\n              waitDuration: waitDuration,\n            ),\n          ),\n        ).whenComplete(() {\n          stopPeriodQuery = true;\n        });\n      } catch (e) {\n        stopPeriodQuery = true;\n        cancelOutside();\n        // ignore: use_build_context_synchronously\n        showErrorMessageEnhanced(context, e);\n      }\n    }\n\n    try {\n      request(widget.initWaitDuration);\n    } catch (e) {\n      cancelOutside();\n      showErrorMessageEnhanced(context, e);\n    }\n  }\n\n  Future<IslandResult> queryCompletionTaskStatus({\n    required String taskId,\n    required int retryTimes,\n    required int delaySeconds,\n    Map<String, dynamic>? params,\n  }) async {\n    if (retryTimes > 60) {\n      return Future.error(AppLocale.generateTimeout.getString(context));\n    }\n\n    final resp = await APIServer().asyncTaskStatus(taskId);\n    switch (resp.status) {\n      case 'success':\n        if (params != null && resp.originImage != null && resp.originImage != '') {\n          params['image'] = resp.originImage;\n        }\n        if (params != null && resp.width != null) {\n          params['width'] = resp.width;\n        }\n        if (params != null && resp.height != null) {\n          params['height'] = resp.height;\n        }\n\n        return IslandResult(\n          result: resp.resources ?? const [],\n          params: params,\n        );\n      case 'failed':\n        return Future.error(resp.errors!.join(\";\"));\n      default:\n        if (stopPeriodQuery) {\n          // ignore: use_build_context_synchronously\n          return Future.error(AppLocale.generateTimeout.getString(context));\n        }\n\n        return await Future.delayed(Duration(seconds: delaySeconds), () async {\n          return await queryCompletionTaskStatus(\n            taskId: taskId,\n            retryTimes: retryTimes + 1,\n            delaySeconds: 3,\n            params: params,\n          );\n        });\n    }\n  }\n\n  double _calImageSelectorHeight(BuildContext context) {\n    var width = MediaQuery.of(context).size.width;\n    if (width > CustomSize.smallWindowSize) {\n      width = CustomSize.smallWindowSize;\n    }\n\n    return width - 15 * 2 - 10 * 2 - 10;\n  }\n}\n"
  },
  {
    "path": "lib/page/creative_island/gallery/components/image_card.dart",
    "content": "import 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/image.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/random_avatar.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:flutter/material.dart';\n\nclass ImageCard extends StatelessWidget {\n  final List<String> images;\n  final String? username;\n  final int? userId;\n  final int hotValue;\n  final Function()? onTap;\n  const ImageCard({\n    super.key,\n    required this.images,\n    this.username,\n    this.userId,\n    this.hotValue = 0,\n    this.onTap,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return InkWell(\n      onTap: onTap,\n      child: Container(\n        decoration: BoxDecoration(\n          color: customColors.backgroundContainerColor,\n          borderRadius: CustomSize.borderRadius,\n        ),\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          children: [\n            ConstrainedBox(\n              constraints: const BoxConstraints(\n                minHeight: 50,\n              ),\n              child: ClipRRect(\n                borderRadius: const BorderRadius.only(topLeft: CustomSize.radius, topRight: CustomSize.radius),\n                child: images.isEmpty\n                    ? Image.asset('assets/image-broken.png')\n                    : CachedNetworkImageEnhanced(\n                        imageUrl: imageURL(images.first, qiniuImageTypeThumbMedium),\n                        fit: BoxFit.cover,\n                      ),\n              ),\n            ),\n            Container(\n              padding: const EdgeInsets.symmetric(\n                horizontal: 10,\n                vertical: 8,\n              ),\n              child: Row(\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: [\n                  Row(\n                    children: [\n                      RandomAvatar(\n                        id: userId ?? 0,\n                        size: 15,\n                        usage: AvatarUsage.user,\n                      ),\n                      const SizedBox(width: 8),\n                      Text(\n                        username ?? '匿名',\n                        style: TextStyle(\n                          color: customColors.weakTextColor,\n                          fontSize: 12,\n                        ),\n                      ),\n                    ],\n                  ),\n                  Row(\n                    children: [\n                      Icon(\n                        Icons.local_fire_department,\n                        size: 12,\n                        color: hotValue > 0 ? Colors.amber : null,\n                      ),\n                      const SizedBox(width: 4),\n                      Text(\n                        '$hotValue',\n                        style: TextStyle(\n                          color: customColors.weakTextColor,\n                          fontSize: 12,\n                        ),\n                      ),\n                    ],\n                  ),\n                ],\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/creative_island/gallery/data/gallery_datasource.dart",
    "content": "import 'package:askaide/helper/logger.dart';\nimport 'package:askaide/repo/api/creative.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:loading_more_list/loading_more_list.dart';\n\nclass GalleryDatasource extends LoadingMoreBase<CreativeGallery> {\n  int pageindex = 1;\n  bool _hasMore = true;\n  bool forceRefresh = false;\n\n  @override\n  bool get hasMore => (_hasMore && length < 300) || forceRefresh;\n\n  @override\n  Future<bool> loadData([bool isloadMoreAction = false]) async {\n    try {\n      final resp = await APIServer().creativeGallery(\n        page: pageindex,\n        perPage: 20,\n        // cache: !forceRefresh,\n      );\n      if (pageindex == 1) {\n        clear();\n      }\n\n      for (var element in resp.data) {\n        add(element);\n      }\n\n      if (resp.page == resp.lastPage) {\n        _hasMore = false;\n      }\n\n      pageindex = resp.page + 1;\n      return true;\n    } catch (e) {\n      Logger.instance.e(e);\n      return false;\n    }\n  }\n\n  @override\n  Future<bool> refresh([bool notifyStateChanged = false]) async {\n    _hasMore = true;\n    pageindex = 1;\n    //force to refresh list when you don't want clear list before request\n    //for the case, if your list already has 20 items.\n    forceRefresh = !notifyStateChanged;\n    var result = await super.refresh(notifyStateChanged);\n    forceRefresh = false;\n    return result;\n  }\n}\n"
  },
  {
    "path": "lib/page/creative_island/gallery/gallery.dart",
    "content": "import 'package:askaide/helper/ability.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/sliver_component.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/page/creative_island/gallery/components/image_card.dart';\nimport 'package:askaide/page/creative_island/gallery/data/gallery_datasource.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/api/creative.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:loading_more_list/loading_more_list.dart';\n\nclass GalleryScreen extends StatefulWidget {\n  final SettingRepository setting;\n  const GalleryScreen({super.key, required this.setting});\n\n  @override\n  State<GalleryScreen> createState() => _GalleryScreenState();\n}\n\nclass _GalleryScreenState extends State<GalleryScreen> {\n  final GalleryDatasource datasource = GalleryDatasource();\n  @override\n  void initState() {\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    datasource.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        backgroundColor: customColors.backgroundColor,\n        body: _buildIslandItems(customColors),\n      ),\n    );\n  }\n\n  /// 创作岛列表\n  Widget _buildIslandItems(\n    CustomColors customColors,\n  ) {\n    return SliverComponent(\n      title: Text(\n        AppLocale.discover.getString(context),\n        style: TextStyle(\n          fontSize: CustomSize.appBarTitleSize,\n          color: customColors.backgroundInvertedColor,\n        ),\n      ),\n      actions: [\n        if (Ability().enableCreationIsland)\n          IconButton(\n            onPressed: () {\n              context.push('/creative-draw');\n            },\n            icon: const Icon(Icons.palette_outlined),\n          ),\n      ],\n      child: BackgroundContainer(\n        setting: widget.setting,\n        enabled: false,\n        backgroundColor: customColors.backgroundColor,\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            // Container(\n            //   padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),\n            //   child: Text('热门作品'),\n            // ),\n            Expanded(\n              child: RefreshIndicator(\n                color: customColors.linkColor,\n                displacement: 20,\n                onRefresh: () {\n                  return datasource.refresh();\n                },\n                child: LoadingMoreList(\n                  ListConfig<CreativeGallery>(\n                    itemBuilder: (context, item, index) {\n                      return ImageCard(\n                        images: [item.preview],\n                        username: item.username,\n                        userId: item.userId,\n                        hotValue: item.hotValue,\n                        onTap: () => context.push('/creative-draw/gallery/${item.id}'),\n                      );\n                    },\n                    sourceList: datasource,\n                    padding: const EdgeInsets.all(10),\n                    extendedListDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount(\n                      crossAxisCount: _calCrossAxisCount(context),\n                      crossAxisSpacing: 10,\n                      mainAxisSpacing: 10,\n                    ),\n                    indicatorBuilder: (context, status) {\n                      String msg = '';\n                      switch (status) {\n                        case IndicatorStatus.noMoreLoad:\n                          msg = '~ 没有更多了 ~';\n                          break;\n                        case IndicatorStatus.loadingMoreBusying:\n                          msg = '加载中...';\n                          break;\n                        case IndicatorStatus.error:\n                          msg = '加载失败，请稍后再试';\n                          break;\n                        case IndicatorStatus.empty:\n                          msg = '暂无数据';\n                          break;\n                        default:\n                          return const Center(child: LoadingIndicator());\n                      }\n                      return Container(\n                        padding: const EdgeInsets.all(15),\n                        alignment: Alignment.center,\n                        child: Text(\n                          msg,\n                          style: TextStyle(\n                            color: customColors.weakTextColor,\n                            fontSize: 14,\n                          ),\n                        ),\n                      );\n                    },\n                  ),\n                ),\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n\n  int _calCrossAxisCount(BuildContext context) {\n    double width = MediaQuery.of(context).size.width;\n    if (width > CustomSize.maxWindowSize) {\n      width = CustomSize.maxWindowSize;\n    }\n    return (width / 220).round();\n  }\n}\n"
  },
  {
    "path": "lib/page/creative_island/gallery/gallery_item.dart",
    "content": "import 'package:askaide/bloc/gallery_bloc.dart';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/helper/image.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/attached_button_panel.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/gallery_item_share.dart';\nimport 'package:askaide/page/component/image_action.dart';\nimport 'package:askaide/page/component/image_preview.dart';\nimport 'package:askaide/page/component/item_selector_search.dart';\nimport 'package:askaide/page/component/random_avatar.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:clipboard/clipboard.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass GalleryItemScreen extends StatefulWidget {\n  final SettingRepository setting;\n  final int galleryId;\n  const GalleryItemScreen({\n    super.key,\n    required this.setting,\n    required this.galleryId,\n  });\n\n  @override\n  State<GalleryItemScreen> createState() => _GalleryItemScreenState();\n}\n\nclass _GalleryItemScreenState extends State<GalleryItemScreen> {\n  @override\n  void initState() {\n    super.initState();\n\n    context.read<GalleryBloc>().add(GalleryItemLoadEvent(id: widget.galleryId));\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          leading: IconButton(\n            icon: const Icon(Icons.close),\n            onPressed: () => Navigator.of(context).pop(),\n          ),\n          backgroundColor: customColors.backgroundColor,\n          toolbarHeight: CustomSize.toolbarHeight,\n          actions: [\n            BlocBuilder<GalleryBloc, GalleryState>(\n              buildWhen: (previous, current) => current is GalleryItemLoaded,\n              builder: (context, state) {\n                if (state is GalleryItemLoaded && state.isInternalUser && state.item.status == 1) {\n                  return TextButton(\n                    onPressed: () {\n                      openConfirmDialog(\n                        context,\n                        '确认取消？',\n                        () => APIServer()\n                            .cancelShareCreativeHistoryToGallery(historyId: state.item.creativeHistoryId!)\n                            .then((value) {\n                          showSuccessMessage(AppLocale.operateSuccess.getString(context));\n\n                          context\n                              .read<GalleryBloc>()\n                              .add(GalleryItemLoadEvent(id: widget.galleryId, forceRefresh: true));\n                        }),\n                      );\n                    },\n                    child: Text(\n                      AppLocale.cancelShare.getString(context),\n                      style: TextStyle(\n                        color: customColors.weakLinkColor,\n                        fontSize: 12,\n                      ),\n                    ),\n                  );\n                }\n\n                return const SizedBox();\n              },\n            ),\n          ],\n        ),\n        extendBodyBehindAppBar: true,\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          child: BlocBuilder<GalleryBloc, GalleryState>(\n            buildWhen: (previous, current) => current is GalleryItemLoaded,\n            builder: (context, state) {\n              if (state is GalleryItemLoaded) {\n                return Align(\n                  alignment: Alignment.topCenter,\n                  child: ConstrainedBox(\n                    constraints: const BoxConstraints(\n                      maxWidth: CustomSize.smallWindowSize,\n                    ),\n                    child: SingleChildScrollView(\n                      child: SafeArea(\n                        child: Container(\n                          padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 10),\n                          child: Column(\n                            mainAxisSize: MainAxisSize.min,\n                            crossAxisAlignment: CrossAxisAlignment.center,\n                            children: [\n                              for (var img in state.item.images)\n                                Container(\n                                  decoration: BoxDecoration(\n                                    color: customColors.backgroundColor,\n                                    borderRadius: CustomSize.borderRadius,\n                                  ),\n                                  // padding: const EdgeInsets.symmetric(\n                                  //   horizontal: 10,\n                                  //   vertical: 10,\n                                  // ),\n                                  margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),\n                                  child: NetworkImagePreviewer(\n                                    url: img,\n                                    preview: imageURL(img, qiniuImageTypeThumb),\n                                    hidePreviewButton: true,\n                                  ),\n                                ),\n                              Container(\n                                padding: const EdgeInsets.symmetric(\n                                  horizontal: 15,\n                                  vertical: 8,\n                                ),\n                                child: Row(\n                                  mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                                  children: [\n                                    Row(\n                                      children: [\n                                        RandomAvatar(\n                                          id: state.item.userId ?? 0,\n                                          usage: AvatarUsage.user,\n                                          size: 15,\n                                        ),\n                                        const SizedBox(width: 8),\n                                        Text(\n                                          state.item.username ?? '匿名',\n                                          style: TextStyle(\n                                            color: customColors.weakTextColor,\n                                            fontSize: 12,\n                                          ),\n                                        ),\n                                      ],\n                                    ),\n                                    Row(\n                                      children: [\n                                        const Icon(\n                                          Icons.local_fire_department,\n                                          size: 12,\n                                        ),\n                                        const SizedBox(width: 4),\n                                        Text(\n                                          '${state.item.hotValue}',\n                                          style: TextStyle(\n                                            color: customColors.weakTextColor,\n                                            fontSize: 12,\n                                          ),\n                                        ),\n                                      ],\n                                    ),\n                                  ],\n                                ),\n                              ),\n                              const SizedBox(height: 10),\n                              ColumnBlock(\n                                innerPanding: 10,\n                                padding: const EdgeInsets.all(15),\n                                children: [\n                                  if (state.item.prompt != null && state.item.prompt!.isNotEmpty)\n                                    TextItem(\n                                      title: 'Prompt',\n                                      value: state.item.prompt!,\n                                    ),\n                                  if (state.item.negativePrompt != null && state.item.negativePrompt!.isNotEmpty)\n                                    TextItem(\n                                      title: 'Negative Prompt',\n                                      value: state.item.negativePrompt!,\n                                    ),\n                                ],\n                              ),\n                              Padding(\n                                padding: const EdgeInsets.symmetric(\n                                  horizontal: 5,\n                                  vertical: 10,\n                                ),\n                                child: Row(\n                                  mainAxisSize: MainAxisSize.min,\n                                  children: [\n                                    EnhancedButton(\n                                      title: AppLocale.share.getString(context),\n                                      icon: const Icon(Icons.share, size: 14),\n                                      width: 80,\n                                      color: customColors.backgroundInvertedColor,\n                                      backgroundColor: customColors.backgroundColor,\n                                      onPressed: () {\n                                        Navigator.push(\n                                          context,\n                                          MaterialPageRoute(\n                                            fullscreenDialog: true,\n                                            builder: (context) => GalleryItemShareScreen(\n                                              images: state.item.images,\n                                              prompt: state.item.prompt,\n                                              negativePrompt: state.item.negativePrompt,\n                                            ),\n                                          ),\n                                        );\n                                      },\n                                    ),\n                                    if (Ability().enableCreationIsland) ...[\n                                      const SizedBox(width: 10),\n                                      EnhancedButton(\n                                        title: AppLocale.shortcut.getString(context),\n                                        icon: const Icon(Icons.webhook, size: 14),\n                                        width: 80,\n                                        color: customColors.backgroundInvertedColor,\n                                        backgroundColor: customColors.backgroundColor,\n                                        onPressed: () {\n                                          if (state.item.images.length > 1) {\n                                            List<SelectorItem<String>> items = [];\n                                            for (var i = 0; i < state.item.images.length; i++) {\n                                              items.add(SelectorItem(\n                                                NetworkImagePreviewer(\n                                                  url: state.item.images[i],\n                                                  notClickable: true,\n                                                  hidePreviewButton: true,\n                                                  borderRadius: CustomSize.borderRadiusAll,\n                                                ),\n                                                state.item.images[i],\n                                              ));\n                                            }\n                                            openListSelectDialog(\n                                              context,\n                                              items,\n                                              (selected) {\n                                                context.pop();\n\n                                                openImageWorkflowActionDialog(\n                                                  context,\n                                                  customColors,\n                                                  selected.value,\n                                                );\n\n                                                return false;\n                                              },\n                                              horizontal: true,\n                                              horizontalCount: 2,\n                                              heightFactor: 0.8,\n                                              innerPadding: const EdgeInsets.symmetric(\n                                                vertical: 10,\n                                              ),\n                                              title: AppLocale.selectImageToShortcut.getString(context),\n                                            );\n                                          } else {\n                                            openImageWorkflowActionDialog(\n                                              context,\n                                              customColors,\n                                              state.item.images.first,\n                                            );\n                                          }\n                                        },\n                                      ),\n                                      const SizedBox(width: 10),\n                                      Expanded(\n                                        child: EnhancedButton(\n                                          title: AppLocale.makeSameStyle.getString(context),\n                                          onPressed: () {\n                                            context.push(\n                                                '/creative-draw/create?mode=text-to-image&id=${state.item.creativeId}&gallery_copy_id=${state.item.id}');\n                                          },\n                                        ),\n                                      ),\n                                    ]\n                                  ],\n                                ),\n                              ),\n                            ],\n                          ),\n                        ),\n                      ),\n                    ),\n                  ),\n                );\n              }\n\n              return const Center(\n                child: Text('Loading ...'),\n              );\n            },\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nclass TextItem extends StatefulWidget {\n  final String title;\n  final String value;\n\n  const TextItem({\n    super.key,\n    required this.title,\n    required this.value,\n  });\n\n  @override\n  State<TextItem> createState() => _TextItemState();\n}\n\nclass _TextItemState extends State<TextItem> {\n  String valueTranslated = '';\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return SizedBox(\n      width: double.infinity,\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          Text(\n            widget.title,\n            style: TextStyle(\n              fontSize: 14,\n              fontWeight: FontWeight.bold,\n              color: customColors.weakTextColor,\n            ),\n          ),\n          const SizedBox(height: 5),\n          GestureDetector(\n            onLongPressStart: (details) {\n              HapticFeedbackHelper.mediumImpact();\n\n              BotToast.showAttachedWidget(\n                target: details.globalPosition,\n                duration: const Duration(seconds: 8),\n                animationDuration: const Duration(milliseconds: 200),\n                animationReverseDuration: const Duration(milliseconds: 200),\n                preferDirection: PreferDirection.topCenter,\n                ignoreContentClick: false,\n                onlyOne: true,\n                allowClick: true,\n                enableSafeArea: true,\n                attachedBuilder: (cancel) => AttachedButtonPanel(\n                  buttons: [\n                    TextButton.icon(\n                      onPressed: () {\n                        FlutterClipboard.copy(widget.value).then((value) {\n                          showSuccessMessage('已复制到剪贴板');\n                        });\n                        cancel();\n                      },\n                      label: const Text(''),\n                      icon: const Column(\n                        mainAxisSize: MainAxisSize.min,\n                        children: [\n                          Icon(\n                            Icons.copy,\n                            color: Color.fromARGB(255, 255, 255, 255),\n                            size: 14,\n                          ),\n                          Text(\n                            \"复制\",\n                            style: TextStyle(fontSize: 12, color: Colors.white),\n                          ),\n                        ],\n                      ),\n                    ),\n                    TextButton.icon(\n                      onPressed: () {\n                        cancel();\n\n                        APIServer().translate(widget.value).then((value) {\n                          setState(() {\n                            valueTranslated = value.result!;\n                          });\n                        }).onError((error, stackTrace) {\n                          showErrorMessage(resolveError(context, error!));\n                        });\n                      },\n                      label: const Text(''),\n                      icon: const Column(\n                        mainAxisSize: MainAxisSize.min,\n                        children: [\n                          Icon(\n                            Icons.translate,\n                            color: Color.fromARGB(255, 255, 255, 255),\n                            size: 14,\n                          ),\n                          Text(\n                            '翻译',\n                            style: TextStyle(\n                              fontSize: 12,\n                              color: Colors.white,\n                            ),\n                          )\n                        ],\n                      ),\n                    ),\n                  ],\n                ),\n              );\n            },\n            child: Text(\n              widget.value,\n              style: TextStyle(\n                fontSize: 12,\n                color: customColors.weakTextColor,\n              ),\n              maxLines: 5,\n              overflow: TextOverflow.ellipsis,\n            ),\n          ),\n          if (valueTranslated != '')\n            Column(\n              crossAxisAlignment: CrossAxisAlignment.start,\n              children: [\n                const SizedBox(height: 10),\n                Row(children: [\n                  Icon(\n                    Icons.check_circle,\n                    size: 12,\n                    color: customColors.linkColor,\n                  ),\n                  const SizedBox(width: 5),\n                  Text(\n                    '中文翻译 ↓',\n                    style: TextStyle(\n                      fontSize: 12,\n                      color: customColors.weakTextColor,\n                    ),\n                  ),\n                ]),\n                const SizedBox(height: 5),\n                GestureDetector(\n                  onLongPressStart: (details) {\n                    HapticFeedbackHelper.mediumImpact();\n\n                    BotToast.showAttachedWidget(\n                      target: details.globalPosition,\n                      duration: const Duration(seconds: 8),\n                      animationDuration: const Duration(milliseconds: 200),\n                      animationReverseDuration: const Duration(milliseconds: 200),\n                      preferDirection: PreferDirection.topCenter,\n                      ignoreContentClick: false,\n                      onlyOne: true,\n                      allowClick: true,\n                      enableSafeArea: true,\n                      attachedBuilder: (cancel) => AttachedButtonPanel(\n                        buttons: [\n                          TextButton.icon(\n                            onPressed: () {\n                              FlutterClipboard.copy(valueTranslated).then((value) {\n                                showSuccessMessage('已复制到剪贴板');\n                              });\n                              cancel();\n                            },\n                            label: const Text(''),\n                            icon: const Column(\n                              mainAxisSize: MainAxisSize.min,\n                              children: [\n                                Icon(\n                                  Icons.copy,\n                                  color: Color.fromARGB(255, 255, 255, 255),\n                                  size: 14,\n                                ),\n                                Text(\n                                  \"复制\",\n                                  style: TextStyle(fontSize: 12, color: Colors.white),\n                                ),\n                              ],\n                            ),\n                          ),\n                        ],\n                      ),\n                    );\n                  },\n                  child: Text(\n                    valueTranslated,\n                    style: TextStyle(\n                      fontSize: 12,\n                      color: customColors.weakTextColor,\n                    ),\n                    maxLines: 5,\n                    overflow: TextOverflow.ellipsis,\n                  ),\n                ),\n              ],\n            )\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/creative_island/my_creation.dart",
    "content": "import 'package:askaide/bloc/creative_island_bloc.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/helper/image.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/button.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/page/creative_island/draw/data/draw_history_datasource.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/api/creative.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:loading_more_list/loading_more_list.dart';\n\nclass MyCreationScreen extends StatefulWidget {\n  final SettingRepository setting;\n  final String mode;\n  const MyCreationScreen({super.key, required this.setting, required this.mode});\n\n  @override\n  State<MyCreationScreen> createState() => _MyCreationScreenState();\n}\n\nclass _MyCreationScreenState extends State<MyCreationScreen> {\n  final DrawHistoryDatasource datasource = DrawHistoryDatasource();\n\n  @override\n  void dispose() {\n    datasource.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          title: const Text(\n            '我的创作',\n            style: TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n          toolbarHeight: CustomSize.toolbarHeight,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          maxWidth: CustomSize.maxWindowSize,\n          backgroundColor: customColors.backgroundColor,\n          child: SafeArea(\n            child: RefreshIndicator(\n              color: customColors.linkColor,\n              onRefresh: () async {\n                context\n                    .read<CreativeIslandBloc>()\n                    .add(CreativeIslandHistoriesAllLoadEvent(forceRefresh: true, mode: widget.mode));\n              },\n              child: LoadingMoreList(\n                ListConfig<CreativeItemInServer>(\n                  extendedListDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount(\n                    crossAxisCount: _calCrossAxisCount(context),\n                    crossAxisSpacing: 10,\n                    mainAxisSpacing: 10,\n                  ),\n                  itemBuilder: (context, item, index) {\n                    return Material(\n                      color: customColors.backgroundContainerColor,\n                      borderRadius: CustomSize.borderRadius,\n                      child: InkWell(\n                        borderRadius: CustomSize.borderRadiusAll,\n                        onTap: () {\n                          context.push('/creative-island/${item.islandId}/history/${item.id}?show_error=true');\n                        },\n                        onLongPress: () {\n                          openModalBottomSheet(\n                            context,\n                            (context) {\n                              return Column(\n                                mainAxisSize: MainAxisSize.min,\n                                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                                children: [\n                                  const SizedBox(height: 20),\n                                  Column(\n                                    mainAxisSize: MainAxisSize.min,\n                                    children: [\n                                      Button(\n                                        title: '查看作品',\n                                        onPressed: () {\n                                          context.push(\n                                              '/creative-island/${item.islandId}/history/${item.id}?show_error=true');\n                                          context.pop();\n                                        },\n                                        size: const ButtonSize.full(),\n                                        color: customColors.weakLinkColor,\n                                        backgroundColor: const Color.fromARGB(36, 222, 222, 222),\n                                      ),\n                                      const SizedBox(height: 10),\n                                      Button(\n                                        title: '删除作品',\n                                        onPressed: () {\n                                          onItemDelete(\n                                            context,\n                                            item,\n                                            index,\n                                            onFinished: () {\n                                              context.pop();\n                                            },\n                                          );\n                                        },\n                                        size: const ButtonSize.full(),\n                                        color: customColors.weakLinkColor,\n                                        backgroundColor: const Color.fromARGB(36, 222, 222, 222),\n                                      ),\n                                      const SizedBox(height: 10),\n                                      Button(\n                                        title: AppLocale.cancel.getString(context),\n                                        backgroundColor: const Color.fromARGB(36, 222, 222, 222),\n                                        color: customColors.dialogDefaultTextColor?.withAlpha(150),\n                                        onPressed: () {\n                                          context.pop();\n                                        },\n                                        size: const ButtonSize.full(),\n                                      ),\n                                      const SizedBox(height: 10),\n                                    ],\n                                  ),\n                                ],\n                              );\n                            },\n                            heightFactor: 0.25,\n                          );\n                        },\n                        child: Column(\n                          children: [\n                            Stack(\n                              children: [\n                                _buildAnswerImagePreview(context, item),\n                                // TODO 风格名称，测试阶段使用\n                                if (item.filterName != null && item.filterName!.isNotEmpty)\n                                  Positioned(\n                                    bottom: 0,\n                                    child: Container(\n                                      padding: const EdgeInsets.symmetric(\n                                        horizontal: 8,\n                                        vertical: 2,\n                                      ),\n                                      decoration: BoxDecoration(\n                                        color: Colors.black.withOpacity(0.5),\n                                        borderRadius: const BorderRadius.only(\n                                            topRight: CustomSize.radius, bottomLeft: CustomSize.radius),\n                                      ),\n                                      child: Text(\n                                        item.filterName!,\n                                        style: const TextStyle(\n                                          fontSize: 12,\n                                          color: Colors.white,\n                                        ),\n                                      ),\n                                    ),\n                                  ),\n                                if (item.isShared)\n                                  Positioned(\n                                    right: 0,\n                                    top: 0,\n                                    child: Container(\n                                      padding: const EdgeInsets.symmetric(\n                                        horizontal: 8,\n                                        vertical: 2,\n                                      ),\n                                      decoration: BoxDecoration(\n                                        color: Colors.black.withOpacity(0.5),\n                                        borderRadius: const BorderRadius.only(\n                                          topRight: CustomSize.radius,\n                                          bottomLeft: CustomSize.radius,\n                                        ),\n                                      ),\n                                      child: const Text(\n                                        '公开',\n                                        style: TextStyle(\n                                          fontSize: 12,\n                                          color: Colors.white,\n                                        ),\n                                      ),\n                                    ),\n                                  )\n                              ],\n                            ),\n                            Container(\n                              padding: const EdgeInsets.all(8),\n                              child: Row(\n                                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                                children: [\n                                  buildIslandTypeText(customColors, item),\n                                  Text(\n                                    humanTime(item.createdAt, withTime: true),\n                                    style: TextStyle(\n                                      fontSize: 12,\n                                      color: customColors.weakTextColor?.withAlpha(150),\n                                    ),\n                                  ),\n                                ],\n                              ),\n                            ),\n                          ],\n                        ),\n                      ),\n                    );\n                  },\n                  sourceList: datasource,\n                  padding: const EdgeInsets.all(10),\n                  indicatorBuilder: (context, status) {\n                    String msg = '';\n                    switch (status) {\n                      case IndicatorStatus.noMoreLoad:\n                        msg = '~ 没有更多了 ~';\n                        break;\n                      case IndicatorStatus.loadingMoreBusying:\n                        msg = '加载中...';\n                        break;\n                      case IndicatorStatus.error:\n                        msg = '加载失败，请稍后再试';\n                        break;\n                      case IndicatorStatus.empty:\n                        msg = '您还没有创作过作品哦';\n                        break;\n                      default:\n                        return const Center(child: LoadingIndicator());\n                    }\n                    return Container(\n                      padding: const EdgeInsets.all(15),\n                      alignment: Alignment.center,\n                      child: Text(\n                        msg,\n                        style: TextStyle(\n                          color: customColors.weakTextColor,\n                          fontSize: 14,\n                        ),\n                      ),\n                    );\n                  },\n                ),\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget buildIslandTypeText(CustomColors customColors, CreativeItemInServer item) {\n    return Text(\n      item.islandTitle ?? '',\n      style: TextStyle(\n        color: customColors.weakTextColor?.withAlpha(150),\n        fontSize: 12,\n      ),\n    );\n  }\n\n  void onItemDelete(BuildContext context, CreativeItemInServer item, int index, {Function? onFinished}) {\n    openConfirmDialog(context, AppLocale.confirmDelete.getString(context), () {\n      APIServer().deleteCreativeHistoryItem(item.islandId, hisId: item.id).then((value) {\n        // datasource.refresh(true);\n        datasource.removeAt(index);\n        setState(() {});\n        showSuccessMessage(AppLocale.operateSuccess.getString(context));\n        onFinished?.call();\n      });\n    });\n  }\n\n  Widget _buildAnswerImagePreview(\n    BuildContext context,\n    CreativeItemInServer item,\n  ) {\n    if (item.isVideoType && item.originalImage != null) {\n      return ConstrainedBox(\n        constraints: const BoxConstraints(\n          minHeight: 100,\n        ),\n        child: Stack(\n          children: [\n            ClipRRect(\n              borderRadius: const BorderRadius.only(topLeft: CustomSize.radius, topRight: CustomSize.radius),\n              child: CachedNetworkImageEnhanced(\n                imageUrl: imageURL(item.originalImage!, qiniuImageTypeThumbMedium),\n                fit: BoxFit.cover,\n              ),\n            ),\n            Positioned(\n              right: 10,\n              bottom: 10,\n              child: Image.asset(\n                'assets/play.png',\n                width: 40,\n                opacity: const AlwaysStoppedAnimation(0.7),\n              ),\n            ),\n          ],\n        ),\n      );\n    } else if (item.isImageType && item.images.isNotEmpty) {\n      return ConstrainedBox(\n        constraints: const BoxConstraints(\n          minHeight: 100,\n        ),\n        child: Stack(\n          children: [\n            ClipRRect(\n              borderRadius: const BorderRadius.only(topLeft: CustomSize.radius, topRight: CustomSize.radius),\n              child: CachedNetworkImageEnhanced(\n                imageUrl: imageURL(item.images.first, qiniuImageTypeThumbMedium),\n                fit: BoxFit.cover,\n              ),\n            ),\n            if (item.params['image'] != null && item.params['image'] != '')\n              Positioned(\n                left: 8,\n                bottom: 8,\n                child: SizedBox(\n                  width: 60,\n                  height: 60,\n                  child: ClipRRect(\n                    borderRadius: CustomSize.borderRadius,\n                    child: CachedNetworkImageEnhanced(\n                      imageUrl: imageURL(item.params['image'], qiniuImageTypeAvatar),\n                      fit: BoxFit.cover,\n                    ),\n                  ),\n                ),\n              ),\n          ],\n        ),\n      );\n    }\n\n    if (item.isFailed) {\n      return ConstrainedBox(\n        constraints: const BoxConstraints(\n          minHeight: 150,\n        ),\n        child: const Center(\n          child: Column(\n            mainAxisAlignment: MainAxisAlignment.center,\n            children: [\n              Icon(\n                Icons.error_outline,\n                size: 40,\n                color: Colors.red,\n              ),\n              SizedBox(height: 10),\n              Text('创作失败', style: TextStyle(color: Colors.red))\n            ],\n          ),\n        ),\n      );\n    }\n\n    return ConstrainedBox(\n      constraints: const BoxConstraints(\n        minHeight: 150,\n      ),\n      child: Center(\n        child: Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: [\n            Icon(\n              Icons.hourglass_bottom,\n              size: 40,\n              color: Colors.blue[700],\n            ),\n            const SizedBox(height: 10),\n            Text('创作中', style: TextStyle(color: Colors.blue[700]))\n          ],\n        ),\n      ),\n    );\n  }\n\n  int _calCrossAxisCount(BuildContext context) {\n    var width = MediaQuery.of(context).size.width;\n    if (width > CustomSize.maxWindowSize) {\n      width = CustomSize.maxWindowSize;\n    }\n\n    return (width / 220).round();\n  }\n}\n"
  },
  {
    "path": "lib/page/creative_island/my_creation_item.dart",
    "content": "import 'package:askaide/bloc/creative_island_bloc.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/page/creative_island/draw/components/content_preview.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/api/creative.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\n\nclass MyCreationItemPage extends StatefulWidget {\n  final String islandId;\n  final int itemId;\n  final SettingRepository setting;\n  final bool showErrorMessage;\n\n  const MyCreationItemPage({\n    super.key,\n    required this.setting,\n    required this.islandId,\n    required this.itemId,\n    required this.showErrorMessage,\n  });\n\n  @override\n  State<MyCreationItemPage> createState() => _MyCreationItemPageState();\n}\n\nclass _MyCreationItemPageState extends State<MyCreationItemPage> with SingleTickerProviderStateMixin {\n  late final TabController _tabController;\n\n  @override\n  void initState() {\n    super.initState();\n\n    _tabController = TabController(length: 2, vsync: this);\n    _tabController.animateTo(1);\n\n    context.read<CreativeIslandBloc>().add(CreativeIslandHistoryItemLoadEvent(widget.itemId));\n  }\n\n  @override\n  void dispose() {\n    _tabController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: BlocBuilder<CreativeIslandBloc, CreativeIslandState>(\n        buildWhen: (previous, current) =>\n            current is CreativeIslandHistoryItemLoaded || current is CreativeIslandHistoryItemLoading,\n        builder: (context, state) {\n          if (state is CreativeIslandHistoryItemLoaded) {\n            return Scaffold(\n              appBar: AppBar(\n                toolbarHeight: CustomSize.toolbarHeight,\n                leading: IconButton(\n                  icon: const Icon(Icons.close),\n                  onPressed: () => Navigator.of(context).pop(),\n                ),\n                title: Text(\n                  state.item!.showBetaFeature ? '#${state.item!.id}' : '',\n                  style: TextStyle(\n                    color: customColors.weakTextColor,\n                  ),\n                ),\n                actions: buildActions(state, context, customColors),\n              ),\n              backgroundColor: customColors.backgroundColor,\n              body: BackgroundContainer(\n                setting: widget.setting,\n                enabled: false,\n                backgroundColor: customColors.backgroundColor,\n                maxWidth: CustomSize.smallWindowSize,\n                child: SafeArea(\n                  child: SingleChildScrollView(\n                    child: Column(\n                      mainAxisSize: MainAxisSize.min,\n                      children: [\n                        Container(\n                          child: state.item!.isSuccessful\n                              ? CreativeIslandContentPreview(\n                                  result: IslandResult(\n                                    result: state.item!.images,\n                                    params: state.item!.params,\n                                  ),\n                                  customColors: customColors,\n                                  item: state.item,\n                                )\n                              : _buildNotSuccessBox(state, customColors),\n                        ),\n                        ColumnBlock(\n                          innerPanding: 10,\n                          padding: const EdgeInsets.all(15),\n                          margin: const EdgeInsets.symmetric(\n                            vertical: 10,\n                            horizontal: 10,\n                          ),\n                          children: [\n                            if (state.item!.prompt != null && state.item!.prompt != '')\n                              Column(\n                                crossAxisAlignment: CrossAxisAlignment.start,\n                                children: [\n                                  Text(\n                                    AppLocale.ideaPrompt.getString(context),\n                                    style: TextStyle(\n                                      fontSize: 15,\n                                      fontWeight: FontWeight.bold,\n                                      color: customColors.textfieldLabelColor,\n                                    ),\n                                  ),\n                                  const SizedBox(height: 10),\n                                  SelectableText(\n                                    state.item!.prompt ?? '',\n                                    style: TextStyle(\n                                      color: customColors.weakTextColor,\n                                    ),\n                                  ),\n                                ],\n                              ),\n                            if (state.item!.arguments != null)\n                              ..._buildItemArguments(\n                                state.item!.creativeItemArguments,\n                                customColors,\n                              ),\n                          ],\n                        ),\n                      ],\n                    ),\n                  ),\n                ),\n              ),\n            );\n          }\n\n          return const Center(\n            child: CircularProgressIndicator(),\n          );\n        },\n      ),\n    );\n  }\n\n  List<Widget> buildActions(\n    CreativeIslandHistoryItemLoaded state,\n    BuildContext context,\n    CustomColors customColors,\n  ) {\n    if (state.item!.userId != APIServer().localUserID() && state.item!.isSuccessful) {\n      return [\n        TextButton(\n          onPressed: () {\n            openConfirmDialog(context, 'Are you sure to ban this project?', () {\n              APIServer().forbidCreativeHistoryItem(historyId: state.item!.id).then((value) {\n                showSuccessMessage(AppLocale.operateSuccess.getString(context));\n\n                context.read<CreativeIslandBloc>().add(CreativeIslandHistoryItemLoadEvent(\n                      widget.itemId,\n                      forceRefresh: true,\n                    ));\n              });\n            });\n          },\n          child: Row(\n            children: [\n              const Icon(\n                Icons.block,\n                color: Colors.amber,\n                size: 14,\n              ),\n              const SizedBox(width: 5),\n              Text(\n                'Ban',\n                style: TextStyle(\n                  color: customColors.weakLinkColor,\n                  fontSize: 12,\n                ),\n              ),\n            ],\n          ),\n        ),\n      ];\n    }\n\n    return [\n      if (state.item!.isSuccessful && state.item!.showBetaFeature)\n        TextButton(\n          onPressed: () {\n            if (state.item!.isShared) {\n              APIServer().cancelShareCreativeHistoryToGallery(historyId: state.item!.id).then((value) {\n                showSuccessMessage(AppLocale.operateSuccess.getString(context));\n\n                context.read<CreativeIslandBloc>().add(CreativeIslandHistoryItemLoadEvent(\n                      widget.itemId,\n                      forceRefresh: true,\n                    ));\n              });\n            } else {\n              APIServer().shareCreativeHistoryToGallery(historyId: state.item!.id).then((value) {\n                showSuccessMessage(AppLocale.operateSuccess.getString(context));\n\n                context.read<CreativeIslandBloc>().add(CreativeIslandHistoryItemLoadEvent(\n                      widget.itemId,\n                      forceRefresh: true,\n                    ));\n              });\n            }\n          },\n          child: Text(\n            state.item!.isShared ? 'Set Private' : 'Set Public',\n            style: TextStyle(\n              color: customColors.weakLinkColor,\n              fontSize: 12,\n            ),\n          ),\n        )\n    ];\n  }\n\n  Widget _buildNotSuccessBox(\n    CreativeIslandHistoryItemLoaded state,\n    CustomColors customColors,\n  ) {\n    if (state.item != null && state.item!.isFailed) {\n      return Center(\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          children: [\n            const Icon(\n              Icons.error_outline,\n              size: 50,\n              color: Colors.red,\n            ),\n            const SizedBox(height: 10),\n            Text(\n              AppLocale.generateFailed.getString(context),\n              style: const TextStyle(color: Colors.red),\n              textAlign: TextAlign.center,\n            ),\n            const SizedBox(height: 10),\n            SelectableText(\n              widget.showErrorMessage ? '${state.item!.answer}' : 'Error Code：${state.item!.errorCode}',\n              textAlign: TextAlign.center,\n              style: TextStyle(\n                fontSize: 10,\n                color: customColors.weakTextColor,\n              ),\n            ),\n            const SizedBox(height: 20),\n          ],\n        ),\n      );\n    }\n\n    return Center(\n      child: Column(\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          Icon(\n            Icons.info_outline,\n            size: 50,\n            color: customColors.weakTextColor,\n          ),\n          const SizedBox(height: 10),\n          Text(\n            AppLocale.generate.getString(context),\n            style: TextStyle(\n              color: customColors.backgroundInvertedColor,\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n\n  List<Widget> _buildItemArguments(CreativeItemArguments arg, CustomColors customColors) {\n    final children = <Widget>[];\n\n    if (arg.negativePrompt != null && arg.negativePrompt != '') {\n      children.add(\n        Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            Text(\n              AppLocale.excludeContents.getString(context),\n              style: TextStyle(\n                fontSize: 15,\n                fontWeight: FontWeight.bold,\n                color: customColors.textfieldLabelColor,\n              ),\n            ),\n            const SizedBox(height: 10),\n            SelectableText(\n              arg.negativePrompt!,\n              style: TextStyle(\n                color: customColors.weakTextColor,\n              ),\n            ),\n          ],\n        ),\n      );\n    }\n\n    // if (arg.modelName != null && arg.modelName != '') {\n    //   children.add(\n    //     Column(\n    //       crossAxisAlignment: CrossAxisAlignment.start,\n    //       children: [\n    //         Text(\n    //           'AI 模型',\n    //           style: TextStyle(\n    //             fontSize: 15,\n    //             fontWeight: FontWeight.bold,\n    //             color: customColors.textfieldLabelColor,\n    //           ),\n    //         ),\n    //         const SizedBox(height: 10),\n    //         SelectableText(\n    //           arg.modelName!,\n    //           style: TextStyle(\n    //             color: customColors.weakTextColor,\n    //           ),\n    //         ),\n    //       ],\n    //     ),\n    //   );\n    // }\n\n    if (arg.filterName != null && arg.filterName != '') {\n      children.add(\n        Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            Text(\n              AppLocale.style.getString(context),\n              style: TextStyle(\n                fontSize: 15,\n                fontWeight: FontWeight.bold,\n                color: customColors.textfieldLabelColor,\n              ),\n            ),\n            const SizedBox(height: 10),\n            SelectableText(\n              arg.filterName!,\n              style: TextStyle(\n                color: customColors.weakTextColor,\n              ),\n            ),\n          ],\n        ),\n      );\n    }\n\n    // if (arg.seed != null && arg.seed! > 0) {\n    //   children.add(\n    //     Column(\n    //       crossAxisAlignment: CrossAxisAlignment.start,\n    //       children: [\n    //         Text(\n    //           'Seed',\n    //           style: TextStyle(\n    //             fontSize: 15,\n    //             fontWeight: FontWeight.bold,\n    //             color: customColors.textfieldLabelColor,\n    //           ),\n    //         ),\n    //         const SizedBox(height: 10),\n    //         SelectableText(\n    //           '${arg.seed!}',\n    //           style: TextStyle(\n    //             color: customColors.weakTextColor,\n    //           ),\n    //         ),\n    //       ],\n    //     ),\n    //   );\n    // }\n\n    // if (arg.image != null && arg.image != '') {\n    //   children.add(\n    //     Column(\n    //       crossAxisAlignment: CrossAxisAlignment.start,\n    //       children: [\n    //         Text(\n    //           '原图',\n    //           style: TextStyle(\n    //             fontSize: 15,\n    //             fontWeight: FontWeight.bold,\n    //             color: customColors.textfieldLabelColor,\n    //           ),\n    //         ),\n    //         const SizedBox(height: 10),\n    //         NetworkImagePreviewer(url: arg.image!, hidePreviewButton: true),\n    //       ],\n    //     ),\n    //   );\n    // }\n\n    return children;\n  }\n}\n"
  },
  {
    "path": "lib/page/custom_scaffold.dart",
    "content": "import 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\n\nclass CustomScaffold extends StatefulWidget {\n  final SettingRepository settings;\n  final Widget title;\n  final List<Widget>? actions;\n  final Widget body;\n  final Widget? drawer;\n  final Widget? appBarBackground;\n  final AppBar? backAppBar;\n  final bool showBackAppBar;\n\n  const CustomScaffold({\n    super.key,\n    required this.settings,\n    required this.title,\n    this.actions,\n    required this.body,\n    this.drawer,\n    this.appBarBackground,\n    this.backAppBar,\n    this.showBackAppBar = false,\n  });\n\n  @override\n  State<CustomScaffold> createState() => _CustomScaffoldState();\n}\n\nclass _CustomScaffoldState extends State<CustomScaffold> {\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return Scaffold(\n      appBar: buildAppBar(),\n      backgroundColor: customColors.backgroundContainerColor,\n      body: BackgroundContainer(\n        setting: widget.settings,\n        maxWidth: CustomSize.maxWindowSize,\n        child: widget.body,\n      ),\n      drawer: widget.drawer,\n    );\n  }\n\n  AppBar? buildAppBar() {\n    if (widget.showBackAppBar && widget.backAppBar != null) {\n      return widget.backAppBar;\n    }\n\n    return AppBar(\n      title: widget.title,\n      centerTitle: true,\n      toolbarHeight: CustomSize.toolbarHeight,\n      elevation: 0,\n      actions: widget.actions,\n      flexibleSpace: SizedBox(\n        width: double.infinity,\n        child: ShaderMask(\n          shaderCallback: (rect) {\n            return const LinearGradient(\n              begin: Alignment.topCenter,\n              end: Alignment.bottomCenter,\n              colors: [Colors.black, Colors.transparent],\n            ).createShader(Rect.fromLTRB(0, 0, rect.width, rect.height));\n          },\n          blendMode: BlendMode.dstIn,\n          child: widget.appBarBackground,\n        ),\n      ),\n      leading: widget.drawer != null\n          ? Builder(\n              builder: (context) => IconButton(\n                icon: const Icon(Icons.sort),\n                onPressed: () => Scaffold.of(context).openDrawer(),\n              ),\n            )\n          : null,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/data/chat_history_datasource.dart",
    "content": "import 'package:askaide/helper/logger.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/chat_message_repo.dart';\nimport 'package:askaide/repo/model/chat_history.dart';\nimport 'package:loading_more_list/loading_more_list.dart';\n\nclass ChatHistoryDatasource extends LoadingMoreBase<ChatHistory> {\n  int pageindex = 1;\n  bool _hasMore = true;\n  bool forceRefresh = false;\n\n  String? keyword;\n\n  final ChatMessageRepository repo;\n  ChatHistoryDatasource(this.repo);\n\n  @override\n  bool get hasMore => (_hasMore && length < 300) || forceRefresh;\n\n  @override\n  Future<bool> loadData([bool isloadMoreAction = false]) async {\n    try {\n      final histories = await repo.recentChatHistories(\n        30,\n        keyword: keyword,\n        offset: 30 * (pageindex - 1),\n        userId: APIServer().localUserID(),\n      );\n\n      if (pageindex == 1) {\n        clear();\n      }\n\n      for (var element in histories) {\n        add(element);\n      }\n\n      if (histories.isEmpty) {\n        _hasMore = false;\n      }\n\n      pageindex = pageindex + 1;\n      return true;\n    } catch (e) {\n      Logger.instance.e(e);\n      return false;\n    }\n  }\n\n  @override\n  Future<bool> refresh([bool notifyStateChanged = false, String? keyword]) async {\n    this.keyword = keyword;\n    _hasMore = true;\n    pageindex = 1;\n    //force to refresh list when you don't want clear list before request\n    //for the case, if your list already has 20 items.\n    forceRefresh = !notifyStateChanged;\n    var result = await super.refresh(notifyStateChanged);\n    forceRefresh = false;\n    return result;\n  }\n}\n"
  },
  {
    "path": "lib/page/data/group_message_datasource.dart",
    "content": "import 'package:askaide/helper/logger.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/model/group.dart';\nimport 'package:loading_more_list/loading_more_list.dart';\n\nclass GroupMessageDatasource extends LoadingMoreBase<GroupMessage> {\n  int startId = 0;\n  bool _hasMore = true;\n  bool forceRefresh = false;\n\n  final int groupId;\n\n  GroupMessageDatasource({required this.groupId});\n\n  @override\n  bool get hasMore => _hasMore || forceRefresh;\n\n  @override\n  Future<bool> loadData([bool isloadMoreAction = false]) async {\n    try {\n      final messages = await APIServer()\n          .chatGroupMessages(groupId, startId: startId, cache: false);\n\n      if (startId == 0) {\n        clear();\n      }\n\n      for (var element in messages.data) {\n        add(element);\n      }\n\n      if (messages.data.isEmpty) {\n        _hasMore = false;\n      }\n\n      startId = messages.lastId;\n      return true;\n    } catch (e) {\n      Logger.instance.e(e);\n      return false;\n    }\n  }\n\n  @override\n  Future<bool> refresh([bool notifyStateChanged = false]) async {\n    _hasMore = true;\n    startId = 1;\n    //force to refresh list when you don't want clear list before request\n    //for the case, if your list already has 20 items.\n    forceRefresh = !notifyStateChanged;\n    var result = await super.refresh(notifyStateChanged);\n    forceRefresh = false;\n    return result;\n  }\n}\n"
  },
  {
    "path": "lib/page/data/notification_datasource.dart",
    "content": "import 'package:askaide/helper/logger.dart';\nimport 'package:askaide/repo/api/notification.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:loading_more_list/loading_more_list.dart';\n\nclass NotificationDatasource extends LoadingMoreBase<NotifyMessage> {\n  int startId = 0;\n  bool _hasMore = true;\n  bool forceRefresh = false;\n\n  NotificationDatasource();\n\n  @override\n  bool get hasMore => _hasMore || forceRefresh;\n\n  @override\n  Future<bool> loadData([bool isloadMoreAction = false]) async {\n    try {\n      final messages =\n          await APIServer().notifications(startId: startId, cache: false);\n\n      if (startId == 0) {\n        clear();\n      }\n\n      for (var element in messages.data) {\n        add(element);\n      }\n\n      if (messages.data.isEmpty) {\n        _hasMore = false;\n      }\n\n      startId = messages.lastId;\n      return true;\n    } catch (e) {\n      Logger.instance.e(e);\n      return false;\n    }\n  }\n\n  @override\n  Future<bool> refresh([bool notifyStateChanged = false]) async {\n    _hasMore = true;\n    startId = 0;\n    //force to refresh list when you don't want clear list before request\n    //for the case, if your list already has 20 items.\n    forceRefresh = !notifyStateChanged;\n    var result = await super.refresh(notifyStateChanged);\n    forceRefresh = false;\n    return result;\n  }\n}\n"
  },
  {
    "path": "lib/page/drawer.dart",
    "content": "import 'package:askaide/bloc/account_bloc.dart';\nimport 'package:askaide/bloc/chat_chat_bloc.dart';\nimport 'package:askaide/bloc/room_bloc.dart';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/account_quota_card.dart';\nimport 'package:askaide/page/component/chat/role_avatar.dart';\nimport 'package:askaide/page/component/social_icon.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/api/user.dart';\nimport 'package:askaide/repo/model/chat_history.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass LeftDrawer extends StatefulWidget {\n  const LeftDrawer({super.key});\n\n  @override\n  State<LeftDrawer> createState() => _LeftDrawerState();\n}\n\nclass _LeftDrawerState extends State<LeftDrawer> {\n  @override\n  void initState() {\n    super.initState();\n\n    reload();\n  }\n\n  void reload() {\n    if (Ability().isUserLogon()) {\n      context.read<AccountBloc>().add(AccountLoadEvent(cache: false));\n    }\n\n    context.read<ChatChatBloc>().add(ChatChatLoadRecentHistories());\n    context.read<RoomBloc>().add(RoomsRecentLoadEvent());\n  }\n\n  double maxDrawerWidth() {\n    final width = MediaQuery.of(context).size.width * 0.85;\n    return width > 334.0 ? 334.0 : width;\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return Drawer(\n      width: maxDrawerWidth(),\n      shape: RoundedRectangleBorder(\n        borderRadius: PlatformTool.isDesktop()\n            ? BorderRadius.zero\n            : const BorderRadius.only(\n                topRight: CustomSize.radius,\n                bottomRight: CustomSize.radius,\n              ),\n      ),\n      backgroundColor: customColors.backgroundContainerColor,\n      shadowColor: customColors.backgroundInvertedColor,\n      child: SafeArea(\n        top: false,\n        child: Padding(\n          padding: const EdgeInsets.only(right: 10),\n          child: Column(\n            children: [\n              Expanded(\n                child: SingleChildScrollView(\n                  child: Column(\n                    children: [\n                      const SafeArea(child: SizedBox(height: 20)),\n                      if (Ability().isUserLogon() && Ability().enableDigitalHuman)\n                        ListTile(\n                          leading: const Icon(Icons.group_outlined),\n                          title: Text(AppLocale.homeTitle.getString(context)),\n                          onTap: () {\n                            context.push('/characters').whenComplete(reload);\n                          },\n                        ),\n                      if (Ability().isUserLogon() && Ability().enableDigitalHuman)\n                        BlocBuilder<RoomBloc, RoomState>(\n                          buildWhen: (previous, current) => current is RoomsRecentLoaded,\n                          builder: (_, state) {\n                            if (state is RoomsRecentLoaded) {\n                              return ListView.builder(\n                                shrinkWrap: true,\n                                padding: const EdgeInsets.all(0),\n                                physics: const NeverScrollableScrollPhysics(),\n                                itemCount: state.rooms.length,\n                                itemBuilder: (context, index) {\n                                  final item = state.rooms[index];\n                                  return ListTile(\n                                    contentPadding: const EdgeInsets.only(left: 30),\n                                    dense: true,\n                                    leading: RoleAvatar(\n                                      avatarUrl: item.avatarUrl,\n                                      name: item.name,\n                                      avatarSize: 25,\n                                    ),\n                                    title: Text(item.name),\n                                    onTap: () {\n                                      context.push('/room/${item.id}/chat').whenComplete(reload);\n                                    },\n                                  );\n                                },\n                              );\n                            }\n\n                            return const SizedBox();\n                          },\n                        ),\n                      if (Ability().enableGallery)\n                        ListTile(\n                          leading: const Icon(Icons.auto_awesome_outlined),\n                          title: Text(AppLocale.discover.getString(context)),\n                          onTap: () {\n                            context.push('/creative-gallery').whenComplete(reload);\n                          },\n                        ),\n                      // ListTile(\n                      //   leading: const Icon(Icons.palette_outlined),\n                      //   title: Text(AppLocale.creativeIsland.getString(context)),\n                      //   onTap: () {\n                      //     context.push('/creative-draw');\n                      //   },\n                      // ),\n                      if (Ability().enableGallery || (Ability().isUserLogon() && Ability().enableDigitalHuman))\n                        Divider(\n                          color: customColors.weakTextColor?.withAlpha(50),\n                          height: 10,\n                          indent: 10,\n                          endIndent: 10,\n                        ),\n                      BlocBuilder<ChatChatBloc, ChatChatState>(\n                        buildWhen: (previous, current) => current is ChatChatRecentHistoriesLoaded,\n                        builder: (_, state) {\n                          if (state is ChatChatRecentHistoriesLoaded) {\n                            // Group histories by time\n                            final now = DateTime.now();\n                            final groups = <String, List<ChatHistory>>{};\n\n                            for (var history in state.histories) {\n                              final created = DateTime.fromMillisecondsSinceEpoch(\n                                  (history.createdAt ?? DateTime.now()).millisecondsSinceEpoch);\n                              final difference = now.difference(created);\n\n                              String groupKey;\n                              if (difference.inDays < 4) {\n                                groupKey = AppLocale.recently.getString(context);\n                              } else if (difference.inDays < 7) {\n                                groupKey = '4 ${AppLocale.daysAgo.getString(context)}';\n                              } else if (difference.inDays < 14) {\n                                groupKey = AppLocale.lastWeek.getString(context);\n                              } else if (difference.inDays < 30) {\n                                final weeks = (difference.inDays / 7).floor();\n                                groupKey = '$weeks ${AppLocale.weeksAgo.getString(context)}';\n                              } else if (difference.inDays < 365) {\n                                if (difference.inDays < 60) {\n                                  groupKey = AppLocale.lastMonth.getString(context);\n                                }\n                                final months = (difference.inDays / 30).floor();\n                                groupKey = '$months ${AppLocale.monthsAgo.getString(context)}';\n                              } else if (difference.inDays < 730) {\n                                groupKey = AppLocale.lastYear.getString(context);\n                              } else {\n                                groupKey = AppLocale.longTimeAgo.getString(context);\n                              }\n\n                              groups.putIfAbsent(groupKey, () => []).add(history);\n                            }\n\n                            return Column(\n                              children: [\n                                ListView.builder(\n                                  shrinkWrap: true,\n                                  padding: const EdgeInsets.all(0),\n                                  physics: const NeverScrollableScrollPhysics(),\n                                  itemCount:\n                                      groups.entries.fold(0, (sum, entry) => (sum ?? 0) + entry.value.length + 1),\n                                  itemBuilder: (context, index) {\n                                    int itemCount = 0;\n                                    for (var entry in groups.entries) {\n                                      if (index == itemCount) {\n                                        return Row(\n                                          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                                          children: [\n                                            Container(\n                                              padding: const EdgeInsets.only(left: 16, top: 8, bottom: 8),\n                                              child: Text(\n                                                entry.key,\n                                                style: TextStyle(\n                                                  color: Theme.of(context).colorScheme.secondary,\n                                                  fontSize: 12,\n                                                ),\n                                              ),\n                                            ),\n                                            const SizedBox(),\n                                            if (index == 0)\n                                              IconButton(\n                                                onPressed: () {\n                                                  context.push('/chat-chat/history').whenComplete(() {\n                                                    if (context.mounted) {\n                                                      context.read<ChatChatBloc>().add(ChatChatLoadRecentHistories());\n                                                    }\n                                                  });\n                                                },\n                                                icon: Icon(\n                                                  Icons.filter_list,\n                                                  color: Theme.of(context).colorScheme.secondary,\n                                                  size: 16,\n                                                ),\n                                              ),\n                                          ],\n                                        );\n                                      }\n\n                                      itemCount += 1;\n\n                                      if (index < itemCount + entry.value.length) {\n                                        final item = entry.value[index - itemCount];\n                                        return ListTile(\n                                          contentPadding: const EdgeInsets.only(left: 30),\n                                          title: Text(\n                                            item.title ?? 'Unknown',\n                                            maxLines: 1,\n                                            overflow: TextOverflow.ellipsis,\n                                          ),\n                                          onTap: () {\n                                            context.push(\n                                                '/chat-anywhere?chat_id=${item.id}&model=${item.model}&title=${item.title}');\n                                          },\n                                        );\n                                      }\n\n                                      itemCount += entry.value.length;\n                                    }\n\n                                    return const SizedBox();\n                                  },\n                                ),\n                              ],\n                            );\n                          }\n\n                          return SocialIconGroup(\n                            isSettingTiles: true,\n                          );\n                        },\n                      ),\n                    ],\n                  ),\n                ),\n              ),\n              Container(\n                height: 100,\n                padding: const EdgeInsets.only(left: 20, right: 10),\n                child: buildAccountCard(context),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget buildAccountCard(BuildContext context) {\n    return Stack(\n      children: [\n        Positioned(\n          right: 0,\n          top: 6,\n          child: IconButton(\n            onPressed: () {\n              context.push('/setting');\n            },\n            icon: const Icon(Icons.more_horiz),\n            tooltip: AppLocale.settings.getString(context),\n          ),\n        ),\n        Container(\n          margin: const EdgeInsets.only(top: 15),\n          child: BlocBuilder<AccountBloc, AccountState>(\n            builder: (_, state) {\n              UserInfo? userInfo;\n              if (state is AccountLoaded) {\n                userInfo = state.user;\n              }\n\n              return AccountQuotaCard(\n                userInfo: userInfo,\n                noBorder: true,\n                onPaymentReturn: () {\n                  if (userInfo != null) {\n                    context.read<AccountBloc>().add(AccountLoadEvent(cache: false));\n                  }\n                },\n              );\n            },\n          ),\n        ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/home.dart",
    "content": "import 'package:askaide/bloc/account_bloc.dart';\nimport 'package:askaide/bloc/chat_chat_bloc.dart';\nimport 'package:askaide/bloc/chat_message_bloc.dart';\nimport 'package:askaide/bloc/room_bloc.dart';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/cache.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/helper/model.dart';\nimport 'package:askaide/helper/upload.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/chat/component/model_switcher.dart';\nimport 'package:askaide/page/chat/component/stop_button.dart';\nimport 'package:askaide/page/chat/character_chat.dart';\nimport 'package:askaide/page/component/audio_player.dart';\nimport 'package:askaide/page/component/chat/chat_input.dart';\nimport 'package:askaide/page/component/chat/chat_input_button.dart';\nimport 'package:askaide/page/component/chat/chat_preview.dart';\nimport 'package:askaide/page/component/chat/empty.dart';\nimport 'package:askaide/page/component/chat/file_upload.dart';\nimport 'package:askaide/page/component/chat/help_tips.dart';\nimport 'package:askaide/page/component/chat/message_state_manager.dart';\nimport 'package:askaide/page/component/chat/role_avatar.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/enhanced_error.dart';\nimport 'package:askaide/page/component/global_alert.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/select_mode_toolbar.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/page/custom_scaffold.dart';\nimport 'package:askaide/page/drawer.dart';\nimport 'package:askaide/repo/api/model.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/model/message.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:auto_size_text/auto_size_text.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:askaide/repo/model/model.dart' as mm;\nimport 'package:go_router/go_router.dart';\nimport 'package:quickalert/models/quickalert_type.dart';\nimport 'package:url_launcher/url_launcher_string.dart';\n\nclass NewHomePage extends StatefulWidget {\n  final SettingRepository settings;\n\n  final bool showInitialDialog;\n  final int? reward;\n\n  /// 聊天内容窗口状态管理器\n  final MessageStateManager stateManager;\n  const NewHomePage({\n    super.key,\n    required this.settings,\n    required this.stateManager,\n    this.showInitialDialog = false,\n    this.reward,\n  });\n\n  @override\n  State<NewHomePage> createState() => _NewHomePageState();\n}\n\nclass _NewHomePageState extends State<NewHomePage> {\n  // 聊天内容界面控制器\n  final ChatPreviewController chatPreviewController = ChatPreviewController();\n  // 聊天内容滚动控制器\n  final ScrollController scrollController = ScrollController();\n  // 输入框是否可编辑\n  final ValueNotifier<bool> enableInput = ValueNotifier(true);\n  // 音频播放器控制器\n  final AudioPlayerController audioPlayerController = AudioPlayerController(useRemoteAPI: true);\n\n  // 聊天室 ID，当没有值时，会在第一个聊天消息发送后自动设置新值\n  int? chatId;\n  // The selected image files for image upload\n  List<FileUpload> selectedImageFiles = [];\n  // The selected file for file upload\n  FileUpload? selectedFile;\n\n  // 是否显示音频播放器\n  bool showAudioPlayer = false;\n  // 是否显示音频播放器加载中\n  bool audioLoadding = false;\n\n  /// 当前选择的模型\n  mm.Model? selectedModel;\n  // 全量模型列表\n  List<mm.Model> supportModels = [];\n  // 当前聊天所使用的模型（v2）\n  HomeModelV2? currentModelV2;\n\n  /// 是否启用搜索\n  bool enableSearch = false;\n\n  /// 是否启用推理\n  bool enableReasoning = false;\n\n  @override\n  void initState() {\n    super.initState();\n\n    Cache().intGet(key: 'last_chat_id').then((value) {\n      chatId = value;\n      reloadPage(loadAll: true);\n    });\n\n    reloadModels(cache: false);\n    initListeners();\n\n    showInitialDialog();\n  }\n\n  void showInitialDialog() {\n    if (widget.showInitialDialog) {\n      WidgetsBinding.instance.addPostFrameCallback((_) {\n        showBeautyDialog(\n          context,\n          type: QuickAlertType.info,\n          text:\n              '恭喜您，账号创建成功！${(widget.reward != null && widget.reward! > 0) ? '\\n\\n为了庆祝这一时刻，我们向您的账户赠送了 ${widget.reward} 个智慧果。' : ''}',\n          confirmBtnText: AppLocale.startChat.getString(context),\n          onConfirmBtnTap: () {\n            context.pop();\n          },\n        );\n      });\n    } else {\n      // 版本检查\n      APIServer().versionCheck().then((resp) {\n        final lastVersion = widget.settings.get('last_server_version');\n        if (resp.serverVersion == lastVersion && !resp.forceUpdate) {\n          return;\n        }\n\n        if (resp.hasUpdate) {\n          showBeautyDialog(\n            context,\n            type: QuickAlertType.success,\n            text: resp.message,\n            confirmBtnText: AppLocale.updateApp.getString(context),\n            onConfirmBtnTap: () {\n              launchUrlString(resp.url, mode: LaunchMode.externalApplication);\n            },\n            cancelBtnText: AppLocale.notUpdateApp.getString(context),\n            showCancelBtn: true,\n          );\n        }\n\n        widget.settings.set('last_server_version', resp.serverVersion);\n      });\n    }\n  }\n\n  /// 重新加载页面\n  void reloadPage({bool loadAll = false}) {\n    // 加载当前用户信息\n    context.read<AccountBloc>().add(AccountLoadEvent());\n\n    if (loadAll) {\n      // 加载当前聊天室信息\n      context.read<RoomBloc>().add(RoomLoadEvent(\n            chatAnywhereRoomId,\n            chatHistoryId: chatId,\n            cascading: true,\n          ));\n\n      // 查询最近聊天记录\n      context.read<ChatMessageBloc>().add(ChatMessageGetRecentEvent(chatHistoryId: chatId));\n    }\n  }\n\n  /// 加载模型列表，用于查询模型名称\n  Future<void> reloadModels({bool cache = true}) async {\n    var value = await ModelAggregate.models(cache: cache);\n    setState(() {\n      supportModels = value;\n    });\n\n    var cacheValue = await Cache().stringGet(key: 'last_selected_model');\n\n    final selected = supportModels.where((e) => e.id == cacheValue).firstOrNull;\n    if (selected != null) {\n      setState(() {\n        selectedModel = selected;\n      });\n    }\n\n    if (selectedModel == null && supportModels.isNotEmpty) {\n      setState(() {\n        selectedModel = supportModels.where((e) => e.isDefault && !e.userNoPermission).firstOrNull;\n        selectedModel ??= supportModels.where((e) => !e.userNoPermission).firstOrNull;\n      });\n    }\n\n    if (selectedModel != null) {\n      loadCurrentModel(selectedModel!.id);\n    }\n  }\n\n  void initListeners() {\n    chatPreviewController.addListener(() {\n      setState(() {});\n    });\n\n    audioPlayerController.onPlayStopped = () {\n      setState(() {\n        showAudioPlayer = false;\n      });\n    };\n    audioPlayerController.onPlayAudioStarted = () {\n      setState(() {\n        showAudioPlayer = true;\n      });\n    };\n    audioPlayerController.onPlayAudioLoading = (loading) {\n      setState(() {\n        audioLoadding = loading;\n      });\n    };\n  }\n\n  /// 创建新的聊天\n  void createNewChat() {\n    Cache().setInt(\n      key: 'last_chat_id',\n      value: 0,\n      duration: const Duration(days: 3650),\n    );\n    setState(() {\n      chatId = null;\n    });\n\n    reloadPage(loadAll: true);\n  }\n\n  /// 更新当前聊天\n  void updateCurrentChat(int chatId) {\n    Cache().setInt(\n      key: 'last_chat_id',\n      value: chatId,\n      duration: const Duration(days: 3650),\n    );\n    if (this.chatId == chatId) {\n      return;\n    }\n\n    setState(() {\n      this.chatId = chatId;\n    });\n    reloadPage();\n  }\n\n  @override\n  void dispose() {\n    scrollController.dispose();\n    chatPreviewController.dispose();\n    audioPlayerController.dispose();\n\n    super.dispose();\n  }\n\n  Future<void> loadCurrentModel(String model) async {\n    if (!model.startsWith('v2@') || currentModelV2 != null) {\n      return;\n    }\n\n    currentModelV2 = await APIServer().customHomeModelsItemV2(\n      uniqueKey: model.split('v2@').last,\n    );\n\n    setState(() {});\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return WindowFrameWidget(\n      child: CustomScaffold(\n        settings: widget.settings,\n        showBackAppBar: chatPreviewController.selectMode,\n        backAppBar: AppBar(\n          title: Text(\n            AppLocale.select.getString(context),\n            style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n          elevation: 0,\n          leadingWidth: 80,\n          leading: TextButton(\n            onPressed: () {\n              chatPreviewController.exitSelectMode();\n            },\n            child: Text(\n              AppLocale.cancel.getString(context),\n              style: TextStyle(color: customColors.linkColor),\n            ),\n          ),\n          toolbarHeight: CustomSize.toolbarHeight,\n        ),\n        // 标题，点击后弹出模型选择对话框\n        title: GestureDetector(\n          onTap: () {\n            reloadModels(cache: false);\n\n            ModelSwitcher.openActionDialog(\n              // ignore: use_build_context_synchronously\n              context: context,\n              onSelected: (selected) {\n                setState(() {\n                  selectedModel = selected;\n                });\n\n                if (selected != null) {\n                  Cache().setString(\n                    key: 'last_selected_model',\n                    value: selected.id,\n                    duration: const Duration(days: 3650),\n                  );\n                }\n              },\n              initValue: selectedModel,\n            );\n          },\n          child: SizedBox(\n            width: MediaQuery.of(context).size.width / 2,\n            child: Column(\n              children: [\n                Row(\n                  mainAxisAlignment: MainAxisAlignment.center,\n                  mainAxisSize: MainAxisSize.min,\n                  children: [\n                    Flexible(\n                      child: AutoSizeText(\n                        selectedModel != null ? selectedModel!.name : AppLocale.selectModel.getString(context),\n                        maxFontSize: 15,\n                        minFontSize: 12,\n                        maxLines: 1,\n                        overflow: TextOverflow.ellipsis,\n                        style: TextStyle(\n                          fontSize: CustomSize.appBarTitleSize,\n                          color: customColors.backgroundInvertedColor,\n                          fontWeight: FontWeight.bold,\n                        ),\n                      ),\n                    ),\n                    const SizedBox(width: 3),\n                    Icon(\n                      Icons.arrow_forward_ios,\n                      color: customColors.backgroundInvertedColor!.withAlpha(150),\n                      size: CustomSize.appBarTitleSize * 0.8,\n                    ),\n                  ],\n                ),\n              ],\n            ),\n          ),\n        ),\n        actions: [\n          IconButton(\n            icon: const Icon(Icons.maps_ugc_outlined),\n            onPressed: createNewChat,\n          ),\n        ],\n        body: BlocConsumer<RoomBloc, RoomState>(\n          listenWhen: (previous, current) => current is RoomLoaded,\n          listener: (context, state) async {\n            if (state is RoomLoaded && currentModelV2 == null) {\n              await loadCurrentModel(state.room.model);\n            }\n          },\n          buildWhen: (previous, current) => current is RoomLoaded,\n          builder: (context, room) {\n            // 加载聊天室\n            if (room is RoomLoaded) {\n              if (room.error != null) {\n                return EnhancedErrorWidget(error: room.error);\n              }\n\n              return buildChatComponents(\n                customColors,\n                context,\n                room,\n              );\n            } else {\n              return Container();\n            }\n          },\n        ),\n        drawer: MultiBlocProvider(\n          providers: [\n            BlocProvider.value(\n              value: context.read<AccountBloc>(),\n            ),\n            BlocProvider.value(\n              value: context.read<ChatChatBloc>(),\n            ),\n          ],\n          child: const LeftDrawer(),\n        ),\n      ),\n    );\n  }\n\n  /// 构建聊天室窗口\n  Widget buildChatComponents(\n    CustomColors customColors,\n    BuildContext context,\n    RoomLoaded room,\n  ) {\n    return Column(\n      children: [\n        if (Ability().showGlobalAlert) const GlobalAlert(pageKey: 'chat'),\n        if (showAudioPlayer)\n          EnhancedAudioPlayer(\n            controller: audioPlayerController,\n            loading: audioLoadding,\n          ),\n        // 聊天内容窗口\n        Expanded(\n          child: Stack(\n            fit: StackFit.expand,\n            children: [\n              BlocConsumer<ChatMessageBloc, ChatMessageState>(\n                listener: (context, state) {\n                  if (state is ChatHistoryInited) {\n                    updateCurrentChat(state.chatId);\n                  }\n\n                  if (state is ChatMessagesLoaded && state.error == null) {\n                    setState(() {\n                      selectedImageFiles = [];\n                    });\n                  }\n                  // 显示错误提示\n                  else if (state is ChatMessagesLoaded && state.error != null) {\n                    showErrorMessageEnhanced(context, state.error);\n                  } else if (state is ChatMessageUpdated) {\n                    // 聊天内容窗口滚动到底部\n                    if (!state.processing && scrollController.hasClients) {\n                      scrollController.animateTo(\n                        0,\n                        duration: const Duration(milliseconds: 500),\n                        curve: Curves.easeOut,\n                      );\n                    }\n\n                    if (state.processing && enableInput.value) {\n                      // 聊天回复中时，禁止输入框编辑\n                      setState(() {\n                        enableInput.value = false;\n                      });\n                    } else if (!state.processing && !enableInput.value) {\n                      // 聊天回复完成时，取消输入框的禁止编辑状态\n                      setState(() {\n                        enableInput.value = true;\n                      });\n                    }\n                  }\n                },\n                buildWhen: (prv, cur) => cur is ChatMessagesLoaded,\n                builder: (context, state) {\n                  if (state is ChatMessagesLoaded) {\n                    return buildChatPreviewArea(\n                      state,\n                      room.examples ?? [],\n                      room,\n                      customColors,\n                      chatPreviewController.selectMode,\n                    );\n                  }\n                  return const Center(child: CircularProgressIndicator());\n                },\n              ),\n              if (!enableInput.value)\n                Positioned(\n                  bottom: 10,\n                  width: CustomSize.adaptiveMaxWindowWidth(context),\n                  child: Center(\n                    child: StopButton(\n                      label: AppLocale.stopOutput.getString(context),\n                      onPressed: () {\n                        HapticFeedbackHelper.mediumImpact();\n                        context.read<ChatMessageBloc>().add(ChatMessageStopEvent());\n                      },\n                    ),\n                  ),\n                ),\n            ],\n          ),\n        ),\n\n        // 聊天输入窗口\n        if (!chatPreviewController.selectMode)\n          Container(\n            decoration: BoxDecoration(\n              borderRadius: const BorderRadius.only(\n                topLeft: CustomSize.radius,\n                topRight: CustomSize.radius,\n              ),\n              color: customColors.chatInputPanelBackground,\n            ),\n            child: BlocBuilder<ChatMessageBloc, ChatMessageState>(\n              buildWhen: (previous, current) => current is ChatMessagesLoaded,\n              builder: (context, state) {\n                var enableImageUpload = false;\n                var showReasoning = false;\n                var showSearch = false;\n                if (state is ChatMessagesLoaded) {\n                  if (currentModelV2 != null) {\n                    enableImageUpload = currentModelV2?.supportVision ?? false;\n                    showReasoning = currentModelV2?.supportReasoning ?? false;\n                    showSearch = currentModelV2?.supportSearch ?? false;\n                  } else {\n                    var model = state.chatHistory?.model ?? room.room.model;\n                    final cur = supportModels.where((e) => e.id == model).firstOrNull;\n                    enableImageUpload = cur?.supportVision ?? false;\n                    showReasoning = cur?.supportReasoning ?? false;\n                    showSearch = cur?.supportSearch ?? false;\n                  }\n                }\n\n                enableImageUpload = selectedModel == null ? enableImageUpload : (selectedModel?.supportVision ?? false);\n                showReasoning = selectedModel == null ? showReasoning : (selectedModel?.supportReasoning ?? false);\n                showSearch = selectedModel == null ? showSearch : (selectedModel?.supportSearch ?? false);\n\n                return ChatInput(\n                  enableNotifier: enableInput,\n                  onSubmit: (value) {\n                    handleSubmit(value);\n                    FocusManager.instance.primaryFocus?.unfocus();\n                  },\n                  enableImageUpload: enableImageUpload,\n                  onImageSelected: (files) {\n                    setState(() {\n                      selectedImageFiles = files;\n                    });\n                  },\n                  selectedImageFiles: enableImageUpload ? selectedImageFiles : [],\n                  hintText: AppLocale.askMeAnyQuestion.getString(context),\n                  onVoiceRecordTappedEvent: () {\n                    audioPlayerController.stop();\n                  },\n                  onStopGenerate: () {\n                    context.read<ChatMessageBloc>().add(ChatMessageStopEvent());\n                  },\n                  toolsBuilder: () {\n                    return [\n                      if (showReasoning)\n                        ChatInputButton(\n                          text: AppLocale.reasoning.getString(context),\n                          icon: Icons.tips_and_updates_outlined,\n                          onPressed: () {\n                            setState(() {\n                              enableReasoning = !enableReasoning;\n                            });\n                          },\n                          isActive: enableReasoning,\n                        ),\n                      if (showSearch)\n                        ChatInputButton(\n                          text: AppLocale.onlineSearch.getString(context),\n                          icon: Icons.language_outlined,\n                          onPressed: () {\n                            setState(() {\n                              enableSearch = !enableSearch;\n                            });\n                          },\n                          isActive: enableSearch,\n                        ),\n                    ];\n                  },\n                );\n              },\n            ),\n          ),\n\n        // 选择模式工具栏\n        if (chatPreviewController.selectMode) SelectModeToolbar(chatPreviewController: chatPreviewController),\n      ],\n    );\n  }\n\n  /// 构建聊天内容窗口\n  Widget buildChatPreviewArea(\n    ChatMessagesLoaded loadedState,\n    List<ChatExample> examples,\n    RoomLoaded room,\n    CustomColors customColors,\n    bool selectMode,\n  ) {\n    final loadedMessages = loadedState.messages as List<Message>;\n\n    // 聊天内容为空时，显示示例页面\n    if (loadedMessages.isEmpty) {\n      return EmptyPreview(\n        examples: examples,\n        onSubmit: handleSubmit,\n        cardMode: true,\n      );\n    }\n\n    final messages = loadedMessages.map((e) {\n      if (e.model != null && !e.model!.startsWith('v2@')) {\n        final mod = supportModels.where((m) => m.id == e.model).firstOrNull;\n        if (mod != null) {\n          e.senderName = mod.shortName;\n          e.avatarUrl = mod.avatarUrl;\n        }\n      }\n\n      if (e.avatarUrl == null || e.senderName == null) {\n        if (loadedState.chatHistory != null && loadedState.chatHistory!.model != null) {\n          if (currentModelV2 != null) {\n            e.senderName = currentModelV2!.name;\n            e.avatarUrl = currentModelV2!.avatarUrl;\n          } else {\n            final mod = supportModels.where((e) => e.id == loadedState.chatHistory!.model!).firstOrNull;\n            if (mod != null) {\n              e.senderName = mod.shortName;\n              e.avatarUrl = mod.avatarUrl;\n            }\n          }\n        }\n      }\n\n      final stateMessage = room.states[widget.stateManager.getKey(e.roomId ?? 0, e.id ?? 0)] ?? MessageState();\n      return MessageWithState(e, stateMessage);\n    }).toList();\n\n    chatPreviewController.setAllMessageIds(messages);\n\n    return ChatPreview(\n      padding: enableInput.value ? null : const EdgeInsets.only(bottom: 35),\n      messages: messages,\n      scrollController: scrollController,\n      controller: chatPreviewController,\n      stateManager: widget.stateManager,\n      robotAvatar: selectMode\n          ? null\n          : RoleAvatar(\n              avatarUrl: room.room.avatarUrl,\n              his: loadedState.chatHistory,\n              alternativeAvatarUrl: currentModelV2?.avatarUrl,\n            ),\n      senderNameBuilder: (message) {\n        if (message.senderName == null) {\n          return null;\n        }\n\n        return Container(\n          margin: const EdgeInsets.fromLTRB(0, 0, 10, 7),\n          padding: const EdgeInsets.symmetric(horizontal: 13),\n          child: Text(\n            message.senderName!,\n            style: TextStyle(\n              color: customColors.weakTextColor,\n              fontSize: 12,\n            ),\n          ),\n        );\n      },\n      onDeleteMessage: (id) {\n        handleDeleteMessage(context, id, chatHistoryId: chatId);\n      },\n      onResentEvent: (message, index) {\n        scrollController.animateTo(0, duration: const Duration(milliseconds: 500), curve: Curves.easeOut);\n\n        handleSubmit(message.text, messagetType: message.type, index: index, isResent: true);\n      },\n      onSpeakEvent: (message) {\n        audioPlayerController.playAudio(message.text);\n      },\n      helpWidgets: loadedState.processing || loadedMessages.last.isInitMessage()\n          ? null\n          : [HelpTips(onSubmitMessage: handleSubmit)],\n    );\n  }\n\n  /// 提交新消息\n  void handleSubmit(\n    String text, {\n    messagetType = MessageType.text,\n    int? index,\n    bool isResent = false,\n  }) async {\n    setState(() {\n      enableInput.value = false;\n    });\n\n    if (selectedImageFiles.isNotEmpty) {\n      final cancel = BotToast.showCustomLoading(\n        toastBuilder: (cancel) {\n          return LoadingIndicator(\n            message: AppLocale.imageUploading.getString(context),\n          );\n        },\n        allowClick: false,\n      );\n\n      try {\n        final uploader = ImageUploader(widget.settings);\n\n        for (var file in selectedImageFiles) {\n          if (file.uploaded) {\n            continue;\n          }\n\n          if (file.file.bytes != null) {\n            final res = await uploader.base64(\n              imageData: file.file.bytes,\n              maxSize: 1024 * 1024,\n              compressWidth: 512,\n              compressHeight: 512,\n            );\n            file.setUrl(res);\n          } else {\n            final res = await uploader.base64(\n              path: file.file.path!,\n              maxSize: 1024 * 1024,\n              compressWidth: 512,\n              compressHeight: 512,\n            );\n            file.setUrl(res);\n          }\n        }\n      } catch (e) {\n        // ignore: use_build_context_synchronously\n        showErrorMessageEnhanced(context, e);\n        return;\n      } finally {\n        cancel();\n      }\n    }\n\n    // ignore: use_build_context_synchronously\n    context.read<ChatMessageBloc>().add(\n          ChatMessageSendEvent(\n            Message(\n              Role.sender,\n              text,\n              user: 'me',\n              ts: DateTime.now(),\n              model: selectedModel?.id,\n              type: messagetType,\n              chatHistoryId: chatId,\n              images: selectedImageFiles.where((e) => e.uploaded).map((e) => e.url!).toList(),\n              flags: [\n                if (enableSearch) 'search',\n                if (enableReasoning) 'reasoning',\n              ],\n            ),\n            tempModel: selectedModel?.id,\n            index: index,\n            isResent: isResent,\n          ),\n        );\n\n    // ignore: use_build_context_synchronously\n    context.read<RoomBloc>().add(RoomLoadEvent(chatAnywhereRoomId, cascading: false));\n  }\n}\n"
  },
  {
    "path": "lib/page/lab/avatar_selector.dart",
    "content": "import 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/random_avatar.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\n\nclass AvatarSelectorScreen extends StatefulWidget {\n  final AvatarUsage usage;\n  const AvatarSelectorScreen({super.key, required this.usage});\n\n  @override\n  State<AvatarSelectorScreen> createState() => _AvatarSelectorScreenState();\n}\n\nclass _AvatarSelectorScreenState extends State<AvatarSelectorScreen> {\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(AppLocale.avatar.getString(context)),\n        centerTitle: true,\n      ),\n      body: GridView.count(\n        crossAxisCount: 4,\n        childAspectRatio: 0.9,\n        padding: const EdgeInsets.all(8),\n        children: List.generate(500, (index) {\n          return Column(\n            mainAxisSize: MainAxisSize.min,\n            children: [\n              RandomAvatar(id: 500 + index, size: 60, usage: widget.usage),\n              const SizedBox(height: 8),\n              Text('${500 + index}'),\n            ],\n          );\n        }),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/lab/creative_models.dart",
    "content": "import 'package:askaide/bloc/creative_island_bloc.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/enhanced_input.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/image_preview.dart';\nimport 'package:askaide/page/component/item_selector_search.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api/image_model.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:intl/intl.dart';\n\nclass CreativeModelScreen extends StatefulWidget {\n  final SettingRepository setting;\n  const CreativeModelScreen({super.key, required this.setting});\n\n  @override\n  State<CreativeModelScreen> createState() => _CreativeModelScreenState();\n}\n\nclass _CreativeModelScreenState extends State<CreativeModelScreen> {\n  List<ImageModel> imageModels = [];\n  List<ImageModelFilter> imageModelFilters = [];\n\n  @override\n  void initState() {\n    APIServer().imageModels().then((models) {\n      setState(() {\n        imageModels = models;\n      });\n    });\n\n    APIServer().imageModelFilters().then((filters) {\n      setState(() {\n        imageModelFilters = filters;\n      });\n    });\n\n    context.read<CreativeIslandBloc>().add(CreativeIslandGalleryLoadEvent(mode: \"all\"));\n    super.initState();\n  }\n\n  ImageModel? selectedModel;\n  ImageModelFilter? selectedFilter;\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: const Text(\n            'Creation Island History',\n            style: TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          backgroundColor: customColors.backgroundColor,\n          enabled: false,\n          child: Column(\n            children: [\n              ColumnBlock(\n                margin: const EdgeInsets.all(10),\n                padding: const EdgeInsets.all(10),\n                children: [\n                  EnhancedInput(\n                    title: Text(\n                      AppLocale.model.getString(context),\n                      style: TextStyle(\n                        color: customColors.textfieldLabelColor,\n                        fontSize: 16,\n                      ),\n                    ),\n                    value: Container(\n                      alignment: Alignment.centerRight,\n                      width: MediaQuery.of(context).size.width - 200,\n                      child: Text(\n                        selectedModel?.modelName ?? 'Auto',\n                        overflow: TextOverflow.ellipsis,\n                      ),\n                    ),\n                    onPressed: () {\n                      openListSelectDialog(\n                        context,\n                        [\n                          SelectorItem(const Text('Auto'), null),\n                          ...imageModels\n                              .map(\n                                (e) => SelectorItem(\n                                  Stack(\n                                    children: [\n                                      Container(\n                                        padding: const EdgeInsets.only(top: 25, bottom: 10),\n                                        alignment: Alignment.center,\n                                        child: Text(\n                                          e.modelName,\n                                          textAlign: TextAlign.center,\n                                          style: const TextStyle(fontSize: 14),\n                                          textWidthBasis: TextWidthBasis.longestLine,\n                                        ),\n                                      ),\n                                      Positioned(\n                                        left: 0,\n                                        top: 0,\n                                        child: Container(\n                                          padding: const EdgeInsets.symmetric(\n                                            horizontal: 5,\n                                            vertical: 3,\n                                          ),\n                                          decoration: BoxDecoration(\n                                            borderRadius: CustomSize.borderRadius,\n                                            color: modelTypeTagColors[e.vendor],\n                                          ),\n                                          child: Text(\n                                            e.vendor,\n                                            style: const TextStyle(\n                                              fontSize: 12,\n                                              color: Colors.white,\n                                            ),\n                                          ),\n                                        ),\n                                      ),\n                                    ],\n                                  ),\n                                  e.id,\n                                  search: (keywrod) {\n                                    return e.modelName.toLowerCase().contains(keywrod.toLowerCase()) ||\n                                        e.vendor.contains(keywrod.toLowerCase());\n                                  },\n                                ),\n                              )\n                              .toList(),\n                        ],\n                        (value) {\n                          setState(() {\n                            if (value.value == null) {\n                              selectedModel = null;\n                              selectedFilter = null;\n                              context.read<CreativeIslandBloc>().add(CreativeIslandGalleryLoadEvent(mode: \"all\"));\n                              return;\n                            }\n\n                            selectedModel = imageModels.firstWhere((e) => e.id == value.value);\n\n                            if (selectedModel != null) {\n                              final matchedFilters =\n                                  imageModelFilters.where((e) => e.modelId == selectedModel!.modelId).toList();\n                              selectedFilter = matchedFilters.isNotEmpty ? matchedFilters.first : null;\n                              context\n                                  .read<CreativeIslandBloc>()\n                                  .add(CreativeIslandGalleryLoadEvent(mode: \"all\", model: selectedModel!.realModel));\n                            } else {\n                              selectedFilter = null;\n                              context.read<CreativeIslandBloc>().add(CreativeIslandGalleryLoadEvent(mode: \"all\"));\n                            }\n                          });\n                          return true;\n                        },\n                        heightFactor: 0.8,\n                        value: selectedModel?.id,\n                        innerPadding: const EdgeInsets.symmetric(\n                          horizontal: 10,\n                          vertical: 0,\n                        ),\n                        enableSearch: true,\n                      );\n                    },\n                  ),\n                  if (selectedFilter != null)\n                    Row(\n                      children: [\n                        if (selectedFilter!.previewImage != null && selectedFilter!.previewImage!.isNotEmpty)\n                          SizedBox(\n                            width: 70,\n                            height: 70,\n                            child: NetworkImagePreviewer(\n                              url: selectedFilter!.previewImage!,\n                              hidePreviewButton: true,\n                            ),\n                          ),\n                        const SizedBox(width: 20),\n                        Text(selectedFilter!.name),\n                      ],\n                    ),\n                ],\n              ),\n              Expanded(\n                child: RefreshIndicator(\n                  color: customColors.linkColor,\n                  onRefresh: () async {\n                    context.read<CreativeIslandBloc>().add(CreativeIslandGalleryLoadEvent(\n                          forceRefresh: true,\n                          mode: \"all\",\n                          model: selectedModel?.realModel,\n                        ));\n                  },\n                  child: BlocConsumer<CreativeIslandBloc, CreativeIslandState>(\n                    listenWhen: (previous, current) => current is CreativeIslandGalleryLoaded,\n                    buildWhen: (previous, current) => current is CreativeIslandGalleryLoaded,\n                    listener: (context, state) {\n                      if (state is CreativeIslandHistoriesAllLoaded) {\n                        if (state.error != null) {\n                          showErrorMessageEnhanced(context, state.error);\n                        }\n                      }\n                    },\n                    builder: (context, state) {\n                      if (state is CreativeIslandGalleryLoaded) {\n                        return GridView.count(\n                          padding: const EdgeInsets.all(10),\n                          crossAxisCount: _calCrossAxisCount(context),\n                          crossAxisSpacing: 10,\n                          mainAxisSpacing: 10,\n                          children: state.items.map(\n                            (e) {\n                              return GestureDetector(\n                                onTap: () {\n                                  context.push('/creative-island/${e.islandId}/history/${e.id}?show_error=true');\n                                },\n                                child: Container(\n                                  decoration: BoxDecoration(borderRadius: CustomSize.borderRadius),\n                                  child: Stack(\n                                    children: [\n                                      if (e.firstImagePreview.startsWith('http://') ||\n                                          e.firstImagePreview.startsWith('https://'))\n                                        ClipRRect(\n                                          borderRadius: CustomSize.borderRadius,\n                                          child: e.firstImagePreview.endsWith('.mp4')\n                                              ? CachedNetworkImageEnhanced(\n                                                  imageUrl: e.params['image'] ?? e.firstImagePreview,\n                                                  fit: BoxFit.cover,\n                                                  height: double.infinity,\n                                                )\n                                              : CachedNetworkImageEnhanced(\n                                                  imageUrl: e.firstImagePreview,\n                                                  fit: BoxFit.cover,\n                                                ),\n                                        )\n                                      else if (e.isProcessing)\n                                        Container(\n                                          padding: const EdgeInsets.all(5),\n                                          decoration: BoxDecoration(\n                                            borderRadius: CustomSize.borderRadius,\n                                            color: const Color.fromARGB(255, 148, 124, 245),\n                                          ),\n                                          child: const Center(\n                                            child: Text(\n                                              'Processing...',\n                                              textAlign: TextAlign.center,\n                                              maxLines: 4,\n                                              style: TextStyle(\n                                                color: Colors.white,\n                                                fontSize: 10,\n                                                overflow: TextOverflow.ellipsis,\n                                              ),\n                                            ),\n                                          ),\n                                        )\n                                      else\n                                        Container(\n                                          padding: const EdgeInsets.all(5),\n                                          decoration: BoxDecoration(\n                                            borderRadius: CustomSize.borderRadius,\n                                            color: Colors.amber,\n                                          ),\n                                          child: Center(\n                                            child: Text(\n                                              e.answer ?? '',\n                                              textAlign: TextAlign.center,\n                                              maxLines: 4,\n                                              style: const TextStyle(\n                                                color: Colors.red,\n                                                fontSize: 10,\n                                                overflow: TextOverflow.ellipsis,\n                                              ),\n                                            ),\n                                          ),\n                                        ),\n                                      Positioned(\n                                        right: 10,\n                                        bottom: 10,\n                                        child: Container(\n                                          padding: const EdgeInsets.symmetric(\n                                            horizontal: 5,\n                                            vertical: 3,\n                                          ),\n                                          decoration: BoxDecoration(\n                                            color: customColors.backgroundColor?.withAlpha(200),\n                                            borderRadius: CustomSize.borderRadius,\n                                          ),\n                                          child: Text(\n                                            '${DateFormat('HH:mm').format(e.createdAt!.toLocal())}@${e.userId}#${e.id}',\n                                            style: TextStyle(\n                                              fontSize: 12,\n                                              color: customColors.weakTextColor,\n                                            ),\n                                          ),\n                                        ),\n                                      ),\n                                      if (e.islandName != null)\n                                        Positioned(\n                                          left: 0,\n                                          top: 0,\n                                          child: Container(\n                                            padding: const EdgeInsets.symmetric(\n                                              horizontal: 8,\n                                              vertical: 5,\n                                            ),\n                                            decoration: BoxDecoration(\n                                              borderRadius: const BorderRadius.only(\n                                                topLeft: CustomSize.radius,\n                                                bottomRight: CustomSize.radius,\n                                              ),\n                                              color: customColors.linkColor,\n                                            ),\n                                            child: Text(\n                                              e.islandName!,\n                                              style: const TextStyle(\n                                                fontSize: 10,\n                                                color: Colors.white,\n                                              ),\n                                            ),\n                                          ),\n                                        )\n                                    ],\n                                  ),\n                                ),\n                              );\n                            },\n                          ).toList(),\n                        );\n                      }\n                      return const Center(child: CircularProgressIndicator());\n                    },\n                  ),\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n\n  int _calCrossAxisCount(BuildContext context) {\n    double width = MediaQuery.of(context).size.width;\n    if (width > CustomSize.maxWindowSize) {\n      width = CustomSize.maxWindowSize;\n    }\n    return (width / 220).round();\n  }\n}\n"
  },
  {
    "path": "lib/page/lab/draw_board.dart",
    "content": "import 'dart:io';\nimport 'dart:typed_data';\n\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/helper/logger.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/creative_island/draw/components/image_selector.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:file_saver/file_saver.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_drawing_board/flutter_drawing_board.dart';\nimport 'package:flutter_drawing_board/paint_contents.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:image_gallery_saver/image_gallery_saver.dart';\n\nclass DrawboardScreen extends StatefulWidget {\n  const DrawboardScreen({super.key});\n\n  @override\n  State<DrawboardScreen> createState() => _DrawboardScreenState();\n}\n\nclass _DrawboardScreenState extends State<DrawboardScreen> {\n  final DrawMaskBoardController controller = DrawMaskBoardController();\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: const Text('画板'),\n        centerTitle: true,\n      ),\n      body: SafeArea(\n        child: ColumnBlock(\n          children: [\n            ImageSelector(\n              onImageSelected: ({path, data}) {\n                if (path == null || path.isEmpty) return;\n\n                Navigator.push(\n                  context,\n                  MaterialPageRoute(\n                    fullscreenDialog: true,\n                    builder: (context) => Scaffold(\n                      appBar: AppBar(\n                        actions: [\n                          IconButton(\n                            onPressed: () async {\n                              final imageData = await controller.save();\n\n                              if (imageData == null) {\n                                // ignore: use_build_context_synchronously\n                                showErrorMessageEnhanced(context, '获取图片数据失败');\n                                return;\n                              }\n\n                              // save imageData to file\n                              if (PlatformTool.isIOS() || PlatformTool.isAndroid()) {\n                                await ImageGallerySaver.saveImage(\n                                  imageData.buffer.asUint8List(),\n                                  quality: 100,\n                                );\n\n                                showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                              } else {\n                                if (PlatformTool.isWindows()) {\n                                  FileSaver.instance\n                                      .saveAs(\n                                    name: randomId(),\n                                    ext: '.png',\n                                    bytes: imageData.buffer.asUint8List(),\n                                    mimeType: MimeType.png,\n                                  )\n                                      .then((value) async {\n                                    if (value == null) {\n                                      return;\n                                    }\n\n                                    await File(value).writeAsBytes(imageData.buffer.asUint8List());\n\n                                    Logger.instance.d('File saved successfully: $value');\n                                    showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                                  });\n                                } else {\n                                  FileSaver.instance\n                                      .saveFile(\n                                    name: randomId(),\n                                    ext: 'png',\n                                    bytes: imageData.buffer.asUint8List(),\n                                    mimeType: MimeType.png,\n                                  )\n                                      .then((value) {\n                                    showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                                  });\n                                }\n                              }\n                            },\n                            icon: const Icon(Icons.save),\n                          ),\n                        ],\n                      ),\n                      body: DrawMaskBoard(\n                        backgroundImage: File(path),\n                        controller: controller,\n                      ),\n                    ),\n                  ),\n                );\n              },\n              title: AppLocale.referenceImage.getString(context),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n\nclass DrawMaskBoardController {\n  DrawingController? controller;\n  Future<ByteData?> Function()? onSave;\n\n  init({\n    DrawingController? controller,\n    Future<ByteData?> Function()? onSave,\n  }) {\n    this.controller = controller;\n    this.onSave = onSave;\n  }\n\n  Future<ByteData?> save() {\n    if (onSave == null) return Future.value(null);\n    return onSave!();\n  }\n\n  unsetController() {\n    controller = null;\n    onSave = null;\n  }\n}\n\nclass DrawMaskBoard extends StatefulWidget {\n  final File backgroundImage;\n  final DrawMaskBoardController controller;\n  const DrawMaskBoard({\n    super.key,\n    required this.backgroundImage,\n    required this.controller,\n  });\n\n  @override\n  State<DrawMaskBoard> createState() => _DrawMaskBoardState();\n}\n\nclass _DrawMaskBoardState extends State<DrawMaskBoard> {\n  final DrawingController _controller = DrawingController();\n\n  bool showBackground = true;\n  double strokeWidth = 10;\n  String selectedToolbar = 'draw';\n\n  @override\n  void dispose() {\n    widget.controller.unsetController();\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    widget.controller.init(\n        controller: _controller,\n        onSave: () async {\n          setState(() {\n            showBackground = false;\n          });\n\n          await Future.delayed(const Duration(milliseconds: 100));\n\n          try {\n            return _controller.getImageData();\n          } finally {\n            setState(() {\n              showBackground = true;\n            });\n          }\n        });\n\n    _controller.setStyle(color: Colors.white, strokeWidth: strokeWidth);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return Column(\n      children: [\n        Column(\n          children: [\n            Row(\n              children: [\n                IconButton(\n                  onPressed: () {\n                    _controller.setPaintContent = SimpleLine();\n                    setState(() {\n                      selectedToolbar = 'draw';\n                    });\n                  },\n                  icon: Icon(\n                    Icons.edit,\n                    color: selectedToolbar == 'draw' ? customColors.linkColor : customColors.weakLinkColor,\n                  ),\n                ),\n                IconButton(\n                  onPressed: () {\n                    _controller.setPaintContent = Eraser();\n                    setState(() {\n                      selectedToolbar = 'eraser';\n                    });\n                  },\n                  icon: Icon(\n                    CupertinoIcons.bandage,\n                    color: selectedToolbar == 'eraser' ? customColors.linkColor : customColors.weakLinkColor,\n                  ),\n                ),\n                IconButton(\n                  icon: Icon(\n                    Icons.undo,\n                    color: customColors.weakLinkColor,\n                  ),\n                  onPressed: () {\n                    _controller.undo();\n                  },\n                ),\n                IconButton(\n                  icon: Icon(\n                    Icons.redo,\n                    color: customColors.weakLinkColor,\n                  ),\n                  onPressed: () {\n                    _controller.redo();\n                  },\n                ),\n                IconButton(\n                  icon: Icon(\n                    Icons.delete_forever_outlined,\n                    color: customColors.weakLinkColor,\n                  ),\n                  onPressed: () {\n                    _controller.clear();\n                  },\n                )\n              ],\n            ),\n            Padding(\n              padding: const EdgeInsets.symmetric(horizontal: 10),\n              child: Row(\n                children: [\n                  Text(\n                    '画笔粗细',\n                    style: TextStyle(\n                      fontSize: 12,\n                      color: customColors.weakTextColor,\n                    ),\n                  ),\n                  Expanded(\n                    child: Transform.scale(\n                      scale: 0.8,\n                      child: Slider(\n                        value: strokeWidth,\n                        min: 1,\n                        max: 100,\n                        activeColor: customColors.weakLinkColor,\n                        onChanged: (value) {\n                          setState(() {\n                            strokeWidth = value;\n                            _controller.setStyle(strokeWidth: strokeWidth);\n                          });\n                        },\n                      ),\n                    ),\n                  ),\n                  Text(\n                    '${strokeWidth.toInt()}',\n                    style: TextStyle(\n                      fontSize: 12,\n                      color: customColors.weakTextColor,\n                    ),\n                  ),\n                ],\n              ),\n            ),\n          ],\n        ),\n        Expanded(\n          child: FutureBuilder(\n            future: decodeImageFromList(widget.backgroundImage.readAsBytesSync()),\n            builder: (context, snapshot) {\n              if (!snapshot.hasData) {\n                return const Center(child: CircularProgressIndicator());\n              }\n\n              return DrawingBoard(\n                controller: _controller,\n                background: Container(\n                  width: MediaQuery.of(context).size.width,\n                  height: MediaQuery.of(context).size.width /\n                      snapshot.data!.width.toDouble() *\n                      snapshot.data!.height.toDouble(),\n                  color: Colors.black,\n                  child: showBackground ? Image.file(widget.backgroundImage, fit: BoxFit.fitWidth) : null,\n                ),\n                showDefaultActions: false,\n              );\n            },\n          ),\n        ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/lab/prompt.dart",
    "content": "import 'package:askaide/helper/helper.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/effect/glass.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/enhanced_textfield.dart';\nimport 'package:askaide/page/component/item_selector_search.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\n\nclass PromptScreen extends StatefulWidget {\n  final String? prompt;\n\n  const PromptScreen({super.key, this.prompt});\n\n  @override\n  State<PromptScreen> createState() => _PromptScreenState();\n}\n\nclass _PromptScreenState extends State<PromptScreen> {\n  TextEditingController controller = TextEditingController(text: '');\n\n  @override\n  void initState() {\n    super.initState();\n    controller.text = widget.prompt ?? '';\n  }\n\n  @override\n  void dispose() {\n    controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(AppLocale.prompt.getString(context)),\n        centerTitle: true,\n        leading: IconButton(\n          icon: const Icon(Icons.close),\n          onPressed: () {\n            Navigator.of(context).pop();\n          },\n        ),\n      ),\n      body: Container(\n        padding: const EdgeInsets.all(10),\n        child: Column(children: [\n          EnhancedTextField(\n            labelText: AppLocale.prompt.getString(context),\n            labelPosition: LabelPosition.top,\n            inputSelector: InputSelector(\n              title: Text(\n                AppLocale.examples.getString(context),\n                style: TextStyle(color: customColors.linkColor),\n                textScaler: const TextScaler.linear(0.8),\n              ),\n              onTap: () {\n                openSystemPromptSelectDialog(\n                  context,\n                  customColors,\n                );\n              },\n            ),\n            customColors: customColors,\n            controller: controller,\n            maxLines: 6,\n            minLines: 2,\n            maxLength: 500,\n            hintText: AppLocale.promptHint.getString(context),\n          ),\n          const SizedBox(height: 20),\n          EnhancedButton(\n            title: AppLocale.ok.getString(context),\n            onPressed: () {\n              Navigator.of(context).pop(controller.text);\n            },\n          ),\n        ]),\n      ),\n    );\n  }\n\n  void openSystemPromptSelectDialog(\n    BuildContext context,\n    CustomColors customColors,\n  ) {\n    openModalBottomSheet(\n      context,\n      (context) {\n        return FutureBuilder(\n          future: APIServer().prompts(),\n          builder: (context, snapshot) {\n            if (snapshot.hasError) {\n              showErrorMessage(resolveError(context, snapshot.error!));\n            }\n\n            return FractionallySizedBox(\n              heightFactor: 0.8,\n              child: GlassEffect(\n                child: ItemSearchSelector(\n                  items: (snapshot.data ?? [])\n                      .map(\n                        (e) => SelectorItem<String>(\n                          Text(\n                            e.title,\n                            textAlign: TextAlign.center,\n                            overflow: TextOverflow.ellipsis,\n                            style: TextStyle(\n                              color: customColors.chatExampleItemText,\n                            ),\n                          ),\n                          e.content,\n                          search: (keywrod) => e.title\n                              .toLowerCase()\n                              .contains(keywrod.toLowerCase()),\n                        ),\n                      )\n                      .toList(),\n                  onSelected: (value) {\n                    controller.text = value.value;\n                    return true;\n                  },\n                ),\n              ),\n            );\n          },\n        );\n      },\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/lab/user_center.dart",
    "content": "import 'package:askaide/bloc/account_bloc.dart';\nimport 'package:askaide/bloc/creative_island_bloc.dart';\nimport 'package:askaide/page/component/account_quota_card.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/invite_card.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:go_router/go_router.dart';\n\nclass UserCenterScreen extends StatefulWidget {\n  final SettingRepository settings;\n  const UserCenterScreen({super.key, required this.settings});\n\n  @override\n  State<UserCenterScreen> createState() => _UserCenterScreenState();\n}\n\nclass _UserCenterScreenState extends State<UserCenterScreen> {\n  @override\n  void initState() {\n    context.read<AccountBloc>().add(AccountLoadEvent());\n    context.read<CreativeIslandBloc>().add(CreativeIslandGalleryLoadEvent(mode: \"default\"));\n\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    // final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return BackgroundContainer(\n      setting: widget.settings,\n      child: Scaffold(\n        appBar: AppBar(\n          title: const Text(\n            '我的信息',\n            style: TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n        ),\n        backgroundColor: Colors.transparent,\n        body: SafeArea(\n          child: BlocBuilder<AccountBloc, AccountState>(\n            buildWhen: (previous, current) => current is AccountLoaded,\n            builder: (_, state) {\n              if (state is AccountLoaded) {\n                return BlocConsumer<CreativeIslandBloc, CreativeIslandState>(\n                    listenWhen: (previous, current) => current is CreativeIslandGalleryLoaded,\n                    buildWhen: (previous, current) => current is CreativeIslandGalleryLoaded,\n                    listener: (context, state) {\n                      if (state is CreativeIslandHistoriesAllLoaded) {\n                        if (state.error != null) {\n                          showErrorMessageEnhanced(context, state.error);\n                        }\n                      }\n                    },\n                    builder: (context, state2) {\n                      if (state2 is CreativeIslandGalleryLoaded) {\n                        return SingleChildScrollView(\n                          child: Column(\n                            mainAxisSize: MainAxisSize.min,\n                            children: [\n                              AccountQuotaCard(\n                                userInfo: state.user!,\n                                onPaymentReturn: () {\n                                  context.read<AccountBloc>().add(AccountLoadEvent(cache: false));\n                                },\n                              ),\n                              InviteCard(userInfo: state.user!),\n                              GridView.count(\n                                padding: const EdgeInsets.all(15),\n                                crossAxisCount: 4,\n                                crossAxisSpacing: 10,\n                                mainAxisSpacing: 10,\n                                shrinkWrap: true,\n                                physics: const NeverScrollableScrollPhysics(),\n                                cacheExtent: 100,\n                                children: state2.items.where((e) => e.images.isNotEmpty).map(\n                                  (e) {\n                                    return GestureDetector(\n                                      onTap: () {\n                                        context.push('/creative-island/${e.islandId}/history/${e.id}');\n                                      },\n                                      child: ClipRRect(\n                                        borderRadius: CustomSize.borderRadius,\n                                        child: CachedNetworkImageEnhanced(\n                                          imageUrl: e.firstImagePreview,\n                                          fit: BoxFit.cover,\n                                        ),\n                                      ),\n                                    );\n                                  },\n                                ).toList(),\n                              ),\n                            ],\n                          ),\n                        );\n                      }\n\n                      return const Center(child: CircularProgressIndicator());\n                    });\n              }\n\n              return const Center(\n                child: CircularProgressIndicator(),\n              );\n            },\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/setting/account_security.dart",
    "content": "import 'dart:async';\n\nimport 'package:askaide/bloc/account_bloc.dart';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/logger.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/enhanced_popup_menu.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:fluwx/fluwx.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:settings_ui/settings_ui.dart';\n\nclass AccountSecurityScreen extends StatefulWidget {\n  final SettingRepository settings;\n  const AccountSecurityScreen({super.key, required this.settings});\n\n  @override\n  State<AccountSecurityScreen> createState() => _AccountSecurityScreenState();\n}\n\nclass _AccountSecurityScreenState extends State<AccountSecurityScreen> {\n  StreamSubscription<BaseWeChatResponse>? _weChatResponse;\n\n  @override\n  void dispose() {\n    _weChatResponse?.cancel();\n    super.dispose();\n  }\n\n  var wechatInstalled = false;\n\n  @override\n  void initState() {\n    context.read<AccountBloc>().add(AccountLoadEvent());\n\n    if (Ability().enableWechatSignin) {\n      isWeChatInstalled.then((installed) {\n        setState(() {\n          wechatInstalled = installed;\n        });\n\n        if (!installed) {\n          return;\n        }\n\n        _weChatResponse = weChatResponseEventHandler.distinct((a, b) => a == b).listen((event) {\n          if (event is WeChatAuthResponse) {\n            if (event.errCode != 0) {\n              showErrorMessage(event.errStr!);\n              return;\n            }\n\n            if (event.code == null) {\n              showErrorMessage(AppLocale.signInFailed.getString(context));\n              return;\n            }\n\n            APIServer().bindWechat(code: event.code!).then((_) {\n              context.read<AccountBloc>().add(AccountLoadEvent());\n              showSuccessMessage(AppLocale.operateSuccess.getString(context));\n            }).onError((error, stackTrace) {\n              showErrorMessageEnhanced(context, error!);\n            });\n          }\n        });\n      });\n    }\n\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: Text(\n            AppLocale.accountSettings.getString(context),\n            style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n          actions: [\n            EnhancedPopupMenu(\n              items: [\n                EnhancedPopupMenuItem(\n                  title: AppLocale.deleteAccount.getString(context),\n                  icon: Icons.delete_forever,\n                  iconColor: Colors.red,\n                  onTap: (ctx) {\n                    context.push('/user/destroy');\n                  },\n                ),\n              ],\n            )\n          ],\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.settings,\n          backgroundColor: customColors.backgroundColor,\n          enabled: false,\n          child: SafeArea(\n            child: BlocConsumer<AccountBloc, AccountState>(\n              listenWhen: (previous, current) => current is AccountLoaded,\n              listener: (context, state) {\n                if (state is AccountLoaded) {\n                  if (state.error != null) {\n                    showErrorMessageEnhanced(context, state.error!);\n                  }\n                }\n              },\n              buildWhen: (previous, current) => current is AccountLoaded,\n              builder: (_, state) {\n                if (state is AccountLoaded) {\n                  return buildSettingsList(\n                    context,\n                    [\n                      SettingsSection(\n                        title: Text(AppLocale.basicInfo.getString(context)),\n                        tiles: [\n                          SettingsTile(\n                            title: Text(AppLocale.nickname.getString(context)),\n                            trailing: Row(\n                              children: [\n                                Text(\n                                  state.user!.user.name == null || state.user!.user.name == ''\n                                      ? AppLocale.unset.getString(context)\n                                      : state.user!.user.name!,\n                                  style: TextStyle(\n                                    color: customColors.weakTextColor?.withAlpha(200),\n                                    fontSize: 13,\n                                  ),\n                                ),\n                                const Icon(\n                                  CupertinoIcons.chevron_forward,\n                                  size: 18,\n                                  color: Colors.grey,\n                                ),\n                              ],\n                            ),\n                            onPressed: (context) {\n                              openTextFieldDialog(\n                                context,\n                                title: AppLocale.setNickname.getString(context),\n                                hint: AppLocale.inputYourNickname.getString(context),\n                                maxLine: 1,\n                                maxLength: 30,\n                                defaultValue: state.user?.user.name,\n                                onSubmit: (value) {\n                                  context.read<AccountBloc>().add(AccountUpdateEvent(realname: value));\n                                  return true;\n                                },\n                              );\n                            },\n                          ),\n                          SettingsTile(\n                            title: Text(AppLocale.phone.getString(context)),\n                            trailing: Row(\n                              children: [\n                                Text(\n                                  state.user!.user.phone == null || state.user!.user.phone == ''\n                                      ? AppLocale.bindPhone.getString(context)\n                                      : state.user!.user.phone!,\n                                  style: TextStyle(\n                                    color: customColors.weakTextColor?.withAlpha(200),\n                                    fontSize: 13,\n                                  ),\n                                ),\n                                if (state.user!.user.phone == null || state.user!.user.phone == '')\n                                  const SizedBox(width: 5),\n                                if (state.user!.user.phone == null || state.user!.user.phone == '')\n                                  const Icon(\n                                    CupertinoIcons.chevron_forward,\n                                    size: 18,\n                                    color: Colors.grey,\n                                  ),\n                              ],\n                            ),\n                            onPressed: (context) {\n                              if (state.user!.user.phone == null || state.user!.user.phone == '') {\n                                context.push('/bind-phone?is_signin=false').then((value) => Logger.instance.d(value));\n                              }\n                            },\n                          ),\n                          if (Ability().enableWechatSignin && wechatInstalled)\n                            SettingsTile(\n                              title: Text(AppLocale.wechatAccount.getString(context)),\n                              trailing: Row(\n                                children: [\n                                  Text(\n                                    state.user!.user.unionId == null || state.user!.user.unionId == ''\n                                        ? AppLocale.bind.getString(context)\n                                        : AppLocale.bound.getString(context),\n                                    style: TextStyle(\n                                      color: customColors.weakTextColor?.withAlpha(200),\n                                      fontSize: 13,\n                                    ),\n                                  ),\n                                  if (state.user!.user.unionId == null || state.user!.user.unionId == '')\n                                    const SizedBox(width: 5),\n                                  if (state.user!.user.unionId == null || state.user!.user.unionId == '')\n                                    const Icon(\n                                      CupertinoIcons.chevron_forward,\n                                      size: 18,\n                                      color: Colors.grey,\n                                    ),\n                                ],\n                              ),\n                              onPressed: (context) async {\n                                if (state.user!.user.unionId == null || state.user!.user.unionId == '') {\n                                  final ok =\n                                      await sendWeChatAuth(scope: \"snsapi_userinfo\", state: \"wechat_sdk_demo_test\");\n                                  if (!ok) {\n                                    showErrorMessage(AppLocale.installWeChat.getString(context));\n                                  }\n                                }\n                              },\n                            ),\n                          SettingsTile(\n                            title: Text(state.user!.control.isSetPassword\n                                ? AppLocale.modifyPassword.getString(context)\n                                : AppLocale.setPassword.getString(context)),\n                            trailing: const Icon(\n                              CupertinoIcons.chevron_forward,\n                              size: 18,\n                              color: Colors.grey,\n                            ),\n                            onPressed: (context) {\n                              context.push('/user/change-password');\n                            },\n                          ),\n                        ],\n                      ),\n                      SettingsSection(\n                        tiles: [\n                          SettingsTile(\n                            title: Text(AppLocale.signOut.getString(context)),\n                            trailing: const Icon(\n                              Icons.logout,\n                              size: 18,\n                              color: Colors.grey,\n                            ),\n                            onPressed: (_) {\n                              openConfirmDialog(\n                                context,\n                                AppLocale.confirmSignOut.getString(context),\n                                () {\n                                  context.read<AccountBloc>().add(AccountSignOutEvent());\n                                  context.go('/login');\n                                },\n                                danger: true,\n                              );\n                            },\n                          ),\n                        ],\n                      ),\n                    ],\n                  );\n                }\n\n                return const Center(child: CircularProgressIndicator());\n              },\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nWidget buildSettingsList(\n  BuildContext context,\n  List<AbstractSettingsSection> sections,\n) {\n  final customColors = Theme.of(context).extension<CustomColors>()!;\n  return SafeArea(\n    top: false,\n    child: RefreshIndicator(\n      color: customColors.linkColor,\n      displacement: 20,\n      onRefresh: () async {\n        context.read<AccountBloc>().add(AccountLoadEvent());\n      },\n      child: SettingsList(\n        platform: DevicePlatform.iOS,\n        lightTheme: SettingsThemeData(\n          settingsListBackground: Colors.transparent,\n          settingsSectionBackground: customColors.settingsSectionBackground,\n        ),\n        darkTheme: SettingsThemeData(\n          settingsListBackground: Colors.transparent,\n          settingsSectionBackground: customColors.settingsSectionBackground,\n          titleTextColor: const Color.fromARGB(255, 239, 239, 239),\n        ),\n        sections: sections,\n        contentPadding: const EdgeInsets.all(0),\n      ),\n    ),\n  );\n}\n"
  },
  {
    "path": "lib/page/setting/article.dart",
    "content": "import 'package:askaide/helper/ability.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/chat/markdown.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api/article.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:intl/intl.dart';\nimport 'package:url_launcher/url_launcher_string.dart';\n\nclass ArticleScreen extends StatefulWidget {\n  final SettingRepository settings;\n  final int id;\n  const ArticleScreen({super.key, required this.settings, required this.id});\n\n  @override\n  State<ArticleScreen> createState() => _ArticleScreenState();\n}\n\nclass _ArticleScreenState extends State<ArticleScreen> {\n  Article article = Article(\n    id: 0,\n    title: 'Title',\n    content: 'Content',\n  );\n\n  @override\n  void initState() {\n    APIServer().article(id: widget.id).then((value) {\n      setState(() {\n        article = value;\n      });\n    });\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          title: Text(\n            article.title,\n            style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          toolbarHeight: CustomSize.toolbarHeight,\n          centerTitle: true,\n          leading: IconButton(\n            icon: Icon(\n              Icons.close,\n              color: customColors.weakLinkColor,\n            ),\n            onPressed: () {\n              if (context.canPop()) {\n                context.pop();\n              } else {\n                context.go(Ability().homeRoute);\n              }\n            },\n          ),\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.settings,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          child: SafeArea(\n            top: false,\n            child: Container(\n              padding: const EdgeInsets.symmetric(horizontal: 10),\n              child: SingleChildScrollView(\n                child: ColumnBlock(\n                  padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),\n                  children: [\n                    Column(\n                      crossAxisAlignment: CrossAxisAlignment.start,\n                      children: [\n                        Row(\n                          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                          children: [\n                            Text(\n                              'Author: ${article.author ?? 'Admin'}',\n                              style: TextStyle(\n                                fontSize: 12,\n                                color: customColors.weakTextColor,\n                              ),\n                            ),\n                            if (article.createdAt != null)\n                              Text(\n                                DateFormat('yyyy/MM/dd HH:mm').format(article.createdAt!.toLocal()),\n                                style: TextStyle(\n                                  fontSize: 12,\n                                  color: customColors.weakTextColor?.withAlpha(100),\n                                ),\n                              ),\n                          ],\n                        ),\n                        const SizedBox(height: 10),\n                        Markdown(\n                          data: article.content,\n                          onUrlTap: (value) {\n                            if (value.startsWith(\"aidea-app://\")) {\n                              var route = value.substring('aidea-app://'.length);\n                              context.push(route);\n                            } else {\n                              launchUrlString(value);\n                            }\n                          },\n                        ),\n                      ],\n                    )\n                  ],\n                ),\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/setting/background_selector.dart",
    "content": "import 'dart:ui';\n\nimport 'package:askaide/bloc/background_image_bloc.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/image.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:file_picker/file_picker.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\n\nclass BackgroundSelectorScreen extends StatefulWidget {\n  final SettingRepository setting;\n\n  const BackgroundSelectorScreen({super.key, required this.setting});\n\n  @override\n  State<BackgroundSelectorScreen> createState() => _BackgroundSelectorScreenState();\n}\n\nclass _BackgroundSelectorScreenState extends State<BackgroundSelectorScreen> {\n  final TextEditingController _controller = TextEditingController();\n  bool selectDialogOpened = false;\n  double blur = 10;\n  bool showOriginalImage = false;\n\n  @override\n  void initState() {\n    super.initState();\n    context.read<BackgroundImageBloc>().add(BackgroundImageLoadEvent());\n\n    _controller.text = widget.setting.stringDefault(settingBackgroundImage, '');\n    blur = widget.setting.doubleDefault(settingBackgroundImageBlur, 10);\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(AppLocale.backgroundSetting.getString(context)),\n        centerTitle: true,\n      ),\n      backgroundColor: customColors.backgroundContainerColor,\n      body: SafeArea(\n        child: SingleChildScrollView(\n          child: Container(\n            padding: const EdgeInsets.all(10),\n            child: Column(\n              crossAxisAlignment: CrossAxisAlignment.start,\n              children: [\n                const Text('图片选择'),\n                const SizedBox(height: 10),\n                BlocBuilder<BackgroundImageBloc, BackgroundImageState>(\n                  buildWhen: (previous, current) => current is BackgroundImageLoaded,\n                  builder: (context, state) {\n                    if (state is BackgroundImageLoaded) {\n                      return GridView.count(\n                        crossAxisCount: 5,\n                        shrinkWrap: true,\n                        mainAxisSpacing: 5,\n                        crossAxisSpacing: 5,\n                        physics: const NeverScrollableScrollPhysics(),\n                        children: [\n                          InkWell(\n                            onTap: () {\n                              setState(() {\n                                _controller.text = '';\n                                blur = 0;\n                              });\n                            },\n                            child: Stack(\n                              children: [\n                                ClipRRect(\n                                  borderRadius: CustomSize.borderRadius,\n                                  child: Image.asset('assets/light-dark-auto.png'),\n                                ),\n                                Positioned(\n                                  child: Container(\n                                    alignment: Alignment.center,\n                                    child: const Text(\n                                      '跟随系统',\n                                      style: TextStyle(\n                                        fontSize: 12,\n                                        fontWeight: FontWeight.w500,\n                                        color: Color.fromARGB(255, 146, 146, 146),\n                                      ),\n                                    ),\n                                  ),\n                                ),\n                              ],\n                            ),\n                          ),\n                          for (var img in state.images)\n                            InkWell(\n                              onTap: () {\n                                setState(() {\n                                  _controller.text = img.url;\n                                });\n                              },\n                              child: ClipRRect(\n                                borderRadius: CustomSize.borderRadius,\n                                child: CachedNetworkImageEnhanced(imageUrl: img.preview),\n                              ),\n                            ),\n                          Material(\n                            borderRadius: CustomSize.borderRadius,\n                            child: InkWell(\n                              borderRadius: CustomSize.borderRadiusAll,\n                              onTap: () async {\n                                if (selectDialogOpened) return;\n\n                                selectDialogOpened = true;\n                                HapticFeedbackHelper.mediumImpact();\n                                FilePickerResult? result = await FilePicker.platform\n                                    .pickFiles(type: FileType.image)\n                                    .whenComplete(() => selectDialogOpened = false);\n                                if (result != null && result.files.isNotEmpty) {\n                                  setState(() {\n                                    _controller.text = result.files.first.path!;\n                                  });\n                                }\n                              },\n                              child: Container(\n                                width: MediaQuery.of(context).size.width - 20,\n                                decoration: BoxDecoration(\n                                  border: Border.all(\n                                    color: customColors.textFieldBorderColor!,\n                                    style: BorderStyle.solid,\n                                  ),\n                                  borderRadius: CustomSize.borderRadius,\n                                ),\n                                padding: const EdgeInsets.all(10),\n                                child: Column(\n                                  mainAxisAlignment: MainAxisAlignment.center,\n                                  children: [\n                                    Icon(\n                                      Icons.camera_alt,\n                                      size: 30,\n                                      color: customColors.chatInputPanelText,\n                                    ),\n                                    Text(\n                                      AppLocale.custom.getString(context),\n                                      style: TextStyle(\n                                        fontSize: 10,\n                                        fontWeight: FontWeight.w500,\n                                        color: customColors.chatInputPanelText?.withOpacity(0.8),\n                                      ),\n                                    ),\n                                  ],\n                                ),\n                              ),\n                            ),\n                          ),\n                        ],\n                      );\n                    }\n\n                    return const Center(\n                      child: CircularProgressIndicator(),\n                    );\n                  },\n                ),\n                const SizedBox(height: 10),\n                const SizedBox(height: 10),\n                const Text('图片预览'),\n                const SizedBox(height: 10),\n                ClipRRect(\n                  borderRadius: CustomSize.borderRadius,\n                  child: GestureDetector(\n                    onLongPressStart: (details) {\n                      setState(() {\n                        showOriginalImage = true;\n                      });\n                    },\n                    onLongPressEnd: (details) {\n                      setState(() {\n                        showOriginalImage = false;\n                      });\n                    },\n                    child: Container(\n                      decoration: BoxDecoration(\n                        image: _controller.text != ''\n                            ? DecorationImage(\n                                image: resolveImageProvider(_controller.text),\n                                fit: BoxFit.cover,\n                              )\n                            : null,\n                        color: customColors.backgroundContainerColor,\n                        borderRadius: CustomSize.borderRadius,\n                        border: Border.all(\n                          color: customColors.textFieldBorderColor!,\n                          style: BorderStyle.solid,\n                        ),\n                      ),\n                      child: BackdropFilter(\n                        filter: ImageFilter.blur(\n                          sigmaX: showOriginalImage ? 0 : blur,\n                          sigmaY: showOriginalImage ? 0 : blur,\n                        ),\n                        child: const SizedBox(width: double.infinity, height: 200),\n                      ),\n                    ),\n                  ),\n                ),\n                const SizedBox(height: 20),\n                Row(\n                  mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                  children: [\n                    const Text('模糊程度'),\n                    Text(blur.toStringAsFixed(0)),\n                  ],\n                ),\n                Slider(\n                  value: blur,\n                  min: 0,\n                  max: 50,\n                  divisions: 10,\n                  label: blur == 0 ? '无模糊' : '模糊程度：${blur.toStringAsFixed(0)}',\n                  activeColor: customColors.linkColor,\n                  onChanged: (value) {\n                    setState(() {\n                      blur = value;\n                    });\n                  },\n                ),\n                const SizedBox(height: 10),\n                EnhancedButton(\n                  onPressed: () {\n                    widget.setting.set(settingBackgroundImageBlur, blur.toString());\n\n                    final originalFilepath = widget.setting.get(settingBackgroundImage);\n\n                    if (originalFilepath != _controller.text) {\n                      // 移除原图\n                      if (originalFilepath != null && originalFilepath != '' && !originalFilepath.startsWith('http')) {\n                        removeExternalFile(originalFilepath);\n                      }\n\n                      // 复制新图\n                      if (_controller.text != '') {\n                        if (!_controller.text.startsWith('http')) {\n                          copyExternalFileToAppDocs(_controller.text).then((value) {\n                            widget.setting.set(settingBackgroundImage, value);\n                          });\n                        } else {\n                          widget.setting.set(settingBackgroundImage, _controller.text);\n                        }\n                      } else {\n                        // 恢复为原图\n                        widget.setting.set(settingBackgroundImage, '');\n                      }\n                    }\n\n                    showSuccessMessage(AppLocale.operateSuccess.getString(context));\n                    // Navigator.pop(context);\n                  },\n                  title: AppLocale.save.getString(context),\n                ),\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/setting/bind_phone_page.dart",
    "content": "import 'dart:convert';\n\nimport 'package:askaide/bloc/account_bloc.dart';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/verify_code_input.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass BindPhoneScreen extends StatefulWidget {\n  final SettingRepository setting;\n  final bool isSignIn;\n  const BindPhoneScreen({super.key, required this.setting, this.isSignIn = true});\n\n  @override\n  State<BindPhoneScreen> createState() => _BindPhoneScreenState();\n}\n\nclass _BindPhoneScreenState extends State<BindPhoneScreen> {\n  final TextEditingController _usernameController = TextEditingController();\n  final TextEditingController _inviteCodeController = TextEditingController();\n  final TextEditingController _verificationCodeController = TextEditingController();\n\n  String verifyCodeId = '';\n  final phoneNumberValidator = RegExp(r\"^1[3456789]\\d{9}$\");\n\n  @override\n  void initState() {\n    super.initState();\n\n    context.read<AccountBloc>().add(AccountLoadEvent(cache: false));\n\n    // Clipboard.getData(Clipboard.kTextPlain).then((value) {\n    //   if (value == null || value.text == null || value.text == '') {\n    //     return;\n    //   }\n\n    //   if (value.text!.trim().contains(RegExp(r'\\$AIDEA\\.INV\\.\\w+\\$'))) {\n    //     final match = RegExp(r'\\$AIDEA\\.INV\\.(\\w+)\\$').firstMatch(value.text!);\n    //     if (match != null) {\n    //       final val = match.group(1);\n    //       if (val != null) {\n    //         _inviteCodeController.text = val;\n    //       }\n    //     }\n    //   }\n    // });\n  }\n\n  @override\n  void dispose() {\n    _inviteCodeController.dispose();\n    _usernameController.dispose();\n    _verificationCodeController.dispose();\n\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: Text(\n            AppLocale.bindPhone.getString(context),\n            style: const TextStyle(\n              fontSize: CustomSize.appBarTitleSize,\n            ),\n          ),\n          centerTitle: true,\n          leading: IconButton(\n            onPressed: () {\n              if (widget.isSignIn) {\n                context.go('${Ability().homeRoute}?show_initial_dialog=false&reward=0');\n              } else {\n                context.pop();\n              }\n\n              // 当返回值为 logout 时，表示需要退出登录\n              // if (widget.isSignIn) {\n              //   context.pop('logout');\n              // } else {\n              //   context.pop();\n              // }\n            },\n            icon: Icon(widget.isSignIn ? Icons.close : Icons.arrow_back_ios),\n          ),\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          child: BlocBuilder<AccountBloc, AccountState>(\n            buildWhen: (previous, current) => current is AccountLoaded,\n            builder: (context, state) {\n              if (state is AccountLoaded) {\n                return Column(\n                  children: [\n                    Padding(\n                      padding: const EdgeInsets.only(left: 15.0, right: 15.0, top: 15, bottom: 0),\n                      child: TextFormField(\n                        controller: _usernameController,\n                        keyboardType: TextInputType.number,\n                        inputFormatters: [FilteringTextInputFormatter.singleLineFormatter],\n                        decoration: InputDecoration(\n                          border: const OutlineInputBorder(),\n                          enabledBorder: const OutlineInputBorder(\n                            borderSide: BorderSide(color: Color.fromARGB(200, 192, 192, 192)),\n                          ),\n                          focusedBorder: OutlineInputBorder(\n                            borderSide: BorderSide(color: customColors.linkColor ?? Colors.green),\n                          ),\n                          floatingLabelStyle: TextStyle(color: customColors.linkColor!),\n                          isDense: true,\n                          floatingLabelBehavior: FloatingLabelBehavior.always,\n                          labelText: AppLocale.account.getString(context),\n                          labelStyle: const TextStyle(fontSize: 17),\n                          hintText: AppLocale.phoneInputTips.getString(context),\n                          hintStyle: TextStyle(\n                            color: customColors.textfieldHintColor,\n                            fontSize: 15,\n                          ),\n                        ),\n                      ),\n                    ),\n                    Padding(\n                      padding: const EdgeInsets.only(left: 15.0, right: 5.0, top: 15, bottom: 0),\n                      child: VerifyCodeInput(\n                        controller: _verificationCodeController,\n                        onVerifyCodeSent: (id) {\n                          verifyCodeId = id;\n                        },\n                        sendVerifyCode: () {\n                          return APIServer().sendBindPhoneCode(_usernameController.text.trim());\n                        },\n                        sendCheck: () {\n                          final username = _usernameController.text.trim();\n                          final isPhoneNumber = phoneNumberValidator.hasMatch(username);\n\n                          if (!isPhoneNumber) {\n                            showErrorMessage(AppLocale.phoneNumberFormatError.getString(context));\n                            return false;\n                          }\n\n                          return true;\n                        },\n                      ),\n                    ),\n                    if (state.user!.user.invitedBy == null || state.user!.user.invitedBy == 0)\n                      Padding(\n                        padding: const EdgeInsets.only(left: 15.0, right: 15.0, top: 15, bottom: 0),\n                        child: TextFormField(\n                          controller: _inviteCodeController,\n                          inputFormatters: [FilteringTextInputFormatter.singleLineFormatter],\n                          decoration: InputDecoration(\n                            border: const OutlineInputBorder(),\n                            enabledBorder: const OutlineInputBorder(\n                              borderSide: BorderSide(color: Color.fromARGB(200, 192, 192, 192)),\n                            ),\n                            focusedBorder: OutlineInputBorder(\n                              borderSide: BorderSide(color: customColors.linkColor!),\n                            ),\n                            floatingLabelStyle: TextStyle(color: customColors.linkColor!),\n                            isDense: true,\n                            floatingLabelBehavior: FloatingLabelBehavior.always,\n                            labelText: AppLocale.inviteCode.getString(context),\n                            labelStyle: const TextStyle(fontSize: 17),\n                            hintText: AppLocale.inviteCodeInputTips.getString(context),\n                            hintStyle: TextStyle(\n                              color: customColors.textfieldHintColor,\n                              fontSize: 15,\n                            ),\n                          ),\n                        ),\n                      ),\n                    const SizedBox(height: 15),\n                    Container(\n                      height: 45,\n                      width: double.infinity,\n                      margin: const EdgeInsets.symmetric(horizontal: 15),\n                      decoration: BoxDecoration(\n                        color: customColors.linkColor,\n                        borderRadius: CustomSize.borderRadius,\n                      ),\n                      child: TextButton(\n                        onPressed: onSubmit,\n                        child: Text(\n                          AppLocale.ok.getString(context),\n                          style: const TextStyle(color: Colors.white, fontSize: 18),\n                        ),\n                      ),\n                    ),\n                  ],\n                );\n              }\n\n              return const Center(child: CircularProgressIndicator());\n            },\n          ),\n        ),\n      ),\n    );\n  }\n\n  onSubmit() {\n    final username = _usernameController.text.trim();\n    if (username == '') {\n      showErrorMessage(AppLocale.accountRequired.getString(context));\n      return;\n    }\n\n    if (!phoneNumberValidator.hasMatch(username)) {\n      showErrorMessage(AppLocale.phoneNumberFormatError.getString(context));\n      return;\n    }\n\n    if (verifyCodeId == '') {\n      showErrorMessage(AppLocale.pleaseGetVerifyCodeFirst.getString(context));\n      return;\n    }\n\n    final verificationCode = _verificationCodeController.text.trim();\n    if (verificationCode == '') {\n      showErrorMessage(AppLocale.verifyCodeRequired.getString(context));\n      return;\n    }\n    if (verificationCode.length != 6) {\n      showErrorMessage(AppLocale.verifyCodeFormatError.getString(context));\n      return;\n    }\n\n    final inviteCode = _inviteCodeController.text.trim();\n    if (inviteCode != '' && inviteCode.length > 20) {\n      showErrorMessage(AppLocale.inviteCodeFormatError.getString(context));\n      return;\n    }\n\n    final cancel = BotToast.showCustomLoading(\n      toastBuilder: (cancel) {\n        return LoadingIndicator(\n          message: AppLocale.processingWait.getString(context),\n        );\n      },\n      allowClick: false,\n      duration: const Duration(seconds: 120),\n    );\n\n    APIServer()\n        .bindPhone(\n      username: username,\n      verifyCodeId: verifyCodeId,\n      verifyCode: verificationCode,\n      inviteCode: inviteCode,\n    )\n        .then((value) async {\n      await widget.setting.set(settingAPIServerToken, value.token);\n      await widget.setting.set(settingUserInfo, jsonEncode(value));\n\n      if (widget.isSignIn) {\n        if (context.mounted) {\n          // ignore: use_build_context_synchronously\n          context.go(\n              '${Ability().homeRoute}?show_initial_dialog=${value.isNewUser ? \"true\" : \"false\"}&reward=${value.reward}');\n        }\n      } else {\n        if (context.mounted) {\n          // ignore: use_build_context_synchronously\n          showSuccessMessage(AppLocale.operateSuccess.getString(context));\n        }\n      }\n    }).catchError((e) {\n      showErrorMessage(resolveError(context, e));\n    }).whenComplete(() => cancel());\n  }\n}\n"
  },
  {
    "path": "lib/page/setting/change_password.dart",
    "content": "import 'package:askaide/helper/helper.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/password_field.dart';\nimport 'package:askaide/page/component/verify_code_input.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass ChangePasswordScreen extends StatefulWidget {\n  final SettingRepository setting;\n\n  const ChangePasswordScreen({super.key, required this.setting});\n\n  @override\n  State<ChangePasswordScreen> createState() => _ChangePasswordScreenState();\n}\n\nclass _ChangePasswordScreenState extends State<ChangePasswordScreen> {\n  final TextEditingController _passwordController = TextEditingController();\n  final TextEditingController _verificationCodeController = TextEditingController();\n\n  String verifyCodeId = '';\n\n  @override\n  void dispose() {\n    _passwordController.dispose();\n    _verificationCodeController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: Text(\n            AppLocale.modifyPassword.getString(context),\n            style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          backgroundColor: customColors.backgroundColor,\n          enabled: false,\n          child: Container(\n            padding: const EdgeInsets.all(10),\n            child: Column(\n              children: [\n                ColumnBlock(\n                  innerPanding: 15,\n                  padding: const EdgeInsets.only(top: 20, left: 10, right: 10),\n                  showDivider: false,\n                  children: [\n                    PasswordField(\n                      controller: _passwordController,\n                      labelText: AppLocale.newPassword.getString(context),\n                      hintText: AppLocale.passwordInputTips.getString(context),\n                      inColumnBlock: false,\n                    ),\n                    VerifyCodeInput(\n                      inColumnBlock: false,\n                      controller: _verificationCodeController,\n                      onVerifyCodeSent: (id) {\n                        verifyCodeId = id;\n                      },\n                      sendVerifyCode: () {\n                        return APIServer().sendResetPasswordCodeForSignedUser();\n                      },\n                      sendCheck: () {\n                        return true;\n                      },\n                    ),\n                  ],\n                ),\n                Container(\n                  height: 45,\n                  width: double.infinity,\n                  decoration: BoxDecoration(\n                    color: customColors.linkColor,\n                    borderRadius: CustomSize.borderRadius,\n                  ),\n                  child: TextButton(\n                    onPressed: onResetSubmit,\n                    child: Text(\n                      AppLocale.ok.getString(context),\n                      style: const TextStyle(color: Colors.white, fontSize: 18),\n                    ),\n                  ),\n                ),\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  onResetSubmit() {\n    final password = _passwordController.text.trim();\n    if (password == '' || password.length < 8 || password.length > 20) {\n      showErrorMessage(AppLocale.passwordFormatError.getString(context));\n      return;\n    }\n\n    if (verifyCodeId == '') {\n      showErrorMessage(AppLocale.pleaseGetVerifyCodeFirst.getString(context));\n      return;\n    }\n\n    final verificationCode = _verificationCodeController.text.trim();\n    if (verificationCode == '') {\n      showErrorMessage(AppLocale.verifyCodeRequired.getString(context));\n      return;\n    }\n    if (verificationCode.length != 6) {\n      showErrorMessage(AppLocale.verifyCodeFormatError.getString(context));\n      return;\n    }\n\n    final cancel = BotToast.showCustomLoading(\n      toastBuilder: (cancel) {\n        return LoadingIndicator(\n          message: AppLocale.processingWait.getString(context),\n        );\n      },\n      allowClick: false,\n      duration: const Duration(seconds: 120),\n    );\n\n    APIServer()\n        .resetPasswordByCodeSignedUser(\n      password: password,\n      verifyCodeId: verifyCodeId,\n      verifyCode: verificationCode,\n    )\n        .then((value) {\n      showSuccessMessage(AppLocale.operateSuccess.getString(context));\n      if (context.canPop()) {\n        context.pop();\n      }\n    }).catchError((e) {\n      showErrorMessage(resolveError(context, e));\n    }).whenComplete(() => cancel());\n  }\n}\n"
  },
  {
    "path": "lib/page/setting/custom_home_models.dart",
    "content": "import 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/helper/image.dart';\nimport 'package:askaide/helper/logger.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/message_box.dart';\nimport 'package:askaide/page/component/model_indicator.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/random_avatar.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api/model.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass CustomHomeModelsPage extends StatefulWidget {\n  final SettingRepository setting;\n  const CustomHomeModelsPage({super.key, required this.setting});\n\n  @override\n  State<CustomHomeModelsPage> createState() => _CustomHomeModelsPageState();\n}\n\nclass _CustomHomeModelsPageState extends State<CustomHomeModelsPage> {\n  List<HomeModelV2> models = [\n    HomeModelV2(\n      type: 'model',\n      id: 'openai:gpt-3.5-turbo',\n      supportVision: false,\n      name: 'GPT-3.5',\n    ),\n    HomeModelV2(\n      type: 'model',\n      id: 'openai:gpt-4',\n      supportVision: false,\n      name: 'GPT-4',\n    ),\n    HomeModelV2(\n      type: 'model',\n      id: '',\n      supportVision: false,\n      name: 'Unset',\n    ),\n  ];\n\n  @override\n  void initState() {\n    if (Ability().homeModels.isNotEmpty) {\n      models = Ability().homeModels;\n\n      if (models.length < 3) {\n        models.add(HomeModelV2(\n          type: 'model',\n          id: '',\n          supportVision: false,\n          name: 'Unset',\n        ));\n      }\n    }\n\n    APIServer().capabilities(cache: false).then((cap) {\n      Ability().updateCapabilities(cap);\n\n      if (cap.homeModels.isNotEmpty) {\n        models = cap.homeModels;\n\n        if (models.length < 3) {\n          models.add(HomeModelV2(\n            type: 'model',\n            id: '',\n            supportVision: false,\n            name: 'Unset',\n          ));\n        }\n\n        if (mounted) {\n          setState(() {});\n        }\n      }\n    });\n\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: Text(\n            AppLocale.customHomeModels.getString(context),\n            style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n          elevation: 0,\n        ),\n        backgroundColor: customColors.backgroundContainerColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          child: SingleChildScrollView(\n            padding: const EdgeInsets.symmetric(horizontal: 16),\n            child: Column(\n              children: [\n                const MessageBox(\n                  message: '用于设置聊一聊中的常用模型。模型 3 为可选项，长按可重置',\n                  type: MessageBoxType.info,\n                ),\n                const SizedBox(height: 10),\n                ColumnBlock(\n                  innerPanding: 5,\n                  padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),\n                  children: [\n                    for (var i = 0; i < models.length; i++)\n                      GestureDetector(\n                        onTap: () {\n                          openSelectCustomModelDialog(\n                            context,\n                            (selected) {\n                              setState(() {\n                                models[i] = selected;\n                              });\n                            },\n                            initValue: models[i].id,\n                          );\n                        },\n                        onLongPress: () {\n                          if (models[i].id.isNotEmpty && i == models.length - 1) {\n                            openConfirmDialog(\n                              context,\n                              '确认重置该模型？',\n                              () {\n                                setState(() {\n                                  models[i] = HomeModelV2(\n                                    type: 'model',\n                                    id: '',\n                                    supportVision: false,\n                                    name: 'Unset',\n                                  );\n                                });\n                              },\n                              confirmText: AppLocale.reset.getString(context),\n                            );\n                          }\n                        },\n                        child: Stack(\n                          children: [\n                            Container(\n                              padding: const EdgeInsets.symmetric(vertical: 15),\n                              width: double.infinity,\n                              decoration: BoxDecoration(\n                                color: iconAndColors[i].color,\n                                borderRadius: CustomSize.borderRadius,\n                              ),\n                              child: ModelIndicator(\n                                model: models[i],\n                                iconAndColor: iconAndColors[i],\n                                selected: true,\n                              ),\n                            ),\n                            Positioned(\n                              right: 0,\n                              top: 0,\n                              child: Container(\n                                padding: const EdgeInsets.symmetric(\n                                  horizontal: 10,\n                                  vertical: 5,\n                                ),\n                                child: Text(\n                                  '模型 ${i + 1}',\n                                  style: const TextStyle(\n                                    color: Colors.white,\n                                    fontSize: 10,\n                                  ),\n                                ),\n                              ),\n                            ),\n                          ],\n                        ),\n                      ),\n                  ],\n                ),\n                const SizedBox(height: 10),\n                EnhancedButton(\n                  title: AppLocale.save.getString(context),\n                  onPressed: () async {\n                    final cancelLoading = BotToast.showCustomLoading(\n                      toastBuilder: (cancel) {\n                        return LoadingIndicator(\n                          message: AppLocale.processingWait.getString(context),\n                        );\n                      },\n                      allowClick: false,\n                      duration: const Duration(seconds: 120),\n                    );\n\n                    try {\n                      final selectedModels = models.where((e) => e.id != '').map((e) => e.uniqueKey).toList();\n                      await APIServer().updateCustomHomeModelsV2(models: selectedModels);\n\n                      APIServer().capabilities(cache: false).then((value) => Ability().updateCapabilities(value));\n\n                      showSuccessMessage(\n                          // ignore: use_build_context_synchronously\n                          AppLocale.operateSuccess.getString(context));\n                    } catch (e) {\n                      // ignore: use_build_context_synchronously\n                      showErrorMessageEnhanced(context, e);\n                    } finally {\n                      cancelLoading();\n                    }\n                  },\n                ),\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nvoid openSelectCustomModelDialog(\n  BuildContext context,\n  Function(HomeModelV2 selected) onSelected, {\n  String? initValue,\n}) {\n  openModalBottomSheet(\n    context,\n    (context) {\n      return FutureBuilder(\n          future: APIServer().customHomeModelsV2(cache: false),\n          builder: (context, snapshot) {\n            if (snapshot.hasError) {\n              showErrorMessage(resolveError(context, snapshot.error!));\n            }\n\n            if (!snapshot.hasData) {\n              return const Center(child: CircularProgressIndicator());\n            }\n\n            return HomeModelItem(\n              models: snapshot.data!,\n              onSelected: (selected) {\n                onSelected(selected);\n                context.pop();\n              },\n              initValue: initValue,\n            );\n          });\n    },\n    heightFactor: 0.9,\n  );\n}\n\nclass HomeModelItem extends StatelessWidget {\n  final List<HomeModelV2> models;\n  final Function(HomeModelV2 selected) onSelected;\n  final String? initValue;\n\n  const HomeModelItem({\n    super.key,\n    required this.models,\n    required this.onSelected,\n    this.initValue,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    var modelsByType = <String, List<HomeModelV2>>{\n      'model': [],\n      'room_gallery': [],\n      'rooms': [],\n    };\n\n    for (var model in models) {\n      modelsByType[model.type]!.add(model);\n    }\n\n    return models.isNotEmpty\n        ? Padding(\n            padding: const EdgeInsets.only(top: 15),\n            child: DefaultTabController(\n              length: modelsByType.length,\n              child: Column(\n                mainAxisSize: MainAxisSize.min,\n                children: [\n                  TabBar(\n                    indicatorColor: customColors.linkColor,\n                    labelColor: customColors.linkColor,\n                    unselectedLabelColor: customColors.textfieldLabelColor,\n                    tabs: const [\n                      Tab(text: '模型'),\n                      Tab(text: '内置数字人'),\n                      Tab(text: '我的数字人'),\n                    ],\n                  ),\n                  const SizedBox(height: 10),\n                  Expanded(\n                    child: TabBarView(\n                      children: [\n                        buildTabView(\n                          context,\n                          customColors,\n                          models.where((e) => e.type == 'model').toList(),\n                        ),\n                        buildTabView(\n                          context,\n                          customColors,\n                          models.where((e) => e.type == 'room_gallery').toList(),\n                        ),\n                        buildTabView(\n                          context,\n                          customColors,\n                          models.where((e) => e.type == 'rooms').toList(),\n                        ),\n                      ],\n                    ),\n                  ),\n                ],\n              ),\n            ),\n          )\n        : const Center(\n            child: Text(\n              '没有可用模型\\n请先登录或者配置 OpenAI 的 Keys',\n              textAlign: TextAlign.center,\n            ),\n          );\n  }\n\n  Widget buildTabView(\n    BuildContext context,\n    CustomColors customColors,\n    List<HomeModelV2> models,\n  ) {\n    return ListView.separated(\n      shrinkWrap: true,\n      itemCount: models.length,\n      itemBuilder: (context, i) {\n        var item = models[i];\n        if (item.avatarUrl == null) {\n          Logger.instance.w(item.toJson());\n        }\n\n        return ListTile(\n          title: Container(\n            alignment: Alignment.center,\n            padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),\n            child: Row(\n              mainAxisAlignment: MainAxisAlignment.spaceBetween,\n              mainAxisSize: MainAxisSize.min,\n              children: [\n                buildAvatar(avatarUrl: item.avatarUrl, size: 40),\n                const SizedBox(width: 20),\n                Expanded(\n                  child: Container(\n                    alignment: Alignment.centerLeft,\n                    child: Row(children: [\n                      Text(\n                        item.name,\n                        overflow: TextOverflow.ellipsis,\n                      ),\n                    ]),\n                  ),\n                ),\n                SizedBox(\n                  width: 10,\n                  child: Icon(\n                    Icons.check,\n                    color: initValue == item.id ? customColors.linkColor : Colors.transparent,\n                  ),\n                ),\n              ],\n            ),\n          ),\n          onTap: () {\n            onSelected(item);\n          },\n        );\n      },\n      separatorBuilder: (BuildContext context, int index) {\n        return Divider(\n          height: 1,\n          color: customColors.columnBlockDividerColor,\n        );\n      },\n    );\n  }\n\n  Widget buildAvatar({String? avatarUrl, int? id, int size = 30}) {\n    if (avatarUrl != null && avatarUrl.startsWith('http')) {\n      return RemoteAvatar(\n        avatarUrl: imageURL(avatarUrl, qiniuImageTypeAvatar),\n        size: size,\n      );\n    }\n\n    return RandomAvatar(\n      id: id ?? 0,\n      size: size,\n      usage: Ability().isUserLogon() ? AvatarUsage.room : AvatarUsage.legacy,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/setting/destroy_account.dart",
    "content": "import 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/message_box.dart';\nimport 'package:askaide/page/component/verify_code_input.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass DestroyAccountScreen extends StatefulWidget {\n  final SettingRepository setting;\n\n  const DestroyAccountScreen({super.key, required this.setting});\n\n  @override\n  State<DestroyAccountScreen> createState() => _DestroyAccountScreenState();\n}\n\nclass _DestroyAccountScreenState extends State<DestroyAccountScreen> {\n  final TextEditingController _passwordController = TextEditingController();\n  final TextEditingController _verificationCodeController = TextEditingController();\n\n  String verifyCodeId = '';\n\n  @override\n  void dispose() {\n    _passwordController.dispose();\n    _verificationCodeController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: Text(\n            AppLocale.deleteAccount.getString(context),\n            style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          backgroundColor: customColors.backgroundColor,\n          child: Container(\n            padding: const EdgeInsets.all(10),\n            child: Column(\n              children: [\n                const MessageBox(\n                  message:\n                      '请注意，注销账号后：\\n1. 您的数据将被清空，包括角色、创作岛历史纪录、充值数据、智慧果使用明细等全部数据；\\n2. 您未使用完的智慧果将会被销毁，无法继续使用，无法退回；\\n3. 注销操作不可逆，一旦账号注销，所有被删除数据均无法恢复。',\n                  type: MessageBoxType.warning,\n                ),\n                const SizedBox(height: 15),\n                ColumnBlock(\n                  padding: const EdgeInsets.only(top: 20, left: 10, right: 10, bottom: 20),\n                  children: [\n                    VerifyCodeInput(\n                      inColumnBlock: false,\n                      controller: _verificationCodeController,\n                      onVerifyCodeSent: (id) {\n                        verifyCodeId = id;\n                      },\n                      sendVerifyCode: () {\n                        return APIServer().sendDestroyAccountSMSCode();\n                      },\n                      sendCheck: () {\n                        return true;\n                      },\n                    ),\n                  ],\n                ),\n                const SizedBox(height: 15),\n                Container(\n                  height: 45,\n                  width: double.infinity,\n                  decoration: BoxDecoration(color: Colors.red, borderRadius: CustomSize.borderRadius),\n                  child: TextButton(\n                    onPressed: onDestroySubmit,\n                    child: Text(\n                      AppLocale.confirmDeleteAccount.getString(context),\n                      style: const TextStyle(color: Colors.white, fontSize: 18),\n                    ),\n                  ),\n                ),\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  onDestroySubmit() {\n    if (verifyCodeId == '') {\n      showErrorMessage(AppLocale.pleaseGetVerifyCodeFirst.getString(context));\n      return;\n    }\n\n    final verificationCode = _verificationCodeController.text.trim();\n    if (verificationCode == '') {\n      showErrorMessage(AppLocale.verifyCodeRequired.getString(context));\n      return;\n    }\n    if (verificationCode.length != 6) {\n      showErrorMessage(AppLocale.verifyCodeFormatError.getString(context));\n      return;\n    }\n\n    final cancel = BotToast.showCustomLoading(\n      toastBuilder: (cancel) {\n        return LoadingIndicator(\n          message: AppLocale.processingWait.getString(context),\n        );\n      },\n      allowClick: false,\n      duration: const Duration(seconds: 120),\n    );\n\n    APIServer()\n        .destroyAccount(\n      verifyCodeId: verifyCodeId,\n      verifyCode: verificationCode,\n    )\n        .then((value) async {\n      await widget.setting.set(settingAPIServerToken, '');\n      await widget.setting.set(settingUserInfo, '');\n\n      showSuccessMessage('账号注销成功');\n\n      if (context.mounted) {\n        // ignore: use_build_context_synchronously\n        context.go('/login');\n      }\n    }).catchError((e) {\n      showErrorMessage(resolveError(context, e));\n    }).whenComplete(() => cancel());\n  }\n}\n"
  },
  {
    "path": "lib/page/setting/diagnosis.dart",
    "content": "import 'dart:io';\n\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/logger.dart';\nimport 'package:askaide/helper/path.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:quickalert/quickalert.dart';\nimport 'package:sqflite_common_ffi/sqflite_ffi.dart';\n\nclass DiagnosisScreen extends StatefulWidget {\n  final SettingRepository setting;\n  const DiagnosisScreen({super.key, required this.setting});\n\n  @override\n  State<DiagnosisScreen> createState() => _DiagnosisScreenState();\n}\n\nclass _DiagnosisScreenState extends State<DiagnosisScreen> {\n  String diagnosisInfo = '';\n  bool isUploaded = false;\n\n  final ScrollController _controller = ScrollController();\n\n  @override\n  void initState() {\n    super.initState();\n    if (!PlatformTool.isWeb()) {\n      File(PathHelper().getLogfilePath).exists().then(\n            (exist) => {\n              if (exist)\n                File(PathHelper().getLogfilePath).readAsString().then((value) {\n                  setState(() {\n                    diagnosisInfo = value;\n                  });\n\n                  Future.delayed(const Duration(milliseconds: 100), () {\n                    _controller.jumpTo(_controller.position.maxScrollExtent);\n                  });\n                })\n              else\n                setState(() {\n                  diagnosisInfo = 'No log file found';\n                })\n            },\n          );\n    }\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        backgroundColor: customColors.backgroundColor,\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: Text(\n            AppLocale.errorLog.getString(context),\n            style: const TextStyle(\n              fontSize: CustomSize.appBarTitleSize,\n            ),\n          ),\n          centerTitle: true,\n          actions: [\n            TextButton(\n              onPressed: () {\n                openConfirmDialog(\n                  context,\n                  'This action will erase all settings and data, do you want to proceed?',\n                  () async {\n                    final databasePath = (await databaseFactory.getDatabasesPath()).replaceAll('\\\\', '/');\n\n                    Logger.instance.d('databasePath: $databasePath');\n\n                    try {\n                      // 删除数据库目录\n                      await Directory(databasePath).delete(\n                        recursive: true,\n                      );\n\n                      showSuccessMessage(\n                        // ignore: use_build_context_synchronously\n                        AppLocale.operateSuccess.getString(context),\n                      );\n\n                      SystemChannels.platform.invokeMethod('SystemNavigator.pop');\n                    } catch (e) {\n                      Logger.instance.e(e);\n                      showBeautyDialog(\n                        // ignore: use_build_context_synchronously\n                        context,\n                        type: QuickAlertType.error,\n                        text:\n                            'Data file deletion failed. Please close the application first, manually delete the directory $databasePath, and then restart the application.',\n                      );\n                    }\n                  },\n                  danger: true,\n                );\n              },\n              child: Text(\n                '重置系统',\n                style: TextStyle(\n                  color: isUploaded ? customColors.weakTextColor?.withAlpha(100) : customColors.weakLinkColor,\n                  fontSize: 12,\n                ),\n              ),\n            ),\n            if (diagnosisInfo.isNotEmpty)\n              TextButton(\n                onPressed: () {\n                  if (isUploaded) {\n                    showSuccessMessage('已上报');\n                    return;\n                  }\n\n                  APIServer().diagnosisUpload(data: diagnosisInfo).then((value) {\n                    showSuccessMessage('上报成功');\n                    setState(() {\n                      isUploaded = true;\n                    });\n                  }).onError((error, stackTrace) {\n                    showErrorMessageEnhanced(context, error!);\n                  });\n                },\n                child: Text(\n                  AppLocale.report.getString(context),\n                  style: TextStyle(\n                    color: isUploaded ? customColors.weakTextColor?.withAlpha(100) : customColors.weakLinkColor,\n                    fontSize: 12,\n                  ),\n                ),\n              ),\n          ],\n        ),\n        body: BackgroundContainer(\n          setting: widget.setting,\n          backgroundColor: customColors.backgroundColor,\n          enabled: false,\n          child: Container(\n            padding: const EdgeInsets.all(10),\n            child: SingleChildScrollView(\n              controller: _controller,\n              child: Column(\n                children: [\n                  ColumnBlock(\n                    innerPanding: 5,\n                    padding: const EdgeInsets.all(10),\n                    children: [\n                      Text(\n                        'Server: ${APIServer().url}',\n                        style: const TextStyle(\n                          fontSize: 10,\n                        ),\n                      ),\n                      Text(\n                        'User ID: ${APIServer().localUserID()}',\n                        style: const TextStyle(\n                          fontSize: 10,\n                        ),\n                      ),\n                      const Text(\n                        'Client Version: $clientVersion',\n                        style: TextStyle(\n                          fontSize: 10,\n                        ),\n                      ),\n                      Text(\n                        'OS: ${PlatformTool.operatingSystem()} | ${PlatformTool.operatingSystemVersion()}',\n                        style: const TextStyle(\n                          fontSize: 10,\n                        ),\n                      ),\n                      Text(\n                        'OpenAI Custom: ${Ability().enableLocalOpenAI}',\n                        style: const TextStyle(\n                          fontSize: 10,\n                        ),\n                      ),\n                      FutureBuilder(\n                        future: databaseFactory.getDatabasesPath(),\n                        builder: (context, snapshot) {\n                          return Text(\n                            'Local Database: ${snapshot.data?.replaceAll('\\\\', '/')}',\n                            style: const TextStyle(\n                              fontSize: 10,\n                            ),\n                          );\n                        },\n                      ),\n                      Text(\n                        'Log File: ${PathHelper().getLogfilePath}',\n                        style: const TextStyle(\n                          fontSize: 10,\n                        ),\n                      ),\n                      Text(\n                        'Cache Directory: ${PathHelper().getCachePath}',\n                        style: const TextStyle(\n                          fontSize: 10,\n                        ),\n                      ),\n                      Text(\n                        'Main Directory: ${PathHelper().getHomePath}',\n                        style: const TextStyle(\n                          fontSize: 10,\n                        ),\n                      ),\n                    ],\n                  ),\n                  ColumnBlock(\n                    children: [\n                      Text(\n                        diagnosisInfo,\n                        style: const TextStyle(\n                          fontSize: 10,\n                        ),\n                      ),\n                    ],\n                  ),\n                ],\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/setting/notification.dart",
    "content": "import 'package:askaide/helper/haptic_feedback.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/page/data/notification_datasource.dart';\nimport 'package:askaide/repo/api/notification.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:flutter_slidable/flutter_slidable.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:loading_more_list/loading_more_list.dart';\n\nclass NotificationScreen extends StatefulWidget {\n  final SettingRepository setting;\n  const NotificationScreen({super.key, required this.setting});\n\n  @override\n  State<NotificationScreen> createState() => _NotificationScreenState();\n}\n\nclass _NotificationScreenState extends State<NotificationScreen> {\n  final NotificationDatasource datasource = NotificationDatasource();\n\n  @override\n  void dispose() {\n    datasource.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          title: Text(\n            AppLocale.notification.getString(context),\n            style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          toolbarHeight: CustomSize.toolbarHeight,\n          centerTitle: true,\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          backgroundColor: customColors.backgroundColor,\n          enabled: false,\n          child: SafeArea(\n            top: false,\n            left: false,\n            right: false,\n            child: RefreshIndicator(\n              color: customColors.linkColor,\n              displacement: 20,\n              onRefresh: () {\n                return datasource.refresh();\n              },\n              child: LoadingMoreList(\n                ListConfig<NotifyMessage>(\n                  itemBuilder: (context, item, index) {\n                    return NotifyMessageItem(\n                      message: item,\n                      customColors: customColors,\n                      onTap: () {\n                        context\n                            .push(Uri(path: '/article', queryParameters: {'id': item.articleId.toString()}).toString());\n                      },\n                    );\n                  },\n                  sourceList: datasource,\n                  indicatorBuilder: (context, status) {\n                    String msg = '';\n                    switch (status) {\n                      case IndicatorStatus.noMoreLoad:\n                        msg = '';\n                        break;\n                      case IndicatorStatus.loadingMoreBusying:\n                        msg = 'Loading...';\n                        break;\n                      case IndicatorStatus.error:\n                        msg = 'Failed to load, please try again later.';\n                        break;\n                      case IndicatorStatus.empty:\n                        msg = 'No data';\n                        break;\n                      default:\n                        return const Center(child: LoadingIndicator());\n                    }\n                    return Container(\n                      padding: const EdgeInsets.all(15),\n                      alignment: Alignment.center,\n                      child: Text(\n                        msg,\n                        style: TextStyle(\n                          color: customColors.weakTextColor,\n                          fontSize: 14,\n                        ),\n                      ),\n                    );\n                  },\n                ),\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nclass NotifyMessageItem extends StatelessWidget {\n  const NotifyMessageItem({\n    super.key,\n    required this.message,\n    required this.customColors,\n    required this.onTap,\n  });\n\n  final NotifyMessage message;\n  final CustomColors customColors;\n  final VoidCallback onTap;\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      margin: const EdgeInsets.symmetric(\n        horizontal: 15,\n        vertical: 5,\n      ),\n      decoration: BoxDecoration(borderRadius: CustomSize.borderRadius),\n      child: Slidable(\n        endActionPane: ActionPane(\n          motion: const ScrollMotion(),\n          children: [\n            const SizedBox(width: 10),\n            SlidableAction(\n              label: 'Details',\n              borderRadius: CustomSize.borderRadiusAll,\n              backgroundColor: Colors.green,\n              icon: Icons.info_outline,\n              onPressed: (_) {\n                HapticFeedbackHelper.lightImpact();\n                onTap();\n              },\n            ),\n          ],\n        ),\n        child: Material(\n          color: customColors.listTileBackgroundColor,\n          borderRadius: CustomSize.borderRadius,\n          child: InkWell(\n            child: ListTile(\n              contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),\n              shape: RoundedRectangleBorder(borderRadius: CustomSize.borderRadius),\n              title: Row(\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: [\n                  Expanded(\n                    child: Text(\n                      message.title.trim(),\n                      overflow: TextOverflow.ellipsis,\n                      style: TextStyle(\n                        color: customColors.weakTextColor,\n                        fontSize: 15,\n                      ),\n                      maxLines: 1,\n                    ),\n                  ),\n                  Text(\n                    humanTime(message.createdAt),\n                    style: TextStyle(\n                      color: customColors.weakTextColor?.withAlpha(65),\n                      fontSize: 12,\n                    ),\n                  ),\n                ],\n              ),\n              dense: true,\n              subtitle: Padding(\n                padding: const EdgeInsets.only(top: 5),\n                child: Text(\n                  message.content.trim().replaceAll(\"\\n\", \" \"),\n                  maxLines: 1,\n                  overflow: TextOverflow.ellipsis,\n                  style: TextStyle(\n                    color: customColors.weakTextColor?.withAlpha(150),\n                    fontSize: 12,\n                    overflow: TextOverflow.ellipsis,\n                  ),\n                ),\n              ),\n              onTap: () {\n                HapticFeedbackHelper.lightImpact();\n                onTap();\n              },\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/setting/openai_setting.dart",
    "content": "import 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/column_block.dart';\nimport 'package:askaide/page/component/enhanced_button.dart';\nimport 'package:askaide/page/component/enhanced_textfield.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/message_box.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:dio/dio.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass OpenAISettingScreen extends StatefulWidget {\n  final SettingRepository settings;\n  final String? source;\n  const OpenAISettingScreen({\n    super.key,\n    required this.settings,\n    this.source,\n  });\n\n  @override\n  State<OpenAISettingScreen> createState() => _OpenAISettingScreenState();\n}\n\nclass _OpenAISettingScreenState extends State<OpenAISettingScreen> {\n  final TextEditingController _apiKeyController = TextEditingController();\n  final TextEditingController _organizationController = TextEditingController();\n  final TextEditingController _urlController = TextEditingController();\n\n  bool enableOpenAISelfHosted = true;\n  bool? verifySuccess;\n\n  @override\n  void initState() {\n    super.initState();\n    _apiKeyController.text = widget.settings.stringDefault(settingOpenAIAPIToken, '');\n    _organizationController.text = widget.settings.stringDefault(settingOpenAIOrganization, '');\n    _urlController.text = widget.settings.stringDefault(settingOpenAIURL, '');\n    if (widget.source == 'setting') {\n      enableOpenAISelfHosted = widget.settings.boolDefault(settingOpenAISelfHosted, false);\n    }\n  }\n\n  @override\n  void dispose() {\n    _apiKeyController.dispose();\n    _organizationController.dispose();\n    _urlController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: const Text(\n            'OpenAI Setting',\n            style: TextStyle(\n              fontSize: CustomSize.appBarTitleSize,\n            ),\n          ),\n          centerTitle: true,\n          elevation: 0,\n          actions: [\n            if (widget.source != 'setting')\n              TextButton(\n                onPressed: () {\n                  context.go(Ability().homeRoute);\n                },\n                child: Text(\n                  'Do not set',\n                  style: TextStyle(\n                    color: customColors.weakLinkColor,\n                    fontSize: 13,\n                  ),\n                ),\n              ),\n          ],\n        ),\n        backgroundColor: customColors.backgroundColor,\n        body: BackgroundContainer(\n          setting: widget.settings,\n          enabled: false,\n          child: SizedBox(\n            height: double.infinity,\n            child: SingleChildScrollView(\n              padding: const EdgeInsets.all(16),\n              child: Column(\n                children: [\n                  MessageBox(\n                    message: AppLocale.enableCustomOpenAI.getString(context),\n                    type: MessageBoxType.info,\n                  ),\n                  const SizedBox(height: 10),\n                  ColumnBlock(\n                    children: [\n                      if (widget.source == 'setting')\n                        Padding(\n                          padding: const EdgeInsets.symmetric(vertical: 5),\n                          child: Row(\n                            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                            children: [\n                              Text(\n                                AppLocale.enable.getString(context),\n                                style: TextStyle(\n                                  color: customColors.textfieldLabelColor,\n                                  fontSize: 16,\n                                ),\n                              ),\n                              CupertinoSwitch(\n                                activeColor: customColors.linkColor,\n                                value: enableOpenAISelfHosted,\n                                onChanged: (value) {\n                                  setState(() {\n                                    enableOpenAISelfHosted = value;\n                                  });\n                                },\n                              ),\n                            ],\n                          ),\n                        ),\n                      EnhancedTextField(\n                        customColors: customColors,\n                        maxLength: 128,\n                        labelText: 'Server URL',\n                        labelWidth: 104,\n                        labelPosition: LabelPosition.left,\n                        controller: _urlController,\n                        showCounter: false,\n                        hintText: 'https://api.openai.com',\n                      ),\n                      EnhancedTextField(\n                        customColors: customColors,\n                        maxLength: 128,\n                        labelText: 'API Key',\n                        labelWidth: 104,\n                        labelPosition: LabelPosition.left,\n                        controller: _apiKeyController,\n                        showCounter: false,\n                        obscureText: true,\n                        hintText: 'sk-xxxxxxx',\n                      ),\n                      EnhancedTextField(\n                        customColors: customColors,\n                        labelText: 'Organization ID',\n                        labelFontSize: 14,\n                        labelWidth: 104,\n                        maxLength: 128,\n                        labelPosition: LabelPosition.left,\n                        controller: _organizationController,\n                        showCounter: false,\n                        hintText: AppLocale.optional.getString(context),\n                      ),\n                    ],\n                  ),\n                  const SizedBox(height: 10),\n                  EnhancedButton(\n                    title: widget.source == 'setting' ? AppLocale.save.getString(context) : 'Enable',\n                    onPressed: () {\n                      var url = _urlController.text;\n                      var apiKey = _apiKeyController.text;\n                      var organization = _organizationController.text;\n\n                      if (url == '') {\n                        url = 'https://api.openai.com';\n                      }\n\n                      if (!url.startsWith('http://') && !url.startsWith('https://')) {\n                        showErrorMessageEnhanced(context, 'The URL must begin with http:// or https://.');\n                        return;\n                      }\n\n                      if (!enableOpenAISelfHosted) {\n                        onSaveAndEnter(apiKey, organization, url, context);\n                        return;\n                      }\n\n                      if (enableOpenAISelfHosted && apiKey == '') {\n                        showErrorMessageEnhanced(context, 'API Key cannot be empty');\n                        return;\n                      }\n\n                      verifySecretKey().then((value) {\n                        onSaveAndEnter(apiKey, organization, url, context);\n                      }).onError((error, stackTrace) {\n                        showErrorMessage(error.toString());\n                      });\n                    },\n                  ),\n                ],\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  void onSaveAndEnter(String apiKey, String organization, String url, BuildContext context) async {\n    await widget.settings.set(settingOpenAIAPIToken, apiKey);\n    await widget.settings.set(settingOpenAIOrganization, organization);\n    await widget.settings.set(settingOpenAIURL, url);\n\n    if (widget.source == 'setting') {\n      await widget.settings.set(\n        settingOpenAISelfHosted,\n        enableOpenAISelfHosted ? 'true' : 'false',\n      );\n      // ignore: use_build_context_synchronously\n      showSuccessMessage(AppLocale.operateSuccess.getString(context));\n    } else {\n      await widget.settings.set(settingOpenAISelfHosted, 'true');\n      if (context.mounted) {\n        context.go(Ability().homeRoute);\n      }\n    }\n  }\n\n  Future<void> verifySecretKey() async {\n    var url = _urlController.text;\n    var apiKey = _apiKeyController.text;\n    var organization = _organizationController.text;\n\n    if (url == '') {\n      url = 'https://api.openai.com';\n    }\n\n    if (!url.startsWith('http://') && !url.startsWith('https://')) {\n      return Future.error('The URL must begin with http:// or https://.');\n    }\n\n    if (apiKey == '') {\n      return Future.error('API Key cannot be empty');\n    }\n\n    final headers = <String, dynamic>{\n      'Authorization': 'Bearer $apiKey',\n    };\n\n    if (organization != '') {\n      headers['OpenAI-Organization'] = organization;\n    }\n\n    final cancelLoading = BotToast.showCustomLoading(\n      toastBuilder: (cancel) {\n        return LoadingIndicator(\n          message: AppLocale.processingWait.getString(context),\n        );\n      },\n      allowClick: false,\n      duration: const Duration(seconds: 120),\n    );\n\n    final dio = Dio(BaseOptions(\n      baseUrl: url,\n      connectTimeout: const Duration(seconds: 5),\n    ));\n\n    try {\n      final resp = await dio.get(\n        '/v1/models',\n        options: Options(\n          headers: headers,\n          receiveDataWhenStatusError: true,\n          sendTimeout: const Duration(seconds: 3),\n          receiveTimeout: const Duration(seconds: 3),\n        ),\n      );\n\n      if (resp.statusCode != 200) {\n        cancelLoading();\n        setState(() {\n          verifySuccess = false;\n        });\n        return Future.error('Verification failed, please check the API Key: ${resp.data}');\n      }\n\n      cancelLoading();\n      setState(() {\n        verifySuccess = true;\n      });\n    } catch (e) {\n      setState(() {\n        verifySuccess = false;\n      });\n\n      cancelLoading();\n      if (e is DioException) {\n        if (e.response != null && e.response!.data != null) {\n          return Future.error(\n              'Verification failed, please check the network or API Key: ${e.response!.data[\"error\"][\"message\"]}');\n        } else {\n          return Future.error('Verification failed, please check the network or API Key: ${e.error}');\n        }\n      } else {\n        return Future.error('Verification failed, please check the network or API Key: ${e.toString()}');\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/page/setting/retrieve_password_screen.dart",
    "content": "import 'package:askaide/helper/helper.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/password_field.dart';\nimport 'package:askaide/page/component/verify_code_input.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:go_router/go_router.dart';\n\nclass RetrievePasswordScreen extends StatefulWidget {\n  final String? username;\n  final SettingRepository setting;\n  const RetrievePasswordScreen({super.key, this.username, required this.setting});\n\n  @override\n  State<RetrievePasswordScreen> createState() => _RetrievePasswordScreenState();\n}\n\nclass _RetrievePasswordScreenState extends State<RetrievePasswordScreen> {\n  final TextEditingController _usernameController = TextEditingController();\n  final TextEditingController _passwordController = TextEditingController();\n  final TextEditingController _verificationCodeController = TextEditingController();\n\n  String verifyCodeId = '';\n\n  final phoneNumberValidator = RegExp(r\"^1[3456789]\\d{9}$\");\n  final emailValidator = RegExp(r\"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\\.[a-zA-Z]+\");\n\n  @override\n  void initState() {\n    super.initState();\n\n    if (widget.username != null) {\n      _usernameController.text = widget.username!;\n    }\n  }\n\n  @override\n  void dispose() {\n    _usernameController.dispose();\n    _passwordController.dispose();\n    _verificationCodeController.dispose();\n\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: Text(\n            AppLocale.resetPassword.getString(context),\n            style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n        ),\n        backgroundColor: customColors.backgroundContainerColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          child: Column(\n            children: [\n              Padding(\n                padding: const EdgeInsets.only(left: 15.0, right: 15.0, top: 15, bottom: 0),\n                child: TextFormField(\n                  controller: _usernameController,\n                  inputFormatters: [FilteringTextInputFormatter.singleLineFormatter],\n                  keyboardType: TextInputType.phone,\n                  decoration: InputDecoration(\n                    border: const OutlineInputBorder(),\n                    enabledBorder: const OutlineInputBorder(\n                      borderSide: BorderSide(color: Color.fromARGB(200, 192, 192, 192)),\n                    ),\n                    focusedBorder: OutlineInputBorder(\n                      borderSide: BorderSide(color: customColors.linkColor!),\n                    ),\n                    isDense: true,\n                    floatingLabelBehavior: FloatingLabelBehavior.always,\n                    labelText: AppLocale.account.getString(context),\n                    hintText: AppLocale.accountInputTips.getString(context),\n                    hintStyle: TextStyle(\n                      color: customColors.textfieldHintColor,\n                      fontSize: 15,\n                    ),\n                  ),\n                ),\n              ),\n              Padding(\n                padding: const EdgeInsets.only(left: 15.0, right: 15.0, top: 15, bottom: 0),\n                child: PasswordField(\n                  controller: _passwordController,\n                  labelText: AppLocale.newPassword.getString(context),\n                  hintText: AppLocale.passwordInputTips.getString(context),\n                ),\n              ),\n              Padding(\n                padding: const EdgeInsets.only(left: 15.0, right: 10.0, top: 15, bottom: 0),\n                child: VerifyCodeInput(\n                  controller: _verificationCodeController,\n                  onVerifyCodeSent: (id) {\n                    verifyCodeId = id;\n                  },\n                  sendVerifyCode: () {\n                    return APIServer().sendResetPasswordCode(\n                      _usernameController.text.trim(),\n                      verifyType: phoneNumberValidator.hasMatch(_usernameController.text) ? 'sms' : 'email',\n                    );\n                  },\n                  sendCheck: () {\n                    final username = _usernameController.text.trim();\n                    final isPhoneNumber = phoneNumberValidator.hasMatch(username);\n                    final isEmail = emailValidator.hasMatch(username);\n\n                    if (username == '') {\n                      showErrorMessage(AppLocale.accountRequired.getString(context));\n                      return false;\n                    }\n\n                    if (!isPhoneNumber && !isEmail) {\n                      showErrorMessage(AppLocale.accountFormatError.getString(context));\n                      return false;\n                    }\n\n                    return true;\n                  },\n                ),\n              ),\n              const SizedBox(height: 15),\n              Container(\n                height: 45,\n                width: double.infinity,\n                margin: const EdgeInsets.symmetric(horizontal: 15),\n                decoration: BoxDecoration(\n                  color: customColors.linkColor,\n                  borderRadius: CustomSize.borderRadius,\n                ),\n                child: TextButton(\n                  onPressed: onResetSubmit,\n                  child: Text(\n                    AppLocale.resetPassword.getString(context),\n                    style: const TextStyle(color: Colors.white, fontSize: 18),\n                  ),\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n\n  onResetSubmit() {\n    final username = _usernameController.text.trim();\n    if (username == '') {\n      showErrorMessage(AppLocale.accountRequired.getString(context));\n      return;\n    }\n\n    if (!phoneNumberValidator.hasMatch(username) && !emailValidator.hasMatch(username)) {\n      showErrorMessage(AppLocale.accountFormatError.getString(context));\n      return;\n    }\n\n    final password = _passwordController.text.trim();\n    if (password == '' || password.length < 8 || password.length > 20) {\n      showErrorMessage(AppLocale.passwordFormatError.getString(context));\n      return;\n    }\n\n    if (verifyCodeId == '') {\n      showErrorMessage(AppLocale.pleaseGetVerifyCodeFirst.getString(context));\n      return;\n    }\n\n    final verificationCode = _verificationCodeController.text.trim();\n    if (verificationCode == '') {\n      showErrorMessage(AppLocale.verifyCodeRequired.getString(context));\n      return;\n    }\n    if (verificationCode.length != 6) {\n      showErrorMessage(AppLocale.verifyCodeFormatError.getString(context));\n      return;\n    }\n\n    final cancel = BotToast.showCustomLoading(\n      toastBuilder: (cancel) {\n        return LoadingIndicator(\n          message: AppLocale.processingWait.getString(context),\n        );\n      },\n      allowClick: false,\n      duration: const Duration(seconds: 120),\n    );\n\n    APIServer()\n        .resetPasswordByCode(\n      username: username,\n      password: password,\n      verifyCodeId: verifyCodeId,\n      verifyCode: verificationCode,\n    )\n        .then((value) {\n      showSuccessMessage(AppLocale.passwordResetOK.getString(context));\n      context.pop();\n    }).catchError((e) {\n      showErrorMessage(resolveError(context, e));\n    }).whenComplete(() => cancel());\n  }\n}\n"
  },
  {
    "path": "lib/page/setting/setting_screen.dart",
    "content": "import 'dart:io';\n\nimport 'package:askaide/bloc/account_bloc.dart';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/cache.dart';\nimport 'package:askaide/helper/env.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/helper/http.dart';\nimport 'package:askaide/helper/logger.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/page/setting/account_security.dart';\nimport 'package:askaide/page/component/account_quota_card.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/invite_card.dart';\nimport 'package:askaide/page/component/item_selector_search.dart';\nimport 'package:askaide/page/component/sliver_component.dart';\nimport 'package:askaide/page/component/social_icon.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/theme/theme.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/repo/api/user.dart';\nimport 'package:askaide/repo/api_server.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:flutter_phoenix/flutter_phoenix.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:quickalert/models/quickalert_type.dart';\nimport 'package:settings_ui/settings_ui.dart';\nimport 'package:url_launcher/url_launcher.dart';\nimport 'package:url_launcher/url_launcher_string.dart';\n\nclass SettingScreen extends StatefulWidget {\n  final SettingRepository settings;\n  const SettingScreen({super.key, required this.settings});\n\n  @override\n  State<SettingScreen> createState() => _SettingScreenState();\n}\n\nclass _SettingScreenState extends State<SettingScreen> {\n  @override\n  void initState() {\n    context.read<AccountBloc>().add(AccountLoadEvent());\n\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        backgroundColor: customColors.backgroundColor,\n        body: SliverComponent(\n          title: Text(\n            AppLocale.settings.getString(context),\n            style: TextStyle(\n              fontSize: CustomSize.appBarTitleSize,\n              color: customColors.backgroundInvertedColor,\n            ),\n          ),\n          actions: [\n            BlocBuilder<AccountBloc, AccountState>(\n              buildWhen: (previous, current) => current is AccountLoaded,\n              builder: (context, state) {\n                if (userHasLabPermission(state)) {\n                  return IconButton(\n                    onPressed: () {\n                      context.push('/admin/dashboard');\n                    },\n                    icon: const Icon(Icons.developer_board_outlined),\n                    tooltip: 'Admin Dashboard',\n                  );\n                }\n\n                return const SizedBox();\n              },\n            ),\n            IconButton(\n              onPressed: () {\n                context.push('/notifications');\n              },\n              icon: const Icon(Icons.notifications_outlined),\n              tooltip: 'Notifications',\n            ),\n          ],\n          child: BackgroundContainer(\n            setting: widget.settings,\n            enabled: false,\n            backgroundColor: customColors.backgroundColor,\n            child: BlocBuilder<AccountBloc, AccountState>(\n              builder: (_, state) {\n                return buildSettingsList(\n                  context,\n                  [\n                    // 智慧果信息、充值入口\n                    // _buildAccountQuotaCard(context, state),\n\n                    // 账号信息\n                    SettingsSection(\n                      title: Text(AppLocale.accountInfo.getString(context)),\n                      tiles: _buildAccountSetting(state, customColors),\n                    ),\n\n                    // 邀请卡片\n                    if (state is AccountLoaded && state.user != null) _buildInviteCard(context, state),\n\n                    // 自定义设置\n                    SettingsSection(\n                      title: Text(AppLocale.custom.getString(context)),\n                      tiles: [\n                        // 主题设置\n                        _buildCommonThemeSetting(customColors),\n                        // 语言设置\n                        _buildCommonLanguageSetting(),\n                        // OpenAI 自定义配置\n                        // if (Ability().enableOpenAI) _buildOpenAISelfHostedSetting(customColors),\n                        // 用户 API Keys 配置\n                        if (state is AccountLoaded && state.user != null && Ability().supportAPIKeys)\n                          _buildUserAPIKeySetting(customColors),\n                      ],\n                    ),\n\n                    // 系统信息\n                    SettingsSection(\n                      title: Text(AppLocale.systemInfo.getString(context)),\n                      tiles: [\n                        // 只有 Web 端才展示 App 下载\n                        if (PlatformTool.isWeb())\n                          SettingsTile(\n                            title: const Text('APP 下载'),\n                            trailing: const Icon(\n                              Icons.download,\n                              size: 18,\n                              color: Colors.grey,\n                            ),\n                            onPressed: (context) {\n                              launchUrlString(\n                                'https://aidea.aicode.cc',\n                                mode: LaunchMode.externalApplication,\n                              );\n                            },\n                          ),\n                        // 服务状态\n                        if (Ability().serviceStatusPage != '')\n                          SettingsTile(\n                            title: Text(AppLocale.serviceStatus.getString(context)),\n                            trailing: const Icon(\n                              CupertinoIcons.chevron_forward,\n                              size: 18,\n                              color: Colors.grey,\n                            ),\n                            onPressed: (_) {\n                              launchUrlString(Ability().serviceStatusPage);\n                            },\n                          ),\n                        // 清空缓存\n                        SettingsTile(\n                          title: Text(AppLocale.clearCache.getString(context)),\n                          trailing: const Icon(\n                            CupertinoIcons.refresh,\n                            size: 18,\n                            color: Colors.grey,\n                          ),\n                          onPressed: (_) {\n                            openConfirmDialog(\n                              context,\n                              AppLocale.confirmClearCache.getString(context),\n                              () async {\n                                await Cache().clearAll();\n                                await HttpClient.cleanCache();\n\n                                showSuccessMessage(\n                                  // ignore: use_build_context_synchronously\n                                  AppLocale.operateSuccess.getString(context),\n                                );\n\n                                if (context.mounted) {\n                                  Phoenix.rebirth(context);\n                                }\n                              },\n                              danger: true,\n                            );\n                          },\n                        ),\n\n                        // 检查更新\n                        if (!PlatformTool.isIOS())\n                          SettingsTile(\n                            title: Text(AppLocale.updateCheck.getString(context)),\n                            trailing: const Icon(\n                              CupertinoIcons.chevron_forward,\n                              size: 18,\n                              color: Colors.grey,\n                            ),\n                            onPressed: (_) {\n                              APIServer().versionCheck(cache: false).then((resp) {\n                                if (resp.hasUpdate) {\n                                  showBeautyDialog(\n                                    context,\n                                    type: QuickAlertType.success,\n                                    text: resp.message,\n                                    confirmBtnText: '去更新',\n                                    onConfirmBtnTap: () {\n                                      launchUrlString(\n                                        resp.url,\n                                        mode: LaunchMode.externalApplication,\n                                      );\n                                    },\n                                    cancelBtnText: '暂不更新',\n                                    showCancelBtn: true,\n                                  );\n                                } else {\n                                  showSuccessMessage(AppLocale.latestVersion.getString(context));\n                                }\n                              });\n                            },\n                          ),\n                        // 用户协议\n                        SettingsTile(\n                          title: Text(AppLocale.userTerms.getString(context)),\n                          trailing: const Icon(\n                            CupertinoIcons.chevron_forward,\n                            size: 18,\n                            color: Colors.grey,\n                          ),\n                          onPressed: (_) {\n                            launchUrl(Uri.parse('https://ai.aicode.cc/terms-user.html'));\n                          },\n                        ),\n                        // 隐私政策\n                        SettingsTile(\n                          title: Text(AppLocale.privacyPolicy.getString(context)),\n                          trailing: const Icon(\n                            CupertinoIcons.chevron_forward,\n                            size: 18,\n                            color: Colors.grey,\n                          ),\n                          onPressed: (_) {\n                            launchUrl(Uri.parse('https://ai.aicode.cc/privacy-policy.html'));\n                          },\n                        ),\n\n                        // 关于\n                        SettingsTile(\n                          title: Text(AppLocale.about.getString(context)),\n                          trailing: const Icon(\n                            CupertinoIcons.chevron_forward,\n                            size: 18,\n                            color: Colors.grey,\n                          ),\n                          onPressed: (_) {\n                            var tapCount = 0;\n                            showAboutDialog(\n                              context: context,\n                              applicationName: 'AIdea',\n                              applicationIcon: GestureDetector(\n                                onTap: () {\n                                  if (userHasLabPermission(state)) {\n                                    return;\n                                  }\n\n                                  tapCount++;\n\n                                  if (tapCount > 5) {\n                                    tapCount = 0;\n\n                                    final showLab = forceShowLab();\n                                    widget.settings.set(settingForceShowLab, showLab ? 'false' : 'true');\n\n                                    showSuccessMessage(showLab ? 'Lab Feature Turned Off' : 'Labs features enabled');\n\n                                    setState(() {});\n                                  }\n                                },\n                                child: Image.asset('assets/app.png', width: 40),\n                              ),\n                              applicationVersion: clientVersion,\n                              children: [\n                                Text(AppLocale.aIdeaApp.getString(context)),\n                              ],\n                            );\n                          },\n                        ),\n                      ],\n                    ),\n\n                    if (userHasLabPermission(state) || forceShowLab())\n                      SettingsSection(\n                        title: Text(AppLocale.lab.getString(context)),\n                        tiles: [\n                          // 自定义服务器\n                          _buildServerSelfHostedSetting(customColors),\n                          // 诊断\n                          SettingsTile(\n                            title: Text(AppLocale.diagnostic.getString(context)),\n                            trailing: const Icon(\n                              CupertinoIcons.chevron_forward,\n                              size: 18,\n                              color: Colors.grey,\n                            ),\n                            onPressed: (context) {\n                              context.push('/diagnosis');\n                            },\n                          ),\n                        ],\n                      ),\n                    // 社交媒体图标\n                    _buildSocialIcons(context),\n                    // 版权信息\n                    CustomSettingsSection(\n                      child: Column(\n                        children: [\n                          Text(\n                            'Copyright © 2023-${DateTime.now().year}',\n                            style: TextStyle(\n                              color: customColors.weakTextColor,\n                            ),\n                          ),\n                          GestureDetector(\n                            onTap: () {\n                              launchUrlString(\n                                'https://aidea.aicode.cc',\n                                mode: LaunchMode.externalApplication,\n                              );\n                            },\n                            child: Text(\n                              'Gulu Artificial Intelligence Technology Co., Ltd.',\n                              style: TextStyle(\n                                color: customColors.weakTextColor,\n                                fontSize: 12,\n                              ),\n                            ),\n                          ),\n                          const SizedBox(height: 15),\n                        ],\n                      ),\n                    ),\n                  ],\n                );\n              },\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  /// 用户是否有实验室访问权限\n  bool userHasLabPermission(AccountState state) {\n    return state is AccountLoaded && state.error == null && state.user != null && state.user!.control.withLab;\n  }\n\n  /// 是否强制显示实验室功能\n  bool forceShowLab() {\n    return widget.settings.boolDefault(settingForceShowLab, false);\n  }\n\n  CustomSettingsSection _buildAccountQuotaCard(\n    BuildContext context,\n    AccountState state,\n  ) {\n    UserInfo? userInfo;\n    if (state is AccountLoaded) {\n      userInfo = state.user;\n    }\n\n    return CustomSettingsSection(\n      child: AccountQuotaCard(\n        userInfo: userInfo,\n        onPaymentReturn: () {\n          if (userInfo != null) {\n            context.read<AccountBloc>().add(AccountLoadEvent(cache: false));\n          }\n        },\n      ),\n    );\n  }\n\n  CustomSettingsSection _buildInviteCard(BuildContext context, AccountLoaded state) {\n    if (state.error != null || !state.user!.showInviteMessage) {\n      return CustomSettingsSection(\n        child: Container(),\n      );\n    }\n\n    return CustomSettingsSection(\n      child: InviteCard(userInfo: state.user!),\n    );\n  }\n\n  SettingsTile _buildCommonLanguageSetting() {\n    return SettingsTile(\n      title: Text(AppLocale.language.getString(context)),\n      trailing: const Icon(\n        CupertinoIcons.chevron_forward,\n        size: 18,\n        color: Colors.grey,\n      ),\n      onPressed: (_) {\n        final current = widget.settings.stringDefault(settingLanguage, 'zh');\n        openModalBottomSheet(\n          context,\n          (context) {\n            return ListView(\n              shrinkWrap: true,\n              children: [\n                ListTile(\n                  title: Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                    children: [\n                      Text(AppLocale.followSystem.getString(context)),\n                      current == '' ? const Icon(Icons.check, color: Colors.green) : const SizedBox(),\n                    ],\n                  ),\n                  onTap: () async {\n                    await widget.settings.set(settingLanguage, '');\n                    FlutterLocalization.instance.translate(resolveSystemLanguage(Platform.localeName));\n                    if (context.mounted) {\n                      context.pop();\n                    }\n                  },\n                ),\n                ListTile(\n                  title: Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                    children: [\n                      const Text('简体中文'),\n                      current == 'zh-CHS' ? const Icon(Icons.check, color: Colors.green) : const SizedBox(),\n                    ],\n                  ),\n                  onTap: () async {\n                    await widget.settings.set(settingLanguage, 'zh-CHS');\n                    FlutterLocalization.instance.translate('zh-CHS');\n                    if (context.mounted) {\n                      context.pop();\n                    }\n                  },\n                ),\n                ListTile(\n                  title: Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                    children: [\n                      const Text('English'),\n                      current == 'en' ? const Icon(Icons.check, color: Colors.green) : const SizedBox(),\n                    ],\n                  ),\n                  onTap: () async {\n                    await widget.settings.set(settingLanguage, 'en');\n                    FlutterLocalization.instance.translate('en');\n                    if (context.mounted) {\n                      context.pop();\n                    }\n                  },\n                ),\n              ],\n            );\n          },\n          heightFactor: 0.3,\n        );\n      },\n    );\n  }\n\n  Future<List<SelectorItem<String>>> _defaultServerList() async {\n    return [\n      SelectorItem(const Text('官方正式服务器'), apiServerURL),\n      SelectorItem(const Text('本地开发环境'), 'http://localhost:8080'),\n      SelectorItem(const Text('局域网开发环境'), 'http://192.168.31.217:8080'),\n    ];\n  }\n\n  List<SettingsTile> _buildAccountSetting(AccountState state, CustomColors customColors) {\n    if (state is AccountLoaded) {\n      if (state.error != null && state.user == null) {\n        return [\n          SettingsTile(\n            title: Text(resolveError(context, state.error!)),\n            trailing: const Icon(\n              CupertinoIcons.chevron_forward,\n              size: 18,\n              color: Colors.grey,\n            ),\n            onPressed: (_) {\n              context.read<AccountBloc>().add(AccountSignOutEvent());\n              context.go('/login');\n            },\n          ),\n        ];\n      }\n      return [\n        SettingsTile(\n          title: Text(\n            state.user!.user.displayName(),\n            overflow: TextOverflow.ellipsis,\n          ),\n          trailing: Row(children: [\n            Text(\n              AppLocale.accountSettings.getString(context),\n              style: TextStyle(\n                color: customColors.weakTextColor?.withAlpha(200),\n                fontSize: 13,\n              ),\n            ),\n            const Icon(\n              CupertinoIcons.chevron_forward,\n              size: 18,\n              color: Colors.grey,\n            ),\n          ]),\n          onPressed: (context) {\n            context.push('/setting/account-security');\n          },\n        ),\n        SettingsTile(\n          title: Text(AppLocale.freeQuota.getString(context)),\n          trailing: const Icon(\n            CupertinoIcons.chevron_forward,\n            size: 18,\n            color: Colors.grey,\n          ),\n          onPressed: (context) {\n            context.push('/free-statistics');\n          },\n        ),\n      ];\n    } else if (state is AccountLoading) {\n      return [\n        SettingsTile(\n          title: const Text('Loading...'),\n        ),\n      ];\n    }\n\n    return [\n      SettingsTile(\n        leading: const Icon(Icons.account_circle),\n        title: Text(AppLocale.signIn.getString(context)),\n        trailing: const Icon(\n          CupertinoIcons.chevron_forward,\n          size: 18,\n          color: Colors.grey,\n        ),\n        onPressed: (_) {\n          context.go('/login');\n        },\n      ),\n    ];\n  }\n\n  SettingsTile _buildCommonThemeSetting(CustomColors customColors) {\n    return SettingsTile.navigation(\n      title: Text(AppLocale.themeMode.getString(context)),\n      onPressed: (context) {\n        final current = widget.settings.stringDefault(settingThemeMode, 'system');\n\n        openModalBottomSheet(\n          context,\n          (context) {\n            return ListView(\n              shrinkWrap: true,\n              children: [\n                ListTile(\n                  title: Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                    children: [\n                      Text(AppLocale.followSystem.getString(context)),\n                      current == 'system' ? const Icon(Icons.check, color: Colors.green) : const SizedBox(),\n                    ],\n                  ),\n                  onTap: () async {\n                    await widget.settings.set(settingThemeMode, 'system');\n                    AppTheme.instance.mode = AppTheme.themeModeFormString('system');\n                    if (context.mounted) {\n                      context.pop();\n                    }\n                  },\n                ),\n                ListTile(\n                  title: Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                    children: [\n                      Text(AppLocale.lightThemeMode.getString(context)),\n                      current == 'light' ? const Icon(Icons.check, color: Colors.green) : const SizedBox(),\n                    ],\n                  ),\n                  onTap: () async {\n                    await widget.settings.set(settingThemeMode, 'light');\n                    AppTheme.instance.mode = AppTheme.themeModeFormString('light');\n                    if (context.mounted) {\n                      context.pop();\n                    }\n                  },\n                ),\n                ListTile(\n                  title: Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                    children: [\n                      Text(AppLocale.darkThemeMode.getString(context)),\n                      current == 'dark' ? const Icon(Icons.check, color: Colors.green) : const SizedBox(),\n                    ],\n                  ),\n                  onTap: () async {\n                    await widget.settings.set(settingThemeMode, 'dark');\n                    AppTheme.instance.mode = AppTheme.themeModeFormString('dark');\n                    if (context.mounted) {\n                      context.pop();\n                    }\n                  },\n                ),\n              ],\n            );\n          },\n          heightFactor: 0.3,\n        );\n      },\n    );\n  }\n\n  SettingsTile _buildOpenAISelfHostedSetting(CustomColors customColors) {\n    return SettingsTile.navigation(\n      title: const Text('OpenAI'),\n      value: Text(\n        widget.settings.boolDefault(settingOpenAISelfHosted, false)\n            ? AppLocale.enable.getString(context)\n            : AppLocale.disable.getString(context),\n        style: TextStyle(\n          color: customColors.weakTextColor?.withAlpha(200),\n          fontSize: 13,\n        ),\n      ),\n      onPressed: (context) {\n        context.push('/setting/openai-custom?source=setting');\n      },\n    );\n  }\n\n  /// 用户 API Key 配置\n  SettingsTile _buildUserAPIKeySetting(CustomColors customColors) {\n    return SettingsTile.navigation(\n      title: Text(AppLocale.userApiKeys.getString(context)),\n      onPressed: (context) {\n        context.push('/setting/user-api-keys');\n      },\n    );\n  }\n\n  SettingsTile _buildServerSelfHostedSetting(CustomColors customColors) {\n    return SettingsTile(\n      title: const Text('Custom API Server'),\n      trailing: const Icon(\n        CupertinoIcons.chevron_forward,\n        size: 18,\n        color: Colors.grey,\n      ),\n      onPressed: (_) {\n        openTextFieldDialog(\n          context,\n          title: 'Server Address',\n          defaultValue: widget.settings.stringDefault(settingServerURL, apiServerURL),\n          withSuffixIcon: true,\n          enableSearch: false,\n          futureDataSources: _defaultServerList(),\n          onSubmit: (value) {\n            widget.settings.set(settingServerURL, value.trim()).then((value) {\n              openConfirmDialog(\n                context,\n                'Settings successful, will take effect after app restart',\n                () {\n                  try {\n                    SystemChannels.platform.invokeMethod('SystemNavigator.pop');\n                  } catch (e) {\n                    Logger.instance.e(e);\n                    showErrorMessage('Application restart failed, please restart manually');\n                  }\n                },\n                danger: true,\n                confirmText: 'Restart now',\n                cancelText: 'Restart later',\n              );\n            });\n            return true;\n          },\n        );\n      },\n    );\n  }\n\n  CustomSettingsSection _buildSocialIcons(BuildContext context) {\n    return CustomSettingsSection(\n      child: SocialIconGroup(\n        isSettingTiles: true,\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/page/setting/user_api_keys.dart",
    "content": "import 'package:askaide/bloc/user_api_keys_bloc.dart';\nimport 'package:askaide/helper/helper.dart';\nimport 'package:askaide/lang/lang.dart';\nimport 'package:askaide/page/component/background_container.dart';\nimport 'package:askaide/page/component/dialog.dart';\nimport 'package:askaide/page/component/loading.dart';\nimport 'package:askaide/page/component/message_box.dart';\nimport 'package:askaide/page/component/theme/custom_size.dart';\nimport 'package:askaide/page/component/theme/custom_theme.dart';\nimport 'package:askaide/page/component/windows.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:bot_toast/bot_toast.dart';\nimport 'package:clipboard/clipboard.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localization/flutter_localization.dart';\nimport 'package:flutter_slidable/flutter_slidable.dart';\nimport 'package:go_router/go_router.dart';\nimport 'package:quickalert/quickalert.dart';\n\nclass UserAPIKeysScreen extends StatefulWidget {\n  final SettingRepository setting;\n\n  const UserAPIKeysScreen({super.key, required this.setting});\n\n  @override\n  State<UserAPIKeysScreen> createState() => _UserAPIKeysScreenState();\n}\n\nclass _UserAPIKeysScreenState extends State<UserAPIKeysScreen> {\n  Function? cancelDialog;\n\n  @override\n  void initState() {\n    super.initState();\n    context.read<UserApiKeysBloc>().add(UserApiKeysLoad());\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var customColors = Theme.of(context).extension<CustomColors>()!;\n\n    return WindowFrameWidget(\n      backgroundColor: customColors.backgroundColor,\n      child: Scaffold(\n        appBar: AppBar(\n          toolbarHeight: CustomSize.toolbarHeight,\n          title: Text(\n            AppLocale.userApiKeys.getString(context),\n            style: const TextStyle(fontSize: CustomSize.appBarTitleSize),\n          ),\n          centerTitle: true,\n          elevation: 0,\n          actions: [\n            IconButton(\n              icon: const Icon(Icons.add),\n              onPressed: () {\n                openTextFieldDialog(\n                  context,\n                  title: 'API Key',\n                  hint: 'API Key 名称',\n                  onSubmit: (value) {\n                    context.read<UserApiKeysBloc>().add(UserApiKeyCreate(value));\n                    return true;\n                  },\n                );\n              },\n            ),\n          ],\n        ),\n        backgroundColor: customColors.backgroundContainerColor,\n        body: BackgroundContainer(\n          setting: widget.setting,\n          enabled: false,\n          child: SingleChildScrollView(\n            padding: const EdgeInsets.symmetric(horizontal: 16),\n            child: Column(\n              children: [\n                const MessageBox(\n                  message:\n                      'You can use API Key to access your data in other applications, and the protocol is fully compatible with OpenAI\\'s official API.',\n                  type: MessageBoxType.info,\n                ),\n                const SizedBox(height: 10),\n                BlocConsumer<UserApiKeysBloc, UserApiKeysState>(\n                  listener: (context, state) {\n                    if (state is UserApiKeyLoaded) {\n                      showBeautyDialog(\n                        context,\n                        type: QuickAlertType.success,\n                        title: 'API Key',\n                        text: state.key.token,\n                        confirmBtnText: 'Copy to clipboard',\n                        onConfirmBtnTap: () {\n                          FlutterClipboard.copy(state.key.token).then((value) {\n                            showSuccessMessage('Copied to clipboard');\n                            context.pop();\n                          });\n\n                          cancelDialog?.call();\n                        },\n                        showCancelBtn: true,\n                        onCancelBtnTap: () => context.pop(),\n                        barrierDismissible: true,\n                      );\n                    }\n                  },\n                  buildWhen: (previous, current) => current is UserApiKeysLoaded,\n                  builder: (context, state) {\n                    if (state is UserApiKeysLoaded) {\n                      if (state.keys.isEmpty) {\n                        return Container(\n                          margin: const EdgeInsets.only(top: 50),\n                          alignment: Alignment.center,\n                          child: Center(\n                            child: Text(\n                              'You haven\\'t created any API Key yet.',\n                              style: TextStyle(\n                                fontSize: 14,\n                                color: customColors.weakTextColor,\n                              ),\n                            ),\n                          ),\n                        );\n                      }\n                      return ListView.builder(\n                        itemCount: state.keys.length,\n                        shrinkWrap: true,\n                        physics: const NeverScrollableScrollPhysics(),\n                        itemBuilder: (context, index) {\n                          final item = state.keys[index];\n                          return Container(\n                            margin: const EdgeInsets.symmetric(vertical: 5),\n                            decoration: BoxDecoration(borderRadius: CustomSize.borderRadius),\n                            child: Slidable(\n                              endActionPane: ActionPane(\n                                motion: const ScrollMotion(),\n                                children: [\n                                  const SizedBox(width: 10),\n                                  SlidableAction(\n                                    label: AppLocale.delete.getString(context),\n                                    borderRadius: CustomSize.borderRadiusAll,\n                                    backgroundColor: Colors.red,\n                                    icon: Icons.delete,\n                                    onPressed: (_) {\n                                      openConfirmDialog(\n                                        context,\n                                        AppLocale.confirmDelete.getString(context),\n                                        () {\n                                          context.read<UserApiKeysBloc>().add(UserApiKeyDelete(item.id));\n                                        },\n                                        danger: true,\n                                      );\n                                    },\n                                  ),\n                                ],\n                              ),\n                              child: Material(\n                                color: customColors.backgroundColor?.withAlpha(200),\n                                borderRadius: const BorderRadius.all(CustomSize.radius),\n                                child: InkWell(\n                                  child: ListTile(\n                                    contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),\n                                    shape: RoundedRectangleBorder(borderRadius: CustomSize.borderRadius),\n                                    title: Row(\n                                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                                      children: [\n                                        Expanded(\n                                          child: Text(\n                                            item.name,\n                                            overflow: TextOverflow.ellipsis,\n                                            style: TextStyle(\n                                              color: customColors.weakTextColor,\n                                              fontSize: 15,\n                                            ),\n                                            maxLines: 1,\n                                          ),\n                                        ),\n                                        Text(\n                                          humanTime(DateTime.now()),\n                                          style: TextStyle(\n                                            color: customColors.weakTextColor?.withAlpha(65),\n                                            fontSize: 12,\n                                          ),\n                                        ),\n                                      ],\n                                    ),\n                                    subtitle: Padding(\n                                      padding: const EdgeInsets.only(top: 5),\n                                      child: Text(\n                                        item.token,\n                                        maxLines: 1,\n                                        overflow: TextOverflow.ellipsis,\n                                        style: TextStyle(\n                                          color: customColors.weakTextColor?.withAlpha(150),\n                                          fontSize: 12,\n                                          overflow: TextOverflow.ellipsis,\n                                        ),\n                                      ),\n                                    ),\n                                    dense: true,\n                                    onTap: () {\n                                      cancelDialog = BotToast.showCustomLoading(\n                                        toastBuilder: (cancel) {\n                                          return LoadingIndicator(\n                                            message: AppLocale.imageUploading.getString(context),\n                                          );\n                                        },\n                                        allowClick: false,\n                                      );\n\n                                      context.read<UserApiKeysBloc>().add(\n                                            UserApiKeyLoad(item.id),\n                                          );\n                                    },\n                                  ),\n                                ),\n                              ),\n                            ),\n                          );\n                        },\n                      );\n                    }\n\n                    return const Center(child: LoadingIndicator());\n                  },\n                ),\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/repo/api/admin/channels.dart",
    "content": "class AdminChannel {\n  int? id;\n  String name;\n  String type;\n  String? server;\n  String? secret;\n\n  AdminChannelMeta? meta;\n\n  String get display {\n    return name;\n  }\n\n  AdminChannel({\n    this.id,\n    required this.name,\n    required this.type,\n    this.server,\n    this.secret,\n    this.meta,\n  });\n\n  factory AdminChannel.fromJson(Map<String, dynamic> json) {\n    return AdminChannel(\n      id: json['id'],\n      name: json['name'],\n      type: json['type'],\n      server: json['server'],\n      secret: json['secret'],\n      meta:\n          json['meta'] != null ? AdminChannelMeta.fromJson(json['meta']) : null,\n    );\n  }\n\n  Map<String, dynamic> toJson() {\n    return {\n      'id': id,\n      'name': name,\n      'type': type,\n      'server': server,\n      'secret': secret,\n      'meta': meta?.toJson(),\n    };\n  }\n}\n\nclass AdminChannelMeta {\n  bool? usingProxy;\n  bool? openaiAzure;\n  String? openaiAzureAPIVersion;\n\n  AdminChannelMeta({\n    this.usingProxy,\n    this.openaiAzure,\n    this.openaiAzureAPIVersion,\n  });\n\n  factory AdminChannelMeta.fromJson(Map<String, dynamic> json) {\n    return AdminChannelMeta(\n      usingProxy: json['using_proxy'],\n      openaiAzure: json['openai_azure'],\n      openaiAzureAPIVersion: json['openai_azure_api_version'],\n    );\n  }\n\n  Map<String, dynamic> toJson() {\n    return {\n      'using_proxy': usingProxy,\n      'openai_azure': openaiAzure,\n      'openai_azure_api_version': openaiAzureAPIVersion,\n    };\n  }\n}\n\nclass AdminChannelAddReq {\n  String name;\n  String type;\n  String? server;\n  String? secret;\n\n  AdminChannelMeta? meta;\n\n  AdminChannelAddReq({\n    required this.name,\n    required this.type,\n    this.server,\n    this.secret,\n    this.meta,\n  });\n\n  Map<String, dynamic> toJson() {\n    return {\n      'name': name,\n      'type': type,\n      'server': server,\n      'secret': secret,\n      'meta': meta?.toJson(),\n    };\n  }\n}\n\nclass AdminChannelUpdateReq {\n  String? name;\n  String? type;\n  String? server;\n  String? secret;\n\n  AdminChannelMeta? meta;\n\n  AdminChannelUpdateReq({\n    this.name,\n    this.type,\n    this.server,\n    this.secret,\n    this.meta,\n  });\n\n  Map<String, dynamic> toJson() {\n    return {\n      'name': name,\n      'type': type,\n      'server': server,\n      'secret': secret,\n      'meta': meta?.toJson(),\n    };\n  }\n}\n\nclass AdminChannelType {\n  String name;\n  String? display;\n  bool dynamicType;\n\n  String get text {\n    return display ?? name;\n  }\n\n  AdminChannelType({\n    required this.name,\n    this.display,\n    required this.dynamicType,\n  });\n\n  factory AdminChannelType.fromJson(Map<String, dynamic> json) {\n    return AdminChannelType(\n      name: json['name'],\n      display: json['display'],\n      dynamicType: json['dynamic'] ?? false,\n    );\n  }\n\n  Map<String, dynamic> toJson() {\n    return {\n      'name': name,\n      'display': display,\n      'dynamic': dynamicType,\n    };\n  }\n}\n"
  },
  {
    "path": "lib/repo/api/admin/models.dart",
    "content": "class AdminModel {\n  String modelId;\n  String name;\n  String? shortName;\n  String? description;\n  String? avatarUrl;\n  int status;\n  AdminModelMeta? meta;\n  List<AdminModelProvider> providers;\n\n  bool get isVision => meta?.vision ?? false;\n  int get inputPrice => meta?.inputPrice ?? 0;\n  int get outputPrice => meta?.outputPrice ?? 0;\n  int get perReqPrice => meta?.perReqPrice ?? 0;\n  int get maxContext => meta?.maxContext ?? 0;\n  bool get enabled => status == 1;\n\n  AdminModel({\n    required this.modelId,\n    required this.name,\n    this.shortName,\n    this.description,\n    this.avatarUrl,\n    required this.status,\n    this.meta,\n    required this.providers,\n  });\n\n  factory AdminModel.fromJson(Map<String, dynamic> json) {\n    return AdminModel(\n      modelId: json['model_id'],\n      name: json['name'],\n      shortName: json['short_name'],\n      description: json['description'],\n      avatarUrl: json['avatar_url'],\n      status: json['status'],\n      meta: json['meta'] != null ? AdminModelMeta.fromJson(json['meta']) : null,\n      providers: ((json['providers'] ?? []) as List).map((e) => AdminModelProvider.fromJson(e)).toList(),\n    );\n  }\n\n  Map<String, dynamic> toJson() {\n    return {\n      'model_id': modelId,\n      'name': name,\n      'short_name': shortName,\n      'description': description,\n      'avatar_url': avatarUrl,\n      'status': status,\n      'meta': meta?.toJson(),\n      'providers': providers.map((e) => e.toJson()).toList(),\n    };\n  }\n}\n\nclass AdminModelMeta {\n  bool? vision;\n  bool? restricted;\n  int? maxContext;\n  int? inputPrice;\n  int? outputPrice;\n  int? perReqPrice;\n  String? prompt;\n\n  String? tag;\n  String? tagTextColor;\n  String? tagBgColor;\n\n  bool? isNew;\n  bool? isRecommend;\n  String? category;\n\n  bool? search;\n  bool? reasoning;\n\n  double? temperature;\n  int? searchCount;\n  int? searchPrice;\n  AdminModelMeta({\n    this.vision,\n    this.restricted,\n    this.maxContext,\n    this.inputPrice,\n    this.outputPrice,\n    this.perReqPrice,\n    this.prompt,\n    this.tag,\n    this.tagTextColor,\n    this.tagBgColor,\n    this.isNew,\n    this.isRecommend,\n    this.category,\n    this.search,\n    this.reasoning,\n    this.temperature,\n    this.searchCount,\n    this.searchPrice,\n  });\n\n  factory AdminModelMeta.fromJson(Map<String, dynamic> json) {\n    return AdminModelMeta(\n      vision: json['vision'] ?? false,\n      restricted: json['restricted'] ?? false,\n      maxContext: json['max_context'],\n      inputPrice: json['input_price'] ?? 0,\n      outputPrice: json['output_price'] ?? 0,\n      perReqPrice: json['per_req_price'] ?? 0,\n      prompt: json['prompt'],\n      tag: json['tag'],\n      tagTextColor: json['tag_text_color'],\n      tagBgColor: json['tag_bg_color'],\n      isNew: json['is_new'] ?? false,\n      isRecommend: json['is_recommend'] ?? false,\n      category: json['category'],\n      search: json['search'] ?? false,\n      reasoning: json['reasoning'] ?? false,\n      temperature: json['temperature'] ?? 0.0,\n      searchCount: json['search_count'] ?? 0,\n      searchPrice: json['search_price'] ?? 0,\n    );\n  }\n\n  Map<String, dynamic> toJson() {\n    return {\n      'vision': vision,\n      'restricted': restricted,\n      'max_context': maxContext,\n      'input_price': inputPrice,\n      'output_price': outputPrice,\n      'per_req_price': perReqPrice,\n      'prompt': prompt,\n      'tag': tag,\n      'tag_text_color': tagTextColor,\n      'tag_bg_color': tagBgColor,\n      'is_new': isNew,\n      'is_recommend': isRecommend,\n      'category': category,\n      'search': search,\n      'reasoning': reasoning,\n      'temperature': temperature,\n      'search_count': searchCount,\n      'search_price': searchPrice,\n    };\n  }\n}\n\nclass AdminModelProvider {\n  int? id;\n  String? name;\n  String? modelRewrite;\n  String? type;\n\n  AdminModelProvider({\n    this.id,\n    this.name,\n    this.modelRewrite,\n    this.type,\n  });\n\n  factory AdminModelProvider.fromJson(Map<String, dynamic> json) {\n    return AdminModelProvider(\n      id: json['id'],\n      name: json['name'],\n      modelRewrite: json['model_rewrite'],\n      type: json['type'],\n    );\n  }\n\n  Map<String, dynamic> toJson() {\n    return {\n      'id': id,\n      'name': name,\n      'model_rewrite': modelRewrite,\n      'type': type,\n    };\n  }\n}\n\nclass AdminModelAddReq {\n  String modelId;\n  String name;\n  String? shortName;\n  String? description;\n  String? avatarUrl;\n  int status;\n  AdminModelMeta? meta;\n  List<AdminModelProvider>? providers;\n\n  AdminModelAddReq({\n    required this.modelId,\n    required this.name,\n    this.shortName,\n    this.description,\n    this.avatarUrl,\n    required this.status,\n    this.meta,\n    this.providers,\n  });\n\n  Map<String, dynamic> toJson() {\n    return {\n      'model_id': modelId,\n      'name': name,\n      'short_name': shortName,\n      'description': description,\n      'avatar_url': avatarUrl,\n      'status': status,\n      'meta': meta?.toJson(),\n      'providers': providers?.map((e) => e.toJson()).toList(),\n    };\n  }\n}\n\nclass AdminModelUpdateReq {\n  String name;\n  String? shortName;\n  String? description;\n  String? avatarUrl;\n  int status;\n  AdminModelMeta? meta;\n  List<AdminModelProvider>? providers;\n\n  AdminModelUpdateReq({\n    required this.name,\n    this.shortName,\n    this.description,\n    this.avatarUrl,\n    required this.status,\n    this.meta,\n    this.providers,\n  });\n\n  Map<String, dynamic> toJson() {\n    return {\n      'name': name,\n      'short_name': shortName,\n      'description': description,\n      'avatar_url': avatarUrl,\n      'status': status,\n      'meta': meta?.toJson(),\n      'providers': providers?.map((e) => e.toJson()).toList(),\n    };\n  }\n}\n"
  },
  {
    "path": "lib/repo/api/admin/payment.dart",
    "content": "class AdminPaymentHistory {\n  final int id;\n  final int userId;\n  final String paymentId;\n  final String? source;\n  final int quantity;\n  final int retailPrice;\n  final String environment;\n  final DateTime purchaseAt;\n\n  AdminPaymentHistory({\n    required this.id,\n    required this.userId,\n    required this.paymentId,\n    required this.quantity,\n    required this.retailPrice,\n    required this.environment,\n    required this.purchaseAt,\n    this.source,\n  });\n\n  factory AdminPaymentHistory.fromJson(Map<String, dynamic> json) {\n    return AdminPaymentHistory(\n      id: json['id'],\n      userId: json['user_id'],\n      paymentId: json['payment_id'],\n      quantity: json['quantity'],\n      retailPrice: json['retail_price'],\n      environment: json['environment'],\n      purchaseAt: DateTime.parse(json['purchase_at']),\n      source: json['source'],\n    );\n  }\n\n  Map<String, dynamic> toJson() {\n    return {\n      'id': id,\n      'user_id': userId,\n      'payment_id': paymentId,\n      'quantity': quantity,\n      'retail_price': retailPrice,\n      'environment': environment,\n      'purchase_at': purchaseAt.toIso8601String(),\n      'source': source,\n    };\n  }\n}\n"
  },
  {
    "path": "lib/repo/api/admin/users.dart",
    "content": "class AdminUser {\n  final int id;\n  final String? email;\n  final String? phone;\n  final String? realname;\n  final String? avatar;\n  final String? unionId;\n  final String? appleUid;\n  final int? invitedBy;\n  final String? inviteCode;\n  final String? userType;\n  final String? status;\n  final String? preferSigninMethod;\n  final DateTime? createdAt;\n\n  AdminUser({\n    required this.id,\n    this.email,\n    this.phone,\n    this.realname,\n    this.avatar,\n    this.unionId,\n    this.appleUid,\n    this.invitedBy,\n    this.inviteCode,\n    required this.userType,\n    this.preferSigninMethod,\n    this.createdAt,\n    this.status,\n  });\n\n  String get displayName {\n    if (realname != null && realname!.isNotEmpty) {\n      return realname!;\n    }\n\n    if (email != null && email!.isNotEmpty) {\n      return email!;\n    }\n\n    if (phone != null && phone!.isNotEmpty) {\n      return phone!;\n    }\n\n    return '-';\n  }\n\n  factory AdminUser.fromJson(Map<String, dynamic> json) {\n    return AdminUser(\n      id: json['id'],\n      email: json['email'],\n      phone: json['phone'],\n      realname: json['realname'],\n      avatar: json['avatar'],\n      unionId: json['union_id'],\n      appleUid: json['apple_uid'],\n      invitedBy: json['invite_by'],\n      inviteCode: json['invite_code'],\n      userType: json['user_type'],\n      preferSigninMethod: json['prefer_signin_method'],\n      createdAt:\n          json['CreatedAt'] != null ? DateTime.parse(json['CreatedAt']) : null,\n      status: json['status'],\n    );\n  }\n\n  Map<String, dynamic> toJson() {\n    return {\n      'id': id,\n      'email': email,\n      'phone': phone,\n      'realname': realname,\n      'avatar': avatar,\n      'union_id': unionId,\n      'apple_uid': appleUid,\n      'invite_by': invitedBy,\n      'invite_code': inviteCode,\n      'user_type': userType,\n      'prefer_signin_method': preferSigninMethod,\n      'CreatedAt': createdAt?.toIso8601String(),\n      'status': status,\n    };\n  }\n}\n"
  },
  {
    "path": "lib/repo/api/article.dart",
    "content": "class Article {\n  int id;\n  String title;\n  String content;\n  String? author;\n  String? type;\n  DateTime? createdAt;\n\n  Article({\n    required this.id,\n    required this.title,\n    required this.content,\n    this.author,\n    this.type,\n    this.createdAt,\n  });\n\n  factory Article.fromJson(Map<String, dynamic> json) {\n    return Article(\n      id: json['id'],\n      title: json['title'],\n      content: json['content'],\n      author: json['author'],\n      type: json['type'],\n      createdAt: json['created_at'] != null\n          ? DateTime.parse(json['created_at'])\n          : null,\n    );\n  }\n\n  Map<String, dynamic> toJson() {\n    return {\n      'id': id,\n      'title': title,\n      'content': content,\n      'author': author,\n      'type': type,\n      'created_at': createdAt?.toIso8601String(),\n    };\n  }\n}\n"
  },
  {
    "path": "lib/repo/api/creative.dart",
    "content": "import 'dart:convert';\n\nimport 'package:intl/intl.dart';\n\nclass CreativeGalleryItemResponse {\n  CreativeGallery item;\n  bool isInternalUser;\n\n  CreativeGalleryItemResponse(this.item, this.isInternalUser);\n\n  toJson() => {\n        'data': item.toJson(),\n        'is_internal_user': isInternalUser,\n      };\n\n  static CreativeGalleryItemResponse fromJson(Map<String, dynamic> json) {\n    return CreativeGalleryItemResponse(\n      CreativeGallery.fromJson(json['data']),\n      json['is_internal_user'] ?? false,\n    );\n  }\n}\n\nclass CreativeGallery {\n  int id;\n  int? userId;\n  String? username;\n  int? creativeHistoryId;\n  int creativeType;\n  String creativeId;\n  String? meta;\n  String? prompt;\n  String? negativePrompt;\n  String? answer;\n  int refCount;\n  int starLevel;\n  int hotValue;\n  int status;\n  DateTime? createdAt;\n  DateTime? updatedAt;\n\n  String? previewImage;\n\n  CreativeGallery({\n    required this.id,\n    this.userId,\n    this.username,\n    this.creativeHistoryId,\n    required this.creativeType,\n    required this.creativeId,\n    this.meta,\n    this.prompt,\n    this.negativePrompt,\n    this.answer,\n    this.refCount = 0,\n    this.starLevel = 0,\n    this.hotValue = 0,\n    this.status = 0,\n    this.previewImage,\n    this.createdAt,\n    this.updatedAt,\n  });\n\n  Map<String, dynamic> get metaMap {\n    if (meta == null || meta!.isEmpty) {\n      return {};\n    }\n\n    return jsonDecode(meta!);\n  }\n\n  List<String> get images {\n    try {\n      if (creativeType == 2 && answer != null && answer != '') {\n        return (jsonDecode(answer!) as List<dynamic>)\n            .map((e) => e.toString())\n            .toList();\n      }\n      return [];\n    } catch (e) {\n      return [];\n    }\n  }\n\n  /// 封面图\n  String get preview {\n    if (previewImage != null && previewImage != '') {\n      return previewImage!;\n    }\n\n    if (images.isNotEmpty) {\n      return images.first;\n    }\n\n    return '';\n  }\n\n  toJson() => {\n        'id': id,\n        'user_id': userId,\n        'username': username,\n        'creative_history_id': creativeHistoryId,\n        'creative_type': creativeType,\n        'creative_id': creativeId,\n        'meta': meta,\n        'prompt': prompt,\n        'negative_prompt': negativePrompt,\n        'answer': answer,\n        'ref_count': refCount,\n        'star_level': starLevel,\n        'hot_value': hotValue,\n        'status': status,\n        'preview_image': previewImage,\n        'created_at': createdAt?.toIso8601String(),\n        'updated_at': updatedAt?.toIso8601String(),\n      };\n\n  static CreativeGallery fromJson(Map<String, dynamic> json) {\n    return CreativeGallery(\n      id: json['id'],\n      userId: json['user_id'],\n      username: json['username'],\n      creativeHistoryId: json['creative_history_id'],\n      creativeType: json['creative_type'],\n      creativeId: json['creative_id'] ?? 'text-to-image',\n      meta: json['meta'],\n      prompt: json['prompt'],\n      negativePrompt: json['negative_prompt'],\n      answer: json['answer'],\n      refCount: json['ref_count'] ?? 0,\n      starLevel: json['star_level'] ?? 0,\n      hotValue: json['hot_value'] ?? 0,\n      status: json['status'] ?? 0,\n      previewImage: json['preview_image'],\n      createdAt: json['created_at'] != null\n          ? DateTime.parse(json['created_at'])\n          : null,\n      updatedAt: json['updated_at'] != null\n          ? DateTime.parse(json['updated_at'])\n          : null,\n    );\n  }\n}\n\nclass CreativeIslandCapacity {\n  bool showAIRewrite;\n  bool showUpscaleBy;\n  bool showNegativeText;\n  bool showStyle;\n  bool showSeed;\n  bool showImageCount;\n  bool showPromptForImage2Image;\n  List<String> allowRatios;\n  List<CreativeIslandVendorModel> vendorModels;\n  List<String> allowUpscaleBy;\n  bool showImageStrength;\n  List<CreativeIslandImageFilter> filters;\n  List<CreativeIslandArtisticStyle> artisticStyles;\n  List<CreativeIslandArtisticStyle> artisticTextStyles;\n  List<CreativeIslandArtisticStyle> artisticTextFonts;\n\n  CreativeIslandCapacity({\n    required this.showAIRewrite,\n    required this.showUpscaleBy,\n    required this.showNegativeText,\n    required this.showSeed,\n    required this.showStyle,\n    required this.showImageCount,\n    required this.allowRatios,\n    required this.showPromptForImage2Image,\n    this.vendorModels = const [],\n    this.allowUpscaleBy = const [],\n    this.showImageStrength = false,\n    this.filters = const [],\n    this.artisticStyles = const [],\n    this.artisticTextStyles = const [],\n    this.artisticTextFonts = const [],\n  });\n\n  toJson() => {\n        'show_ai_rewrite': showAIRewrite,\n        'show_upscale_by': showUpscaleBy,\n        'show_negative_text': showNegativeText,\n        'show_style': showStyle,\n        'show_seed': showSeed,\n        'show_image_count': showImageCount,\n        'show_prompt_for_image2image': showPromptForImage2Image,\n        'allow_ratios': allowRatios,\n        'vendor_models': vendorModels.map((e) => e.toJson()).toList(),\n        'allow_upscale_by': allowUpscaleBy,\n        'show_image_strength': showImageStrength,\n        'filters': filters.map((e) => e.toJson()).toList(),\n        'artistic_styles': artisticStyles.map((e) => e.toJson()).toList(),\n        'artistic_text_styles':\n            artisticTextStyles.map((e) => e.toJson()).toList(),\n        'artistic_text_fonts':\n            artisticTextFonts.map((e) => e.toJson()).toList(),\n      };\n\n  static CreativeIslandCapacity fromJson(Map<String, dynamic> json) {\n    return CreativeIslandCapacity(\n      showAIRewrite: json['show_ai_rewrite'] ?? false,\n      showUpscaleBy: json['show_upscale_by'] ?? false,\n      showNegativeText: json['show_negative_text'] ?? false,\n      showStyle: json['show_style'] ?? false,\n      showSeed: json['show_seed'] ?? false,\n      showImageCount: json['show_image_count'] ?? false,\n      showPromptForImage2Image: json['show_prompt_for_image2image'] ?? false,\n      allowRatios: (json['allow_ratios'] as List<dynamic>)\n          .map((e) => e.toString())\n          .toList(),\n      vendorModels: ((json['vendor_models'] ?? []) as List<dynamic>)\n          .map((e) => CreativeIslandVendorModel.fromJson(e))\n          .toList(),\n      allowUpscaleBy: (json['allow_upscale_by'] as List<dynamic>)\n          .map((e) => e.toString())\n          .toList(),\n      showImageStrength: json['show_image_strength'] ?? false,\n      filters: ((json['filters'] ?? []) as List<dynamic>)\n          .map((e) => CreativeIslandImageFilter.fromJson(e))\n          .toList(),\n      artisticStyles: ((json['artistic_styles'] ?? []) as List<dynamic>)\n          .map((e) => CreativeIslandArtisticStyle.fromJson(e))\n          .toList(),\n      artisticTextStyles:\n          ((json['artistic_text_styles'] ?? []) as List<dynamic>)\n              .map((e) => CreativeIslandArtisticStyle.fromJson(e))\n              .toList(),\n      artisticTextFonts: ((json['artistic_text_fonts'] ?? []) as List<dynamic>)\n          .map((e) => CreativeIslandArtisticStyle.fromJson(e))\n          .toList(),\n    );\n  }\n}\n\nclass CreativeIslandArtisticStyle {\n  String id;\n  String name;\n  String? previewImage;\n\n  CreativeIslandArtisticStyle({\n    required this.id,\n    required this.name,\n    this.previewImage,\n  });\n\n  toJson() => {\n        'id': id,\n        'name': name,\n        'preview_image': previewImage,\n      };\n\n  static CreativeIslandArtisticStyle fromJson(Map<String, dynamic> json) {\n    return CreativeIslandArtisticStyle(\n      id: json['id'],\n      name: json['name'],\n      previewImage: json['preview_image'],\n    );\n  }\n}\n\nclass CreativeIslandImageFilter {\n  int id;\n  String name;\n  String? description;\n  String previewImage;\n\n  CreativeIslandImageFilter({\n    required this.id,\n    required this.name,\n    required this.previewImage,\n    this.description,\n  });\n\n  toJson() => {\n        'id': id,\n        'name': name,\n        'description': description,\n        'preview_image': previewImage,\n      };\n\n  static CreativeIslandImageFilter fromJson(Map<String, dynamic> json) {\n    return CreativeIslandImageFilter(\n      id: json['id'],\n      name: json['name'],\n      description: json['description'],\n      previewImage: json['preview_image'],\n    );\n  }\n}\n\nclass CreativeIslandVendorModel {\n  String id;\n  String? vendor;\n  String name;\n  bool upscale;\n  bool showStyle;\n  bool showImageStrength;\n  String? introUrl;\n\n  CreativeIslandVendorModel({\n    required this.id,\n    required this.name,\n    required this.upscale,\n    this.vendor,\n    this.showStyle = false,\n    this.showImageStrength = false,\n    this.introUrl,\n  });\n\n  toJson() => {\n        'id': id,\n        'name': name,\n        'vendor': vendor,\n        'upscale': upscale,\n        'show_style': showStyle,\n        'show_image_strength': showImageStrength,\n        'intro_url': introUrl,\n      };\n\n  static CreativeIslandVendorModel fromJson(Map<String, dynamic> json) {\n    return CreativeIslandVendorModel(\n      id: json['id'],\n      name: json['name'],\n      vendor: json['vendor'],\n      upscale: json['upscale'] ?? false,\n      showStyle: json['show_style'] ?? false,\n      showImageStrength: json['show_image_strength'] ?? false,\n      introUrl: json['intro_url'],\n    );\n  }\n}\n\nclass CreativeIslandCompletionResp {\n  String content;\n  String type;\n  List<String> resources;\n\n  CreativeIslandCompletionResp({\n    required this.content,\n    required this.type,\n    this.resources = const [],\n  });\n\n  toJson() => {\n        'content': content,\n        'type': type,\n        'resources': resources,\n      };\n\n  static CreativeIslandCompletionResp fromJson(Map<String, dynamic> json) {\n    return CreativeIslandCompletionResp(\n      content: json['content'],\n      type: json['type'],\n      resources: json['resources'] != null\n          ? (json['resources'] as List<dynamic>)\n              .map((e) => e.toString())\n              .toList()\n          : [],\n    );\n  }\n}\n\nclass CreativeIslandItems {\n  List<CreativeIslandItem> items;\n  List<String> categories;\n  String? backgroundImage;\n\n  CreativeIslandItems(this.items, this.categories, {this.backgroundImage});\n}\n\nclass CreativeIslandItemExtSize {\n  int width;\n  int height;\n  String aspectRatio;\n\n  CreativeIslandItemExtSize({\n    required this.width,\n    required this.height,\n    required this.aspectRatio,\n  });\n\n  toJson() => {\n        'width': width,\n        'height': height,\n        'aspect_ratio': aspectRatio,\n      };\n\n  static CreativeIslandItemExtSize fromJson(Map<String, dynamic> json) {\n    return CreativeIslandItemExtSize(\n      width: json['width'],\n      height: json['height'],\n      aspectRatio: json['aspect_ratio'],\n    );\n  }\n}\n\nclass CreativeIslandItemExtension {\n  bool? aiRewrite;\n  bool? showAIRewrite;\n  String? upscaleBy;\n  bool? showNegativeText;\n  bool? showAdvanceButton;\n  List<CreativeIslandItemExtSize>? allowSizes;\n\n  CreativeIslandItemExtension({\n    this.aiRewrite,\n    this.showAIRewrite,\n    this.upscaleBy,\n    this.showNegativeText,\n    this.allowSizes,\n    this.showAdvanceButton,\n  });\n\n  toJson() => {\n        'ai_rewrite': aiRewrite,\n        'show_ai_rewrite': showAIRewrite,\n        'upscale_by': upscaleBy,\n        'show_negative_text': showNegativeText,\n        'allow_sizes': allowSizes?.map((e) => e.toJson()).toList(),\n        'show_advance_button': showAdvanceButton,\n      };\n\n  static CreativeIslandItemExtension fromJson(Map<String, dynamic> json) {\n    return CreativeIslandItemExtension(\n      aiRewrite: json['ai_rewrite'],\n      showAIRewrite: json['show_ai_rewrite'],\n      upscaleBy: json['upscale_by'],\n      showNegativeText: json['show_negative_text'],\n      showAdvanceButton: json['show_advance_button'],\n      allowSizes: json['allow_sizes'] != null\n          ? (json['allow_sizes'] as List<dynamic>)\n              .map((e) => CreativeIslandItemExtSize.fromJson(e))\n              .toList()\n          : null,\n    );\n  }\n}\n\nclass CreativeIslandItem {\n  String id;\n  String title;\n  String? description;\n  bool supportStream;\n  List<String> categories;\n  String vendor;\n  String modelType;\n  String? bgImage;\n  String? bgEmbeddedImage;\n  String? label;\n  String? labelColor;\n  String? titleColor;\n  String? submitBtnText;\n  String? promptInputTitle;\n  int waitSeconds;\n  bool showImageStyleSelector;\n  bool noPrompt;\n\n  String? hint;\n  int? wordCount;\n  CreativeIslandItemExtension? extension;\n\n  /// 是否显示高级按钮\n  bool get showAdvanceButton {\n    if (extension != null && extension!.showAdvanceButton != null) {\n      return extension!.showAdvanceButton!;\n    }\n\n    return false;\n  }\n\n  /// 返回支持的图片尺寸\n  List<CreativeIslandItemExtSize> get imageAllowSizes {\n    if (extension != null && extension!.allowSizes != null) {\n      return extension!.allowSizes!;\n    }\n\n    return [];\n  }\n\n  /// 是否启用 AI 优化的默认值\n  bool get aiRewriteDefaultValue {\n    if (extension != null && extension!.aiRewrite != null) {\n      return extension!.aiRewrite!;\n    }\n\n    return false;\n  }\n\n  /// 是否显示反向提示语输入框\n  bool get isShowNegativeText {\n    if (extension != null && extension!.showNegativeText != null) {\n      return extension!.showNegativeText!;\n    }\n\n    return false;\n  }\n\n  /// 是否显示 AI 重写按钮\n  bool get isShowAIRewrite {\n    if (extension != null && extension!.showAIRewrite != null) {\n      return extension!.showAIRewrite!;\n    }\n\n    return false;\n  }\n\n  CreativeIslandItem({\n    required this.id,\n    required this.title,\n    required this.vendor,\n    required this.modelType,\n    this.categories = const [],\n    this.supportStream = false,\n    this.description,\n    this.bgImage,\n    this.bgEmbeddedImage,\n    this.label,\n    this.labelColor,\n    this.titleColor,\n    this.hint,\n    this.wordCount,\n    this.submitBtnText,\n    this.promptInputTitle,\n    this.waitSeconds = 30,\n    this.showImageStyleSelector = false,\n    this.noPrompt = false,\n    this.extension,\n  });\n\n  toJson() => {\n        'id': id,\n        'title': title,\n        'description': description,\n        'support_stream': supportStream,\n        'model_type': modelType, // 'text-generation' | 'image-generation'\n        'vendor': vendor,\n        'categories': categories,\n        'bg_image': bgImage,\n        'bg_embedded_image': bgEmbeddedImage,\n        'label': label,\n        'label_color': labelColor,\n        'title_color': titleColor,\n        'hint': hint,\n        'word_count': wordCount,\n        'submit_btn_text': submitBtnText,\n        'prompt_input_title': promptInputTitle,\n        'wait_seconds': waitSeconds,\n        'show_image_style_selector': showImageStyleSelector,\n        'no_prompt': noPrompt,\n        'extension': extension?.toJson(),\n      };\n\n  static fromJson(Map<String, dynamic> json) {\n    return CreativeIslandItem(\n      id: json['id'],\n      title: json['title'],\n      description: json['description'],\n      supportStream: json['support_stream'] ?? false,\n      modelType: json['model_type'] ?? 'text-generation',\n      vendor: json['vendor'],\n      categories: ((json['category'] ?? '') as String).split(',').toList(),\n      bgImage: json['bg_image'],\n      bgEmbeddedImage: json['bg_embedded_image'],\n      label: json['label'],\n      labelColor: json['label_color'],\n      titleColor: json['title_color'],\n      hint: json['hint'],\n      wordCount: json['word_count'] ?? 0,\n      submitBtnText: json['submit_btn_text'],\n      promptInputTitle: json['prompt_input_title'],\n      waitSeconds: json['wait_seconds'] ?? 30,\n      showImageStyleSelector: json['show_image_style_selector'] ?? false,\n      noPrompt: json['no_prompt'] ?? false,\n      extension: json['extension'] != null\n          ? CreativeIslandItemExtension.fromJson(json['extension'])\n          : null,\n    );\n  }\n}\n\nclass CreativeIslandCompletionAsyncResp {\n  String taskId;\n\n  CreativeIslandCompletionAsyncResp(this.taskId);\n\n  toJson() => {\n        'task_id': taskId,\n      };\n\n  static CreativeIslandCompletionAsyncResp fromJson(Map<String, dynamic> json) {\n    return CreativeIslandCompletionAsyncResp(json['task_id']);\n  }\n}\n\nclass CreativeItemInServer {\n  int id;\n  int? userId;\n  String islandId;\n  int? islandType;\n  String? vendor;\n  String? islandName;\n  String? islandTitle;\n  String? arguments;\n  String? prompt;\n  String? answer;\n  int? quotaUsed;\n  int? status;\n  int? shared;\n  String? filterName;\n  DateTime? createdAt;\n  DateTime? updatedAt;\n\n  bool showBetaFeature;\n\n  CreativeItemInServer({\n    required this.id,\n    required this.islandId,\n    required this.showBetaFeature,\n    this.userId,\n    this.islandType,\n    this.islandName,\n    this.islandTitle,\n    this.vendor,\n    this.arguments,\n    this.prompt,\n    this.answer,\n    this.quotaUsed,\n    this.status,\n    this.shared,\n    this.createdAt,\n    this.updatedAt,\n  });\n\n  CreativeItemArguments get creativeItemArguments {\n    if (arguments != null) {\n      return CreativeItemArguments.fromJson(jsonDecode(arguments!));\n    }\n\n    return CreativeItemArguments(\n      width: 512,\n      height: 512,\n      steps: 1,\n      imageCount: 1,\n    );\n  }\n\n  bool get isShared => shared == 1;\n\n  String get errorCode => NumberFormat('E000000000').format(id);\n\n  toJson() => {\n        'id': id,\n        'user_id': userId,\n        'island_id': islandId,\n        'island_type': islandType,\n        'island_name': islandName,\n        'island_title': islandTitle,\n        'vendor': vendor,\n        'arguments': arguments,\n        'prompt': prompt,\n        'answer': answer,\n        'quota_used': quotaUsed,\n        'status': status,\n        'shared': shared,\n        'show_beta_feature': showBetaFeature,\n        'created_at': createdAt?.toIso8601String(),\n        'updated_at': updatedAt?.toIso8601String(),\n      };\n\n  static CreativeItemInServer fromJson(Map<String, dynamic> json) {\n    return CreativeItemInServer(\n      id: json['id'],\n      userId: json['user_id'],\n      islandId: json['island_id'],\n      islandType: json['island_type'],\n      islandName: json['island_name'],\n      islandTitle: json['island_title'],\n      vendor: json['vendor'],\n      arguments: json['arguments'],\n      prompt: json['prompt'],\n      answer: json['answer'],\n      quotaUsed: json['quota_used'],\n      status: json['status'],\n      shared: json['shared'],\n      showBetaFeature: json['show_beta_feature'] ?? false,\n      createdAt: DateTime.parse(json['created_at']),\n      updatedAt: DateTime.parse(json['updated_at']),\n    );\n  }\n\n  bool get isSuccessful => status == 3;\n  bool get isFailed =>\n      status != null && (status == 4 || (status! <= 2 && answer != null));\n  bool get isProcessing => status != null && status! <= 2 && answer == null;\n\n  bool get isTextType => islandType == 1;\n  bool get isImageType =>\n      islandType != null && (islandType == 2 || islandType! >= 5);\n\n  bool get isVideoType => islandType != null && islandType == 3;\n\n  List<String> get images {\n    try {\n      if ((isImageType || isVideoType) &&\n          answer != null &&\n          answer != '' &&\n          isSuccessful) {\n        return (jsonDecode(answer!) as List<dynamic>).cast<String>();\n      }\n      return [];\n    } catch (e) {\n      return [];\n    }\n  }\n\n  String get firstImagePreview {\n    final imgs = images;\n    if (imgs.isEmpty) {\n      return \"\";\n    }\n\n    if (!imgs.first.startsWith('https://ssl.aicode.cc/')) {\n      return imgs.first;\n    }\n\n    final original = imgs.first.split('?').first;\n    if (original.toLowerCase().endsWith('.jpg') ||\n        original.toLowerCase().endsWith('.jpeg') ||\n        original.toLowerCase().endsWith('.png') ||\n        original.toLowerCase().endsWith('.webp') ||\n        original.toLowerCase().endsWith('.gif')) {\n      return \"$original-avatar\";\n    }\n\n    return imgs.first;\n  }\n\n  Map<String, dynamic> get params {\n    if (arguments != null) {\n      return jsonDecode(arguments!);\n    }\n\n    return {};\n  }\n\n  /// 上传的原图\n  String? get originalImage {\n    return params['image'];\n  }\n\n  String get markdownAnswer {\n    if (isProcessing) {\n      return '正在生成中...';\n    }\n\n    if (isFailed) {\n      return '生成失败\\n\\n```\\n$answer\\n```';\n    }\n\n    if (isImageType && answer != null && isSuccessful) {\n      return images.map((e) => '![image]($e)').join(\"\\n\\n\");\n    }\n\n    return answer ?? '';\n  }\n}\n\nclass CreativeItemArguments {\n  int? width;\n  int? height;\n  int? steps;\n  int? imageCount;\n  String? image;\n  int? wordCount;\n  String? negativePrompt;\n  String? realPrompt;\n  String? realNegativePrompt;\n  String? modelName;\n  int? seed;\n  String? filterName;\n\n  CreativeItemArguments({\n    this.width,\n    this.height,\n    this.steps,\n    this.imageCount,\n    this.image,\n    this.wordCount,\n    this.negativePrompt,\n    this.realPrompt,\n    this.realNegativePrompt,\n    this.modelName,\n    this.seed,\n    this.filterName,\n  });\n\n  toJson() => {\n        'width': width,\n        'height': height,\n        'steps': steps,\n        'image_count': imageCount,\n        'image': image,\n        'word_count': wordCount,\n        'negative_prompt': negativePrompt,\n        'real_prompt': realPrompt,\n        'real_negative_prompt': realNegativePrompt,\n        'model_name': modelName,\n        'seed': seed,\n        'filter_name': filterName,\n      };\n\n  static CreativeItemArguments fromJson(Map<String, dynamic> json) {\n    return CreativeItemArguments(\n      width: json['width'],\n      height: json['height'],\n      steps: json['steps'],\n      imageCount: json['image_count'] ?? 1,\n      image: json['image'],\n      wordCount: json['word_count'],\n      negativePrompt: json['negative_prompt'],\n      realPrompt: json['real_prompt'],\n      realNegativePrompt: json['real_negative_prompt'],\n      modelName: json['model_name'],\n      seed: json['seed'],\n      filterName: json['filter_name'],\n    );\n  }\n}\n\nclass CreativeIslandItemV2 {\n  String id;\n  String title;\n  String titleColor;\n  String previewImage;\n  String routeUri;\n  String tag;\n  String? note;\n  String size;\n\n  CreativeIslandItemV2({\n    required this.id,\n    required this.title,\n    required this.titleColor,\n    required this.previewImage,\n    required this.routeUri,\n    this.tag = '',\n    this.note,\n    this.size = 'large',\n  });\n\n  toJson() => {\n        'id': id,\n        'title': title,\n        'title_color': titleColor,\n        'preview_image': previewImage,\n        'route_uri': routeUri,\n        'tag': tag,\n        'note': note,\n        'size': size,\n      };\n\n  static CreativeIslandItemV2 fromJson(Map<String, dynamic> json) {\n    return CreativeIslandItemV2(\n      id: json['id'],\n      title: json['title'],\n      titleColor: json['title_color'],\n      previewImage: json['preview_image'],\n      routeUri: json['route_uri'],\n      tag: json['tag'] ?? '',\n      note: json['note'],\n      size: json['size'] ?? 'large',\n    );\n  }\n}\n"
  },
  {
    "path": "lib/repo/api/image_model.dart",
    "content": "import 'dart:convert';\n\nclass ImageModel {\n  int id;\n  String modelId;\n  String modelName;\n  String vendor;\n  String? realModel;\n  int? status;\n\n  ImageModel({\n    required this.id,\n    required this.modelId,\n    required this.modelName,\n    required this.vendor,\n    this.realModel,\n    this.status,\n  });\n\n  toJson() => {\n        'id': id,\n        'model_id': modelId,\n        'model_name': modelName,\n        'vendor': vendor,\n        'real_model': realModel,\n        'status': status,\n      };\n\n  static ImageModel fromJson(Map<String, dynamic> json) {\n    return ImageModel(\n      id: json['id'],\n      modelId: json['model_id'],\n      modelName: json['model_name'],\n      vendor: json['vendor'],\n      realModel: json['real_model'],\n      status: json['status'],\n    );\n  }\n}\n\nclass ImageModelFilter {\n  int id;\n  String name;\n  String modelId;\n  String? previewImage;\n  int? status;\n  String? meta;\n\n  ImageModelFilter({\n    required this.id,\n    required this.name,\n    required this.modelId,\n    this.previewImage,\n    this.status,\n    this.meta,\n  });\n\n  toJson() => {\n        'id': id,\n        'name': name,\n        'model_id': modelId,\n        'preview_image': previewImage,\n        'status': status,\n        'meta': meta,\n      };\n\n  static ImageModelFilter fromJson(Map<String, dynamic> json) {\n    return ImageModelFilter(\n      id: json['id'],\n      name: json['name'],\n      modelId: json['model_id'],\n      previewImage: json['preview_image'],\n      status: json['status'],\n      meta: jsonEncode(json['meta'] ?? {}),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/repo/api/info.dart",
    "content": "import 'package:askaide/repo/api/model.dart';\n\n/// 服务器支持的能力信息\nclass Capabilities {\n  /// 是否支持 Apple Pay\n  final bool applePayEnabled;\n\n  /// 是否支持 Stripe\n  final bool stripeEnabled;\n\n  /// 是否支持微信登录\n  final bool wechatSigninEnabled;\n\n  /// 是否支持微信支付\n  final bool wechatPayEnabled;\n\n  /// 是否支持其它\n  final bool otherPayEnabled;\n\n  /// 是否支持翻译\n  final bool translateEnabled;\n\n  /// 是否支持邮箱\n  final bool mailEnabled;\n\n  /// 是否支持 OpenAI\n  final bool openaiEnabled;\n\n  /// 首页显示的模型信息\n  final List<HomeModelV2> homeModels;\n\n  /// 是否显示首页模型描述\n  final bool showHomeModelDescription;\n\n  /// 首页路由\n  final String homeRoute;\n\n  /// 是否支持 Websocket\n  final bool supportWebsocket;\n\n  /// 是否支持 API Keys 功能\n  final bool supportAPIKeys;\n\n  /// 是否显示绘玩\n  final bool disableGallery;\n\n  /// 是否支持创作岛\n  final bool disableCreationIsland;\n\n  /// 是否禁用数字人\n  final bool disableDigitalHuman;\n\n  /// 是否禁用聊天\n  final bool disableChat;\n\n  /// 服务状态页\n  final String serviceStatusPage;\n\n  /// 是否支持语音转文字\n  final bool enableVoiceToText;\n\n  /// 是否支持文字转语音\n  final bool enableTextToVoice;\n\n  Capabilities({\n    required this.applePayEnabled,\n    required this.otherPayEnabled,\n    required this.translateEnabled,\n    required this.mailEnabled,\n    required this.openaiEnabled,\n    required this.homeModels,\n    this.homeRoute = '/',\n    this.showHomeModelDescription = true,\n    this.supportWebsocket = false,\n    this.supportAPIKeys = false,\n    this.disableGallery = false,\n    this.disableCreationIsland = false,\n    this.disableDigitalHuman = false,\n    this.disableChat = false,\n    this.serviceStatusPage = '',\n    this.wechatSigninEnabled = false,\n    this.stripeEnabled = false,\n    this.wechatPayEnabled = false,\n    this.enableVoiceToText = false,\n    this.enableTextToVoice = false,\n  });\n\n  factory Capabilities.fromJson(Map<String, dynamic> json) {\n    return Capabilities(\n      wechatSigninEnabled: json['wechat_signin_enabled'] ?? false,\n      applePayEnabled: json['apple_pay_enabled'] ?? false,\n      stripeEnabled: json['stripe_enabled'] ?? false,\n      otherPayEnabled: json['other_pay_enabled'] ?? false,\n      translateEnabled: json['translate_enabled'] ?? false,\n      mailEnabled: json['mail_enabled'] ?? false,\n      openaiEnabled: json['openai_enabled'] ?? false,\n      homeModels: ((json['home_models_v2'] ?? []) as List<dynamic>).map((e) => HomeModelV2.fromJson(e)).toList(),\n      // homeRoute: json['home_route'] ?? '/',\n      homeRoute: '/',\n      showHomeModelDescription: json['show_home_model_description'] ?? true,\n      supportWebsocket: json['support_websocket'] ?? false,\n      supportAPIKeys: json['support_api_keys'] ?? false,\n      disableGallery: json['disable_gallery'] ?? false,\n      disableCreationIsland: json['disable_creation_island'] ?? false,\n      disableDigitalHuman: json['disable_digital_human'] ?? false,\n      disableChat: json['disable_chat'] ?? false,\n      serviceStatusPage: json['service_status_page'] ?? '',\n      wechatPayEnabled: json['wechat_pay_enabled'] ?? false,\n      enableVoiceToText: json['enable_voice_to_text'] ?? false,\n      enableTextToVoice: json['enable_text_to_voice'] ?? false,\n    );\n  }\n\n  Map<String, dynamic> toJson() {\n    return {\n      'wechat_signin_enabled': wechatSigninEnabled,\n      'apple_pay_enabled': applePayEnabled,\n      'stripe_enabled': stripeEnabled,\n      'wechat_pay_enabled': wechatPayEnabled,\n      'other_pay_enabled': otherPayEnabled,\n      'translate_enabled': translateEnabled,\n      'mail_enabled': mailEnabled,\n      'openai_enabled': openaiEnabled,\n      'home_models': homeModels.map((e) => e.toJson()).toList(),\n      'home_route': homeRoute,\n      'show_home_model_description': showHomeModelDescription,\n      'support_websocket': supportWebsocket,\n      'support_api_keys': supportAPIKeys,\n      'disable_gallery': disableGallery,\n      'disable_creation_island': disableCreationIsland,\n      'disable_digital_human': disableDigitalHuman,\n      'disable_chat': disableChat,\n      'service_status_page': serviceStatusPage,\n      'enable_voice_to_text': enableVoiceToText,\n      'enable_text_to_voice': enableTextToVoice,\n    };\n  }\n}\n\n/// 首页显示的模型信息\nclass HomeModel {\n  /// 模型名称\n  final String name;\n\n  /// 模型 ID\n  final String modelId;\n\n  /// 模型描述\n  final String desc;\n\n  /// 模型代表色\n  final String color;\n\n  /// 是否是强大的模型\n  final bool powerful;\n\n  /// 是否支持视觉\n  final bool supportVision;\n\n  HomeModel({\n    required this.name,\n    required this.modelId,\n    required this.desc,\n    required this.color,\n    this.powerful = false,\n    this.supportVision = false,\n  });\n\n  factory HomeModel.fromJson(Map<String, dynamic> json) => HomeModel(\n        name: json[\"name\"],\n        modelId: json[\"model_id\"],\n        desc: json[\"desc\"] ?? '',\n        color: json[\"color\"] ?? 'FF67AC5C',\n        powerful: json['powerful'] ?? false,\n        supportVision: json['support_vision'] ?? false,\n      );\n\n  Map<String, dynamic> toJson() => {\n        \"name\": name,\n        \"model_id\": modelId,\n        \"desc\": desc,\n        \"color\": color,\n        \"powerful\": powerful,\n        \"support_vision\": supportVision,\n      };\n}\n"
  },
  {
    "path": "lib/repo/api/keys.dart",
    "content": "class UserAPIKey {\n  int id;\n  int? userId;\n  String name;\n  String token;\n  int status;\n  DateTime? validBefore;\n  DateTime? createdAt;\n\n  UserAPIKey({\n    required this.id,\n    this.userId,\n    required this.name,\n    required this.token,\n    required this.status,\n    this.validBefore,\n    this.createdAt,\n  });\n\n  factory UserAPIKey.fromJson(Map<String, dynamic> json) {\n    return UserAPIKey(\n      id: json['id'],\n      userId: json['user_id'],\n      name: json['name'],\n      token: json['token'],\n      status: json['status'],\n      validBefore: json['valid_before'] != null\n          ? DateTime.parse(json['valid_before'])\n          : null,\n      createdAt: json['created_at'] != null\n          ? DateTime.parse(json['created_at'])\n          : null,\n    );\n  }\n\n  Map<String, dynamic> toJson() {\n    return {\n      'id': id,\n      'user_id': userId,\n      'name': name,\n      'token': token,\n      'status': status,\n      'valid_before': validBefore?.toIso8601String(),\n      'created_at': createdAt?.toIso8601String(),\n    };\n  }\n}\n"
  },
  {
    "path": "lib/repo/api/model.dart",
    "content": "/// 自定义首页模型\nclass HomeModelV2 {\n  /// 类型：model/room_gallery/rooms/room_enterprise\n  String type;\n  String id;\n  String name;\n  String? avatarUrl;\n  String? modelId;\n  String? modelName;\n  bool supportVision;\n  bool supportReasoning;\n  bool supportSearch;\n\n  HomeModelV2({\n    required this.type,\n    required this.id,\n    required this.name,\n    required this.supportVision,\n    this.modelId,\n    this.modelName,\n    this.avatarUrl,\n    this.supportReasoning = false,\n    this.supportSearch = false,\n  });\n\n  String get uniqueKey {\n    return '$type|$id';\n  }\n\n  static HomeModelV2 fromJson(Map<String, dynamic> json) {\n    return HomeModelV2(\n      type: json['type'],\n      id: json['id'],\n      name: json['name'],\n      modelId: json['model_id'],\n      modelName: json['model_name'],\n      supportVision: json['support_vision'] ?? false,\n      supportReasoning: json['support_reasoning'] ?? false,\n      supportSearch: json['support_search'] ?? false,\n      avatarUrl: json['avatar_url'],\n    );\n  }\n\n  toJson() => {\n        'id': id,\n        'type': type,\n        'name': name,\n        'model_id': modelId,\n        'model_name': modelName,\n        'support_vision': supportVision,\n        'support_reasoning': supportReasoning,\n        'support_search': supportSearch,\n        'avatar_url': avatarUrl,\n      };\n}\n"
  },
  {
    "path": "lib/repo/api/notification.dart",
    "content": "class NotifyMessage {\n  int id;\n  int articleId;\n  String title;\n  String content;\n  String? type;\n  DateTime? createdAt;\n\n  NotifyMessage({\n    required this.id,\n    required this.title,\n    required this.content,\n    required this.articleId,\n    this.type,\n    this.createdAt,\n  });\n\n  factory NotifyMessage.fromJson(Map<String, dynamic> json) {\n    return NotifyMessage(\n      id: json['id'],\n      articleId: json['article_id'] ?? json['id'],\n      title: json['title'],\n      content: json['content'],\n      type: json['type'],\n      createdAt: json['created_at'] != null\n          ? DateTime.parse(json['created_at'])\n          : null,\n    );\n  }\n\n  Map<String, dynamic> toJson() {\n    return {\n      'id': id,\n      'article_id': articleId,\n      'title': title,\n      'content': content,\n      'type': type,\n      'created_at': createdAt?.toIso8601String(),\n    };\n  }\n}\n"
  },
  {
    "path": "lib/repo/api/page.dart",
    "content": "class PagedData<T> {\n  int page;\n  int perPage;\n  int? total;\n  int? lastPage;\n  List<T> data;\n\n  PagedData({\n    required this.page,\n    required this.perPage,\n    this.total,\n    this.lastPage,\n    required this.data,\n  });\n}\n\nclass OffsetPageData<T> {\n  int startId;\n  int lastId;\n  int perPage;\n  List<T> data;\n\n  OffsetPageData({\n    required this.startId,\n    required this.lastId,\n    required this.perPage,\n    required this.data,\n  });\n}\n"
  },
  {
    "path": "lib/repo/api/payment.dart",
    "content": "class OtherPayCreatedReponse {\n  String params;\n  String paymentId;\n  bool sandbox;\n\n  OtherPayCreatedReponse(this.params, this.paymentId, {this.sandbox = false});\n\n  toJson() => {\n        'params': params,\n        'payment_id': paymentId,\n        'sandbox': sandbox,\n      };\n\n  static OtherPayCreatedReponse fromJson(Map<String, dynamic> json) {\n    return OtherPayCreatedReponse(\n      json['params'],\n      json['payment_id'],\n      sandbox: json['sandbox'] ?? false,\n    );\n  }\n}\n\nclass PaymentProduct {\n  String id;\n  String name;\n  int quota;\n  int retailPrice;\n  int retailPriceUSD;\n  String expirePolicy;\n  String expirePolicyText;\n  bool recommend;\n  String? description;\n  List<String> methods;\n\n  PaymentProduct({\n    required this.id,\n    required this.name,\n    required this.quota,\n    required this.retailPrice,\n    required this.expirePolicy,\n    required this.expirePolicyText,\n    this.recommend = false,\n    this.description,\n    this.retailPriceUSD = 0,\n    this.methods = const [],\n  });\n\n  String get retailPriceText => '¥${(retailPrice / 100).toStringAsFixed(0)}';\n\n  String get retailPriceUSDText =>\n      '\\$${(retailPriceUSD / 100).toStringAsFixed(2)}';\n\n  /// 是否支持 Stripe 支付\n  bool get supportStripe => methods.contains('stripe') || methods.isEmpty;\n\n  toJson() => {\n        'id': id,\n        'name': name,\n        'quota': quota,\n        'retail_price': retailPrice,\n        'retail_price_usd': retailPriceUSD,\n        'expire_policy': expirePolicy,\n        'expire_policy_text': expirePolicyText,\n        'recommend': recommend,\n        'description': description,\n        'methods': methods,\n      };\n\n  static PaymentProduct fromJson(Map<String, dynamic> json) {\n    return PaymentProduct(\n      id: json['id'],\n      name: json['name'],\n      quota: json['quota'],\n      retailPrice: json['retail_price'],\n      retailPriceUSD: json['retail_price_usd'] ?? 0,\n      expirePolicy: json['expire_policy'],\n      expirePolicyText: json['expire_policy_text'],\n      recommend: json['recommend'] ?? false,\n      description: json['description'],\n      methods: ((json['methods'] ?? []) as List<dynamic>)\n          .map((e) => e.toString())\n          .toList(),\n    );\n  }\n}\n\nclass PaymentProducts {\n  final List<PaymentProduct> consume;\n  final String? note;\n  final bool preferUSD;\n\n  PaymentProducts(this.consume, {this.note, this.preferUSD = false});\n\n  toJson() => {\n        'consume': consume,\n        'note': note,\n        'prefer_usd': preferUSD,\n      };\n\n  static PaymentProducts fromJson(Map<String, dynamic> json) {\n    return PaymentProducts(\n      (json['consume'] as List<dynamic>)\n          .map((e) => PaymentProduct.fromJson(e))\n          .toList(),\n      note: json['note'],\n      preferUSD: json['prefer_usd'] ?? false,\n    );\n  }\n}\n\nclass PaymentStatus {\n  final bool success;\n  final String? note;\n\n  PaymentStatus(this.success, {this.note});\n\n  toJson() => {\n        'success': success,\n        'note': note,\n      };\n\n  static PaymentStatus fromJson(Map<String, dynamic> json) {\n    return PaymentStatus(\n      json['success'],\n      note: json['note'],\n    );\n  }\n}\n\nclass WechatPaymentCreatedResponse {\n  final String paymentId;\n  final bool sandbox;\n  final String? codeUrl;\n  final String? prepayId;\n  final String? package;\n  final String? partnerId;\n  final String? appId;\n  final String? noncestr;\n  final String? timestamp;\n  final String? sign;\n\n  WechatPaymentCreatedResponse(\n    this.paymentId,\n    this.sandbox, {\n    this.codeUrl,\n    this.prepayId,\n    this.package,\n    this.partnerId,\n    this.appId,\n    this.noncestr,\n    this.timestamp,\n    this.sign,\n  });\n\n  toJson() => {\n        'payment_id': paymentId,\n        'sandbox': sandbox,\n        'code_url': codeUrl,\n        'prepay_id': prepayId,\n        'package': package,\n        'partner_id': partnerId,\n        'app_id': appId,\n        'noncestr': noncestr,\n        'timestamp': timestamp,\n        'sign': sign,\n      };\n\n  static WechatPaymentCreatedResponse fromJson(Map<String, dynamic> json) {\n    return WechatPaymentCreatedResponse(\n      json['payment_id'],\n      json['sandbox'] ?? false,\n      codeUrl: json['code_url'],\n      prepayId: json['prepay_id'],\n      package: json['package'],\n      partnerId: json['partner_id'],\n      appId: json['app_id'],\n      noncestr: json['noncestr'],\n      timestamp: json['timestamp'],\n      sign: json['sign'],\n    );\n  }\n}\n\nclass StripePaymentCreatedResponse {\n  final String paymentId;\n  final String customer;\n  final String paymentIntent;\n  final String ephemeralKey;\n  final String publishableKey;\n  final String proxyUrl;\n\n  StripePaymentCreatedResponse(\n    this.paymentId,\n    this.customer,\n    this.paymentIntent,\n    this.ephemeralKey,\n    this.publishableKey,\n    this.proxyUrl,\n  );\n\n  toJson() => {\n        'payment_id': paymentId,\n        'customer': customer,\n        'payment_intent': paymentIntent,\n        'ephemeral_key': ephemeralKey,\n        'publishable_key': publishableKey,\n        'proxy_url': proxyUrl,\n      };\n\n  static StripePaymentCreatedResponse fromJson(Map<String, dynamic> json) {\n    return StripePaymentCreatedResponse(\n      json['payment_id'],\n      json['customer'],\n      json['payment_intent'],\n      json['ephemeral_key'],\n      json['publishable_key'],\n      json['proxy_url'] ?? '',\n    );\n  }\n}\n"
  },
  {
    "path": "lib/repo/api/quota.dart",
    "content": "import 'package:intl/intl.dart';\n\nclass QuotaEvaluated {\n  int cost;\n  bool enough;\n  int? waitDuration;\n\n  QuotaEvaluated({required this.cost, this.enough = true, this.waitDuration});\n\n  toJson() => {\n        'cost': cost,\n        'enough': enough,\n        'wait_duration': waitDuration,\n      };\n\n  static QuotaEvaluated fromJson(Map<String, dynamic> json) {\n    return QuotaEvaluated(\n      cost: json['cost'],\n      enough: json['enough'] ?? true,\n      waitDuration: json['wait_duration'],\n    );\n  }\n}\n\nclass QuotaResp {\n  int total;\n  List<QuotaDetail> details;\n\n  QuotaResp(this.total, this.details);\n\n  toJson() => {\n        'total': total,\n        'details': details.map((e) => e.toJson()).toList(),\n      };\n\n  static QuotaResp fromJson(Map<String, dynamic> json) {\n    return QuotaResp(\n      json['total'],\n      (json['details'] as List).map((e) => QuotaDetail.fromJson(e)).toList(),\n    );\n  }\n}\n\nclass QuotaDetail {\n  int id;\n  int userId;\n  int quota;\n  int rest;\n  String? note;\n  DateTime periodStartAt;\n  DateTime periodEndAt;\n  bool expired;\n  DateTime createdAt;\n\n  QuotaDetail({\n    required this.id,\n    required this.userId,\n    required this.quota,\n    required this.rest,\n    required this.periodStartAt,\n    required this.periodEndAt,\n    required this.expired,\n    required this.createdAt,\n    this.note,\n  });\n\n  toJson() => {\n        'id': id,\n        'user_id': userId,\n        'quota': quota,\n        'rest': rest,\n        'period_start_at': periodStartAt.toIso8601String(),\n        'period_end_at': periodEndAt.toIso8601String(),\n        'expired': expired,\n        'created_at': createdAt.toIso8601String(),\n        'note': note,\n      };\n\n  static QuotaDetail fromJson(Map<String, dynamic> json) {\n    return QuotaDetail(\n      id: json['id'],\n      userId: json['user_id'],\n      quota: json['quota'],\n      rest: json['rest'],\n      note: json['note'],\n      periodStartAt: DateTime.parse(json['period_start_at']),\n      periodEndAt: DateTime.parse(json['period_end_at']),\n      expired: json['expired'],\n      createdAt: DateTime.parse(json['created_at'] ?? json['period_start_at']),\n    );\n  }\n}\n\nclass Quota {\n  int quota;\n  int rest;\n  int used;\n\n  Quota(this.quota, this.used, this.rest);\n\n  double quotaPercent() {\n    return (used * 1.0) / quota;\n  }\n\n  int quotaRemain() {\n    return quota - used;\n  }\n\n  String quotaRemainString() {\n    return (quota - used).toString();\n  }\n\n  String quotaString() {\n    return NumberFormat('0,000').format(quota);\n  }\n\n  String usedString() {\n    return NumberFormat('0,000').format(used);\n  }\n\n  toJson() => {\n        'quota': quota,\n        'used': used,\n        'rest': rest,\n      };\n\n  static fromJson(Map<String, dynamic> json) {\n    return Quota(\n      json['quota'],\n      json['used'],\n      json['rest'],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/repo/api/room_gallery.dart",
    "content": "class RoomGalleryResponse {\n  List<RoomGallery> galleries;\n  List<String> tags;\n\n  RoomGalleryResponse({\n    required this.galleries,\n    required this.tags,\n  });\n\n  toJson() => {\n        'galleries': galleries.map((e) => e.toJson()).toList(),\n        'tags': tags,\n      };\n\n  static RoomGalleryResponse fromJson(Map<String, dynamic> json) {\n    return RoomGalleryResponse(\n      galleries: ((json['data'] ?? []) as List<dynamic>)\n          .map((e) => RoomGallery.fromJson(e))\n          .toList(),\n      tags: ((json['tags'] ?? []) as List<dynamic>)\n          .map((e) => e.toString())\n          .toList(),\n    );\n  }\n}\n\nclass RoomGallery {\n  int id;\n  String name;\n  String avatarUrl;\n  String description;\n  List<String> tags;\n\n  RoomGallery({\n    required this.id,\n    required this.name,\n    required this.avatarUrl,\n    required this.description,\n    required this.tags,\n  });\n\n  toJson() => {\n        'id': id,\n        'name': name,\n        'avatar_url': avatarUrl,\n        'description': description,\n        'tags': tags,\n      };\n\n  static RoomGallery fromJson(Map<String, dynamic> json) {\n    return RoomGallery(\n      id: json['id'],\n      name: json['name'],\n      avatarUrl: json['avatar_url'] ?? '',\n      description: json['description'] ?? '',\n      tags: ((json['tags'] ?? []) as List<dynamic>)\n          .map((e) => e.toString())\n          .toList(),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/repo/api/user.dart",
    "content": "import 'package:askaide/repo/api/quota.dart';\n\nclass User {\n  int id;\n  String? name;\n  String? email;\n  String? phone;\n  String? inviteCode;\n  String? avatar;\n  int? invitedBy;\n  String? unionId;\n\n  User(\n    this.id,\n    this.name,\n    this.email,\n    this.phone, {\n    this.inviteCode,\n    this.avatar,\n    this.invitedBy,\n    this.unionId,\n  });\n\n  /// 是否需要绑定手机号码\n  bool get needBindPhone => phone == null || phone!.isEmpty;\n\n  String displayName() {\n    if (name != null && name!.isNotEmpty) {\n      return name!;\n    }\n\n    if (email != null && email!.isNotEmpty) {\n      return email!;\n    }\n\n    if (phone != null && phone!.isNotEmpty) {\n      return phone!;\n    }\n\n    return '-';\n  }\n\n  toJson() => {\n        'id': id,\n        'name': name,\n        'email': email,\n        'phone': phone,\n        'invite_code': inviteCode,\n        'avatar': avatar,\n        'invited_by': invitedBy,\n        'union_id': unionId,\n      };\n\n  static fromJson(Map<String, dynamic> json) {\n    return User(\n      json['id'],\n      json['name'],\n      json['email'],\n      json['phone'],\n      inviteCode: json['invite_code'],\n      avatar: json['avatar'],\n      invitedBy: json['invited_by'],\n      unionId: json['union_id'],\n    );\n  }\n}\n\nclass UserInfo {\n  User user;\n  Quota quota;\n  UserControl control;\n\n  bool get showInviteMessage =>\n      control.enableInvite && user.inviteCode != null && user.inviteCode != '';\n\n  UserInfo(this.user, this.quota, this.control);\n\n  toJson() => {\n        'user': user.toJson(),\n        'quota': quota.toJson(),\n        'control': control.toJson(),\n      };\n\n  static fromJson(Map<String, dynamic> json) {\n    return UserInfo(\n      User.fromJson(json['user']),\n      Quota.fromJson(json['quota']),\n      UserControl.fromJson(json['control']),\n    );\n  }\n\n  static UserInfo empty() {\n    return UserInfo(\n      User(0, \"-\", \"-\", \"-\"),\n      Quota(0, 0, 0),\n      UserControl(enableInvite: true),\n    );\n  }\n}\n\nclass UserControl {\n  bool isSetPassword;\n  bool enableInvite;\n  String? inviteMessage;\n  String? userCardBg;\n  String? inviteCardBg;\n  String? inviteCardColor;\n  String? inviteCardSlogan;\n  bool withLab;\n\n  UserControl({\n    required this.enableInvite,\n    this.inviteMessage,\n    this.userCardBg,\n    this.inviteCardBg,\n    this.inviteCardColor,\n    this.inviteCardSlogan,\n    this.isSetPassword = false,\n    this.withLab = false,\n  });\n\n  toJson() => {\n        'enable_invite': enableInvite,\n        'invite_message': inviteMessage,\n        'user_card_bg': userCardBg,\n        'invite_card_bg': inviteCardBg,\n        'invite_card_color': inviteCardColor,\n        'invite_card_slogan': inviteCardSlogan,\n        'is_set_password': isSetPassword,\n        'with_lab': withLab,\n      };\n\n  static UserControl fromJson(Map<String, dynamic> json) {\n    return UserControl(\n      enableInvite: json['enable_invite'] ?? true,\n      inviteMessage: json['invite_message'],\n      userCardBg: json['user_card_bg'],\n      inviteCardBg: json['invite_card_bg'],\n      inviteCardColor: json['invite_card_color'],\n      inviteCardSlogan: json['invite_card_slogan'],\n      isSetPassword: json['is_set_pwd'] ?? false,\n      withLab: json['with_lab'] ?? false,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/repo/api_server.dart",
    "content": "import 'dart:convert';\n\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/env.dart';\nimport 'package:askaide/helper/error.dart';\nimport 'package:askaide/helper/event.dart';\nimport 'package:askaide/helper/http.dart';\nimport 'package:askaide/helper/logger.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/page/component/global_alert.dart';\nimport 'package:askaide/repo/api/admin/channels.dart';\nimport 'package:askaide/repo/api/admin/models.dart';\nimport 'package:askaide/repo/api/admin/payment.dart';\nimport 'package:askaide/repo/api/admin/users.dart';\nimport 'package:askaide/repo/api/article.dart';\nimport 'package:askaide/repo/api/creative.dart';\nimport 'package:askaide/repo/api/image_model.dart';\nimport 'package:askaide/repo/api/info.dart';\nimport 'package:askaide/repo/api/keys.dart';\nimport 'package:askaide/repo/api/model.dart';\nimport 'package:askaide/repo/api/notification.dart';\nimport 'package:askaide/repo/api/page.dart';\nimport 'package:askaide/repo/api/payment.dart';\nimport 'package:askaide/repo/api/quota.dart';\nimport 'package:askaide/repo/api/room_gallery.dart';\nimport 'package:askaide/repo/api/user.dart';\nimport 'package:askaide/repo/model/group.dart';\nimport 'package:askaide/repo/model/misc.dart';\nimport 'package:askaide/repo/settings_repo.dart';\nimport 'package:dio/dio.dart';\nimport 'package:flutter/material.dart';\n\nclass APIServer {\n  /// 单例\n  static final APIServer _instance = APIServer._internal();\n  APIServer._internal();\n\n  factory APIServer() {\n    return _instance;\n  }\n\n  GlobalAlertEvent _globalAlertEvent = GlobalAlertEvent(id: '', type: 'info', pages: [], message: '');\n\n  GlobalAlertEvent get globalAlertEvent => _globalAlertEvent;\n\n  late String url;\n  late String apiToken;\n  late String language;\n\n  init(SettingRepository setting) {\n    apiToken = setting.stringDefault(settingAPIServerToken, '');\n    language = setting.stringDefault(settingLanguage, 'zh');\n    url = setting.stringDefault(settingServerURL, apiServerURL);\n\n    Logger.instance.d('API Server URL: $url');\n\n    setting.listen((settings, key, value) {\n      if (key == settingAPIServerToken) {\n        apiToken = settings.getDefault(settingAPIServerToken, '');\n      }\n\n      if (key == settingLanguage) {\n        language = settings.getDefault(settingLanguage, 'zh');\n      }\n\n      if (key == settingServerURL) {\n        url = settings.getDefault(settingServerURL, apiServerURL);\n        Logger.instance.d('API Server URL Changed: $url');\n      }\n    });\n  }\n\n  final List<DioExceptionType> _retryableErrors = [\n    DioExceptionType.connectionTimeout,\n    DioExceptionType.sendTimeout,\n    DioExceptionType.receiveTimeout,\n  ];\n\n  /// 异常处理\n  Object _exceptionHandle(Object e, Object? stackTrace) {\n    Logger.instance.e(e, stackTrace: stackTrace as StackTrace?);\n\n    if (e is DioException) {\n      if (e.response != null) {\n        final resp = e.response!;\n\n        if (resp.data is Map && resp.data['error'] != null && resp.statusCode != 402 && resp.statusCode != 401) {\n          return resp.data['error'] ?? e.toString();\n        }\n\n        if (resp.statusCode != null) {\n          final ret = resolveHTTPStatusCode(resp.statusCode!);\n          if (ret != null) {\n            return ret;\n          }\n        }\n\n        return resp.statusMessage ?? e.toString();\n      }\n\n      if (_retryableErrors.contains(e.type)) {\n        return '请求超时，请重试';\n      }\n    }\n\n    return e.toString();\n  }\n\n  Options _buildRequestOptions({int? requestTimeout = 10000}) {\n    return Options(\n      headers: _buildAuthHeaders(),\n      receiveDataWhenStatusError: true,\n      sendTimeout: requestTimeout != null ? Duration(milliseconds: requestTimeout) : null,\n      receiveTimeout: requestTimeout != null ? Duration(milliseconds: requestTimeout) : null,\n    );\n  }\n\n  Map<String, dynamic> _buildAuthHeaders() {\n    final headers = <String, dynamic>{\n      'X-CLIENT-VERSION': clientVersion,\n      'X-PLATFORM': PlatformTool.operatingSystem(),\n      'X-PLATFORM-VERSION': PlatformTool.operatingSystemVersion(),\n      'X-LANGUAGE': language,\n    };\n\n    if (apiToken == '') {\n      return headers;\n    }\n\n    headers['Authorization'] = 'Bearer $apiToken';\n\n    return headers;\n  }\n\n  /// 获取用户 ID，如果未登录则返回 null\n  int? localUserID() {\n    if (apiToken == '') {\n      return null;\n    }\n\n    // 从 Jwt Token 中获取用户 ID\n    final parts = apiToken.split('.');\n    if (parts.length != 3) {\n      return null;\n    }\n\n    final payload = parts[1];\n    final normalized = base64.normalize(payload);\n    final resp = utf8.decode(base64.decode(normalized));\n    final data = jsonDecode(resp);\n    return data['id'];\n  }\n\n  Future<T> sendGetRequest<T>(\n    String endpoint,\n    T Function(dynamic) parser, {\n    Map<String, dynamic>? queryParameters,\n    int? requestTimeout = 10000,\n  }) async {\n    return request(\n      HttpClient.get(\n        '$url$endpoint',\n        queryParameters: queryParameters,\n        options: _buildRequestOptions(requestTimeout: requestTimeout),\n      ),\n      parser,\n    );\n  }\n\n  Future<T> sendCachedGetRequest<T>(\n    String endpoint,\n    T Function(dynamic) parser, {\n    String? subKey,\n    Duration duration = const Duration(days: 1),\n    Map<String, dynamic>? queryParameters,\n    bool forceRefresh = false,\n  }) async {\n    return request(\n      HttpClient.getCached(\n        '$url$endpoint',\n        queryParameters: queryParameters,\n        subKey: subKey,\n        duration: duration,\n        forceRefresh: forceRefresh,\n        options: _buildRequestOptions(),\n      ),\n      parser,\n    );\n  }\n\n  Future<T> sendPostRequest<T>(\n    String endpoint,\n    T Function(dynamic) parser, {\n    Map<String, dynamic>? queryParameters,\n    Map<String, dynamic>? formData,\n    VoidCallback? finallyCallback,\n  }) async {\n    return request(\n      HttpClient.post(\n        '$url$endpoint',\n        queryParameters: queryParameters,\n        formData: formData,\n        options: _buildRequestOptions(),\n      ),\n      parser,\n      finallyCallback: finallyCallback,\n    );\n  }\n\n  Future<T> sendPostJSONRequest<T>(\n    String endpoint,\n    T Function(dynamic) parser, {\n    Map<String, dynamic>? queryParameters,\n    Map<String, dynamic>? data,\n    VoidCallback? finallyCallback,\n  }) async {\n    return request(\n      HttpClient.postJSON(\n        '$url$endpoint',\n        queryParameters: queryParameters,\n        data: data,\n        options: _buildRequestOptions(),\n      ),\n      parser,\n      finallyCallback: finallyCallback,\n    );\n  }\n\n  Future<T> sendPutRequest<T>(\n    String endpoint,\n    T Function(dynamic) parser, {\n    String? subKey,\n    Duration duration = const Duration(days: 1),\n    Map<String, dynamic>? queryParameters,\n    Map<String, dynamic>? formData,\n    bool forceRefresh = false,\n    VoidCallback? finallyCallback,\n  }) async {\n    return request(\n      HttpClient.put(\n        '$url$endpoint',\n        queryParameters: queryParameters,\n        formData: formData,\n        options: _buildRequestOptions(),\n      ),\n      parser,\n      finallyCallback: finallyCallback,\n    );\n  }\n\n  Future<T> sendPutJSONRequest<T>(\n    String endpoint,\n    T Function(dynamic) parser, {\n    String? subKey,\n    Duration duration = const Duration(days: 1),\n    Map<String, dynamic>? queryParameters,\n    Map<String, dynamic>? data,\n    bool forceRefresh = false,\n    VoidCallback? finallyCallback,\n  }) async {\n    return request(\n      HttpClient.putJSON(\n        '$url$endpoint',\n        queryParameters: queryParameters,\n        data: data,\n        options: _buildRequestOptions(),\n      ),\n      parser,\n      finallyCallback: finallyCallback,\n    );\n  }\n\n  Future<T> sendDeleteRequest<T>(\n    String endpoint,\n    T Function(dynamic) parser, {\n    String? subKey,\n    Duration duration = const Duration(days: 1),\n    Map<String, dynamic>? queryParameters,\n    Map<String, dynamic>? formData,\n    bool forceRefresh = false,\n    VoidCallback? finallyCallback,\n  }) async {\n    return request(\n      HttpClient.delete(\n        '$url$endpoint',\n        queryParameters: queryParameters,\n        formData: formData,\n        options: _buildRequestOptions(),\n      ),\n      parser,\n      finallyCallback: finallyCallback,\n    );\n  }\n\n  Future<T> request<T>(\n    Future<Response<dynamic>> respFuture,\n    T Function(dynamic) parser, {\n    VoidCallback? finallyCallback,\n  }) async {\n    try {\n      final resp = await respFuture;\n      if (resp.statusCode != 200 && resp.statusCode != 304) {\n        return Future.error(resp.data['error']);\n      }\n\n      try {\n        var msg = resp.headers.value('aidea-global-alert-msg');\n        if (msg != null) {\n          msg = utf8.decode(base64Decode(msg));\n        }\n\n        // Logger.instance.d(\"API Response: ${resp.data}\");\n        final globalAlertEvent = GlobalAlertEvent(\n          id: resp.headers.value('aidea-global-alert-id') ?? '',\n          type: resp.headers.value('aidea-global-alert-type') ?? 'info',\n          pages: (resp.headers.value('aidea-global-alert-pages') ?? '').split(',').where((e) => e != '').toList(),\n          message: msg,\n        );\n\n        if (globalAlertEvent.id != '' && globalAlertEvent.id != _globalAlertEvent.id) {\n          _globalAlertEvent = globalAlertEvent;\n          GlobalEvent().emit('global-alert', _globalAlertEvent);\n        }\n      } catch (e) {\n        Logger.instance.e(e);\n      }\n\n      return parser(resp);\n    } catch (e, stackTrace) {\n      return Future.error(_exceptionHandle(e, stackTrace));\n    } finally {\n      finallyCallback?.call();\n    }\n  }\n\n  String? _cacheSubKey() {\n    final localUserId = localUserID();\n    if (localUserId == null) {\n      return null;\n    }\n\n    return 'local-uid=$localUserId';\n  }\n\n  /// 用户配额详情\n  Future<QuotaResp?> quotaDetails() async {\n    return sendGetRequest(\n      '/v1/users/quota',\n      (resp) => QuotaResp.fromJson(resp.data),\n    );\n  }\n\n  /// 用户信息\n  Future<UserInfo?> userInfo({bool cache = true}) async {\n    return sendCachedGetRequest(\n      '/v1/users/current',\n      (resp) => UserInfo.fromJson(resp.data),\n      duration: const Duration(minutes: 1),\n      subKey: _cacheSubKey(),\n      forceRefresh: !cache,\n    );\n  }\n\n  /// 检查手机号是否存在\n  Future<UserExistenceResp> checkPhoneExists(String username) async {\n    return sendPostRequest(\n      '/v1/auth/2in1/check',\n      (resp) => UserExistenceResp.fromJson(resp.data),\n      formData: Map<String, dynamic>.from({\n        'username': username,\n      }),\n    );\n  }\n\n  /// 手机登录或者注册账号\n  Future<SignInResp> signInOrUp({\n    required String username,\n    required String verifyCodeId,\n    required String verifyCode,\n    String? inviteCode,\n    String? wechatBindToken,\n  }) async {\n    return sendPostRequest(\n      '/v1/auth/2in1/sign-inup',\n      (resp) => SignInResp.fromJson(resp.data),\n      formData: Map<String, dynamic>.from({\n        'username': username,\n        'verify_code_id': verifyCodeId,\n        'verify_code': verifyCode,\n        'invite_code': inviteCode,\n        'wechat_bind_token': wechatBindToken,\n      }),\n    );\n  }\n\n  /// 使用密码登录\n  Future<SignInResp> signInWithPassword(\n    String username,\n    String password, {\n    String? wechatBindToken,\n  }) async {\n    return sendPostRequest(\n      '/v1/auth/sign-in',\n      (resp) => SignInResp.fromJson(resp.data),\n      formData: Map<String, dynamic>.from({\n        'username': username,\n        'password': password,\n        'wechat_bind_token': wechatBindToken,\n      }),\n    );\n  }\n\n  /// 使用 Apple 账号登录\n  Future<SignInResp> signInWithApple({\n    required String userIdentifier,\n    String? givenName,\n    String? familyName,\n    String? email,\n    String? authorizationCode,\n    String? identityToken,\n    String? wechatBindToken,\n  }) async {\n    return sendPostRequest(\n      '/v1/auth/sign-in-apple/',\n      (resp) => SignInResp.fromJson(resp.data),\n      formData: Map<String, dynamic>.from({\n        'user_identifier': userIdentifier,\n        'given_name': givenName,\n        'family_name': familyName,\n        'email': email,\n        'authorization_code': authorizationCode,\n        'identity_token': identityToken,\n        'is_ios': PlatformTool.isIOS() || PlatformTool.isMacOS(),\n        'wechat_bind_token': wechatBindToken,\n      }),\n    );\n  }\n\n  /// 尝试使用 微信账号登录\n  Future<TrySignInResp> trySignInWithWechat({\n    required String code,\n  }) async {\n    return sendPostRequest(\n      '/v1/auth/sign-in-wechat/try',\n      (resp) => TrySignInResp.fromJson(resp.data),\n      formData: Map<String, dynamic>.from({\n        'code': code,\n      }),\n    );\n  }\n\n  /// 使用 微信账号登录\n  Future<SignInResp> signInWithWechat({\n    required String token,\n  }) async {\n    return sendPostRequest(\n      '/v1/auth/sign-in-wechat/',\n      (resp) => SignInResp.fromJson(resp.data),\n      formData: Map<String, dynamic>.from({\n        'token': token,\n      }),\n    );\n  }\n\n  /// 绑定微信账号\n  Future<void> bindWechat({required String code}) async {\n    return sendPostRequest(\n      '/v1/auth/bind-wechat/',\n      (resp) => {},\n      formData: Map<String, dynamic>.from({\n        'code': code,\n      }),\n    );\n  }\n\n  /// 获取代理服务器列表\n  Future<List<String>> proxyServers(String service) async {\n    return sendCachedGetRequest(\n      '/v1/proxy/servers',\n      (resp) => (resp['servers'][service] as List).map((e) => e.toString()).toList(),\n      subKey: _cacheSubKey(),\n    );\n  }\n\n  /// 获取模型列表\n  Future<List<Model>> models({bool cache = true}) async {\n    return sendCachedGetRequest(\n      '/v2/models',\n      (resp) {\n        var models = <Model>[];\n        for (var model in resp.data) {\n          models.add(Model.fromJson(model));\n        }\n\n        return models;\n      },\n      subKey: _cacheSubKey(),\n      forceRefresh: !cache,\n    );\n  }\n\n  /// 获取系统级提示语列表\n  Future<List<Prompt>> prompts() async {\n    return sendCachedGetRequest(\n      '/v1/prompts',\n      (resp) {\n        var prompts = <Prompt>[];\n        for (var prompt in resp.data) {\n          prompts.add(Prompt(prompt['title'], prompt['content']));\n        }\n\n        return prompts;\n      },\n      subKey: _cacheSubKey(),\n    );\n  }\n\n  /// 获取提示语示例\n  Future<List<ChatExample>> examples() async {\n    return sendCachedGetRequest(\n      '/v1/examples',\n      (resp) {\n        var examples = <ChatExample>[];\n        for (var example in resp.data) {\n          examples.add(ChatExample(\n            example['title'],\n            content: example['content'],\n            models: example['models'],\n          ));\n        }\n\n        return examples;\n      },\n      subKey: _cacheSubKey(),\n    );\n  }\n\n  ///   获取头像列表\n  Future<List<String>> avatars() async {\n    return sendCachedGetRequest(\n      '/v1/images/avatar',\n      (resp) {\n        return (resp.data['avatars'] as List<dynamic>).map((e) => e.toString()).toList();\n      },\n    );\n  }\n\n  ///  获取背景图列表\n  Future<List<BackgroundImage>> backgrounds() async {\n    return sendCachedGetRequest(\n      '/v1/images/background',\n      (resp) {\n        var images = <BackgroundImage>[];\n        for (var img in resp.data['preset']) {\n          images.add(BackgroundImage.fromJson(img));\n        }\n\n        return images;\n      },\n    );\n  }\n\n  Future<TranslateText> translate(\n    String text, {\n    String from = 'auto',\n  }) async {\n    return sendPostRequest(\n      '/v1/translate/',\n      (resp) => TranslateText.fromJson(resp.data),\n      formData: Map<String, dynamic>.from({\n        'text': text,\n        'from': from,\n      }),\n    );\n  }\n\n  /// 上传初始化\n  Future<UploadInitResponse> uploadInit(\n    String name,\n    int filesize, {\n    String? usage,\n  }) async {\n    return sendPostRequest(\n      '/v1/storage/upload-init',\n      (resp) => UploadInitResponse.fromJson(resp.data),\n      formData: Map<String, dynamic>.from({\n        'filesize': filesize,\n        'name': name,\n        'usage': usage,\n      }),\n    );\n  }\n\n  /// 获取模型支持的提示语示例\n  Future<List<ChatExample>> exampleByTag(String tag) async {\n    return sendCachedGetRequest(\n      '/v1/examples/tags/$tag',\n      (resp) {\n        var examples = <ChatExample>[];\n        for (var example in resp.data) {\n          examples.add(ChatExample(\n            example['title'],\n            content: example['content'],\n            models: ((example['models'] ?? []) as List<dynamic>).map((e) => e.toString()).toList(),\n          ));\n        }\n        return examples;\n      },\n      subKey: _cacheSubKey(),\n    );\n  }\n\n  /// 获取模型支持的反向提示语示例\n  Future<List<ChatExample>> negativePromptExamples(String tag) async {\n    return sendCachedGetRequest(\n      '/v1/examples/negative-prompts/$tag',\n      (resp) {\n        var examples = <ChatExample>[];\n        for (var example in resp.data['data']) {\n          examples.add(ChatExample(\n            example['title'],\n            content: example['content'],\n          ));\n        }\n        return examples;\n      },\n      subKey: _cacheSubKey(),\n    );\n  }\n\n  /// 获取模型支持的提示语示例\n  Future<List<ChatExample>> example(String model) async {\n    return sendCachedGetRequest(\n      '/v1/examples/${Uri.encodeComponent(model)}',\n      (resp) {\n        var examples = <ChatExample>[];\n        for (var example in resp.data) {\n          examples.add(ChatExample(\n            example['title'],\n            content: example['content'],\n            models: ((example['models'] ?? []) as List<dynamic>).map((e) => e.toString()).toList(),\n          ));\n        }\n        return examples;\n      },\n      subKey: _cacheSubKey(),\n    );\n  }\n\n  /// 模型风格列表\n  Future<List<ModelStyle>> modelStyles(String category) async {\n    return sendCachedGetRequest(\n      '/v1/models/$category/styles',\n      (resp) {\n        var items = <ModelStyle>[];\n        for (var item in resp.data) {\n          items.add(ModelStyle.fromJson(item));\n        }\n        return items;\n      },\n      subKey: _cacheSubKey(),\n    );\n  }\n\n  /// 创意岛项目列表\n  Future<CreativeIslandItems> creativeIslandItems({\n    required String mode,\n    bool cache = true,\n  }) async {\n    return sendCachedGetRequest(\n      '/v1/creative-island/items',\n      (resp) {\n        var items = <CreativeIslandItem>[];\n        for (var item in resp.data['items']) {\n          items.add(CreativeIslandItem.fromJson(item));\n        }\n        final categories = (resp.data['categories'] as List<dynamic>).map((e) => e.toString()).toList();\n        return CreativeIslandItems(\n          items,\n          categories,\n          backgroundImage: resp.data['background_image'],\n        );\n      },\n      queryParameters: <String, dynamic>{\"mode\": mode},\n      duration: const Duration(minutes: 60),\n      forceRefresh: !cache,\n    );\n  }\n\n  /// 创意岛项目\n  Future<CreativeIslandItem> creativeIslandItem(String id) async {\n    return sendCachedGetRequest(\n      '/v1/creative-island/items/$id',\n      (resp) => CreativeIslandItem.fromJson(resp.data),\n      subKey: _cacheSubKey(),\n      duration: const Duration(minutes: 60),\n    );\n  }\n\n  /// 创作岛生成消耗量预估\n  Future<QuotaEvaluated> creativeIslandCompletionsEvaluate(String id, Map<String, dynamic> params) async {\n    return sendPostRequest(\n      '/v1/creative-island/completions/$id/evaluate',\n      (resp) => QuotaEvaluated.fromJson(resp.data),\n      formData: params,\n    );\n  }\n\n  /// 创意岛项目生成数据\n  Future<List<String>> creativeIslandCompletions(String id, Map<String, dynamic> params) async {\n    return sendPostRequest(\n      '/v1/creative-island/completions/$id',\n      (resp) {\n        final cicResp = CreativeIslandCompletionResp.fromJson(resp.data);\n        switch (cicResp.type) {\n          case creativeIslandCompletionTypeURLImage:\n            return cicResp.resources;\n          default:\n            return <String>[cicResp.content];\n        }\n      },\n      formData: params,\n    );\n  }\n\n  /// 创意岛项目生成数据\n  Future<String> creativeIslandCompletionsAsync(String id, Map<String, dynamic> params) async {\n    params[\"mode\"] = 'async';\n\n    return sendPostRequest(\n      '/v1/creative-island/completions/$id',\n      (resp) {\n        final cicResp = CreativeIslandCompletionAsyncResp.fromJson(resp.data);\n        return cicResp.taskId;\n      },\n      formData: params,\n    );\n  }\n\n  Future<QuotaEvaluated> creativeIslandCompletionsEvaluateV2(Map<String, dynamic> params) async {\n    return sendPostRequest(\n      '/v2/creative-island/completions/evaluate',\n      (resp) => QuotaEvaluated.fromJson(resp.data),\n      formData: params,\n    );\n  }\n\n  Future<String> creativeIslandCompletionsAsyncV2(Map<String, dynamic> params) async {\n    return sendPostRequest(\n      '/v2/creative-island/completions',\n      (resp) {\n        final cicResp = CreativeIslandCompletionAsyncResp.fromJson(resp.data);\n        return cicResp.taskId;\n      },\n      formData: params,\n    );\n  }\n\n  Future<String> creativeIslandArtisticTextCompletionsAsyncV2(Map<String, dynamic> params) async {\n    return sendPostRequest(\n      '/v2/creative-island/completions/artistic-text',\n      (resp) {\n        final cicResp = CreativeIslandCompletionAsyncResp.fromJson(resp.data);\n        return cicResp.taskId;\n      },\n      formData: params,\n    );\n  }\n\n  Future<String> creativeIslandImageToVideoCompletionsAsyncV2(Map<String, dynamic> params) async {\n    return sendPostRequest(\n      '/v2/creative-island/completions/image-to-video',\n      (resp) {\n        final cicResp = CreativeIslandCompletionAsyncResp.fromJson(resp.data);\n        return cicResp.taskId;\n      },\n      formData: params,\n    );\n  }\n\n  Future<String> creativeIslandImageDirectEdit(\n    String endpoint,\n    Map<String, dynamic> params,\n  ) async {\n    return sendPostRequest(\n      '/v2/creative-island/completions/$endpoint',\n      (resp) {\n        final cicResp = CreativeIslandCompletionAsyncResp.fromJson(resp.data);\n        return cicResp.taskId;\n      },\n      formData: params,\n    );\n  }\n\n  /// 模型风格列表\n  Future<List<ModelStyle>> modelStylesV2({String? modelId}) async {\n    return sendCachedGetRequest(\n      '/v2/models/styles',\n      (resp) {\n        var items = <ModelStyle>[];\n        for (var item in resp.data) {\n          items.add(ModelStyle.fromJson(item));\n        }\n        return items;\n      },\n      queryParameters: {'model_id': modelId},\n    );\n  }\n\n  /// 创作岛能力\n  Future<CreativeIslandCapacity> creativeIslandCapacity({required String mode, required String id}) async {\n    return sendCachedGetRequest(\n      '/v2/creative-island/capacity',\n      (resp) {\n        return CreativeIslandCapacity.fromJson(resp.data);\n      },\n      queryParameters: {'mode': mode, 'id': id},\n    );\n  }\n\n  /// 异步任务执行状态查询\n  Future<AsyncTaskResp> asyncTaskStatus(String taskId) async {\n    return sendGetRequest(\n      '/v1/tasks/$taskId/status',\n      (resp) => AsyncTaskResp.fromJson(resp.data),\n    );\n  }\n\n  /// 发送重置密码验证码\n  Future<String> sendResetPasswordCodeForSignedUser() async {\n    return sendPostRequest(\n      '/v1/users/reset-password/sms-code',\n      (resp) => resp.data['id'],\n    );\n  }\n\n  /// 用户重置密码\n  Future<void> resetPasswordByCodeSignedUser({\n    required String password,\n    required String verifyCodeId,\n    required String verifyCode,\n  }) async {\n    return sendPostRequest(\n      '/v1/users/reset-password',\n      (resp) => resp.data['id'],\n      formData: Map<String, dynamic>.from({\n        'password': password,\n        'verify_code_id': verifyCodeId,\n        'verify_code': verifyCode,\n      }),\n    );\n  }\n\n  /// 使用邮箱验证码重置密码\n  Future<void> resetPasswordByCode({\n    required String username,\n    required String password,\n    required String verifyCodeId,\n    required String verifyCode,\n  }) async {\n    return sendPostRequest(\n      '/v1/auth/reset-password',\n      (resp) => resp.data['id'],\n      formData: Map<String, dynamic>.from({\n        'username': username,\n        'password': password,\n        'verify_code_id': verifyCodeId,\n        'verify_code': verifyCode,\n      }),\n    );\n  }\n\n  /// 发送找回密码验证码\n  Future<String> sendResetPasswordCode(\n    String username, {\n    required String verifyType,\n  }) async {\n    return sendPostRequest(\n      '/v1/auth/reset-password/$verifyType-code',\n      (resp) => resp.data['id'],\n      formData: Map<String, dynamic>.from({\n        'username': username,\n      }),\n    );\n  }\n\n  /// 发送注册或者登录短信验证码\n  Future<String> sendSigninOrSignupVerifyCode(\n    String username, {\n    required String verifyType,\n    required bool isSignup,\n  }) {\n    if (isSignup) {\n      return sendSignupVerifyCode(username, verifyType: verifyType);\n    }\n\n    return sendSigninVerifyCode(username, verifyType: verifyType);\n  }\n\n  /// 发送登录验证码\n  Future<String> sendSigninVerifyCode(\n    String username, {\n    required String verifyType,\n  }) async {\n    return sendPostRequest(\n      '/v1/auth/sign-in/$verifyType-code',\n      (resp) => resp.data['id'],\n      formData: Map<String, dynamic>.from({\n        'username': username,\n      }),\n    );\n  }\n\n  /// 发送注册验证码\n  Future<String> sendSignupVerifyCode(\n    String username, {\n    required String verifyType,\n  }) async {\n    return sendPostRequest(\n      '/v1/auth/sign-up/$verifyType-code',\n      (resp) => resp.data['id'],\n      formData: Map<String, dynamic>.from({\n        'username': username,\n      }),\n    );\n  }\n\n  /// 发送绑定手机号码验证码\n  Future<String> sendBindPhoneCode(String username) async {\n    return sendPostRequest(\n      '/v1/auth/bind-phone/sms-code',\n      (resp) => resp.data['id'],\n      formData: Map<String, dynamic>.from({\n        'username': username,\n      }),\n    );\n  }\n\n  /// 绑定手机号\n  Future<SignInResp> bindPhone({\n    required String username,\n    required String verifyCodeId,\n    required String verifyCode,\n    String? inviteCode,\n  }) async {\n    return sendPostRequest(\n      '/v1/auth/bind-phone',\n      (resp) => SignInResp.fromJson(resp.data),\n      formData: Map<String, dynamic>.from({\n        'username': username,\n        'verify_code_id': verifyCodeId,\n        'verify_code': verifyCode,\n        'invite_code': inviteCode,\n      }),\n    );\n  }\n\n  /// 注册账号\n  Future<SignInResp> signupWithPassword({\n    required String username,\n    required String password,\n    required String verifyCodeId,\n    required String verifyCode,\n    String? inviteCode,\n  }) async {\n    return sendPostRequest(\n      '/v1/auth/sign-up',\n      (resp) => SignInResp.fromJson(resp.data),\n      formData: Map<String, dynamic>.from({\n        'username': username,\n        'password': password,\n        'verify_code_id': verifyCodeId,\n        'verify_code': verifyCode,\n        'invite_code': inviteCode,\n      }),\n    );\n  }\n\n  /// 发送账号销毁手机验证码\n  Future<String> sendDestroyAccountSMSCode() async {\n    return sendPostRequest(\n      '/v1/users/destroy/sms-code',\n      (resp) => resp.data['id'],\n    );\n  }\n\n  /// 账号销毁\n  Future<void> destroyAccount({\n    required String verifyCodeId,\n    required String verifyCode,\n  }) async {\n    return sendDeleteRequest(\n      '/v1/users/destroy',\n      (resp) {},\n      formData: Map<String, dynamic>.from({\n        'verify_code_id': verifyCodeId,\n        'verify_code': verifyCode,\n      }),\n    );\n  }\n\n  /// 版本检查\n  Future<VersionCheckResp> versionCheck({bool cache = true}) async {\n    return sendCachedGetRequest(\n      '/public/info/version-check',\n      (resp) => VersionCheckResp.fromJson(resp.data),\n      queryParameters: Map<String, dynamic>.from({\n        'version': clientVersion,\n        'os': PlatformTool.operatingSystem(),\n        'os_version': PlatformTool.operatingSystemVersion(),\n      }),\n      duration: const Duration(minutes: 180),\n      forceRefresh: !cache,\n    );\n  }\n\n  /// 支付项目列表\n  Future<PaymentProducts> paymentProducts() async {\n    return sendGetRequest(\n      '/v1/payment/products',\n      (resp) => PaymentProducts.fromJson(resp.data),\n    );\n  }\n\n  /// 发起 Apple Pay\n  Future<String> createApplePay(String productId) async {\n    return sendPostRequest(\n      '/v1/payment/apple',\n      (resp) => resp.data['id'],\n      formData: Map<String, dynamic>.from({\n        'product_id': productId,\n      }),\n    );\n  }\n\n  /// 发起支付\n  Future<OtherPayCreatedReponse> createOtherPay(String productId, {required String source}) async {\n    return sendPostRequest(\n      '/v1/payment/others',\n      (resp) => OtherPayCreatedReponse.fromJson(resp.data),\n      formData: Map<String, dynamic>.from({\n        'product_id': productId,\n        'source': source,\n      }),\n    );\n  }\n\n  /// 其它支付客户端确认\n  Future<String> otherPayClientConfirm(Map<String, dynamic> params) async {\n    return sendPostRequest(\n      '/v1/payment/others/client-confirm',\n      (resp) => resp.data['status'],\n      formData: params,\n    );\n  }\n\n  /// 查询支付状态\n  Future<PaymentStatus> queryPaymentStatus(String paymentId) async {\n    return sendGetRequest(\n      '/v1/payment/status/$paymentId',\n      (resp) => PaymentStatus.fromJson(resp.data),\n    );\n  }\n\n  /// 更新 Apple Pay 支付信息\n  Future<String> updateApplePay(\n    String paymentId, {\n    required String productId,\n    required String? localVerifyData,\n    required String? serverVerifyData,\n    required String? verifyDataSource,\n  }) async {\n    return sendPutRequest(\n      '/v1/payment/apple/$paymentId',\n      (resp) => resp.data['status'],\n      formData: Map<String, dynamic>.from({\n        'product_id': productId,\n        'local_verify_data': localVerifyData,\n        'server_verify_data': serverVerifyData,\n        'verify_data_source': verifyDataSource,\n      }),\n    );\n  }\n\n  /// 验证 Apple Pay 支付结果\n  Future<String> verifyApplePay(\n    String paymentId, {\n    required String productId,\n    required String? purchaseId,\n    required String? transactionDate,\n    required String? localVerifyData,\n    required String? serverVerifyData,\n    required String? verifyDataSource,\n    required String status,\n  }) async {\n    return sendPostRequest(\n      '/v1/payment/apple/$paymentId/verify',\n      (resp) => resp.data['status'],\n      formData: Map<String, dynamic>.from({\n        'product_id': productId,\n        'purchase_id': purchaseId,\n        'transaction_date': transactionDate,\n        'local_verify_data': localVerifyData,\n        'server_verify_data': serverVerifyData,\n        'verify_data_source': verifyDataSource,\n        'status': status,\n      }),\n    );\n  }\n\n  /// 取消 Apple Pay\n  Future<String> cancelApplePay(String paymentId, {String? reason}) async {\n    return sendDeleteRequest(\n      '/v1/payment/apple/$paymentId',\n      (resp) => resp.data['status'],\n      formData: Map<String, dynamic>.from({\n        'reason': reason,\n      }),\n    );\n  }\n\n  /// 获取房间列表\n  Future<RoomsResponse> rooms({bool cache = true}) async {\n    return sendCachedGetRequest(\n      '/v2/rooms',\n      (resp) {\n        return RoomsResponse.fromJson(resp.data);\n      },\n      subKey: _cacheSubKey(),\n      forceRefresh: !cache,\n    );\n  }\n\n  /// 获取最近使用过的角色\n  Future<List<RoomInServer>> recentRooms() async {\n    return sendGetRequest(\n      '/v2/rooms/recent',\n      (resp) {\n        var res = <RoomInServer>[];\n        for (var item in resp.data['data']) {\n          res.add(RoomInServer.fromJson(item));\n        }\n\n        return res;\n      },\n    );\n  }\n\n  /// 获取单个房间信息\n  Future<RoomInServer> room({required roomId, bool cache = true}) async {\n    return sendCachedGetRequest(\n      '/v1/rooms/$roomId',\n      (resp) => RoomInServer.fromJson(resp.data),\n      subKey: _cacheSubKey(),\n      forceRefresh: !cache,\n      duration: const Duration(minutes: 120),\n    );\n  }\n\n  /// 创建群聊房间\n  Future<int> createGroupRoom({\n    required String name,\n    String? description,\n    String? avatarUrl,\n    List<GroupMember>? members,\n  }) async {\n    return sendPostJSONRequest(\n      '/v1/group-chat',\n      (resp) => resp.data[\"group_id\"],\n      data: {\n        'name': name,\n        'avatar_url': avatarUrl,\n        'members': members?.map((e) => e.toJson()).toList(),\n      },\n      finallyCallback: () {\n        HttpClient.cleanCache();\n      },\n    );\n  }\n\n  /// 更新群聊房间\n  Future<void> updateGroupRoom({\n    required int groupId,\n    required String name,\n    String? description,\n    String? avatarUrl,\n    List<GroupMember>? members,\n  }) async {\n    return sendPutJSONRequest(\n      '/v1/group-chat/$groupId',\n      (resp) {},\n      data: {\n        'name': name,\n        'avatar_url': avatarUrl,\n        'members': members?.map((e) => e.toJson()).toList(),\n      },\n      finallyCallback: () {\n        HttpClient.cleanCache();\n      },\n    );\n  }\n\n  /// 创建房间\n  Future<int> createRoom({\n    required String name,\n    String? model,\n    String? vendor,\n    String? description,\n    String? systemPrompt,\n    String? avatarUrl,\n    int? avatarId,\n    int? maxContext,\n    String? initMessage,\n  }) async {\n    return sendPostRequest(\n      '/v1/rooms',\n      (resp) => resp.data[\"id\"],\n      formData: Map<String, dynamic>.from({\n        'name': name,\n        'model': model,\n        'vendor': vendor,\n        'description': description,\n        'system_prompt': systemPrompt,\n        'avatar_url': avatarUrl,\n        'avatar_id': avatarId,\n        'max_context': maxContext,\n        'init_message': initMessage,\n      }),\n      finallyCallback: () {\n        HttpClient.cleanCache();\n      },\n    );\n  }\n\n  /// 更新房间信息\n  Future<RoomInServer> updateRoom({\n    required int roomId,\n    required String name,\n    String? model,\n    String? vendor,\n    String? description,\n    String? systemPrompt,\n    String? avatarUrl,\n    int? avatarId,\n    int? maxContext,\n    String? initMessage,\n  }) async {\n    return sendPutRequest(\n      '/v1/rooms/$roomId',\n      (resp) => RoomInServer.fromJson(resp.data),\n      formData: Map<String, dynamic>.from({\n        'name': name,\n        'model': model,\n        'vendor': vendor,\n        'description': description,\n        'system_prompt': systemPrompt,\n        'avatar_url': avatarUrl,\n        'avatar_id': avatarId,\n        'max_context': maxContext,\n        'init_message': initMessage,\n      }),\n      finallyCallback: () {\n        HttpClient.cleanCache();\n      },\n    );\n  }\n\n  /// 删除房间\n  Future<void> deleteRoom({required int roomId}) async {\n    return sendDeleteRequest(\n      '/v1/rooms/$roomId',\n      (resp) {},\n      finallyCallback: () {\n        HttpClient.cleanCache();\n      },\n    );\n  }\n\n  /// 创作岛 Gallery\n  Future<List<CreativeItemInServer>> creativeUserGallery({\n    required String mode,\n    String? model,\n    bool cache = true,\n  }) async {\n    return sendCachedGetRequest(\n      '/v1/creative-island/gallery',\n      (resp) {\n        var res = <CreativeItemInServer>[];\n        for (var item in resp.data['data']) {\n          res.add(CreativeItemInServer.fromJson(item));\n        }\n\n        return res;\n      },\n      queryParameters: <String, dynamic>{\"mode\": mode, \"model\": model},\n      forceRefresh: !cache,\n      duration: const Duration(minutes: 30),\n    );\n  }\n\n  /// 图片模型列表\n  Future<List<ImageModel>> imageModels() async {\n    return sendCachedGetRequest(\n      '/v2/creative-island/models',\n      (resp) {\n        var res = <ImageModel>[];\n        for (var item in resp.data['data']) {\n          res.add(ImageModel.fromJson(item));\n        }\n\n        return res;\n      },\n      subKey: _cacheSubKey(),\n    );\n  }\n\n  /// 图片模型滤镜列表（风格）\n  Future<List<ImageModelFilter>> imageModelFilters() async {\n    return sendCachedGetRequest(\n      '/v2/creative-island/filters',\n      (resp) {\n        var res = <ImageModelFilter>[];\n        for (var item in resp.data['data']) {\n          res.add(ImageModelFilter.fromJson(item));\n        }\n\n        return res;\n      },\n      subKey: _cacheSubKey(),\n    );\n  }\n\n  /// 创作岛历史记录（全量）\n  Future<PagedData<CreativeItemInServer>> creativeHistories({\n    String? mode,\n    bool cache = true,\n    int? page,\n    int? perPage,\n  }) async {\n    return sendGetRequest(\n      '/v2/creative-island/histories',\n      (resp) {\n        var filters = <int, String>{};\n        for (var filter in resp.data['filters']) {\n          filters[filter['id']] = filter['name'];\n        }\n\n        var res = <CreativeItemInServer>[];\n        for (var item in resp.data['data']) {\n          final ret = CreativeItemInServer.fromJson(item);\n          if (ret.params['filter_id'] != null && filters.isNotEmpty) {\n            ret.filterName = filters[ret.params['filter_id']];\n          }\n\n          res.add(ret);\n        }\n\n        return PagedData(\n          data: res,\n          page: resp.data['page'] ?? 1,\n          perPage: resp.data['per_page'] ?? 20,\n          total: resp.data['total'],\n          lastPage: resp.data['last_page'],\n        );\n      },\n      queryParameters: <String, dynamic>{\n        \"mode\": mode,\n        \"page\": page,\n        \"per_page\": perPage,\n      },\n    );\n  }\n\n  /// 分享创作岛历史记录到 Gallery\n  Future<void> shareCreativeHistoryToGallery({required int historyId}) {\n    return sendPostRequest(\n      '/v2/creative-island/histories/$historyId/share',\n      (resp) {},\n    );\n  }\n\n  /// 取消分享创作岛历史记录到 Gallery\n  Future<void> cancelShareCreativeHistoryToGallery({required int historyId}) {\n    return sendDeleteRequest(\n      '/v2/creative-island/histories/$historyId/share',\n      (resp) {},\n    );\n  }\n\n  /// 封禁创作岛历史记录\n  Future<void> forbidCreativeHistoryItem({required int historyId}) {\n    return sendPutRequest(\n      '/v1/admin/creative-island/histories/$historyId/forbid',\n      (resp) {},\n    );\n  }\n\n  /// 创作岛历史记录\n  Future<List<CreativeItemInServer>> creativeItemHistories(String islandId, {bool cache = true}) async {\n    return sendCachedGetRequest(\n      '/v1/creative-island/items/$islandId/histories',\n      (resp) {\n        var res = <CreativeItemInServer>[];\n        for (var item in resp.data['data']) {\n          res.add(CreativeItemInServer.fromJson(item));\n        }\n\n        return res;\n      },\n      subKey: _cacheSubKey(),\n      forceRefresh: !cache,\n      duration: const Duration(minutes: 30),\n    );\n  }\n\n  /// 获取创作岛项目历史详情\n  Future<CreativeItemInServer> creativeHistoryItem({\n    required hisId,\n    bool cache = true,\n  }) async {\n    return sendCachedGetRequest(\n      '/v2/creative-island/histories/$hisId',\n      (resp) => CreativeItemInServer.fromJson(resp.data),\n      subKey: _cacheSubKey(),\n      forceRefresh: !cache,\n      duration: const Duration(minutes: 1),\n    );\n  }\n\n  /// 删除创作岛项目历史记录\n  Future<void> deleteCreativeHistoryItem(String islandId, {required hisId}) async {\n    return sendDeleteRequest(\n      '/v1/creative-island/items/$islandId/histories/$hisId',\n      (resp) {},\n    );\n  }\n\n  /// 获取用户智慧果消耗历史记录\n  Future<List<QuotaUsageInDay>> quotaUsedStatistics({bool cache = true}) async {\n    return sendCachedGetRequest(\n      '/v1/users/quota/usage-stat',\n      (resp) {\n        var res = <QuotaUsageInDay>[];\n        for (var item in resp.data['usages']) {\n          res.add(QuotaUsageInDay.fromJson(item));\n        }\n\n        return res;\n      },\n      subKey: _cacheSubKey(),\n      forceRefresh: !cache,\n      duration: const Duration(minutes: 30),\n    );\n  }\n\n  /// 获取用户智慧果消耗历史记录详情\n  Future<List<QuotaUsageDetailInDay>> quotaUsedDetails({required String date}) async {\n    return sendGetRequest(\n      '/v1/users/quota/usage-stat/$date',\n      (resp) {\n        var res = <QuotaUsageDetailInDay>[];\n        for (var item in resp.data['data']) {\n          res.add(QuotaUsageDetailInDay.fromJson(item));\n        }\n\n        return res;\n      },\n    );\n  }\n\n  Future<PagedData<CreativeGallery>> creativeGallery({\n    bool cache = true,\n    int page = 1,\n    int perPage = 20,\n  }) async {\n    return sendCachedGetRequest(\n      '/v1/creatives/gallery',\n      (resp) {\n        var res = <CreativeGallery>[];\n        for (var item in resp.data['data']) {\n          res.add(CreativeGallery.fromJson(item));\n        }\n\n        return PagedData(\n          page: resp.data['page'] ?? 1,\n          perPage: resp.data['per_page'] ?? 20,\n          total: resp.data['total'],\n          lastPage: resp.data['last_page'],\n          data: res,\n        );\n      },\n      queryParameters: Map.of({\n        'page': page,\n        'per_page': perPage,\n      }),\n      forceRefresh: !cache,\n      duration: const Duration(minutes: 60),\n    );\n  }\n\n  Future<CreativeGalleryItemResponse> creativeGalleryItem({\n    required int id,\n    bool cache = true,\n  }) async {\n    return sendCachedGetRequest(\n      '/v1/creatives/gallery/$id',\n      (resp) => CreativeGalleryItemResponse.fromJson(resp.data),\n      forceRefresh: !cache,\n      duration: const Duration(minutes: 30),\n    );\n  }\n\n  /// 文本转语音\n  Future<List<String>> textToVoice({required String text}) async {\n    return sendPostRequest(\n      '/v1/voice/text2voice',\n      formData: {'text': text},\n      (resp) => (resp.data['results'] as List<dynamic>).map((e) => e.toString()).toList(),\n    );\n  }\n\n  /// 故障日志上报\n  Future<void> diagnosisUpload({required String data}) async {\n    // data 从尾部开始截取 5000 个字符\n    if (data.length > 5000) {\n      data = data.substring(data.length - 5000);\n    }\n\n    return sendPostRequest(\n      '/v1/diagnosis/upload',\n      formData: {'data': data},\n      (resp) {},\n    );\n  }\n\n  /// 获取分享信息\n  Future<ShareInfo> shareInfo() async {\n    return sendCachedGetRequest(\n      '/public/share/info',\n      (resp) => ShareInfo.fromJson(resp.data),\n      duration: const Duration(minutes: 30),\n      subKey: _cacheSubKey(),\n    );\n  }\n\n  Future<RoomGalleryResponse> roomGalleries({bool cache = true}) async {\n    return sendCachedGetRequest(\n      '/v1/room-galleries',\n      (resp) {\n        return RoomGalleryResponse.fromJson(resp.data);\n      },\n      subKey: _cacheSubKey(),\n      forceRefresh: !cache,\n    );\n  }\n\n  Future<RoomGallery> roomGalleryItem({required int id, bool cache = true}) async {\n    return sendCachedGetRequest(\n      '/v1/room-galleries/$id',\n      (resp) => RoomGallery.fromJson(resp.data),\n      subKey: _cacheSubKey(),\n      forceRefresh: !cache,\n    );\n  }\n\n  Future<List<int>> copyRoomGallery({required List<int> ids}) async {\n    return sendPostRequest(\n      '/v1/room-galleries/copy',\n      formData: {'ids': ids.join(',')},\n      (resp) {\n        var ids = <int>[];\n        for (var item in resp.data['ids']) {\n          ids.add(item);\n        }\n\n        return ids;\n      },\n    );\n  }\n\n  Future<List<CreativeIslandItemV2>> creativeIslandItemsV2({bool cache = true}) async {\n    return sendCachedGetRequest(\n      '/v2/creative/items',\n      (resp) {\n        var items = <CreativeIslandItemV2>[];\n        for (var item in resp.data['data']) {\n          items.add(CreativeIslandItemV2.fromJson(item));\n        }\n        return items;\n      },\n      subKey: _cacheSubKey(),\n      forceRefresh: !cache,\n    );\n  }\n\n  /// 绘图提示语 Tags\n  Future<List<PromptCategory>> drawPromptTags({bool cache = true}) async {\n    return sendCachedGetRequest(\n      '/v1/examples/draw/prompt-tags',\n      (resp) {\n        var items = <PromptCategory>[];\n        for (var item in resp.data['data']) {\n          items.add(PromptCategory.fromJson(item));\n        }\n        return items;\n      },\n      subKey: _cacheSubKey(),\n      forceRefresh: !cache,\n    );\n  }\n\n  /// 更新用户头像\n  Future<void> updateUserAvatar({required String avatarURL}) async {\n    return sendPostRequest(\n      '/v1/users/current/avatar',\n      (resp) {},\n      formData: {'avatar_url': avatarURL},\n      finallyCallback: () {\n        HttpClient.cleanCache();\n      },\n    );\n  }\n\n  /// 更新用户昵称\n  Future<void> updateUserRealname({required String realname}) async {\n    return sendPostRequest(\n      '/v1/users/current/realname',\n      (resp) {},\n      formData: {'realname': realname},\n      finallyCallback: () {\n        HttpClient.cleanCache();\n      },\n    );\n  }\n\n  /// 服务器支持的能力\n  Future<Capabilities> capabilities({bool cache = true}) async {\n    return sendCachedGetRequest(\n      '/public/info/capabilities',\n      (resp) => Capabilities.fromJson(resp.data),\n      forceRefresh: !cache,\n    );\n  }\n\n  /// 用户免费聊天次数统计\n  Future<List<FreeModelCount>> userFreeStatistics() async {\n    return sendGetRequest(\n      '/v1/users/stat/free-chat-counts',\n      (resp) {\n        var items = <FreeModelCount>[];\n        for (var item in resp.data['data']) {\n          items.add(FreeModelCount.fromJson(item));\n        }\n        return items;\n      },\n    );\n  }\n\n  /// 免费聊天次数统计(登录不登录都可以访问)\n  Future<List<FreeModelCount>> freeChatCounts() async {\n    return sendGetRequest(\n      '/public/info/free-chat-counts',\n      (resp) {\n        var items = <FreeModelCount>[];\n        for (var item in resp.data['data']) {\n          items.add(FreeModelCount.fromJson(item));\n        }\n        return items;\n      },\n    );\n  }\n\n  /// 用户免费聊天次数统计(单个模型)\n  Future<FreeModelCount> userFreeStatisticsForModel({required String model}) async {\n    return sendGetRequest(\n      '/v1/users/stat/free-chat-counts/${Uri.encodeComponent(model)}',\n      (resp) => FreeModelCount.fromJson(resp.data),\n    );\n  }\n\n  /// 通知信息（促销事件）\n  Future<Map<String, List<PromotionEvent>>> notificationPromotionEvents({bool cache = true}) async {\n    return sendCachedGetRequest(\n      '/v1/notifications/promotions',\n      (value) {\n        var res = <String, List<PromotionEvent>>{};\n        for (var item in value.data['data']) {\n          if (res[item['id']] == null) {\n            res[item['id']] = [];\n          }\n\n          res[item['id']] = [\n            ...res[item['id']]!,\n            PromotionEvent.fromJson(item),\n          ];\n        }\n\n        return res;\n      },\n      subKey: _cacheSubKey(),\n      forceRefresh: !cache,\n    );\n  }\n\n  /// 更新自定义模型\n  Future<void> updateCustomHomeModels({required List<String> models}) async {\n    return sendPostRequest(\n      '/v1/users/custom/home-models',\n      (value) => {},\n      formData: {\n        'models': models.join(','),\n      },\n    );\n  }\n\n  /// 自定义首页模型 v2\n  Future<List<HomeModelV2>> customHomeModelsV2({bool cache = true}) async {\n    return sendCachedGetRequest(\n      '/v2/models/home-models/all',\n      (value) {\n        var res = <HomeModelV2>[];\n        for (var item in value.data['data']) {\n          res.add(HomeModelV2.fromJson(item));\n        }\n\n        return res;\n      },\n      forceRefresh: !cache,\n    );\n  }\n\n  /// 自定义首页模型 v2 详情\n  Future<HomeModelV2> customHomeModelsItemV2({\n    bool cache = true,\n    required String uniqueKey,\n  }) async {\n    return sendCachedGetRequest(\n      '/v2/models/home-models/${Uri.encodeComponent(uniqueKey)}',\n      (value) {\n        return HomeModelV2.fromJson(value.data['data']);\n      },\n      forceRefresh: !cache,\n    );\n  }\n\n  /// 更新自定义模型 v2\n  /// 模型 ID 使用 type:id 形式\n  Future<void> updateCustomHomeModelsV2({required List<String> models}) async {\n    return sendPostRequest(\n      '/v2/users/custom/home-models',\n      (value) => {},\n      formData: {\n        'models': models.join(','),\n      },\n    );\n  }\n\n  /// 群聊 ////////////////////////////////////////////////////////////////////\n\n  /// 群组列表\n  Future<List<RoomInServer>> chatGroups({bool cache = true}) async {\n    return sendCachedGetRequest(\n      '/v1/group-chat',\n      (value) {\n        var res = <RoomInServer>[];\n        for (var item in value.data['data']) {\n          res.add(RoomInServer.fromJson(item));\n        }\n\n        return res;\n      },\n      forceRefresh: !cache,\n    );\n  }\n\n  /// 群组详情\n  Future<ChatGroup> chatGroup(int groupId, {bool cache = true}) async {\n    return sendCachedGetRequest(\n      '/v1/group-chat/$groupId',\n      (value) => ChatGroup.fromJson(value.data),\n      forceRefresh: !cache,\n    );\n  }\n\n  /// 群组聊天消息列表\n  Future<OffsetPageData<GroupMessage>> chatGroupMessages(\n    int groupId, {\n    int startId = 0,\n    int? perPage,\n    bool cache = true,\n  }) async {\n    return sendCachedGetRequest(\n      '/v1/group-chat/$groupId/messages',\n      (resp) {\n        var res = <GroupMessage>[];\n        for (var item in resp.data['data']) {\n          res.add(GroupMessage.fromJson(item));\n        }\n\n        return OffsetPageData(\n          data: res,\n          lastId: resp.data['last_id'],\n          startId: resp.data['start_id'],\n          perPage: resp.data['per_page'],\n        );\n      },\n      queryParameters: {\n        'start_id': startId,\n        'per_page': perPage,\n      },\n      forceRefresh: !cache,\n    );\n  }\n\n  /// 发起群聊消息\n  Future<GroupChatSendResponse> chatGroupSendMessage(int groupId, GroupChatSendRequest req) async {\n    return sendPostJSONRequest(\n      '/v1/group-chat/$groupId/chat',\n      (resp) {\n        return GroupChatSendResponse.fromJson(resp.data);\n      },\n      data: req.toJson(),\n    );\n  }\n\n  /// 群聊发送系统消息\n  Future<GroupMessage> chatGroupSendSystemMessage(\n    int groupId, {\n    required String messageType,\n    String? message,\n  }) async {\n    return sendPostRequest(\n      '/v1/group-chat/$groupId/chat-system',\n      (resp) => GroupMessage.fromJson(resp['data']),\n      formData: {\n        'message_type': messageType,\n        'message': message,\n      },\n    );\n  }\n\n  /// 群组聊天消息状态\n  Future<List<GroupMessage>> chatGroupMessageStatus(int groupId, List<int> messageIds) async {\n    return sendGetRequest(\n      '/v1/group-chat/$groupId/chat-messages',\n      (resp) {\n        var res = <GroupMessage>[];\n        for (var item in resp.data['data']) {\n          res.add(GroupMessage.fromJson(item));\n        }\n\n        return res;\n      },\n      queryParameters: {\n        \"message_ids\": messageIds.join(','),\n      },\n    );\n  }\n\n  /// 清空群组聊天消息\n  Future<void> chatGroupDeleteAllMessages(int groupId) async {\n    return sendDeleteRequest('/v1/group-chat/$groupId/all-chat', (resp) {});\n  }\n\n  /// 删除群组聊天消息\n  Future<void> chatGroupDeleteMessage(int groupId, int messageId) async {\n    return sendDeleteRequest('/v1/group-chat/$groupId/chat/$messageId', (resp) {});\n  }\n\n  /// API 模式 ////////////////////////////////////////////////////////////////////\n  /// 查询用户所有的 API Keys\n  Future<List<UserAPIKey>> userAPIKeys() async {\n    return sendGetRequest('/v1/api-keys', (data) {\n      return ((data.data['data'] ?? []) as List<dynamic>).map((e) => UserAPIKey.fromJson(e)).toList();\n    });\n  }\n\n  /// 查询指定 API Key\n  Future<UserAPIKey> userAPIKeyDetail({required int id}) async {\n    return sendGetRequest('/v1/api-keys/$id', (data) {\n      return UserAPIKey.fromJson(data.data['data']);\n    });\n  }\n\n  /// 创建 API Key\n  Future<String> createAPIKey({required String name}) async {\n    return sendPostRequest(\n      '/v1/api-keys',\n      (data) => data.data['key'],\n      formData: {'name': name},\n    );\n  }\n\n  /// 删除 API Key\n  Future<void> deleteAPIKey({required int id}) async {\n    return sendDeleteRequest('/v1/api-keys/$id', (data) {});\n  }\n\n  /// 消息通知 ////////////////////////////////////////////////////////////////////\n  /// 消息通知列表\n  Future<OffsetPageData<NotifyMessage>> notifications({\n    int startId = 0,\n    int? perPage = 20,\n    bool cache = true,\n  }) async {\n    return sendCachedGetRequest(\n      '/v1/notifications',\n      (resp) {\n        var res = <NotifyMessage>[];\n        for (var item in resp.data['data']) {\n          res.add(NotifyMessage.fromJson(item));\n        }\n\n        return OffsetPageData(\n          data: res,\n          lastId: resp.data['last_id'],\n          startId: resp.data['start_id'],\n          perPage: resp.data['per_page'],\n        );\n      },\n      queryParameters: {\n        'start_id': startId,\n        'per_page': perPage,\n      },\n      forceRefresh: !cache,\n    );\n  }\n\n  /// 文章 ////////////////////////////////////////////////////////////////////\n  /// 文章详情\n  Future<Article> article({\n    required int id,\n    bool cache = true,\n  }) async {\n    return sendCachedGetRequest(\n      '/v1/articles/$id',\n      (resp) {\n        return Article.fromJson(resp.data['data']);\n      },\n      forceRefresh: !cache,\n    );\n  }\n\n  /// 发起 Stripe 支付\n  Future<StripePaymentCreatedResponse> createStripePaymentSheet({\n    required String productId,\n    String? source,\n  }) async {\n    return sendPostRequest(\n      '/v1/payment/stripe/payment-sheet',\n      (resp) {\n        return StripePaymentCreatedResponse.fromJson(resp.data);\n      },\n      formData: {\n        'product_id': productId,\n        'source': source,\n      },\n    );\n  }\n\n  /// 发起微信支付\n  Future<WechatPaymentCreatedResponse> createWechatPayment({\n    required String productId,\n    String? source,\n  }) async {\n    return sendPostRequest(\n      '/v1/payment/wechatpay/',\n      (resp) {\n        return WechatPaymentCreatedResponse.fromJson(resp.data);\n      },\n      formData: {\n        'product_id': productId,\n        'source': source,\n      },\n    );\n  }\n\n  /// 管理员接口：渠道类型\n  Future<List<AdminChannelType>> adminChannelTypes() async {\n    return sendCachedGetRequest('/v1/admin/channel-types', (resp) {\n      var res = <AdminChannelType>[];\n      for (var item in resp.data['data']) {\n        res.add(AdminChannelType.fromJson(item));\n      }\n\n      return res;\n    });\n  }\n\n  /// 管理员接口：返回聚合后的渠道列表\n  Future<List<AdminChannel>> adminChannelsAgg() async {\n    final channels = await sendGetRequest('/v1/admin/channels', (resp) {\n      var res = <AdminChannel>[];\n      for (var item in resp.data['data']) {\n        res.add(AdminChannel.fromJson(item));\n      }\n\n      return res;\n    });\n\n    final channelTypes = await adminChannelTypes();\n    channels.addAll(channelTypes.map((e) => AdminChannel(name: e.text, type: e.name)));\n\n    return channels;\n  }\n\n  /// 管理员接口：返回所有渠道\n  Future<List<AdminChannel>> adminChannels() async {\n    return sendGetRequest('/v1/admin/channels', (resp) {\n      var res = <AdminChannel>[];\n      for (var item in resp.data['data']) {\n        res.add(AdminChannel.fromJson(item));\n      }\n\n      return res;\n    });\n  }\n\n  /// 管理员接口：返回指定渠道\n  Future<AdminChannel> adminChannel({required int id}) async {\n    return sendGetRequest('/v1/admin/channels/$id', (resp) {\n      return AdminChannel.fromJson(resp.data['data']);\n    });\n  }\n\n  /// 管理员接口：创建渠道\n  Future<void> adminCreateChannel(AdminChannelAddReq req) async {\n    return sendPostJSONRequest(\n      '/v1/admin/channels',\n      (resp) {},\n      data: req.toJson(),\n    );\n  }\n\n  /// 管理员接口：更新渠道\n  Future<void> adminUpdateChannel({required int id, required AdminChannelUpdateReq req}) {\n    return sendPutJSONRequest(\n      '/v1/admin/channels/$id',\n      (resp) {},\n      data: req.toJson(),\n    );\n  }\n\n  /// 管理员接口：删除渠道\n  Future<void> adminDeleteChannel({required int id}) {\n    return sendDeleteRequest('/v1/admin/channels/$id', (resp) {});\n  }\n\n  /// 管理员接口：返回所有模型\n  Future<List<AdminModel>> adminModels() async {\n    return sendGetRequest(\n      '/v1/admin/models',\n      (resp) {\n        var res = <AdminModel>[];\n        for (var item in resp.data['data']) {\n          res.add(AdminModel.fromJson(item));\n        }\n\n        return res;\n      },\n      queryParameters: {\n        'sort': 'id:desc',\n      },\n    );\n  }\n\n  /// 管理员接口：返回指定模型\n  Future<AdminModel> adminModel({required String modelId}) async {\n    return sendGetRequest('/v1/admin/models/${Uri.encodeComponent(modelId)}', (resp) {\n      return AdminModel.fromJson(resp.data['data']);\n    });\n  }\n\n  /// 管理员接口：创建模型\n  Future<void> adminCreateModel(AdminModelAddReq req) async {\n    return sendPostJSONRequest(\n      '/v1/admin/models',\n      (resp) {},\n      data: req.toJson(),\n    );\n  }\n\n  /// 管理员接口：更新模型\n  Future<void> adminUpdateModel({required String modelId, required AdminModelUpdateReq req}) {\n    return sendPutJSONRequest(\n      '/v1/admin/models/${Uri.encodeComponent(modelId)}',\n      (resp) {},\n      data: req.toJson(),\n    );\n  }\n\n  /// 管理员接口：删除模型\n  Future<void> adminDeleteModel({required String modelId}) {\n    return sendDeleteRequest('/v1/admin/models/${Uri.encodeComponent(modelId)}', (resp) {});\n  }\n\n  /// 管理员接口：查询用户列表\n  Future<PagedData<AdminUser>> adminUsers({\n    int page = 1,\n    int perPage = 20,\n    String? keyword,\n  }) async {\n    return sendGetRequest(\n      '/v1/admin/users',\n      (resp) {\n        var res = <AdminUser>[];\n        for (var item in resp.data['data']) {\n          res.add(AdminUser.fromJson(item));\n        }\n\n        return PagedData(\n          data: res,\n          page: resp.data['page'] ?? 1,\n          perPage: resp.data['per_page'] ?? 20,\n          total: resp.data['total'],\n          lastPage: resp.data['last_page'],\n        );\n      },\n      queryParameters: {\n        'page': page,\n        'per_page': perPage,\n        'keyword': keyword,\n      },\n    );\n  }\n\n  /// 管理员接口：查询用户详情\n  Future<AdminUser> adminUser({required int id}) async {\n    return sendGetRequest('/v1/admin/users/$id', (resp) {\n      return AdminUser.fromJson(resp.data['data']);\n    });\n  }\n\n  /// 管理员接口：为用户分配智慧果\n  Future<void> adminUserQuotaAssign({\n    required int userId,\n    required int quota,\n    int? validPeriod,\n    String? note,\n  }) {\n    return sendPostJSONRequest(\n      '/v1/admin/quotas/assign',\n      (resp) {},\n      data: {\n        'user_id': userId,\n        'quota': quota,\n        'valid_period': validPeriod,\n        'note': note,\n      },\n    );\n  }\n\n  /// 管理员接口：查询用户当前额度\n  Future<QuotaResp> adminUserQuota({required int userId}) async {\n    return sendGetRequest('/v1/admin/quotas/users/$userId', (resp) {\n      return QuotaResp.fromJson(resp.data);\n    });\n  }\n\n  /// 管理员接口：重新加载配置缓存\n  Future<void> adminSettingsReload() async {\n    return sendPostRequest('/v1/admin/settings/reload', (resp) {});\n  }\n\n  /// 管理员接口：重新加载配置缓存\n  Future<void> adminSettingReload(String key) async {\n    return sendPostRequest('/v1/admin/settings/key/$key/reload', (resp) {});\n  }\n\n  /// 管理员接口：查询所有支付订单\n  Future<PagedData<AdminPaymentHistory>> adminPaymentHistories({\n    int page = 1,\n    int perPage = 20,\n    String? keyword,\n  }) async {\n    return sendGetRequest(\n      '/v1/admin/payments/histories',\n      (resp) {\n        var res = <AdminPaymentHistory>[];\n        for (var item in resp.data['data']) {\n          res.add(AdminPaymentHistory.fromJson(item));\n        }\n\n        return PagedData(\n          data: res,\n          page: resp.data['page'] ?? 1,\n          perPage: resp.data['per_page'] ?? 20,\n          total: resp.data['total'],\n          lastPage: resp.data['last_page'],\n        );\n      },\n      queryParameters: {\n        'page': page,\n        'per_page': perPage,\n        'keyword': keyword,\n      },\n    );\n  }\n\n  /// 管理员接口：查询用户所有的数字人列表\n  Future<List<RoomInServer>> adminUserRooms({required int userId}) async {\n    return sendGetRequest('/v1/admin/messages/$userId/rooms', (resp) {\n      var res = <RoomInServer>[];\n      for (var item in resp.data['data']) {\n        res.add(RoomInServer.fromJson(item));\n      }\n\n      return res;\n    });\n  }\n\n  /// 管理员接口：查询用户指定的数字人\n  Future<RoomInServer> adminUserRoom({required int userId, required int roomId}) async {\n    return sendGetRequest('/v1/admin/messages/$userId/rooms/$roomId', (resp) {\n      return RoomInServer.fromJson(resp.data['data']);\n    });\n  }\n\n  /// 管理员接口：查询用户指定数字人最近聊天历史记录\n  Future<List<MessageInServer>> adminUserRoomMessages({required int userId, required int roomId}) async {\n    return sendGetRequest('/v1/admin/messages/$userId/rooms/$roomId/messages', (resp) {\n      var res = <MessageInServer>[];\n      for (var item in resp.data['data']) {\n        res.add(MessageInServer.fromJson(item));\n      }\n\n      return res;\n    });\n  }\n\n  /// 管理员接口：查询用户指定群聊最近聊天历史记录\n  Future<List<GroupMessage>> adminUserRoomGroupMessages({\n    required int userId,\n    required int roomId,\n  }) async {\n    return sendGetRequest(\n      '/v1/admin/messages/$userId/rooms/$roomId/group-messages',\n      (resp) {\n        var res = <GroupMessage>[];\n        for (var item in resp.data['data']) {\n          res.add(GroupMessage.fromJson(item));\n        }\n\n        return res;\n      },\n    );\n  }\n\n  /// 管理员接口：查询全局最近聊天历史记录\n  Future<PagedData<MessageInServer>> adminRecentlyMessages({\n    int page = 1,\n    int perPage = 20,\n    String? keyword,\n  }) async {\n    return sendGetRequest(\n      '/v1/admin/recent-messages',\n      (resp) {\n        var res = <MessageInServer>[];\n        for (var item in resp.data['data']) {\n          res.add(MessageInServer.fromJson(item));\n        }\n\n        return PagedData(\n          data: res,\n          page: resp.data['page'] ?? 1,\n          perPage: resp.data['per_page'] ?? 20,\n          total: resp.data['total'],\n          lastPage: resp.data['last_page'],\n        );\n      },\n      queryParameters: {\n        'page': page,\n        'per_page': perPage,\n        'keyword': keyword,\n      },\n    );\n  }\n}\n"
  },
  {
    "path": "lib/repo/cache_repo.dart",
    "content": "import 'dart:async';\n\nimport 'package:askaide/repo/data/cache_data.dart';\n\nclass CacheRepository {\n  final CacheDataProvider cacheProvider;\n  var lastGC = DateTime.now();\n\n  CacheRepository(this.cacheProvider);\n\n  Future<void> set(\n    String key,\n    String value,\n    Duration ttl, {\n    String? group,\n  }) async {\n    return cacheProvider.set(key, value, ttl, group: group);\n  }\n\n  Future<String?> get(String key) async {\n    if (DateTime.now().difference(lastGC) > const Duration(minutes: 10)) {\n      cacheProvider.gc().whenComplete(() {\n        lastGC = DateTime.now();\n      });\n    }\n\n    return cacheProvider.get(key);\n  }\n\n  Future<Map<String, String>> getAllInGroup(String group) async {\n    return cacheProvider.getAllInGroup(group);\n  }\n\n  Future<void> remove(String key) async {\n    return cacheProvider.remove(key);\n  }\n\n  Future<void> clearAll() async {\n    return cacheProvider.clearAll();\n  }\n}\n"
  },
  {
    "path": "lib/repo/chat_message_repo.dart",
    "content": "import 'dart:async';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/repo/data/chat_history.dart';\nimport 'package:askaide/repo/data/room_data.dart';\nimport 'package:askaide/repo/model/chat_history.dart';\nimport 'package:askaide/repo/model/message.dart';\nimport 'package:askaide/repo/data/chat_message_data.dart';\nimport 'package:askaide/repo/model/room.dart';\n\nclass ChatMessageRepository {\n  final ChatMessageDataProvider _chatMsgDataProvider;\n  final RoomDataProvider _chatRoomDataProvider;\n  final ChatHistoryProvider _chatHistoryProvider;\n\n  ChatMessageRepository(\n    this._chatRoomDataProvider,\n    this._chatMsgDataProvider,\n    this._chatHistoryProvider,\n  );\n\n  /// 获取所有 room\n  Future<List<Room>> rooms({int? userId}) async {\n    return await _chatRoomDataProvider.chatRooms(userId: userId);\n  }\n\n  /// 创建 room\n  Future<Room> createRoom({\n    required String name,\n    required category,\n    String? description,\n    String? model,\n    String? color,\n    String? systemPrompt,\n    int? userId,\n    int? maxContext,\n  }) async {\n    return await _chatRoomDataProvider.createRoom(\n      name: name,\n      category: category,\n      description: description,\n      model: model,\n      color: color,\n      systemPrompt: systemPrompt,\n      userId: userId,\n      maxContext: maxContext,\n    );\n  }\n\n  /// 删除 room\n  Future<void> deleteRoom(int roomId) async {\n    await _chatRoomDataProvider.deleteRoom(roomId);\n    await _chatMsgDataProvider.clearMessages(roomId);\n  }\n\n  /// 返回 room 中最近的消息\n  Future<List<Message>> getRecentMessages({\n    int? roomId,\n    int? userId,\n    int? chatHistoryId,\n  }) async {\n    if (chatHistoryId != null && chatHistoryId > 0) {\n      roomId = null;\n    }\n\n    return (await _chatMsgDataProvider.getRecentMessages(\n      chatMessagePerPage,\n      roomId: roomId,\n      userId: userId,\n      chatHistoryId: chatHistoryId,\n    ))\n        .reversed\n        .toList();\n  }\n\n  /// 发送消息到 room\n  Future<int> sendMessage(int roomId, Message message) async {\n    return await _chatMsgDataProvider.sendMessage(roomId, message);\n  }\n\n  /// 修复所有消息的状态（pending -> failed）\n  Future<void> fixMessageStatus(int roomId) async {\n    return await _chatMsgDataProvider.fixMessageStatus(roomId);\n  }\n\n  /// 更新消息\n  Future<void> updateMessage(int roomId, int id, Message message) async {\n    return await _chatMsgDataProvider.updateMessage(roomId, id, message);\n  }\n\n  /// 部分更新消息\n  Future<void> updateMessagePart(\n    int roomId,\n    int id,\n    List<MessagePart> parts,\n  ) async {\n    return await _chatMsgDataProvider.updateMessagePart(roomId, id, parts);\n  }\n\n  /// 删除消息\n  Future<void> removeMessage(int roomId, List<int> ids) async {\n    return await _chatMsgDataProvider.removeMessage(roomId, ids);\n  }\n\n  /// 清空 room 中的消息\n  Future<void> clearMessages(int roomId, {int? userId}) async {\n    await _chatMsgDataProvider.clearMessages(roomId, userId: userId);\n  }\n\n  /// 获取 room 中最后一条消息\n  Future<Message?> getLastMessage(int roomId, {int? userId, int? chatHistoryId}) async {\n    return await _chatMsgDataProvider.getLastMessage(roomId, userId: userId, chatHistoryId: chatHistoryId);\n  }\n\n  /// 获取 room\n  Future<Room?> room(int roomId) async {\n    final room = await _chatRoomDataProvider.room(roomId);\n    if (room != null) {\n      room.localRoom = true;\n    }\n\n    return room;\n  }\n\n  /// 更新 room\n  Future<int> updateRoom(Room room) async {\n    return await _chatRoomDataProvider.updateRoom(room);\n  }\n\n  /// 更新 room 最后活跃时间\n  Future<void> updateRoomLastActiveTime(int roomId) async {\n    return await _chatRoomDataProvider.updateRoomLastActiveTime(roomId);\n  }\n\n  Future<ChatHistory> createChatHistory({\n    required String title,\n    int? userId,\n    int? roomId,\n    String? lastMessage,\n    String? model,\n  }) {\n    return _chatHistoryProvider.create(\n      title: title,\n      userId: userId,\n      roomId: roomId,\n      model: model,\n      lastMessage: lastMessage,\n    );\n  }\n\n  Future<List<ChatHistory>> recentChatHistories(\n    int count, {\n    String? keyword,\n    int? userId,\n    int? offset,\n  }) async {\n    return await _chatHistoryProvider.getChatHistories(\n      count,\n      keyword: keyword,\n      userId: userId,\n      offset: offset,\n    );\n  }\n\n  Future<ChatHistory?> getChatHistory(int chatId) async {\n    return await _chatHistoryProvider.history(chatId);\n  }\n\n  Future<int> deleteChatHistory(int chatId) async {\n    return await _chatHistoryProvider.delete(chatId);\n  }\n\n  Future<int> updateChatHistory(int chatId, ChatHistory chatHistory) async {\n    chatHistory.id = chatId;\n    return await _chatHistoryProvider.update(chatHistory);\n  }\n}\n"
  },
  {
    "path": "lib/repo/creative_island_repo.dart",
    "content": "import 'package:askaide/repo/data/creative_island_data.dart';\nimport 'package:askaide/repo/model/creative_island_history.dart';\n\nclass CreativeIslandRepository {\n  final CreativeIslandDataProvider _dataProvider;\n\n  CreativeIslandRepository(this._dataProvider);\n\n  Future<List<CreativeIslandHistory>> getRecentHistories(\n      String itemId, int count,\n      {int? userId}) async {\n    return await _dataProvider.getRecentHistories(\n      itemId,\n      count,\n      userId: userId,\n    );\n  }\n\n  Future<CreativeIslandHistory> create(\n    String itemId, {\n    String? arguments,\n    String? prompt,\n    String? answer,\n    String? taskId,\n    String? status,\n    int? userId,\n  }) async {\n    return await _dataProvider.create(\n      itemId,\n      arguments: arguments,\n      prompt: prompt,\n      answer: answer,\n      taskId: taskId,\n      status: status,\n      userId: userId,\n    );\n  }\n\n  /// 更新\n  Future<void> update(int id, CreativeIslandHistory his) async {\n    await _dataProvider.update(id, his);\n  }\n\n  Future<int> delete(int hisId) async {\n    return await _dataProvider.delete(hisId);\n  }\n\n  Future<CreativeIslandHistory?> history(int id) async {\n    return await _dataProvider.history(id);\n  }\n}\n"
  },
  {
    "path": "lib/repo/data/cache_data.dart",
    "content": "import 'package:sqflite/sqflite.dart';\n\nclass CacheDataProvider {\n  Database conn;\n  CacheDataProvider(this.conn);\n\n  /// 设置缓存\n  Future<void> set(\n    String key,\n    String value,\n    Duration ttl, {\n    String? group,\n  }) async {\n    await conn.delete('cache', where: 'key = ?', whereArgs: [key]);\n    await conn.insert('cache', <String, Object?>{\n      'key': key,\n      'value': value,\n      'group': group,\n      'created_at': DateTime.now().millisecondsSinceEpoch,\n      'valid_before': DateTime.now().add(ttl).millisecondsSinceEpoch,\n    });\n  }\n\n  Future<Map<String, String>> getAllInGroup(String group) async {\n    List<Map<String, Object?>> cacheValue = await conn.query(\n      'cache',\n      where: '`group` = ? AND valid_before >= ?',\n      whereArgs: [group, DateTime.now().millisecondsSinceEpoch],\n    );\n\n    if (cacheValue.isEmpty) {\n      return {};\n    }\n\n    Map<String, String> ret = {};\n    for (var item in cacheValue) {\n      ret[item['key'] as String] = item['value'] as String;\n    }\n\n    return ret;\n  }\n\n  // 查询缓存值\n  Future<String?> get(String key) async {\n    List<Map<String, Object?>> cacheValue = await conn.query(\n      'cache',\n      where: 'key = ? AND valid_before >= ?',\n      whereArgs: [key, DateTime.now().millisecondsSinceEpoch],\n      limit: 1,\n    );\n\n    if (cacheValue.isEmpty) {\n      return null;\n    }\n\n    return cacheValue.first['value'] as String;\n  }\n\n  /// 删除缓存\n  Future<void> remove(String key) async {\n    await conn.delete('cache', where: 'key = ?', whereArgs: [key]);\n  }\n\n  /// 清理过期 keys\n  Future<void> gc() async {\n    await conn.delete(\n      'cache',\n      where: 'valid_before < ?',\n      whereArgs: [DateTime.now().millisecondsSinceEpoch],\n    );\n  }\n\n  /// 清空所有缓存\n  Future<void> clearAll() async {\n    await conn.delete('cache');\n  }\n}\n"
  },
  {
    "path": "lib/repo/data/chat_history.dart",
    "content": "import 'package:askaide/repo/model/chat_history.dart';\nimport 'package:sqflite/sqlite_api.dart';\n\nclass ChatHistoryProvider {\n  Database conn;\n  ChatHistoryProvider(this.conn);\n\n  Future<List<ChatHistory>> getChatHistories(\n    int count, {\n    int? userId,\n    int? offset,\n    String? keyword,\n  }) async {\n    keyword ??= '';\n    final userConditon = userId == null ? ' AND user_id IS NULL' : ' AND user_id = $userId';\n\n    var historyIds = [];\n    if (keyword != '') {\n      final histories = await conn.query(\n        'chat_message',\n        where: 'chat_history_id IS NOT NULL AND text LIKE ? $userConditon',\n        whereArgs: ['%$keyword%'],\n        columns: ['chat_history_id'],\n        distinct: true,\n      );\n\n      historyIds = histories.map((h) => h['chat_history_id']).toList();\n      if (historyIds.isEmpty) {\n        return [];\n      }\n    }\n\n    var keywordCondition = keyword != '' ? 'AND id in (${historyIds.join(',')})' : '';\n    List<Map<String, Object?>> histories = await conn.query(\n      'chat_history',\n      where: 'room_id IS NOT NULL $userConditon $keywordCondition',\n      orderBy: 'updated_at DESC',\n      limit: count,\n      offset: offset,\n    );\n\n    return histories.map((e) => ChatHistory.fromMap(e)).toList();\n  }\n\n  Future<ChatHistory> create({\n    required String title,\n    int? userId,\n    int? roomId,\n    String? lastMessage,\n    String? model,\n  }) async {\n    final history = ChatHistory(\n      title: title,\n      userId: userId,\n      roomId: roomId,\n      lastMessage: lastMessage,\n      model: model,\n      createdAt: DateTime.now(),\n      updatedAt: DateTime.now(),\n    );\n\n    history.id = await conn.insert('chat_history', history.toMap());\n    return history;\n  }\n\n  Future<int> update(ChatHistory his) async {\n    if (his.id == null) {\n      throw Exception('history id is null');\n    }\n\n    his.updatedAt = DateTime.now();\n\n    return conn.update(\n      'chat_history',\n      his.toMap(),\n      where: 'id = ?',\n      whereArgs: [his.id],\n    );\n  }\n\n  Future<int> delete(int id) async {\n    return conn.delete('chat_history', where: 'id = ?', whereArgs: [id]);\n  }\n\n  Future<ChatHistory?> history(int id) async {\n    List<Map<String, Object?>> histories = await conn.query('chat_history', where: 'id = ?', whereArgs: [id], limit: 1);\n    if (histories.isEmpty) {\n      return null;\n    }\n\n    return ChatHistory.fromMap(histories.first);\n  }\n}\n"
  },
  {
    "path": "lib/repo/data/chat_message_data.dart",
    "content": "import 'package:askaide/repo/model/message.dart';\nimport 'package:sqflite/sqlite_api.dart';\n\nclass MessagePart {\n  final String key;\n  final dynamic value;\n\n  MessagePart(this.key, this.value);\n}\n\nclass ChatMessageDataProvider {\n  Database conn;\n  ChatMessageDataProvider(this.conn);\n\n  Future<List<Message>> getRecentMessages(int count, {int? userId, int? chatHistoryId, int? roomId}) async {\n    var userConditon = userId == null ? ' AND user_id IS NULL' : ' AND user_id = $userId';\n\n    if (chatHistoryId != null) {\n      userConditon += ' AND chat_history_id = $chatHistoryId';\n    }\n\n    List<Map<String, Object?>> messages = await conn.query(\n      'chat_message',\n      where: '${roomId == null ? 'room_id IS NOT NULL' : 'room_id = ?'} $userConditon',\n      whereArgs: roomId == null ? [] : [roomId],\n      orderBy: 'id DESC',\n      limit: count,\n    );\n\n    return messages.map((e) => Message.fromMap(e)).toList();\n  }\n\n  Future<Message?> getLastMessage(\n    int roomId, {\n    int? userId,\n    int? chatHistoryId,\n  }) async {\n    var userConditon = userId == null ? ' AND user_id IS NULL' : ' AND user_id = $userId';\n    if (chatHistoryId != null) {\n      userConditon += ' AND chat_history_id = $chatHistoryId';\n    }\n\n    List<Map<String, Object?>> messages = await conn.query(\n      'chat_message',\n      where: 'room_id = ? $userConditon',\n      whereArgs: [roomId],\n      orderBy: 'id DESC',\n      limit: 1,\n    );\n\n    if (messages.isEmpty) {\n      return null;\n    }\n\n    return Message.fromMap(messages.first);\n  }\n\n  Future<int> sendMessage(int roomId, Message message) async {\n    if (roomId > 0) {\n      message.roomId = roomId;\n    }\n\n    return conn.insert('chat_message', message.toMap());\n  }\n\n  Future<void> fixMessageStatus(int roomId) async {\n    return conn.transaction((txn) async {\n      await txn.update(\n        'chat_message',\n        {'status': 2},\n        where: 'room_id = ? AND status = 0',\n        whereArgs: [roomId],\n      );\n    });\n  }\n\n  Future<void> updateMessage(int roomId, int id, Message message) async {\n    return conn.transaction((txn) async {\n      await txn.update(\n        'chat_message',\n        message.toMap(),\n        where: 'id = ? AND room_id = ?',\n        whereArgs: [id, roomId],\n      );\n    });\n  }\n\n  Future<void> updateMessagePart(\n    int roomId,\n    int id,\n    List<MessagePart> parts,\n  ) async {\n    var kvs = <String, Object?>{};\n    for (var element in parts) {\n      kvs[element.key] = element.value;\n    }\n\n    return conn.transaction((txn) async {\n      await txn.update(\n        'chat_message',\n        kvs,\n        where: 'id = ? AND room_id = ?',\n        whereArgs: [id, roomId],\n      );\n    });\n  }\n\n  Future<void> removeMessage(int roomId, List<int> ids) async {\n    var placeholders = List.generate(ids.length, (index) => '?').join(',');\n    ids.add(roomId);\n    return conn.transaction((txn) async {\n      await txn.delete(\n        'chat_message',\n        where: 'id in ($placeholders) AND room_id = ?',\n        whereArgs: ids,\n      );\n    });\n  }\n\n  Future<void> clearMessages(int roomId, {int? userId}) async {\n    final userConditon = userId == null ? ' AND user_id IS NULL' : ' AND user_id = $userId';\n    return conn.transaction((txn) async {\n      await txn.delete(\n        'chat_message',\n        where: 'room_id = ? $userConditon',\n        whereArgs: [roomId],\n      );\n    });\n  }\n}\n"
  },
  {
    "path": "lib/repo/data/creative_island_data.dart",
    "content": "import 'package:askaide/repo/model/creative_island_history.dart';\nimport 'package:sqflite/sqlite_api.dart';\n\nclass CreativeIslandDataProvider {\n  Database conn;\n  CreativeIslandDataProvider(this.conn);\n\n  Future<List<CreativeIslandHistory>> getRecentHistories(\n      String itemId, int count,\n      {int? userId}) async {\n    final userConditon =\n        userId == null ? ' AND user_id IS NULL' : ' AND user_id = $userId';\n\n    List<Map<String, Object?>> histories = await conn.query(\n      'creative_island_history',\n      where: 'item_id = ? $userConditon',\n      whereArgs: [itemId],\n      orderBy: 'id DESC',\n      limit: count,\n    );\n\n    return histories.map((e) => CreativeIslandHistory.fromJson(e)).toList();\n  }\n\n  Future<CreativeIslandHistory> create(\n    String itemId, {\n    String? arguments,\n    String? prompt,\n    String? answer,\n    String? taskId,\n    String? status,\n    int? userId,\n  }) async {\n    final his = CreativeIslandHistory(\n      itemId,\n      arguments: arguments,\n      prompt: prompt,\n      answer: answer,\n      taskId: taskId,\n      status: status,\n      userId: userId,\n    );\n\n    his.id = await conn.insert('creative_island_history', his.toJson());\n    return his;\n  }\n\n  /// 更新\n  Future<void> update(int id, CreativeIslandHistory his) async {\n    await conn.update('creative_island_history', his.toJson(),\n        where: 'id = ?', whereArgs: [id]);\n  }\n\n  /// 删除 room\n  Future<int> delete(int hisId) async {\n    return conn\n        .delete('creative_island_history', where: 'id = ?', whereArgs: [hisId]);\n  }\n\n  /// 获取指定历史信息\n  Future<CreativeIslandHistory?> history(int id) async {\n    List<Map<String, Object?>> histories = await conn.query(\n        'creative_island_history',\n        where: 'id = ?',\n        whereArgs: [id],\n        limit: 1);\n    if (histories.isEmpty) {\n      return null;\n    }\n\n    return CreativeIslandHistory.fromJson(histories.first);\n  }\n}\n"
  },
  {
    "path": "lib/repo/data/room_data.dart",
    "content": "import 'package:askaide/helper/constant.dart';\nimport 'package:askaide/repo/model/room.dart';\nimport 'package:sqflite/sqlite_api.dart';\n\nclass RoomDataProvider {\n  Database conn;\n  RoomDataProvider(this.conn);\n\n  /// 获取所有 room\n  Future<List<Room>> chatRooms({int? userId}) async {\n    final userConditon =\n        userId == null ? 'user_id IS NULL' : 'user_id = $userId';\n    List<Map<String, Object?>> rooms = await conn.query(\n      'chat_room',\n      where: userConditon,\n      orderBy: 'priority DESC, last_active_time DESC',\n    );\n\n    return rooms.map((e) => Room.fromMap(e)).toList();\n  }\n\n  /// 创建 room\n  Future<Room> createRoom({\n    required String name,\n    required String category,\n    String? description,\n    String? model,\n    String? color,\n    String? systemPrompt,\n    int? userId,\n    int? maxContext,\n  }) async {\n    final room = Room(\n      name,\n      category,\n      userId: userId,\n      color: color,\n      model: model ?? defaultChatModel,\n      description: description,\n      systemPrompt: systemPrompt,\n      maxContext: maxContext ?? 10,\n      createdAt: DateTime.now(),\n      lastActiveTime: DateTime.now(),\n      iconData: '57683,MaterialIcons',\n    );\n\n    room.id = await conn.insert('chat_room', room.toJson());\n    return room;\n  }\n\n  /// 删除 room\n  Future<int> deleteRoom(int roomId) async {\n    return conn.delete('chat_room', where: 'id = ?', whereArgs: [roomId]);\n  }\n\n  /// 获取指定 room\n  Future<Room?> room(int roomId) async {\n    List<Map<String, Object?>> rooms = await conn.query('chat_room',\n        where: 'id = ?', whereArgs: [roomId], limit: 1);\n    if (rooms.isEmpty) {\n      return null;\n    }\n\n    return Room.fromMap(rooms.first);\n  }\n\n  /// 更新 room\n  Future<int> updateRoom(Room room) async {\n    if (room.id == null) {\n      throw Exception('room id is null');\n    }\n\n    return conn.update(\n      'chat_room',\n      room.toJson(),\n      where: 'id = ?',\n      whereArgs: [room.id],\n    );\n  }\n\n  /// 更新 Room 最后活跃时间\n  Future<void> updateRoomLastActiveTime(int roomId) async {\n    await conn.update(\n      'chat_room',\n      {\n        'last_active_time': DateTime.now().millisecondsSinceEpoch,\n      },\n      where: 'id = ?',\n      whereArgs: [roomId],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/repo/data/settings_data.dart",
    "content": "import 'package:sqflite/sqflite.dart';\n\nclass SettingDataProvider {\n  Database conn;\n\n  SettingDataProvider(this.conn);\n\n  final _settings = <String, String>{};\n  final _listeners =\n      <Function(SettingDataProvider settings, String key, String value)>[];\n\n  Future<void> loadSettings() async {\n    List<Map<String, Object?>> kvs = await conn.query('settings');\n\n    _settings.clear();\n    for (var kv in kvs) {\n      _settings[kv['key'] as String] = kv['value'] as String;\n    }\n  }\n\n  void listen(\n      Function(SettingDataProvider settings, String key, String value)\n          listener) {\n    _listeners.add(listener);\n  }\n\n  Future<void> set(String key, String value) async {\n    _settings[key] = value;\n    final kvs =\n        await conn.query('settings', where: 'key = ?', whereArgs: [key]);\n    if (kvs.isEmpty) {\n      await conn.insert('settings', {'key': key, 'value': value});\n    } else {\n      await conn.update('settings', {'value': value},\n          where: 'key = ?', whereArgs: [key]);\n    }\n\n    for (var f in _listeners) {\n      f(this, key, value);\n    }\n  }\n\n  String? get(String key) {\n    return _settings[key];\n  }\n\n  String getDefault(String key, String defaultValue) {\n    return _settings[key] ?? defaultValue;\n  }\n\n  int getDefaultInt(String key, int defaultValue) {\n    return int.tryParse(_settings[key] ?? '') ?? defaultValue;\n  }\n\n  bool getDefaultBool(String key, bool defaultValue) {\n    return _settings[key] == 'true' ? true : defaultValue;\n  }\n\n  double getDefaultDouble(String key, double defaultValue) {\n    return double.tryParse(_settings[key] ?? '') ?? defaultValue;\n  }\n}\n"
  },
  {
    "path": "lib/repo/deepai_repo.dart",
    "content": "import 'dart:convert';\n\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/env.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/repo/data/settings_data.dart';\nimport 'package:http/http.dart' as http;\n\nclass DeepAIRepository {\n  late String serverURL;\n  late String apiKey;\n  late bool selfHosted;\n\n  Map<String, String> _headers = {};\n  late String language;\n\n  final SettingDataProvider settings;\n\n  DeepAIRepository(this.settings) {\n    selfHosted = settings.getDefaultBool(settingDeepAISelfHosted, false);\n    language = settings.getDefault(settingLanguage, 'zh');\n\n    _reloadServerConfig();\n\n    settings.listen((settings, key, value) {\n      selfHosted = settings.getDefaultBool(settingDeepAISelfHosted, false);\n      language = settings.getDefault(settingLanguage, 'zh');\n\n      _reloadServerConfig();\n    });\n  }\n\n  void _reloadServerConfig() {\n    if (selfHosted) {\n      serverURL = settings.getDefault(settingDeepAIURL, defaultDeepAIServerURL);\n      apiKey = settings.getDefault(settingDeepAIAPIToken, '');\n      _headers = {};\n    } else {\n      apiKey = settings.getDefault(settingAPIServerToken, '');\n      serverURL = apiServerURL;\n\n      _headers = {\n        'X-CLIENT-VERSION': clientVersion,\n        'X-PLATFORM': PlatformTool.operatingSystem(),\n        'X-PLATFORM-VERSION': PlatformTool.operatingSystemVersion(),\n        'X-LANGUAGE': language,\n      };\n    }\n  }\n\n  // static List<Model> supportModels() {\n  //   return [\n  //     Model(\n  //       'text2img',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '根据文本描述创建图像',\n  //     ),\n  //     Model(\n  //       'cute-creature-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成可爱的动物图像',\n  //     ),\n  //     Model(\n  //       'fantasy-world-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成奇幻世界图像',\n  //     ),\n  //     Model(\n  //       'cyberpunk-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成未来科幻图像',\n  //     ),\n  //     Model(\n  //       'anime-portrait-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成动漫人物图像',\n  //     ),\n  //     Model(\n  //       'old-style-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成老式风格图像',\n  //     ),\n  //     Model(\n  //       'renaissance-painting-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成文艺复兴风格图像',\n  //     ),\n  //     Model(\n  //       'abstract-painting-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成抽象风格图像',\n  //     ),\n  //     Model(\n  //       'impressionism-painting-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成印象派风格图像',\n  //     ),\n  //     Model(\n  //       'surreal-graphics-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成超现实风格图像',\n  //     ),\n  //     Model(\n  //       '3d-objects-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成3D物体图像',\n  //     ),\n  //     Model(\n  //       'origami-3d-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成折纸风格图像',\n  //     ),\n  //     Model(\n  //       'hologram-3d-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成全息图像',\n  //     ),\n  //     Model(\n  //       '3d-character-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成3D人物图像',\n  //     ),\n  //     Model(\n  //       'watercolor-painting-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成水彩风格图像',\n  //     ),\n  //     Model(\n  //       'pop-art-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成流行艺术风格图像',\n  //     ),\n  //     Model(\n  //       'contemporary-architecture-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成现代建筑图像',\n  //     ),\n  //     Model(\n  //       'future-architecture-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成未来建筑图像',\n  //     ),\n  //     Model(\n  //       'watercolor-architecture-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成水彩建筑图像',\n  //     ),\n  //     Model(\n  //       'fantasy-character-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成奇幻人物图像',\n  //     ),\n  //     Model(\n  //       'steampunk-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成蒸汽朋克风格图像',\n  //     ),\n  //     Model(\n  //       'logo-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成Logo图像',\n  //     ),\n  //     Model(\n  //       'pixel-art-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成像素风格图像',\n  //     ),\n  //     Model(\n  //       'street-art-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成街头艺术风格图像',\n  //     ),\n  //     Model(\n  //       'surreal-portrait-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成超现实人物图像',\n  //     ),\n  //     Model(\n  //       'anime-world-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成动漫世界图像',\n  //     ),\n  //     Model(\n  //       'fantasy-portrait-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成奇幻人物图像',\n  //     ),\n  //     Model(\n  //       'comics-portrait-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成漫画人物图像',\n  //     ),\n  //     Model(\n  //       'cyberpunk-portrait-generator',\n  //       'deepai',\n  //       category: modelTypeDeepAI,\n  //       description: '生成未来科幻人物图像',\n  //     ),\n  //   ];\n  // }\n\n  Future<DeepAIPaintResult> painting(\n    String model,\n    String prompt, {\n    int gridSize = 1,\n    int width = 512,\n    int height = 512,\n    String? negativePrompt,\n  }) async {\n    var params = <String, dynamic>{\n      \"text\": prompt,\n      \"grid_size\": gridSize.toString(),\n      \"width\": width.toString(),\n      \"height\": height.toString(),\n    };\n    if (negativePrompt != null) {\n      params['negative_prompt'] = negativePrompt;\n    }\n\n    var url = selfHosted\n        ? Uri.parse('$serverURL/api/$model')\n        : Uri.parse('$serverURL/v1/deepai/images/$model/text-to-image');\n\n    var headers = <String, String>{};\n    headers.addAll(_headers);\n    if (selfHosted) {\n      headers['api-key'] = apiKey;\n    } else {\n      headers['Authorization'] = 'Bearer $apiKey';\n    }\n\n    var resp = await http.post(\n      url,\n      body: params,\n      headers: headers,\n    );\n\n    if (resp.statusCode != 200) {\n      return Future.error((resp.body as Map<String, dynamic>)['error']);\n    }\n\n    var ret = jsonDecode(resp.body) as Map<String, dynamic>;\n\n    return Future.value(DeepAIPaintResult(ret['id'], ret['output_url']));\n  }\n\n  Future<String> paintingAsync(\n    String model,\n    String prompt, {\n    int gridSize = 1,\n    int width = 512,\n    int height = 512,\n    String? negativePrompt,\n  }) async {\n    var params = <String, dynamic>{\n      \"text\": prompt,\n      \"grid_size\": gridSize.toString(),\n      \"width\": width.toString(),\n      \"height\": height.toString(),\n    };\n    if (negativePrompt != null) {\n      params['negative_prompt'] = negativePrompt;\n    }\n\n    var url =\n        Uri.parse('$serverURL/v1/deepai/images/$model/text-to-image-async');\n\n    var headers = <String, String>{};\n    headers.addAll(_headers);\n    headers['Authorization'] = 'Bearer $apiKey';\n\n    var resp = await http.post(\n      url,\n      body: params,\n      headers: headers,\n    );\n\n    if (resp.statusCode != 200) {\n      return Future.error((resp.body as Map<String, dynamic>)['error']);\n    }\n\n    return Future.value(jsonDecode(resp.body)['task_id']);\n  }\n}\n\nclass DeepAIPaintResult {\n  final String id;\n  final String url;\n\n  DeepAIPaintResult(this.id, this.url);\n}\n"
  },
  {
    "path": "lib/repo/model/chat_history.dart",
    "content": "class ChatHistory {\n  int? id;\n  int? userId;\n  int? roomId;\n  String? title;\n  String? lastMessage;\n  String? model;\n  DateTime? createdAt;\n  DateTime? updatedAt;\n\n  ChatHistory({\n    this.id,\n    this.userId,\n    this.roomId,\n    this.title,\n    this.model,\n    this.lastMessage,\n    this.createdAt,\n    this.updatedAt,\n  });\n\n  ChatHistory.fromMap(Map<String, Object?> map) {\n    id = map['id'] as int?;\n    userId = map['user_id'] as int?;\n    roomId = map['room_id'] as int?;\n    title = map['title'] as String?;\n    model = map['model'] as String?;\n    lastMessage = map['last_message'] as String?;\n    createdAt = DateTime.fromMillisecondsSinceEpoch(map['created_at'] as int);\n    updatedAt = DateTime.fromMillisecondsSinceEpoch(map['updated_at'] as int);\n  }\n\n  Map<String, Object?> toMap() {\n    return {\n      'id': id,\n      'user_id': userId,\n      'room_id': roomId,\n      'title': title,\n      'model': model,\n      'last_message': lastMessage,\n      'created_at': createdAt?.millisecondsSinceEpoch,\n      'updated_at': updatedAt?.millisecondsSinceEpoch,\n    };\n  }\n}\n"
  },
  {
    "path": "lib/repo/model/chat_message.dart",
    "content": "import 'dart:convert';\n\nimport 'package:dart_openai/openai.dart';\n\nclass ChatMessage extends OpenAIChatCompletionChoiceMessageModel {\n  final List<String>? images;\n  final String? file;\n  ChatMessage(\n      {required super.role, required super.content, this.images, this.file});\n\n  @override\n  Map<String, dynamic> toMap() {\n    final Map<String, dynamic> res = {\n      \"role\": role.name,\n      \"content\": content,\n    };\n\n    if (file != null || (images != null && images!.isNotEmpty)) {\n      final multipartContent = <dynamic>[];\n\n      if (file != null) {\n        try {\n          multipartContent.add({\n            'type': 'file',\n            'file': jsonDecode(file!),\n          });\n        } catch (ignore) {\n          // ignore\n        }\n      }\n\n      if (images != null && images!.isNotEmpty) {\n        multipartContent.addAll(images\n                ?.map((e) => {\n                      'type': 'image_url',\n                      'image_url': {'url': e}\n                    })\n                .toList() ??\n            []);\n      }\n\n      multipartContent.add({\n        'type': 'text',\n        'text': content,\n      });\n\n      res['multipart_content'] = multipartContent;\n    }\n\n    return res;\n  }\n}\n"
  },
  {
    "path": "lib/repo/model/creative_island_history.dart",
    "content": "class CreativeIslandHistory {\n  int? id;\n  String itemId;\n  int? userId;\n  String? arguments;\n  String? prompt;\n  String? answer;\n  String? taskId;\n  String? status;\n  DateTime createdAt;\n\n  CreativeIslandHistory(\n    this.itemId, {\n    this.id,\n    this.userId,\n    this.arguments,\n    this.prompt,\n    this.answer,\n    DateTime? createdAt,\n    this.taskId,\n    this.status,\n  }) : createdAt = createdAt ?? DateTime.now();\n\n  Map<String, Object?> toJson() {\n    return {\n      'id': id,\n      'item_id': itemId,\n      'user_id': userId,\n      'arguments': arguments,\n      'prompt': prompt,\n      'answer': answer,\n      'task_id': taskId,\n      'status': status,\n      'created_at': createdAt.millisecondsSinceEpoch,\n    };\n  }\n\n  CreativeIslandHistory.fromJson(Map<String, Object?> map)\n      : id = map['id'] as int?,\n        itemId = map['item_id'] as String,\n        userId = map['user_id'] as int?,\n        arguments = map['arguments'] as String?,\n        prompt = map['prompt'] as String?,\n        answer = map['answer'] as String?,\n        taskId = map['task_id'] as String?,\n        status = map['status'] as String?,\n        createdAt =\n            DateTime.fromMillisecondsSinceEpoch(map['created_at'] as int? ?? 0);\n}\n"
  },
  {
    "path": "lib/repo/model/group.dart",
    "content": "import 'package:askaide/repo/model/misc.dart';\n\nconst groupMessageStatusWaiting = 0;\nconst groupMessageStatusSuccess = 1;\nconst groupMessageStatusFailed = 2;\n\nclass ChatGroup {\n  final RoomInServer group;\n  final List<GroupMember> members;\n\n  GroupMember? findMember(int memberId) {\n    return members.where((member) => member.id == memberId).firstOrNull;\n  }\n\n  ChatGroup({\n    required this.group,\n    required this.members,\n  });\n\n  factory ChatGroup.fromJson(Map<String, dynamic> json) {\n    return ChatGroup(\n      group: RoomInServer.fromJson(json['group']),\n      members: (json['members'] as List)\n          .map((member) => GroupMember.fromJson(member))\n          .toList(),\n    );\n  }\n\n  Map<String, dynamic> toJson() {\n    return {\n      'group': group.toJson(),\n      'members': members.map((member) => member.toJson()).toList(),\n    };\n  }\n}\n\nclass GroupMember {\n  final int? id;\n  final String modelId;\n  final String modelName;\n  final String? avatarUrl;\n  final int? status;\n\n  GroupMember({\n    this.id,\n    required this.modelId,\n    required this.modelName,\n    this.avatarUrl,\n    this.status,\n  });\n\n  factory GroupMember.fromJson(Map<String, dynamic> json) {\n    return GroupMember(\n      id: json['id'],\n      modelId: json['model_id'],\n      modelName: json['model_name'],\n      avatarUrl: json['avatar_url'],\n      status: json['status'],\n    );\n  }\n\n  Map<String, dynamic> toJson() {\n    return {\n      'id': id,\n      'model_id': modelId,\n      'model_name': modelName,\n      'avatar_url': avatarUrl,\n      'status': status,\n    };\n  }\n}\n\nclass GroupMessage {\n  final int id;\n  final String message;\n  final String role;\n  final String type;\n  final int groupId;\n  final int? tokenConsumed;\n  final int? quotaConsumed;\n  final int? pid;\n  final int? memberId;\n  final int status;\n  DateTime? createdAt;\n  DateTime? updatedAt;\n\n  /// 管理接口专用\n  final String? model;\n\n  GroupMessage({\n    required this.id,\n    required this.message,\n    required this.role,\n    required this.status,\n    required this.type,\n    required this.groupId,\n    this.tokenConsumed,\n    this.quotaConsumed,\n    this.pid,\n    this.memberId,\n    this.createdAt,\n    this.updatedAt,\n    this.model,\n  });\n\n  factory GroupMessage.fromJson(Map<String, dynamic> json) {\n    return GroupMessage(\n      id: json['id'],\n      message: json['message'] ?? '',\n      role: json['role'] == 1 ? 'user' : 'assistant',\n      type: json['type'] ?? 'text',\n      groupId: json['group_id'],\n      tokenConsumed: json['token_consumed'],\n      quotaConsumed: json['quota_consumed'],\n      pid: json['pid'],\n      memberId: json['member_id'],\n      status: json['status'] ?? 0,\n      createdAt: DateTime.tryParse(json['CreatedAt']),\n      updatedAt: DateTime.tryParse(json['UpdatedAt']),\n      model: json['model'],\n    );\n  }\n\n  Map<String, dynamic> toJson() {\n    return {\n      'id': id,\n      'message': message,\n      'role': role,\n      'type': type,\n      'group_id': groupId,\n      'token_consumed': tokenConsumed,\n      'quota_consumed': quotaConsumed,\n      'pid': pid,\n      'member_id': memberId,\n      'status': status,\n      'CreatedAt': createdAt?.toIso8601String(),\n      'UpdatedAt': updatedAt?.toIso8601String(),\n      'model': model,\n    };\n  }\n}\n\nclass GroupChatSendRequestMessage {\n  final String role;\n  final String content;\n  final int? memberId;\n\n  GroupChatSendRequestMessage({\n    required this.role,\n    required this.content,\n    this.memberId,\n  });\n\n  Map<String, dynamic> toJson() {\n    return {\n      'role': role,\n      'content': content,\n      'member_id': memberId,\n    };\n  }\n\n  factory GroupChatSendRequestMessage.fromJson(Map<String, dynamic> json) {\n    return GroupChatSendRequestMessage(\n      role: json['role'],\n      content: json['content'],\n      memberId: json['member_id'],\n    );\n  }\n}\n\nclass GroupChatSendRequest {\n  final String message;\n  final List<int> memberIds;\n\n  GroupChatSendRequest({\n    required this.message,\n    required this.memberIds,\n  });\n\n  Map<String, dynamic> toJson() {\n    return {\n      'message': message,\n      'member_ids': memberIds,\n    };\n  }\n\n  factory GroupChatSendRequest.fromJson(Map<String, dynamic> json) {\n    return GroupChatSendRequest(\n      message: json['message'],\n      memberIds: (json['member_ids'] as List).map((e) => e as int).toList(),\n    );\n  }\n}\n\nclass GroupChatSendResponseTask {\n  final int memberId;\n  final String taskId;\n  final int answerId;\n\n  GroupChatSendResponseTask({\n    required this.memberId,\n    required this.taskId,\n    required this.answerId,\n  });\n\n  factory GroupChatSendResponseTask.fromJson(Map<String, dynamic> json) {\n    return GroupChatSendResponseTask(\n      memberId: json['member_id'],\n      taskId: json['task_id'],\n      answerId: json['answer_id'],\n    );\n  }\n\n  Map<String, dynamic> toJson() {\n    return {\n      'member_id': memberId,\n      'task_id': taskId,\n      'answer_id': answerId,\n    };\n  }\n}\n\nclass GroupChatSendResponse {\n  final int questionId;\n  final List<GroupChatSendResponseTask> tasks;\n\n  GroupChatSendResponse({\n    required this.questionId,\n    required this.tasks,\n  });\n\n  factory GroupChatSendResponse.fromJson(Map<String, dynamic> json) {\n    return GroupChatSendResponse(\n      questionId: json['question_id'],\n      tasks: (json['tasks'] as List)\n          .map((task) => GroupChatSendResponseTask.fromJson(task))\n          .toList(),\n    );\n  }\n\n  Map<String, dynamic> toJson() {\n    return {\n      'question_id': questionId,\n      'tasks': tasks.map((task) => task.toJson()).toList(),\n    };\n  }\n}\n"
  },
  {
    "path": "lib/repo/model/message.dart",
    "content": "import 'dart:convert';\n\nimport 'package:askaide/helper/helper.dart';\n\n/// 聊天消息\nclass Message {\n  /// 聊天所属的聊天室 ID\n  int? roomId;\n\n  /// 用户ID\n  int? userId;\n\n  /// 聊天历史 ID\n  int? chatHistoryId;\n\n  /// 消息ID\n  int? id;\n\n  /// 消息方向\n  Role role;\n\n  /// 消息内容\n  String text;\n\n  /// 消息附加信息，用于提供模型相关信息\n  String? extra;\n\n  /// 消息发送时的模型\n  String? model;\n\n  /// 消息类型\n  MessageType type;\n\n  /// 发送者\n  String? user;\n\n  /// 时间戳\n  DateTime? ts;\n\n  /// 关联消息ID（问题 ID）\n  int? refId;\n\n  /// 服务端 ID\n  int? serverId;\n\n  /// 消息状态: 1-成功 0-等待应答 2-失败\n  int status;\n\n  /// 消息消耗的配额\n  int? quotaConsumed;\n\n  /// 消息消耗的 token\n  int? tokenConsumed;\n\n  /// 是否当前消息已就绪，不需要持久化\n  bool isReady = true;\n\n  /// 消息发送者的头像，不需要持久化\n  String? avatarUrl;\n\n  /// 消息发送者的名称，不需要持久化\n  String? senderName;\n\n  /// 消息图片列表\n  List<String>? images;\n  // Uploaded file by user (json(name, url))\n  String? file;\n\n  List<String>? flags;\n\n  Message(\n    this.role,\n    this.text, {\n    required this.type,\n    this.userId,\n    this.chatHistoryId,\n    this.id,\n    this.user,\n    this.ts,\n    this.model,\n    this.roomId,\n    this.extra,\n    this.refId,\n    this.serverId,\n    this.status = 1,\n    this.quotaConsumed,\n    this.tokenConsumed,\n    this.avatarUrl,\n    this.senderName,\n    this.images,\n    this.file,\n    this.flags,\n  });\n\n  /// 设置消息附加信息\n  void setExtra(dynamic data) {\n    extra = jsonEncode(data);\n  }\n\n  /// 更新消息附加信息\n  void updateExtra(dynamic data) {\n    // 需要将 data merge 到 extra 中\n    final extraData = decodeExtra();\n    if (extraData != null) {\n      data = <String, dynamic>{...extraData, ...data};\n    }\n\n    extra = jsonEncode(data);\n  }\n\n  /// 将值添加到附加信息的某个数组键中\n  void pushExtra(String key, dynamic value) {\n    var extraData = decodeExtra();\n    extraData ??= <String, dynamic>{};\n\n    if (!extraData.containsKey(key)) {\n      extraData[key] = [];\n    }\n\n    extraData[key]!.add(value);\n    extra = jsonEncode(extraData);\n  }\n\n  /// 从附加信息的某个数组键中删除最后一个值\n  void popExtra(String key) {\n    var extraData = decodeExtra();\n    extraData ??= <String, dynamic>{};\n    extraData[key]!.removeLast();\n    extra = jsonEncode(extraData);\n  }\n\n  /// 获取消息附加信息\n  decodeExtra() {\n    if (extra == null) {\n      return null;\n    }\n\n    return jsonDecode(extra!);\n  }\n\n  /// 是否是系统消息，包括时间线\n  bool isSystem() {\n    return type == MessageType.system || type == MessageType.timeline || type == MessageType.contextBreak;\n  }\n\n  /// 是否是初始消息\n  bool isInitMessage() {\n    return type == MessageType.initMessage;\n  }\n\n  /// 是否是时间线\n  bool isTimeline() {\n    return type == MessageType.timeline;\n  }\n\n  /// 格式化时间\n  String friendlyTime() {\n    return humanTime(ts);\n  }\n\n  /// 是否已失败\n  bool statusIsFailed() {\n    return status == 2;\n  }\n\n  /// 是否已成功\n  bool statusIsSucceed() {\n    return status == 1;\n  }\n\n  /// 是否等待应答\n  bool statusPending() {\n    return status == 0;\n  }\n\n  String get markdownWithImages {\n    var t = text;\n    if (images != null && images!.isNotEmpty) {\n      t = images!.map((e) => '![img]($e)\\n\\n').join('') + t;\n    }\n\n    return t;\n  }\n\n  Map<String, Object?> toMap() {\n    return {\n      'id': id,\n      'user_id': userId,\n      'chat_history_id': chatHistoryId,\n      'role': role.getRoleText(),\n      'text': text,\n      'type': type.getTypeText(),\n      'extra': extra,\n      'model': model,\n      'user': user,\n      'ts': ts?.millisecondsSinceEpoch,\n      'room_id': roomId,\n      'ref_id': refId,\n      'server_id': serverId,\n      'status': status,\n      'token_consumed': tokenConsumed,\n      'quota_consumed': quotaConsumed,\n      'images': images != null ? jsonEncode(images) : null,\n      'file': file,\n      'flags': flags != null ? jsonEncode(flags) : null,\n    };\n  }\n\n  Message.fromMap(Map<String, Object?> map)\n      : id = map['id'] as int,\n        userId = map['user_id'] as int?,\n        chatHistoryId = map['chat_history_id'] as int?,\n        role = Role.getRoleFromText(map['role'] as String),\n        text = map['text'] as String,\n        extra = map['extra'] as String?,\n        model = map['model'] as String?,\n        type = MessageType.getTypeFromText(map['type'] as String),\n        user = map['user'] as String?,\n        refId = map['ref_id'] as int?,\n        serverId = map['server_id'] as int?,\n        status = (map['status'] ?? 1) as int,\n        tokenConsumed = map['token_consumed'] as int?,\n        quotaConsumed = map['quota_consumed'] as int?,\n        ts = map['ts'] == null ? null : DateTime.fromMillisecondsSinceEpoch(map['ts'] as int),\n        roomId = map['room_id'] as int?,\n        images = map['images'] == null ? null : (jsonDecode(map['images'] as String) as List<dynamic>).cast<String>(),\n        file = map['file'] as String?,\n        flags = map['flags'] == null ? null : (jsonDecode(map['flags'] as String) as List<dynamic>).cast<String>();\n}\n\nenum Role {\n  receiver,\n  sender;\n\n  static Role getRoleFromText(String value) {\n    switch (value) {\n      case 'receiver':\n        return Role.receiver;\n      case 'assistant':\n        return Role.receiver;\n      case 'sender':\n        return Role.sender;\n      case 'user':\n        return Role.sender;\n      default:\n        return Role.receiver;\n    }\n  }\n\n  String getRoleText() {\n    switch (this) {\n      case Role.receiver:\n        return 'receiver';\n      case Role.sender:\n        return 'sender';\n      default:\n        return 'receiver';\n    }\n  }\n}\n\nenum MessageType {\n  text,\n  image,\n  file,\n  audio,\n  video,\n  location,\n  command,\n  system,\n  timeline,\n  contextBreak,\n  hide,\n  initMessage;\n\n  String getTypeText() {\n    switch (this) {\n      case MessageType.text:\n        return 'text';\n      case MessageType.image:\n        return 'image';\n      case MessageType.file:\n        return 'file';\n      case MessageType.audio:\n        return 'audio';\n      case MessageType.video:\n        return 'video';\n      case MessageType.location:\n        return 'location';\n      case MessageType.command:\n        return 'command';\n      case MessageType.system:\n        return 'system';\n      case MessageType.timeline:\n        return 'timeline';\n      case MessageType.contextBreak:\n        return 'contextBreak';\n      case MessageType.hide:\n        return 'hide';\n      case MessageType.initMessage:\n        return 'initMessage';\n      default:\n        return 'text';\n    }\n  }\n\n  static MessageType getTypeFromText(String value) {\n    switch (value) {\n      case 'text':\n        return MessageType.text;\n      case 'image':\n        return MessageType.image;\n      case 'file':\n        return MessageType.file;\n      case 'audio':\n        return MessageType.audio;\n      case 'video':\n        return MessageType.video;\n      case 'location':\n        return MessageType.location;\n      case 'command':\n        return MessageType.command;\n      case 'system':\n        return MessageType.system;\n      case 'timeline':\n        return MessageType.timeline;\n      case 'contextBreak':\n        return MessageType.contextBreak;\n      case 'hide':\n        return MessageType.hide;\n      case 'initMessage':\n        return MessageType.initMessage;\n      default:\n        return MessageType.text;\n    }\n  }\n}\n"
  },
  {
    "path": "lib/repo/model/misc.dart",
    "content": "import 'package:askaide/repo/api/room_gallery.dart';\n\nenum PromotionEventClickButtonType {\n  none,\n  url,\n  inAppRoute;\n\n  static PromotionEventClickButtonType fromName(String typeName) {\n    switch (typeName) {\n      case 'url':\n        return PromotionEventClickButtonType.url;\n      case 'in_app_route':\n        return PromotionEventClickButtonType.inAppRoute;\n      default:\n        return PromotionEventClickButtonType.none;\n    }\n  }\n\n  String toName() {\n    switch (this) {\n      case PromotionEventClickButtonType.url:\n        return 'url';\n      case PromotionEventClickButtonType.inAppRoute:\n        return 'in_app_route';\n      default:\n        return 'none';\n    }\n  }\n}\n\nclass PromotionEvent {\n  String? title;\n  String content;\n  PromotionEventClickButtonType clickButtonType;\n  String? clickValue;\n  String? clickButtonColor;\n  String? backgroundImage;\n  String? textColor;\n  bool closeable;\n  int? maxCloseDurationInDays;\n\n  PromotionEvent({\n    this.title,\n    required this.content,\n    required this.clickButtonType,\n    this.clickValue,\n    this.clickButtonColor,\n    this.backgroundImage,\n    this.textColor,\n    required this.closeable,\n    this.maxCloseDurationInDays,\n  });\n\n  toJson() => {\n        'title': title,\n        'content': content,\n        'click_button_type': clickButtonType.toName(),\n        'click_value': clickValue,\n        'click_button_color': clickButtonColor,\n        'background_image': backgroundImage,\n        'text_color': textColor,\n        'closeable': closeable,\n        'max_close_duration_in_days': maxCloseDurationInDays,\n      };\n\n  static PromotionEvent fromJson(Map<String, dynamic> json) {\n    return PromotionEvent(\n      title: json['title'],\n      content: json['content'],\n      clickButtonType: PromotionEventClickButtonType.fromName(json['click_button_type'] ?? ''),\n      clickValue: json['click_value'],\n      clickButtonColor: json['click_button_color'],\n      backgroundImage: json['background_image'],\n      textColor: json['text_color'],\n      closeable: json['closeable'] ?? false,\n      maxCloseDurationInDays: json['max_close_duration_in_days'],\n    );\n  }\n}\n\nclass ShareInfo {\n  String qrCode;\n  String message;\n  String? inviteCode;\n\n  ShareInfo({\n    required this.qrCode,\n    required this.message,\n    this.inviteCode,\n  });\n\n  toJson() => {\n        'qr_code': qrCode,\n        'message': message,\n        'invite_code': inviteCode,\n      };\n\n  static ShareInfo fromJson(Map<String, dynamic> json) {\n    return ShareInfo(\n      qrCode: json['qr_code'],\n      message: json['message'],\n      inviteCode: json['invite_code'],\n    );\n  }\n}\n\nclass QuotaUsageInDay {\n  String date;\n  int used;\n\n  QuotaUsageInDay({\n    required this.date,\n    required this.used,\n  });\n\n  toJson() => {\n        'date': date,\n        'used': used,\n      };\n\n  static QuotaUsageInDay fromJson(Map<String, dynamic> json) {\n    return QuotaUsageInDay(\n      date: json['date'],\n      used: json['used'],\n    );\n  }\n}\n\nclass QuotaUsageDetailInDay {\n  int used;\n  String type;\n  String createdAt;\n\n  QuotaUsageDetailInDay({\n    required this.used,\n    required this.type,\n    required this.createdAt,\n  });\n\n  toJson() => {\n        'used': used,\n        'type': type,\n        'created_at': createdAt,\n      };\n\n  static QuotaUsageDetailInDay fromJson(Map<String, dynamic> json) {\n    return QuotaUsageDetailInDay(\n      used: json['used'],\n      type: json['type'],\n      createdAt: json['created_at'],\n    );\n  }\n}\n\nclass RoomsResponse {\n  List<RoomInServer> rooms;\n  List<RoomGallery>? suggests;\n\n  RoomsResponse({\n    required this.rooms,\n    this.suggests,\n  });\n\n  toJson() => {\n        'rooms': rooms,\n        'suggests': suggests,\n      };\n\n  static RoomsResponse fromJson(Map<String, dynamic> json) {\n    var rooms = <RoomInServer>[];\n    for (var item in json['data'] ?? []) {\n      rooms.add(RoomInServer.fromJson(item));\n    }\n\n    var suggests = <RoomGallery>[];\n    for (var item in json['suggests'] ?? []) {\n      suggests.add(RoomGallery.fromJson(item));\n    }\n\n    return RoomsResponse(\n      rooms: rooms,\n      suggests: suggests,\n    );\n  }\n}\n\nclass RoomInServer {\n  int id;\n  int userId;\n  int avatarId;\n  String? avatarUrl;\n  String name;\n  String? description;\n  int? priority;\n  String model;\n  String vendor;\n  String? systemPrompt;\n  String? initMessage;\n  int maxContext;\n  int? maxTokens;\n  int? roomType;\n  DateTime? lastActiveTime;\n  DateTime? createdAt;\n  DateTime? updatedAt;\n\n  List<String> members;\n\n  RoomInServer({\n    required this.id,\n    required this.userId,\n    required this.avatarId,\n    required this.name,\n    required this.maxContext,\n    this.roomType,\n    this.avatarUrl,\n    this.description,\n    this.priority,\n    required this.model,\n    required this.vendor,\n    this.systemPrompt,\n    this.initMessage,\n    this.lastActiveTime,\n    this.createdAt,\n    this.updatedAt,\n    this.maxTokens,\n    this.members = const [],\n  });\n\n  toJson() => {\n        'id': id,\n        'user_id': userId,\n        'avatar_id': avatarId,\n        'avatar_url': avatarUrl,\n        'name': name,\n        'description': description,\n        'priority': priority,\n        'model': model,\n        'vendor': vendor,\n        'init_message': initMessage,\n        'max_context': maxContext,\n        'room_type': roomType,\n        'max_tokens': maxTokens,\n        'system_prompt': systemPrompt,\n        'last_active_time': lastActiveTime?.toIso8601String(),\n        'created_at': createdAt?.toIso8601String(),\n        'updated_at': updatedAt?.toIso8601String(),\n        'members': members,\n      };\n\n  static RoomInServer fromJson(Map<String, dynamic> json) {\n    return RoomInServer(\n      id: json['id'],\n      userId: json['user_id'],\n      avatarId: json['avatar_id'] ?? 0,\n      avatarUrl: json['avatar_url'],\n      name: json['name'],\n      description: json['description'],\n      priority: json['priority'],\n      model: json['model'] ?? '',\n      vendor: json['vendor'] ?? '',\n      systemPrompt: json['system_prompt'],\n      initMessage: json['init_message'],\n      maxContext: json['max_context'] ?? 10,\n      maxTokens: json['max_tokens'],\n      roomType: json['room_type'],\n      lastActiveTime: json['last_active_time'] != null ? DateTime.parse(json['last_active_time']) : null,\n      createdAt: json['CreatedAt'] != null ? DateTime.parse(json['CreatedAt']) : null,\n      updatedAt: json['UpdatedAt'] != null ? DateTime.parse(json['UpdatedAt']) : null,\n      members: (json['members'] as List<dynamic>?)?.map((e) => e.toString()).toList() ?? [],\n    );\n  }\n}\n\nclass MessageInServer {\n  final int id;\n  final int userId;\n  final int roomId;\n  final String message;\n  final int role;\n  final int tokenConsumed;\n  final int quotaConsumed;\n  final int pid;\n  final String model;\n  final int status;\n  final String error;\n  final DateTime createdAt;\n  final DateTime updatedAt;\n\n  MessageInServer({\n    required this.id,\n    required this.userId,\n    required this.roomId,\n    required this.message,\n    required this.role,\n    required this.tokenConsumed,\n    required this.quotaConsumed,\n    required this.pid,\n    required this.model,\n    required this.status,\n    required this.error,\n    required this.createdAt,\n    required this.updatedAt,\n  });\n\n  toJson() => {\n        'id': id,\n        'user_id': userId,\n        'room_id': roomId,\n        'message': message,\n        'role': role,\n        'token_consumed': tokenConsumed,\n        'quota_consumed': quotaConsumed,\n        'pid': pid,\n        'model': model,\n        'status': status,\n        'error': error,\n        'created_at': createdAt.toIso8601String(),\n        'updated_at': updatedAt.toIso8601String(),\n      };\n\n  static MessageInServer fromJson(Map<String, dynamic> json) {\n    return MessageInServer(\n      id: json['id'] ?? 0,\n      userId: json['user_id'] ?? 0,\n      roomId: json['room_id'] ?? 0,\n      message: json['message'] ?? '',\n      role: json['role'] ?? 1,\n      tokenConsumed: json['token_consumed'] ?? 0,\n      quotaConsumed: json['quota_consumed'] ?? 0,\n      pid: json['pid'] ?? 0,\n      model: json['model'] ?? '',\n      status: json['status'] ?? 1,\n      error: json['error'] ?? '',\n      createdAt: DateTime.parse(json['CreatedAt']),\n      updatedAt: DateTime.parse(json['CreatedAt']),\n    );\n  }\n}\n\nclass VersionCheckResp {\n  bool hasUpdate;\n  String serverVersion;\n  bool forceUpdate;\n  String url;\n  String message;\n\n  VersionCheckResp({\n    required this.hasUpdate,\n    required this.serverVersion,\n    required this.forceUpdate,\n    required this.url,\n    required this.message,\n  });\n\n  toJson() => {\n        'has_update': hasUpdate,\n        'server_version': serverVersion,\n        'force_update': forceUpdate,\n        'url': url,\n        'message': message,\n      };\n\n  static VersionCheckResp fromJson(Map<String, dynamic> json) {\n    return VersionCheckResp(\n      hasUpdate: json['has_update'] ?? false,\n      serverVersion: json['server_version'],\n      forceUpdate: json['force_update'] ?? false,\n      url: json['url'],\n      message: json['message'],\n    );\n  }\n}\n\nclass TrySignInResp {\n  String token;\n  bool exist;\n\n  TrySignInResp({\n    required this.token,\n    required this.exist,\n  });\n\n  toJson() => {\n        'token': token,\n        'exist': exist,\n      };\n\n  static TrySignInResp fromJson(Map<String, dynamic> json) {\n    return TrySignInResp(\n      token: json['token'],\n      exist: json['exist'],\n    );\n  }\n}\n\nclass SignInResp {\n  int id;\n  String name;\n  String? email;\n  String? phone;\n  String token;\n  bool isNewUser;\n  int reward;\n  bool needBindPhone;\n\n  SignInResp({\n    required this.id,\n    required this.name,\n    this.email,\n    required this.token,\n    this.phone,\n    this.isNewUser = false,\n    this.reward = 0,\n    this.needBindPhone = false,\n  });\n\n  toJson() => {\n        'id': id,\n        'name': name,\n        'email': email,\n        'phone': phone,\n        'token': token,\n        'is_new_user': isNewUser,\n        'reward': reward,\n        'need_bind_phone': needBindPhone,\n      };\n\n  static SignInResp fromJson(Map<String, dynamic> json) {\n    return SignInResp(\n      id: json['id'],\n      name: json['name'],\n      email: json['email'],\n      phone: json['phone'],\n      token: json['token'],\n      isNewUser: json['is_new_user'] ?? false,\n      reward: json['reward'] ?? 0,\n      needBindPhone: json['need_bind_phone'] ?? false,\n    );\n  }\n}\n\nclass AsyncTaskResp {\n  String status;\n  List<String>? errors;\n  List<String>? resources;\n  String? originImage;\n  int? width;\n  int? height;\n\n  AsyncTaskResp(this.status, {this.errors, this.resources, this.originImage, this.width, this.height});\n\n  toJson() => {\n        'status': status,\n        'errors': errors,\n        'resources': resources,\n        'origin_image': originImage,\n        'width': width,\n        'height': height,\n      };\n\n  static AsyncTaskResp fromJson(Map<String, dynamic> json) {\n    return AsyncTaskResp(\n      json['status'],\n      errors: json['errors'] != null ? (json['errors'] as List<dynamic>).map((e) => e.toString()).toList() : null,\n      resources:\n          json['resources'] != null ? (json['resources'] as List<dynamic>).map((e) => e.toString()).toList() : null,\n      originImage: json['origin_image'],\n      width: json['width'],\n      height: json['height'],\n    );\n  }\n}\n\nclass Prompt {\n  String title;\n  String content;\n\n  Prompt(this.title, this.content);\n\n  toJson() {\n    return {\n      'title': title,\n      'content': content,\n    };\n  }\n\n  fromJson(Map<String, dynamic> json) {\n    title = json['title'];\n    content = json['content'];\n  }\n}\n\nclass ChatExample {\n  String title;\n  String? content;\n  List<String> models;\n  List<String> tags;\n\n  ChatExample(\n    this.title, {\n    this.content,\n    this.models = const [],\n    this.tags = const [],\n  });\n\n  get text => content ?? title;\n\n  toJson() => {\n        'title': title,\n        'content': content,\n        'models': models,\n        'tags': tags,\n      };\n\n  fromJson(Map<String, dynamic> json) {\n    title = json['title'];\n    content = json['content'];\n    models = json['models'];\n    tags = json['tags'];\n  }\n}\n\nclass TranslateText {\n  String? result;\n  String? speakUrl;\n\n  TranslateText(this.result, this.speakUrl);\n\n  toJson() => {\n        'result': result,\n        'speak_url': speakUrl,\n      };\n\n  static fromJson(Map<String, dynamic> json) {\n    return TranslateText(json['result'], json['speak_url']);\n  }\n}\n\nclass UploadInitResponse {\n  String bucket;\n  String key;\n  String token;\n  String url;\n  bool uploaded;\n\n  UploadInitResponse(\n    this.key,\n    this.bucket,\n    this.token,\n    this.url, {\n    this.uploaded = false,\n  });\n\n  toJson() => {\n        'bucket': bucket,\n        'key': key,\n        'token': token,\n        'url': url,\n        'uploaded': uploaded,\n      };\n\n  static fromJson(Map<String, dynamic> json) {\n    return UploadInitResponse(\n      json['key'],\n      json['bucket'],\n      json['token'],\n      json['url'],\n      uploaded: json['uploaded'] ?? false,\n    );\n  }\n}\n\nclass ModelStyle {\n  String id;\n  String name;\n  String? preview;\n\n  ModelStyle({required this.id, required this.name, this.preview});\n\n  toJson() => {\n        'id': id,\n        'name': name,\n        'preview': preview,\n      };\n\n  static ModelStyle fromJson(Map<String, dynamic> json) {\n    return ModelStyle(\n      id: json['id'],\n      name: json['name'],\n      preview: json['preview'],\n    );\n  }\n}\n\nclass Model {\n  String id;\n  String name;\n  String shortName;\n  String? description;\n  String? priceInfo;\n  bool isChat;\n  bool isImage;\n  bool disabled;\n  String? avatarUrl;\n  bool supportVision;\n  bool supportReasoning;\n  bool supportSearch;\n\n  String category;\n  String? tag;\n  String? tagTextColor;\n  String? tagBgColor;\n  bool isNew;\n  bool isDefault;\n\n  bool userNoPermission;\n\n  String get realModelId {\n    return id.split(':').last;\n  }\n\n  Model({\n    required this.id,\n    required this.name,\n    required this.shortName,\n    required this.category,\n    required this.isChat,\n    required this.isImage,\n    this.description,\n    this.priceInfo,\n    this.disabled = false,\n    this.tag,\n    this.avatarUrl,\n    this.supportVision = false,\n    this.supportReasoning = false,\n    this.supportSearch = false,\n    this.tagBgColor,\n    this.tagTextColor,\n    this.isNew = false,\n    this.isDefault = false,\n    this.userNoPermission = false,\n  });\n\n  toJson() => {\n        'id': id,\n        'name': name,\n        'short_name': shortName,\n        'description': description,\n        'price_info': priceInfo,\n        'category': category,\n        'is_chat': isChat,\n        'is_image': isImage,\n        'disabled': disabled,\n        'tag': tag,\n        'avatar_url': avatarUrl,\n        'support_vision': supportVision,\n        'support_reasoning': supportReasoning,\n        'support_search': supportSearch,\n        'tag_bg_color': tagBgColor,\n        'tag_text_color': tagTextColor,\n        'is_new': isNew,\n        'is_default': isDefault,\n        'user_no_permission': userNoPermission,\n      };\n\n  static Model fromJson(Map<String, dynamic> json) {\n    return Model(\n      id: json['id'],\n      name: json['name'],\n      shortName: json['short_name'] ?? json['name'],\n      description: json['description'],\n      priceInfo: json['price_info'],\n      category: json['category'],\n      isChat: json['is_chat'],\n      isImage: json['is_image'],\n      disabled: json['disabled'] ?? false,\n      tag: json['tag'],\n      avatarUrl: json['avatar_url'],\n      supportVision: json['support_vision'] ?? false,\n      supportReasoning: json['support_reasoning'] ?? false,\n      supportSearch: json['support_search'] ?? false,\n      tagBgColor: json['tag_bg_color'],\n      tagTextColor: json['tag_text_color'],\n      isNew: json['is_new'] ?? false,\n      isDefault: json['is_default'] ?? false,\n      userNoPermission: json['user_no_permission'] ?? false,\n    );\n  }\n}\n\nclass BackgroundImage {\n  String url;\n  String preview;\n\n  BackgroundImage(this.url, this.preview);\n\n  toJson() => {\n        'url': url,\n        'preview': preview,\n      };\n\n  static BackgroundImage fromJson(Map<String, dynamic> json) {\n    return BackgroundImage(\n      json['url'],\n      json['preview'],\n    );\n  }\n}\n\nclass UserExistenceResp {\n  bool exist;\n  String signInMethod;\n\n  UserExistenceResp(this.exist, this.signInMethod);\n\n  toJson() => {\n        'exist': exist,\n        'sign_in_method': signInMethod,\n      };\n\n  static UserExistenceResp fromJson(Map<String, dynamic> json) {\n    return UserExistenceResp(\n      json['exist'],\n      json['sign_in_method'],\n    );\n  }\n}\n\nclass PromptCategory {\n  String name;\n  List<PromptCategory> children;\n  List<PromptTag> tags;\n\n  PromptCategory(this.name, this.children, this.tags);\n\n  toJson() => {\n        'name': name,\n        'children': children,\n        'tags': tags,\n      };\n\n  static PromptCategory fromJson(Map<String, dynamic> json) {\n    var children = <PromptCategory>[];\n    for (var item in json['children'] ?? []) {\n      children.add(PromptCategory.fromJson(item));\n    }\n\n    var tags = <PromptTag>[];\n    for (var item in json['tags'] ?? []) {\n      tags.add(PromptTag.fromJson(item));\n    }\n\n    return PromptCategory(\n      json['name'],\n      children,\n      tags,\n    );\n  }\n}\n\nclass PromptTag {\n  String name;\n  String value;\n\n  PromptTag(this.name, this.value);\n\n  toJson() => {\n        'name': name,\n        'value': value,\n      };\n\n  static PromptTag fromJson(Map<String, dynamic> json) {\n    return PromptTag(\n      json['name'],\n      json['value'],\n    );\n  }\n}\n\nclass FreeModelCount {\n  String model;\n  String name;\n  int leftCount;\n  int maxCount;\n  String? info;\n\n  FreeModelCount({\n    required this.model,\n    required this.name,\n    required this.leftCount,\n    required this.maxCount,\n    this.info,\n  });\n\n  toJson() => {\n        'model': model,\n        'name': name,\n        'left_count': leftCount,\n        'max_count': maxCount,\n        'info': info,\n      };\n\n  static FreeModelCount fromJson(Map<String, dynamic> json) {\n    return FreeModelCount(\n      model: json['model'],\n      name: json['name'] ?? json['model'],\n      leftCount: json['left_count'] ?? 0,\n      maxCount: json['max_count'] ?? 0,\n      info: json['info'],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/repo/model/model.dart",
    "content": "import 'dart:convert';\n\nclass Model {\n  final String id;\n  final String name;\n  final String? shortName;\n  final String ownedBy;\n  String? description;\n  String? priceInfo;\n  bool isChatModel = false;\n  bool disabled;\n  String? avatarUrl;\n  bool supportVision = false;\n  bool supportReasoning = false;\n  bool supportSearch = false;\n  String? tag;\n  String? tagTextColor;\n  String? tagBgColor;\n  bool isNew = false;\n  bool isRecommend = false;\n  String category;\n\n  bool isDefault;\n  bool userNoPermission;\n  Model(\n    this.id,\n    this.name,\n    this.ownedBy, {\n    this.shortName,\n    required this.category,\n    this.description,\n    this.priceInfo,\n    this.isChatModel = false,\n    this.disabled = false,\n    this.tag,\n    this.avatarUrl,\n    this.supportVision = false,\n    this.supportReasoning = false,\n    this.supportSearch = false,\n    this.tagTextColor,\n    this.tagBgColor,\n    this.isNew = false,\n    this.isRecommend = false,\n    this.isDefault = false,\n    this.userNoPermission = false,\n  });\n\n  String uid() {\n    return id;\n  }\n\n  Model copyWith({\n    String? id,\n    String? name,\n    String? shortName,\n    String? ownedBy,\n    String? description,\n    String? priceInfo,\n    bool? isChatModel,\n    bool? disabled,\n    String? avatarUrl,\n    bool? supportVision,\n    bool? supportReasoning,\n    bool? supportSearch,\n    String? tag,\n    String? tagTextColor,\n    String? tagBgColor,\n    bool? isNew,\n    bool? isRecommend,\n    String? category,\n    bool? isDefault,\n    bool? userNoPermission,\n  }) {\n    return Model(\n      id ?? this.id,\n      name ?? this.name,\n      ownedBy ?? this.ownedBy,\n      shortName: shortName ?? this.shortName,\n      description: description ?? this.description,\n      priceInfo: priceInfo ?? this.priceInfo,\n      isChatModel: isChatModel ?? this.isChatModel,\n      disabled: disabled ?? this.disabled,\n      avatarUrl: avatarUrl ?? this.avatarUrl,\n      supportVision: supportVision ?? this.supportVision,\n      supportReasoning: supportReasoning ?? this.supportReasoning,\n      supportSearch: supportSearch ?? this.supportSearch,\n      tag: tag ?? this.tag,\n      tagTextColor: tagTextColor ?? this.tagTextColor,\n      tagBgColor: tagBgColor ?? this.tagBgColor,\n      isNew: isNew ?? this.isNew,\n      isRecommend: isRecommend ?? this.isRecommend,\n      category: category ?? this.category,\n      isDefault: isDefault ?? false,\n      userNoPermission: userNoPermission ?? this.userNoPermission,\n    );\n  }\n\n  ModelPrice get modelPrice {\n    if (priceInfo == null || priceInfo == '') {\n      return ModelPrice(input: 0, output: 0, request: 0, search: 0, note: '');\n    }\n\n    return ModelPrice.fromMap(jsonDecode(priceInfo!) as Map<String, dynamic>);\n  }\n}\n\nclass ModelPrice {\n  final int input;\n  final int output;\n  final int request;\n  final int search;\n  final String note;\n\n  bool get isFree {\n    return input == output && input == 0 && request == 0;\n  }\n\n  bool get hasNote {\n    return note != '';\n  }\n\n  ModelPrice({\n    required this.input,\n    required this.output,\n    required this.request,\n    required this.note,\n    this.search = 0,\n  });\n\n  ModelPrice.fromMap(Map<String, dynamic> map)\n      : input = map['input'] ?? 0,\n        output = map['output'] ?? 0,\n        request = map['request'] ?? 0,\n        search = map['search'] ?? 0,\n        note = map['note'] ?? '';\n}\n"
  },
  {
    "path": "lib/repo/model/room.dart",
    "content": "import 'package:askaide/helper/constant.dart';\n\n/// 聊天室\nclass Room {\n  /// 聊天室 ID\n  int? id;\n\n  /// 用户 ID\n  int? userId;\n\n  /// 头像 ID\n  int? avatarId;\n\n  /// 头像链接\n  String? avatarUrl;\n\n  /// 聊天室名称\n  String name;\n\n  /// 聊天室类别\n  String category;\n\n  /// 显示优先级（排序，值越大越靠前）\n  int priority;\n\n  /// 聊天室采用的模型\n  String model;\n\n  /// 模型初始化消息\n  String? initMessage;\n\n  /// 模型最大上下文数量\n  int maxContext;\n\n  /// 模型最大返回 Token 数量\n  int? maxTokens;\n\n  /// room 类型：local or remote\n  bool? localRoom;\n\n  /// 聊天室类型\n  int? roomType;\n\n  bool get isLocalRoom => localRoom ?? false;\n\n  /// 聊天室头像 标识\n  int get avatar => (avatarId == null || avatarId == 0) ? 0 : avatarId!;\n\n  /// 模型类别\n  String modelCategory() {\n    final segs = model.split(':');\n    if (segs.length == 1) {\n      return 'openai';\n    }\n\n    return segs[0];\n  }\n\n  /// 模型名称\n  String modelName() {\n    final segs = model.split(':');\n    if (segs.length == 1) {\n      return segs[0];\n    }\n\n    return segs[1];\n  }\n\n  /// 聊天室图标\n  String? iconData;\n\n  /// 聊天室图标颜色\n  String? color;\n\n  /// 聊天室描述\n  String? description;\n\n  /// 系统提示\n  String? systemPrompt;\n\n  /// 聊天室创建时间\n  DateTime? createdAt;\n\n  /// 聊天室最后活跃时间\n  DateTime? lastActiveTime;\n\n  /// 聊天室成员头像列表\n  List<String> members;\n\n  Room(\n    this.name,\n    this.category, {\n    this.description,\n    this.id,\n    this.userId,\n    this.avatarId,\n    this.avatarUrl,\n    this.createdAt,\n    this.lastActiveTime,\n    this.iconData,\n    this.systemPrompt,\n    this.priority = 0,\n    this.color,\n    this.roomType,\n    this.initMessage,\n    this.localRoom,\n    this.maxContext = 10,\n    this.maxTokens,\n    this.model = defaultChatModel,\n    this.members = const [],\n  });\n\n  Map<String, Object?> toJson() {\n    return {\n      'id': id,\n      'user_id': userId,\n      'name': name,\n      'category': category,\n      'model': model,\n      'priority': priority,\n      'icon_data': iconData,\n      'color': color,\n      'description': description,\n      'system_prompt': systemPrompt,\n      'init_message': initMessage,\n      'max_context': maxContext,\n      'created_at': createdAt?.millisecondsSinceEpoch,\n      'last_active_time': lastActiveTime?.millisecondsSinceEpoch,\n    };\n  }\n\n  Room.fromMap(Map<String, Object?> map)\n      : id = map['id'] as int,\n        userId = map['user_id'] as int?,\n        avatarId = map['avatar_id'] as int?,\n        avatarUrl = map['avatar_url'] as String?,\n        name = map['name'] as String,\n        category = (map['category'] ?? '') as String,\n        priority = (map['priority'] ?? 0) as int,\n        model = (map['model'] ?? '') as String,\n        iconData = map['icon_data'] as String?,\n        color = map['color'] as String?,\n        roomType = map['room_type'] as int?,\n        systemPrompt = map['system_prompt'] as String?,\n        description = map['description'] as String?,\n        initMessage = map['init_message'] as String?,\n        maxContext = map['max_context'] as int? ?? 10,\n        maxTokens = map['max_tokens'] as int?,\n        members = (map['members'] as List<dynamic>?)\n                ?.map((e) => e as String)\n                .toList() ??\n            [],\n        createdAt =\n            DateTime.fromMillisecondsSinceEpoch(map['created_at'] as int? ?? 0),\n        lastActiveTime = DateTime.fromMillisecondsSinceEpoch(\n            map['last_active_time'] as int? ?? 0);\n}\n"
  },
  {
    "path": "lib/repo/openai_repo.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\nimport 'dart:io';\nimport 'package:askaide/helper/ability.dart';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/env.dart';\nimport 'package:askaide/helper/logger.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/helper/queue.dart';\nimport 'package:askaide/repo/model/chat_message.dart';\nimport 'package:askaide/repo/model/model.dart' as mm;\nimport 'package:dart_openai/openai.dart';\nimport 'package:askaide/repo/data/settings_data.dart';\nimport 'package:web_socket_channel/web_socket_channel.dart';\nimport 'package:web_socket_channel/status.dart' as status;\n\nclass OpenAIRepository {\n  final SettingDataProvider settings;\n\n  late bool selfHosted;\n  late String language;\n\n  OpenAIRepository(this.settings) {\n    selfHosted = settings.getDefaultBool(settingOpenAISelfHosted, false);\n    language = settings.getDefault(settingLanguage, 'zh');\n\n    _reloadServerConfig();\n\n    settings.listen((settings, key, value) {\n      selfHosted = settings.getDefaultBool(settingOpenAISelfHosted, false);\n      language = settings.getDefault(settingLanguage, 'zh');\n\n      _reloadServerConfig();\n    });\n  }\n\n  void _reloadServerConfig() {\n    // 自己的 OpenAI 服务器\n    if (selfHosted) {\n      OpenAI.baseUrl = settings.getDefault(settingOpenAIURL, defaultOpenAIServerURL);\n      OpenAI.organization = settings.get(settingOpenAIOrganization);\n      OpenAI.apiKey = settings.getDefault(settingOpenAIAPIToken, '');\n      OpenAI.externalHeaders = {};\n    } else {\n      // 使用公共服务器\n      OpenAI.apiKey = settings.getDefault(settingAPIServerToken, '');\n      OpenAI.baseUrl = settings.getDefault(settingServerURL, apiServerURL);\n      OpenAI.organization = \"\";\n      OpenAI.externalHeaders = {\n        'X-CLIENT-VERSION': clientVersion,\n        'X-PLATFORM': PlatformTool.operatingSystem(),\n        'X-PLATFORM-VERSION': PlatformTool.operatingSystemVersion(),\n        'X-LANGUAGE': language,\n      };\n    }\n\n    OpenAI.showLogs = true;\n  }\n\n  /// 基于 prompt 生成图片\n  Future<List<String>> createImage(\n    String prompt, {\n    int n = 1,\n    OpenAIImageSize size = OpenAIImageSize.size1024,\n  }) async {\n    var model = await OpenAI.instance.image\n        .create(prompt: prompt, n: n, size: size, responseFormat: OpenAIImageResponseFormat.url);\n    return model.data.map((e) => e.url).toList();\n  }\n\n  /// 判断模型是否支持聊天\n  static bool isChatModel(String model) {\n    return supportForChat[model] != null && supportForChat[model]!.isChatModel;\n  }\n\n  /// 判断模型是否为图片模型\n  static bool isImageModel(String model) {\n    return model == defaultImageModel;\n  }\n\n  // 兼容性列表查看：https://platform.openai.com/docs/models/gpt-3\n  // key: 模型名, value: 是否支持聊天模式\n  static final supportForChat = <String, mm.Model>{\n    'gpt-3.5-turbo': mm.Model(\n      'gpt-3.5-turbo',\n      'GPT-3.5 Turbo',\n      'openai',\n      category: modelTypeOpenAI,\n      isChatModel: true,\n      description: '速度快，成本低',\n      shortName: 'GPT-3.5 Turbo',\n      tag: 'local',\n      avatarUrl: 'https://ssl.aicode.cc/ai-server/assets/avatar/gpt35.png',\n    ),\n    'gpt-3.5-turbo-16k': mm.Model(\n      'gpt-3.5-turbo-16k',\n      'GPT-3.5 Turbo 16k',\n      'openai',\n      category: modelTypeOpenAI,\n      isChatModel: true,\n      shortName: 'GPT-3.5 Turbo 16K',\n      tag: 'local',\n      avatarUrl: 'https://ssl.aicode.cc/ai-server/assets/avatar/gpt35.png',\n    ),\n    'gpt-4': mm.Model(\n      'gpt-4',\n      'GPT-4',\n      'openai',\n      category: modelTypeOpenAI,\n      isChatModel: true,\n      shortName: 'GPT-4',\n      tag: 'local',\n      avatarUrl: 'https://ssl.aicode.cc/ai-server/assets/avatar/gpt4.png',\n    ),\n    'gpt-4-32k': mm.Model(\n      'gpt-4-32k',\n      'GPT-4 32k',\n      'openai',\n      category: modelTypeOpenAI,\n      isChatModel: true,\n      shortName: 'GPT-4 32K',\n      tag: 'local',\n      avatarUrl: 'https://ssl.aicode.cc/ai-server/assets/avatar/gpt4.png',\n    ),\n    'gpt-4o': mm.Model(\n      'gpt-4o',\n      'GPT-4o',\n      'openai',\n      category: modelTypeOpenAI,\n      isChatModel: true,\n      shortName: 'GPT-4o',\n      tag: 'local',\n      avatarUrl: 'https://ssl.aicode.cc/ai-server/assets/avatar/gpt4.png',\n    ),\n    'gpt-4o-mini': mm.Model(\n      'gpt-4o-mini',\n      'GPT-4o-mini',\n      'openai',\n      category: modelTypeOpenAI,\n      isChatModel: true,\n      shortName: 'GPT-4o-mini',\n      tag: 'local',\n      avatarUrl: 'https://ssl.aicode.cc/ai-server/assets/avatar/gpt4.png',\n    ),\n  };\n\n  /// 支持的模型\n  static List<mm.Model> supportModels() {\n    var models = supportForChat.values.toList();\n    // models.add(Model(\n    //   defaultImageModel,\n    //   'openai',\n    //   category: modelTypeOpenAI,\n    //   description: '根据自然语言创建现实的图像和艺术',\n    // ));\n    return models;\n  }\n\n  // /// @deprecated\n  // Future<List<mm.Model>> models() async {\n  //   var models = (await OpenAI.instance.model.list())\n  //       .where((e) => e.ownedBy == 'openai')\n  //       .map((e) => mm.Model(e.id, e.ownedBy, category: modelTypeOpenAI))\n  //       .toList();\n  //   var supportModels =\n  //       models.where((e) => supportForChat.containsKey(e.id)).toList();\n\n  //   supportModels.add(mm.Model(\n  //     defaultImageModel,\n  //     'openai',\n  //     category: modelTypeOpenAI,\n  //     description: defaultModelNotChatDesc,\n  //   ));\n\n  //   return supportModels;\n  // }\n\n  Future<void> completionStream(\n    String model,\n    String message,\n    void Function(ChatStreamRespData data) onData, {\n    double temperature = 1.0,\n    user = 'user',\n    int? maxTokens,\n  }) async {\n    var completer = Completer<void>();\n\n    try {\n      var stream = OpenAI.instance.completion.createStream(\n        model: model,\n        prompt: message,\n        temperature: temperature,\n        n: 1,\n        maxTokens: maxTokens,\n        user: user,\n      );\n\n      stream.listen(\n        (event) {\n          for (var element in event.choices) {\n            onData(ChatStreamRespData(content: element.text));\n          }\n        },\n        onDone: () => completer.complete(),\n        onError: (e) => completer.completeError(e),\n        cancelOnError: true,\n      ).onError((e) {\n        completer.completeError(e);\n      });\n    } catch (e) {\n      completer.completeError(e);\n    }\n\n    return completer.future;\n  }\n\n  Future<void> chatStream(\n    List<ChatMessage> messages,\n    void Function(ChatStreamRespData data) onData, {\n    double temperature = 1.0,\n    user = 'user',\n    String model = defaultChatModel,\n    int? roomId,\n    int? historyId,\n    int? maxTokens,\n    String? tempModel,\n    List<String>? flags,\n  }) async {\n    var completer = Completer<void>();\n\n    try {\n      if (Ability().supportWebSocket) {\n        var serverURL = settings.getDefault(settingServerURL, apiServerURL);\n        if (PlatformTool.isWeb() && (serverURL == '' || serverURL == '/')) {\n          serverURL = '${Uri.base.scheme}://${Uri.base.host}${Uri.base.hasPort ? ':${Uri.base.port}' : ''}';\n        }\n\n        final wsURL = serverURL.startsWith('https://')\n            ? serverURL.replaceFirst('https://', 'wss://')\n            : serverURL.replaceFirst('http://', 'ws://');\n        final wsUriBase = Uri.parse('$wsURL/v1/chat/completions');\n\n        final apiToken = settings.getDefault(settingAPIServerToken, '');\n\n        final wsUri = Uri(\n          scheme: wsUriBase.scheme,\n          host: wsUriBase.host,\n          port: wsUriBase.port,\n          path: wsUriBase.path,\n          queryParameters: {\n            'ws': 'true',\n            'authorization': apiToken,\n            'client-version': clientVersion,\n            'platform-version': PlatformTool.operatingSystemVersion(),\n            'platform': PlatformTool.operatingSystem(),\n            'language': language,\n          },\n        );\n        Logger.instance.d('wsURL: ${wsUri.toString()}');\n\n        var channel = WebSocketChannel.connect(wsUri);\n\n        await channel.ready;\n\n        channel.stream.listen(\n          (event) {\n            final evt = jsonDecode(event);\n            if (evt['code'] != null && evt['code'] > 0) {\n              onData(ChatStreamRespData(\n                content: evt['error'],\n                code: evt['code'],\n                error: evt['error'],\n              ));\n\n              return;\n            }\n\n            final res = OpenAIStreamChatCompletionModel.fromMap(evt);\n            for (var element in res.choices) {\n              if (element.delta.content != null) {\n                try {\n                  onData(ChatStreamRespData(\n                    content: element.delta.content!,\n                    role: element.delta.role,\n                  ));\n                } on QueueFinishedException {\n                  channel.sink.close(status.goingAway);\n                }\n              }\n            }\n          },\n          onDone: () {\n            channel.sink.close();\n            completer.complete();\n          },\n          onError: (e) {\n            channel.sink.close();\n            completer.completeError(e);\n          },\n          cancelOnError: true,\n        ).onError((e) {\n          completer.completeError(e);\n        });\n\n        final data = jsonEncode({\n          'model': model,\n          'temp_model': tempModel,\n          'messages': messages.map((e) => e.toMap()).toList(),\n          'temperature': temperature,\n          'user': user,\n          'max_tokens': maxTokens,\n          'n': Ability().enableLocalOpenAI && (model.startsWith('openai:') || model.startsWith('gpt-'))\n              ? null\n              : roomId, // n 参数暂时用不到，复用作为 roomId\n          'history_id': historyId,\n          'flags': flags,\n        });\n\n        Logger.instance.d('send chat request: $data');\n\n        channel.sink.add(data);\n      } else {\n        var chatStream = OpenAI.instance.chat.createStream(\n          model: model,\n          messages: messages,\n          temperature: temperature,\n          user: user,\n          maxTokens: maxTokens,\n          n: Ability().enableLocalOpenAI ? null : roomId, // n 参数暂时用不到，复用作为 roomId\n        );\n\n        chatStream.listen(\n          (event) {\n            for (var element in event.choices) {\n              if (element.delta.content != null) {\n                onData(ChatStreamRespData(\n                  content: element.delta.content!,\n                  role: element.delta.role,\n                ));\n              }\n            }\n          },\n          onDone: () => completer.complete(),\n          onError: (e) => completer.completeError(e),\n          cancelOnError: true,\n        ).onError((e) {\n          completer.completeError(e);\n        });\n      }\n    } catch (e) {\n      completer.completeError(e);\n    }\n\n    return completer.future;\n  }\n\n  /// 音频文件转文字\n  Future<String> audioTranscription({\n    required File audioFile,\n  }) async {\n    var audioModel = await OpenAI.instance.audio.createTranscription(\n      file: audioFile,\n      model: 'whisper-1',\n    );\n\n    return audioModel.text;\n  }\n}\n\nclass ChatReplyMessage {\n  final int index;\n  final String role;\n  final String content;\n  final String? finishReason;\n\n  ChatReplyMessage({\n    required this.index,\n    required this.role,\n    required this.content,\n    this.finishReason,\n  });\n}\n\nclass ChatStreamRespData {\n  final String? role;\n  final String content;\n  final int? code;\n  final String? error;\n  final String? reasoningContent;\n\n  ChatStreamRespData({\n    this.role,\n    required this.content,\n    this.code,\n    this.error,\n    this.reasoningContent,\n  });\n}\n"
  },
  {
    "path": "lib/repo/settings_repo.dart",
    "content": "import 'package:askaide/repo/data/settings_data.dart';\n\nclass SettingRepository {\n  final SettingDataProvider _dataProvider;\n\n  SettingRepository(this._dataProvider) {\n    _dataProvider.loadSettings();\n  }\n\n  void listen(\n      Function(SettingDataProvider settings, String key, String value)\n          listener) {\n    _dataProvider.listen(listener);\n  }\n\n  Future<void> set(String key, String value) async {\n    return await _dataProvider.set(key, value);\n  }\n\n  String? get(String key) {\n    return _dataProvider.get(key);\n  }\n\n  String stringDefault(String key, String defaultValue) {\n    return _dataProvider.getDefault(key, defaultValue);\n  }\n\n  int intDefault(String key, int defaultValue) {\n    return _dataProvider.getDefaultInt(key, defaultValue);\n  }\n\n  bool boolDefault(String key, bool defaultValue) {\n    return _dataProvider.getDefaultBool(key, defaultValue);\n  }\n\n  double doubleDefault(String key, double defaultValue) {\n    return _dataProvider.getDefaultDouble(key, defaultValue);\n  }\n}\n"
  },
  {
    "path": "lib/repo/stabilityai_repo.dart",
    "content": "import 'dart:convert';\nimport 'package:askaide/helper/constant.dart';\nimport 'package:askaide/helper/env.dart';\nimport 'package:askaide/helper/platform.dart';\nimport 'package:askaide/repo/data/settings_data.dart';\nimport 'package:http/http.dart' as http;\n\n/// StabilityAI 模型\nclass StabilityAIRepository {\n  final SettingDataProvider settings;\n\n  late String serverURL;\n  late String apiKey;\n  late String organization;\n  late String language;\n\n  late bool selfHosted;\n\n  Map<String, String> _headers = {};\n\n  StabilityAIRepository(this.settings) {\n    selfHosted = settings.getDefaultBool(settingStabilityAISelfHosted, false);\n    language = settings.getDefault(settingLanguage, 'zh');\n\n    _reloadServerConfig();\n\n    settings.listen((settings, key, value) {\n      selfHosted = settings.getDefaultBool(settingStabilityAISelfHosted, false);\n      language = settings.getDefault(settingLanguage, 'zh');\n\n      _reloadServerConfig();\n    });\n  }\n\n  void _reloadServerConfig() {\n    if (selfHosted) {\n      serverURL =\n          settings.getDefault(settingStabilityAIURL, defaultStabilityAIURL);\n      organization = settings.getDefault(settingStabilityAIOrganization, '');\n      apiKey = settings.getDefault(settingStabilityAIAPIToken, '');\n\n      _headers = {};\n    } else {\n      apiKey = settings.getDefault(settingAPIServerToken, '');\n      organization = \"\";\n      serverURL = apiServerURL;\n\n      _headers = {\n        'X-CLIENT-VERSION': clientVersion,\n        'X-PLATFORM': PlatformTool.operatingSystem(),\n        'X-PLATFORM-VERSION': PlatformTool.operatingSystemVersion(),\n        'X-LANGUAGE': language,\n      };\n    }\n  }\n\n  /// 创建请求头\n  Map<String, String> _buildRequestHeaders() {\n    var headers = <String, String>{\n      'Authorization': 'Bearer $apiKey',\n    };\n\n    headers.addAll(_headers);\n\n    if (organization.isNotEmpty) {\n      headers['Organization'] = organization;\n    }\n\n    return headers;\n  }\n\n  /// 默认的模型列表\n  // static List<Model> supportModels() {\n  //   return [\n  //     // Model(\n  //     //   'esrgan-v1-x2plus',\n  //     //   modelTypeStabilityAI,\n  //     //   category: modelTypeStabilityAI,\n  //     //   description: 'Real-ESRGAN_x2plus upscaler model',\n  //     // ),\n  //     Model(\n  //       'stable-diffusion-v1',\n  //       modelTypeStabilityAI,\n  //       category: modelTypeStabilityAI,\n  //       description: 'Stability-AI Stable Diffusion v1.4',\n  //     ),\n  //     Model(\n  //       'stable-diffusion-v1-5',\n  //       modelTypeStabilityAI,\n  //       category: modelTypeStabilityAI,\n  //       description: 'Stability-AI Stable Diffusion v1.5',\n  //     ),\n  //     Model(\n  //       'stable-diffusion-512-v2-0',\n  //       modelTypeStabilityAI,\n  //       category: modelTypeStabilityAI,\n  //       description: 'Stability-AI Stable Diffusion v2.0',\n  //     ),\n  //     Model(\n  //       'stable-diffusion-768-v2-0',\n  //       modelTypeStabilityAI,\n  //       category: modelTypeStabilityAI,\n  //       description: 'Stability-AI Stable Diffusion 768 v2.0',\n  //     ),\n  //     Model(\n  //       'stable-diffusion-512-v2-1',\n  //       modelTypeStabilityAI,\n  //       category: modelTypeStabilityAI,\n  //       description: 'Stability-AI Stable Diffusion v2.1',\n  //     ),\n  //     Model(\n  //       'stable-diffusion-768-v2-1',\n  //       modelTypeStabilityAI,\n  //       category: modelTypeStabilityAI,\n  //       description: 'Stability-AI Stable Diffusion 768 v2.1',\n  //     ),\n  //     Model(\n  //       'stable-diffusion-xl-beta-v2-2-2',\n  //       modelTypeStabilityAI,\n  //       category: modelTypeStabilityAI,\n  //       description: 'Stability-AI Stable Diffusion XL Beta v2.2.2',\n  //     ),\n  //     // Model(\n  //     //   'stable-inpainting-v1-0',\n  //     //   modelTypeStabilityAI,\n  //     //   category: modelTypeStabilityAI,\n  //     //   description: 'Stability-AI Stable Inpainting v1.0',\n  //     // ),\n  //     // Model(\n  //     //   'stable-inpainting-512-v2-0',\n  //     //   modelTypeStabilityAI,\n  //     //   category: modelTypeStabilityAI,\n  //     //   description: 'Stability-AI Stable Inpainting v2.0',\n  //     // ),\n  //   ];\n  // }\n\n  /// 查询模型列表\n  // Future<List<Model>> models() async {\n  //   var resp = await http.get(\n  //     Uri.parse('$serverURL/v1/engines/list'),\n  //     headers: <String, String>{\n  //       'Accept': 'application/json',\n  //     }..addAll(_buildRequestHeaders()),\n  //   );\n\n  //   if (resp.statusCode != 200) {\n  //     return Future.error('Failed to load models: ${resp.body}');\n  //   }\n\n  //   var models = <Model>[];\n  //   for (var item in jsonDecode(resp.body) as List) {\n  //     if ((item['type'] as String).toLowerCase() != 'picture') {\n  //       print(item);\n  //       continue;\n  //     }\n\n  //     models.add(Model(\n  //       item['id'],\n  //       modelTypeStabilityAI,\n  //       category: modelTypeStabilityAI,\n  //       description: item['description'],\n  //     ));\n  //   }\n\n  //   return models;\n  // }\n\n  /// 创建图片，返回图片的 base64 编码\n  /// 不同模型价格表： https://platform.stability.ai/docs/getting-started/credits-and-billing#pricing-table\n  /// width,height+steps+engine 决定价格\n  Future<List<String>> createImageBase64(\n    String engine,\n    List<StabilityAIPrompt> prompts, {\n    int width = 0,\n    int height = 0,\n    int cfgScale = 7,\n    int samples = 1,\n    int seed = 0,\n    int steps = 30,\n    // 3d-model analog-film anime cinematic comic-book digital-art enhance fantasy-art\n    // isometric line-art low-poly modeling-compound neon-punk origami photographic\n    // pixel-art tile-texture\n    String? stylePreset,\n  }) async {\n    // 注意：图像宽度和高度必须满足下面条件\n    // For 768 engines: 589,824 ≤ height * width ≤ 1,048,576\n    // All other engines: 262,144 ≤ height * width ≤ 1,048,576\n    if (width == 0) {\n      if (engine.contains('-768-')) {\n        width = 768;\n      } else {\n        width = 512;\n      }\n    }\n\n    if (height == 0) {\n      if (engine.contains('-768-')) {\n        height = 768;\n      } else {\n        height = 512;\n      }\n    }\n\n    var params = <String, dynamic>{\n      'width': width,\n      'height': height,\n      'cfg_scale': cfgScale,\n      'samples': samples,\n      'seed': seed,\n      'steps': steps,\n      'text_prompts': prompts,\n    };\n\n    if (stylePreset != null) {\n      params['style_preset'] = stylePreset;\n    }\n\n    var headers = <String, String>{\n      'Accept': 'application/json',\n      'Content-Type': 'application/json',\n    }..addAll(_buildRequestHeaders());\n\n    final url = selfHosted\n        ? Uri.parse('$serverURL/v1/generation/$engine/text-to-image')\n        : Uri.parse('$serverURL/v1/stabilityai/images/$engine/text-to-image');\n\n    var req = http.Request('POST', url);\n    req.body = jsonEncode(params);\n    req.headers.addAll(headers);\n\n    var resp = await http.Response.fromStream(await http.Client().send(req));\n    if (resp.statusCode != 200) {\n      var ret = jsonDecode(resp.body);\n      return Future.error(ret['error']);\n    }\n\n    var images = <String>[];\n    for (var item in jsonDecode(resp.body)['artifacts'] as List) {\n      images.add(item['base64']);\n    }\n\n    return images;\n  }\n\n  /// 创建图片，返回图片的 base64 编码\n  /// 不同模型价格表： https://platform.stability.ai/docs/getting-started/credits-and-billing#pricing-table\n  /// width,height+steps+engine 决定价格\n  Future<String> createImageBase64Async(\n    String engine,\n    List<StabilityAIPrompt> prompts, {\n    int width = 0,\n    int height = 0,\n    int cfgScale = 7,\n    int samples = 1,\n    int seed = 0,\n    int steps = 30,\n    // 3d-model analog-film anime cinematic comic-book digital-art enhance fantasy-art\n    // isometric line-art low-poly modeling-compound neon-punk origami photographic\n    // pixel-art tile-texture\n    String? stylePreset,\n  }) async {\n    // 注意：图像宽度和高度必须满足下面条件\n    // For 768 engines: 589,824 ≤ height * width ≤ 1,048,576\n    // All other engines: 262,144 ≤ height * width ≤ 1,048,576\n    if (width == 0) {\n      if (engine.contains('-768-')) {\n        width = 768;\n      } else {\n        width = 512;\n      }\n    }\n\n    if (height == 0) {\n      if (engine.contains('-768-')) {\n        height = 768;\n      } else {\n        height = 512;\n      }\n    }\n\n    var params = <String, dynamic>{\n      'width': width,\n      'height': height,\n      'cfg_scale': cfgScale,\n      'samples': samples,\n      'seed': seed,\n      'steps': steps,\n      'text_prompts': prompts,\n    };\n\n    if (stylePreset != null) {\n      params['style_preset'] = stylePreset;\n    }\n\n    var headers = <String, String>{\n      'Accept': 'application/json',\n      'Content-Type': 'application/json',\n    }..addAll(_buildRequestHeaders());\n\n    final url = Uri.parse(\n        '$serverURL/v1/stabilityai/images/$engine/text-to-image-async');\n\n    var req = http.Request('POST', url);\n    req.body = jsonEncode(params);\n    req.headers.addAll(headers);\n\n    var resp = await http.Response.fromStream(await http.Client().send(req));\n    if (resp.statusCode != 200) {\n      var ret = jsonDecode(resp.body);\n      return Future.error(ret['error']);\n    }\n\n    return jsonDecode(resp.body)['task_id'] as String;\n  }\n}\n\nclass StabilityAIPrompt {\n  final String text;\n  final double weight;\n\n  StabilityAIPrompt(this.text, this.weight);\n\n  Map<String, dynamic> toJson() {\n    return {\n      'text': text,\n      'weight': weight,\n    };\n  }\n}\n"
  },
  {
    "path": "linux/.gitignore",
    "content": "flutter/ephemeral\n"
  },
  {
    "path": "linux/CMakeLists.txt",
    "content": "# Project-level configuration.\ncmake_minimum_required(VERSION 3.10)\nproject(runner LANGUAGES CXX)\n\n# The name of the executable created for the application. Change this to change\n# the on-disk name of your application.\nset(BINARY_NAME \"askaide\")\n# The unique GTK application identifier for this application. See:\n# https://wiki.gnome.org/HowDoI/ChooseApplicationID\nset(APPLICATION_ID \"cc.aicode.flutter.askaide.askaide\")\n\n# Explicitly opt in to modern CMake behaviors to avoid warnings with recent\n# versions of CMake.\ncmake_policy(SET CMP0063 NEW)\n\n# Load bundled libraries from the lib/ directory relative to the binary.\nset(CMAKE_INSTALL_RPATH \"$ORIGIN/lib\")\n\n# Root filesystem for cross-building.\nif(FLUTTER_TARGET_PLATFORM_SYSROOT)\n  set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})\n  set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})\n  set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)\n  set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)\n  set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)\n  set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)\nendif()\n\n# Define build configuration options.\nif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)\n  set(CMAKE_BUILD_TYPE \"Debug\" CACHE\n    STRING \"Flutter build mode\" FORCE)\n  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS\n    \"Debug\" \"Profile\" \"Release\")\nendif()\n\n# Compilation settings that should be applied to most targets.\n#\n# Be cautious about adding new options here, as plugins use this function by\n# default. In most cases, you should add new options to specific targets instead\n# of modifying this function.\nfunction(APPLY_STANDARD_SETTINGS TARGET)\n  target_compile_features(${TARGET} PUBLIC cxx_std_14)\n  target_compile_options(${TARGET} PRIVATE -Wall -Werror)\n  target_compile_options(${TARGET} PRIVATE \"$<$<NOT:$<CONFIG:Debug>>:-O3>\")\n  target_compile_definitions(${TARGET} PRIVATE \"$<$<NOT:$<CONFIG:Debug>>:NDEBUG>\")\nendfunction()\n\n# Flutter library and tool build rules.\nset(FLUTTER_MANAGED_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/flutter\")\nadd_subdirectory(${FLUTTER_MANAGED_DIR})\n\n# System-level dependencies.\nfind_package(PkgConfig REQUIRED)\npkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)\n\nadd_definitions(-DAPPLICATION_ID=\"${APPLICATION_ID}\")\n\n# Define the application target. To change its name, change BINARY_NAME above,\n# not the value here, or `flutter run` will no longer work.\n#\n# Any new source files that you add to the application should be added here.\nadd_executable(${BINARY_NAME}\n  \"main.cc\"\n  \"my_application.cc\"\n  \"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc\"\n)\n\n# Apply the standard set of build settings. This can be removed for applications\n# that need different build settings.\napply_standard_settings(${BINARY_NAME})\n\n# Add dependency libraries. Add any application-specific dependencies here.\ntarget_link_libraries(${BINARY_NAME} PRIVATE flutter)\ntarget_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)\n\n# Run the Flutter tool portions of the build. This must not be removed.\nadd_dependencies(${BINARY_NAME} flutter_assemble)\n\n# Only the install-generated bundle's copy of the executable will launch\n# correctly, since the resources must in the right relative locations. To avoid\n# people trying to run the unbundled copy, put it in a subdirectory instead of\n# the default top-level location.\nset_target_properties(${BINARY_NAME}\n  PROPERTIES\n  RUNTIME_OUTPUT_DIRECTORY \"${CMAKE_BINARY_DIR}/intermediates_do_not_run\"\n)\n\n# Generated plugin build rules, which manage building the plugins and adding\n# them to the application.\ninclude(flutter/generated_plugins.cmake)\n\n\n# === Installation ===\n# By default, \"installing\" just makes a relocatable bundle in the build\n# directory.\nset(BUILD_BUNDLE_DIR \"${PROJECT_BINARY_DIR}/bundle\")\nif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)\n  set(CMAKE_INSTALL_PREFIX \"${BUILD_BUNDLE_DIR}\" CACHE PATH \"...\" FORCE)\nendif()\n\n# Start with a clean build bundle directory every time.\ninstall(CODE \"\n  file(REMOVE_RECURSE \\\"${BUILD_BUNDLE_DIR}/\\\")\n  \" COMPONENT Runtime)\n\nset(INSTALL_BUNDLE_DATA_DIR \"${CMAKE_INSTALL_PREFIX}/data\")\nset(INSTALL_BUNDLE_LIB_DIR \"${CMAKE_INSTALL_PREFIX}/lib\")\n\ninstall(TARGETS ${BINARY_NAME} RUNTIME DESTINATION \"${CMAKE_INSTALL_PREFIX}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_ICU_DATA_FILE}\" DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n  COMPONENT Runtime)\n\nforeach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})\n  install(FILES \"${bundled_library}\"\n    DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n    COMPONENT Runtime)\nendforeach(bundled_library)\n\n# Fully re-copy the assets directory on each build to avoid having stale files\n# from a previous install.\nset(FLUTTER_ASSET_DIR_NAME \"flutter_assets\")\ninstall(CODE \"\n  file(REMOVE_RECURSE \\\"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\\\")\n  \" COMPONENT Runtime)\ninstall(DIRECTORY \"${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}\"\n  DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\" COMPONENT Runtime)\n\n# Install the AOT library on non-Debug builds only.\nif(NOT CMAKE_BUILD_TYPE MATCHES \"Debug\")\n  install(FILES \"${AOT_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n    COMPONENT Runtime)\nendif()\n"
  },
  {
    "path": "linux/flutter/CMakeLists.txt",
    "content": "# This file controls Flutter-level build steps. It should not be edited.\ncmake_minimum_required(VERSION 3.10)\n\nset(EPHEMERAL_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/ephemeral\")\n\n# Configuration provided via flutter tool.\ninclude(${EPHEMERAL_DIR}/generated_config.cmake)\n\n# TODO: Move the rest of this into files in ephemeral. See\n# https://github.com/flutter/flutter/issues/57146.\n\n# Serves the same purpose as list(TRANSFORM ... PREPEND ...),\n# which isn't available in 3.10.\nfunction(list_prepend LIST_NAME PREFIX)\n    set(NEW_LIST \"\")\n    foreach(element ${${LIST_NAME}})\n        list(APPEND NEW_LIST \"${PREFIX}${element}\")\n    endforeach(element)\n    set(${LIST_NAME} \"${NEW_LIST}\" PARENT_SCOPE)\nendfunction()\n\n# === Flutter Library ===\n# System-level dependencies.\nfind_package(PkgConfig REQUIRED)\npkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)\npkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)\npkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)\n\nset(FLUTTER_LIBRARY \"${EPHEMERAL_DIR}/libflutter_linux_gtk.so\")\n\n# Published to parent scope for install step.\nset(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)\nset(FLUTTER_ICU_DATA_FILE \"${EPHEMERAL_DIR}/icudtl.dat\" PARENT_SCOPE)\nset(PROJECT_BUILD_DIR \"${PROJECT_DIR}/build/\" PARENT_SCOPE)\nset(AOT_LIBRARY \"${PROJECT_DIR}/build/lib/libapp.so\" PARENT_SCOPE)\n\nlist(APPEND FLUTTER_LIBRARY_HEADERS\n  \"fl_basic_message_channel.h\"\n  \"fl_binary_codec.h\"\n  \"fl_binary_messenger.h\"\n  \"fl_dart_project.h\"\n  \"fl_engine.h\"\n  \"fl_json_message_codec.h\"\n  \"fl_json_method_codec.h\"\n  \"fl_message_codec.h\"\n  \"fl_method_call.h\"\n  \"fl_method_channel.h\"\n  \"fl_method_codec.h\"\n  \"fl_method_response.h\"\n  \"fl_plugin_registrar.h\"\n  \"fl_plugin_registry.h\"\n  \"fl_standard_message_codec.h\"\n  \"fl_standard_method_codec.h\"\n  \"fl_string_codec.h\"\n  \"fl_value.h\"\n  \"fl_view.h\"\n  \"flutter_linux.h\"\n)\nlist_prepend(FLUTTER_LIBRARY_HEADERS \"${EPHEMERAL_DIR}/flutter_linux/\")\nadd_library(flutter INTERFACE)\ntarget_include_directories(flutter INTERFACE\n  \"${EPHEMERAL_DIR}\"\n)\ntarget_link_libraries(flutter INTERFACE \"${FLUTTER_LIBRARY}\")\ntarget_link_libraries(flutter INTERFACE\n  PkgConfig::GTK\n  PkgConfig::GLIB\n  PkgConfig::GIO\n)\nadd_dependencies(flutter flutter_assemble)\n\n# === Flutter tool backend ===\n# _phony_ is a non-existent file to force this command to run every time,\n# since currently there's no way to get a full input/output list from the\n# flutter tool.\nadd_custom_command(\n  OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}\n    ${CMAKE_CURRENT_BINARY_DIR}/_phony_\n  COMMAND ${CMAKE_COMMAND} -E env\n    ${FLUTTER_TOOL_ENVIRONMENT}\n    \"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh\"\n      ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}\n  VERBATIM\n)\nadd_custom_target(flutter_assemble DEPENDS\n  \"${FLUTTER_LIBRARY}\"\n  ${FLUTTER_LIBRARY_HEADERS}\n)\n"
  },
  {
    "path": "linux/flutter/generated_plugin_registrant.cc",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#include \"generated_plugin_registrant.h\"\n\n#include <audioplayers_linux/audioplayers_linux_plugin.h>\n#include <bitsdojo_window_linux/bitsdojo_window_plugin.h>\n#include <file_saver/file_saver_plugin.h>\n#include <flutter_localization/flutter_localization_plugin.h>\n#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>\n#include <media_kit_video/media_kit_video_plugin.h>\n#include <record_linux/record_linux_plugin.h>\n#include <url_launcher_linux/url_launcher_plugin.h>\n\nvoid fl_register_plugins(FlPluginRegistry* registry) {\n  g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"AudioplayersLinuxPlugin\");\n  audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);\n  g_autoptr(FlPluginRegistrar) bitsdojo_window_linux_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"BitsdojoWindowPlugin\");\n  bitsdojo_window_plugin_register_with_registrar(bitsdojo_window_linux_registrar);\n  g_autoptr(FlPluginRegistrar) file_saver_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"FileSaverPlugin\");\n  file_saver_plugin_register_with_registrar(file_saver_registrar);\n  g_autoptr(FlPluginRegistrar) flutter_localization_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"FlutterLocalizationPlugin\");\n  flutter_localization_plugin_register_with_registrar(flutter_localization_registrar);\n  g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"MediaKitLibsLinuxPlugin\");\n  media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar);\n  g_autoptr(FlPluginRegistrar) media_kit_video_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"MediaKitVideoPlugin\");\n  media_kit_video_plugin_register_with_registrar(media_kit_video_registrar);\n  g_autoptr(FlPluginRegistrar) record_linux_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"RecordLinuxPlugin\");\n  record_linux_plugin_register_with_registrar(record_linux_registrar);\n  g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"UrlLauncherPlugin\");\n  url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);\n}\n"
  },
  {
    "path": "linux/flutter/generated_plugin_registrant.h",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#ifndef GENERATED_PLUGIN_REGISTRANT_\n#define GENERATED_PLUGIN_REGISTRANT_\n\n#include <flutter_linux/flutter_linux.h>\n\n// Registers Flutter plugins.\nvoid fl_register_plugins(FlPluginRegistry* registry);\n\n#endif  // GENERATED_PLUGIN_REGISTRANT_\n"
  },
  {
    "path": "linux/flutter/generated_plugins.cmake",
    "content": "#\n# Generated file, do not edit.\n#\n\nlist(APPEND FLUTTER_PLUGIN_LIST\n  audioplayers_linux\n  bitsdojo_window_linux\n  file_saver\n  flutter_localization\n  media_kit_libs_linux\n  media_kit_video\n  record_linux\n  url_launcher_linux\n)\n\nlist(APPEND FLUTTER_FFI_PLUGIN_LIST\n  media_kit_native_event_loop\n)\n\nset(PLUGIN_BUNDLED_LIBRARIES)\n\nforeach(plugin ${FLUTTER_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})\n  target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})\nendforeach(plugin)\n\nforeach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})\nendforeach(ffi_plugin)\n"
  },
  {
    "path": "linux/main.cc",
    "content": "#include \"my_application.h\"\n\nint main(int argc, char** argv) {\n  g_autoptr(MyApplication) app = my_application_new();\n  return g_application_run(G_APPLICATION(app), argc, argv);\n}\n"
  },
  {
    "path": "linux/my_application.cc",
    "content": "#include \"my_application.h\"\n\n#include <flutter_linux/flutter_linux.h>\n#ifdef GDK_WINDOWING_X11\n#include <gdk/gdkx.h>\n#endif\n\n#include \"flutter/generated_plugin_registrant.h\"\n\nstruct _MyApplication {\n  GtkApplication parent_instance;\n  char** dart_entrypoint_arguments;\n};\n\nG_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)\n\n// Implements GApplication::activate.\nstatic void my_application_activate(GApplication* application) {\n  MyApplication* self = MY_APPLICATION(application);\n  GtkWindow* window =\n      GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));\n\n  // Use a header bar when running in GNOME as this is the common style used\n  // by applications and is the setup most users will be using (e.g. Ubuntu\n  // desktop).\n  // If running on X and not using GNOME then just use a traditional title bar\n  // in case the window manager does more exotic layout, e.g. tiling.\n  // If running on Wayland assume the header bar will work (may need changing\n  // if future cases occur).\n  gboolean use_header_bar = TRUE;\n#ifdef GDK_WINDOWING_X11\n  GdkScreen* screen = gtk_window_get_screen(window);\n  if (GDK_IS_X11_SCREEN(screen)) {\n    const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);\n    if (g_strcmp0(wm_name, \"GNOME Shell\") != 0) {\n      use_header_bar = FALSE;\n    }\n  }\n#endif\n  if (use_header_bar) {\n    GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());\n    gtk_widget_show(GTK_WIDGET(header_bar));\n    gtk_header_bar_set_title(header_bar, \"askaide\");\n    gtk_header_bar_set_show_close_button(header_bar, TRUE);\n    gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));\n  } else {\n    gtk_window_set_title(window, \"askaide\");\n  }\n\n  gtk_window_set_default_size(window, 1280, 720);\n  gtk_widget_show(GTK_WIDGET(window));\n\n  g_autoptr(FlDartProject) project = fl_dart_project_new();\n  fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);\n\n  FlView* view = fl_view_new(project);\n  gtk_widget_show(GTK_WIDGET(view));\n  gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));\n\n  fl_register_plugins(FL_PLUGIN_REGISTRY(view));\n\n  gtk_widget_grab_focus(GTK_WIDGET(view));\n}\n\n// Implements GApplication::local_command_line.\nstatic gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {\n  MyApplication* self = MY_APPLICATION(application);\n  // Strip out the first argument as it is the binary name.\n  self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);\n\n  g_autoptr(GError) error = nullptr;\n  if (!g_application_register(application, nullptr, &error)) {\n     g_warning(\"Failed to register: %s\", error->message);\n     *exit_status = 1;\n     return TRUE;\n  }\n\n  g_application_activate(application);\n  *exit_status = 0;\n\n  return TRUE;\n}\n\n// Implements GObject::dispose.\nstatic void my_application_dispose(GObject* object) {\n  MyApplication* self = MY_APPLICATION(object);\n  g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);\n  G_OBJECT_CLASS(my_application_parent_class)->dispose(object);\n}\n\nstatic void my_application_class_init(MyApplicationClass* klass) {\n  G_APPLICATION_CLASS(klass)->activate = my_application_activate;\n  G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;\n  G_OBJECT_CLASS(klass)->dispose = my_application_dispose;\n}\n\nstatic void my_application_init(MyApplication* self) {}\n\nMyApplication* my_application_new() {\n  return MY_APPLICATION(g_object_new(my_application_get_type(),\n                                     \"application-id\", APPLICATION_ID,\n                                     \"flags\", G_APPLICATION_NON_UNIQUE,\n                                     nullptr));\n}\n"
  },
  {
    "path": "linux/my_application.h",
    "content": "#ifndef FLUTTER_MY_APPLICATION_H_\n#define FLUTTER_MY_APPLICATION_H_\n\n#include <gtk/gtk.h>\n\nG_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,\n                     GtkApplication)\n\n/**\n * my_application_new:\n *\n * Creates a new Flutter-based application.\n *\n * Returns: a new #MyApplication.\n */\nMyApplication* my_application_new();\n\n#endif  // FLUTTER_MY_APPLICATION_H_\n"
  },
  {
    "path": "macos/.gitignore",
    "content": "# Flutter-related\n**/Flutter/ephemeral/\n**/Pods/\n\n# Xcode-related\n**/dgph\n**/xcuserdata/\n"
  },
  {
    "path": "macos/Flutter/Flutter-Debug.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"\n#include \"ephemeral/Flutter-Generated.xcconfig\"\n"
  },
  {
    "path": "macos/Flutter/Flutter-Release.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"\n#include \"ephemeral/Flutter-Generated.xcconfig\"\n"
  },
  {
    "path": "macos/Flutter/GeneratedPluginRegistrant.swift",
    "content": "//\n//  Generated file. Do not edit.\n//\n\nimport FlutterMacOS\nimport Foundation\n\nimport audioplayers_darwin\nimport bitsdojo_window_macos\nimport file_saver\nimport flutter_image_compress_macos\nimport flutter_localization\nimport flutter_tts\nimport in_app_purchase_storekit\nimport media_kit_libs_macos_video\nimport media_kit_video\nimport package_info_plus\nimport path_provider_foundation\nimport record_darwin\nimport screen_brightness_macos\nimport share_plus\nimport shared_preferences_foundation\nimport sign_in_with_apple\nimport sqflite\nimport url_launcher_macos\nimport wakelock_plus\n\nfunc RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {\n  AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: \"AudioplayersDarwinPlugin\"))\n  BitsdojoWindowPlugin.register(with: registry.registrar(forPlugin: \"BitsdojoWindowPlugin\"))\n  FileSaverPlugin.register(with: registry.registrar(forPlugin: \"FileSaverPlugin\"))\n  FlutterImageCompressMacosPlugin.register(with: registry.registrar(forPlugin: \"FlutterImageCompressMacosPlugin\"))\n  FlutterLocalizationPlugin.register(with: registry.registrar(forPlugin: \"FlutterLocalizationPlugin\"))\n  FlutterTtsPlugin.register(with: registry.registrar(forPlugin: \"FlutterTtsPlugin\"))\n  InAppPurchasePlugin.register(with: registry.registrar(forPlugin: \"InAppPurchasePlugin\"))\n  MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: \"MediaKitLibsMacosVideoPlugin\"))\n  MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: \"MediaKitVideoPlugin\"))\n  FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: \"FPPPackageInfoPlusPlugin\"))\n  PathProviderPlugin.register(with: registry.registrar(forPlugin: \"PathProviderPlugin\"))\n  RecordPlugin.register(with: registry.registrar(forPlugin: \"RecordPlugin\"))\n  ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: \"ScreenBrightnessMacosPlugin\"))\n  SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: \"SharePlusMacosPlugin\"))\n  SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: \"SharedPreferencesPlugin\"))\n  SignInWithApplePlugin.register(with: registry.registrar(forPlugin: \"SignInWithApplePlugin\"))\n  SqflitePlugin.register(with: registry.registrar(forPlugin: \"SqflitePlugin\"))\n  UrlLauncherPlugin.register(with: registry.registrar(forPlugin: \"UrlLauncherPlugin\"))\n  WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: \"WakelockPlusMacosPlugin\"))\n}\n"
  },
  {
    "path": "macos/Podfile",
    "content": "platform :osx, '10.15'\n\n# CocoaPods analytics sends network stats synchronously affecting flutter build latency.\nENV['COCOAPODS_DISABLE_STATS'] = 'true'\n\nproject 'Runner', {\n  'Debug' => :debug,\n  'Profile' => :release,\n  'Release' => :release,\n}\n\ndef flutter_root\n  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)\n  unless File.exist?(generated_xcode_build_settings_path)\n    raise \"#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \\\"flutter pub get\\\" is executed first\"\n  end\n\n  File.foreach(generated_xcode_build_settings_path) do |line|\n    matches = line.match(/FLUTTER_ROOT\\=(.*)/)\n    return matches[1].strip if matches\n  end\n  raise \"FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \\\"flutter pub get\\\"\"\nend\n\nrequire File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)\n\nflutter_macos_podfile_setup\n\ntarget 'Runner' do\n  use_frameworks!\n  use_modular_headers!\n\n  flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))\nend\n\n# bugfix for xcode15beta https://github.com/CocoaPods/CocoaPods/issues/12012\npost_install do |installer|\n  installer.pods_project.targets.each do |target|\n    flutter_additional_macos_build_settings(target)\n    target.build_configurations.each do |config|\n    xcconfig_path = config.base_configuration_reference.real_path\n    xcconfig = File.read(xcconfig_path)\n    xcconfig_mod = xcconfig.gsub(/DT_TOOLCHAIN_DIR/, \"TOOLCHAIN_DIR\")\n    File.open(xcconfig_path, \"w\") { |file| file << xcconfig_mod }\n    end\n  end\nend"
  },
  {
    "path": "macos/Runner/AppDelegate.swift",
    "content": "import Cocoa\nimport FlutterMacOS\n\n@main\nclass AppDelegate: FlutterAppDelegate {\n  override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {\n    return true\n  }\n}\n"
  },
  {
    "path": "macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n    \"info\": {\n        \"version\": 1,\n        \"author\": \"ai.aicode.cc\"\n    },\n    \"images\": [\n        {\n            \"size\": \"16x16\",\n            \"idiom\": \"mac\",\n            \"filename\": \"app_icon_16.png\",\n            \"scale\": \"1x\"\n        },\n        {\n            \"size\": \"16x16\",\n            \"idiom\": \"mac\",\n            \"filename\": \"app_icon_32.png\",\n            \"scale\": \"2x\"\n        },\n        {\n            \"size\": \"32x32\",\n            \"idiom\": \"mac\",\n            \"filename\": \"app_icon_32.png\",\n            \"scale\": \"1x\"\n        },\n        {\n            \"size\": \"32x32\",\n            \"idiom\": \"mac\",\n            \"filename\": \"app_icon_64.png\",\n            \"scale\": \"2x\"\n        },\n        {\n            \"size\": \"128x128\",\n            \"idiom\": \"mac\",\n            \"filename\": \"app_icon_128.png\",\n            \"scale\": \"1x\"\n        },\n        {\n            \"size\": \"128x128\",\n            \"idiom\": \"mac\",\n            \"filename\": \"app_icon_256.png\",\n            \"scale\": \"2x\"\n        },\n        {\n            \"size\": \"256x256\",\n            \"idiom\": \"mac\",\n            \"filename\": \"app_icon_256.png\",\n            \"scale\": \"1x\"\n        },\n        {\n            \"size\": \"256x256\",\n            \"idiom\": \"mac\",\n            \"filename\": \"app_icon_512.png\",\n            \"scale\": \"2x\"\n        },\n        {\n            \"size\": \"512x512\",\n            \"idiom\": \"mac\",\n            \"filename\": \"app_icon_512.png\",\n            \"scale\": \"1x\"\n        },\n        {\n            \"size\": \"512x512\",\n            \"idiom\": \"mac\",\n            \"filename\": \"app_icon_1024.png\",\n            \"scale\": \"2x\"\n        }\n    ]\n}"
  },
  {
    "path": "macos/Runner/Base.lproj/MainMenu.xib",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion=\"22154\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\" customObjectInstantitationMethod=\"direct\">\n    <dependencies>\n        <deployment identifier=\"macosx\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"22154\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <objects>\n        <customObject id=\"-2\" userLabel=\"File's Owner\" customClass=\"NSApplication\">\n            <connections>\n                <outlet property=\"delegate\" destination=\"Voe-Tx-rLC\" id=\"GzC-gU-4Uq\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"-1\" userLabel=\"First Responder\" customClass=\"FirstResponder\"/>\n        <customObject id=\"-3\" userLabel=\"Application\" customClass=\"NSObject\"/>\n        <customObject id=\"Voe-Tx-rLC\" customClass=\"AppDelegate\" customModule=\"AIdea\" customModuleProvider=\"target\">\n            <connections>\n                <outlet property=\"applicationMenu\" destination=\"uQy-DD-JDr\" id=\"XBo-yE-nKs\"/>\n                <outlet property=\"mainFlutterWindow\" destination=\"QvC-M9-y7g\" id=\"gIp-Ho-8D9\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"YLy-65-1bz\" customClass=\"NSFontManager\"/>\n        <menu title=\"Main Menu\" systemMenu=\"main\" id=\"AYu-sK-qS6\">\n            <items>\n                <menuItem title=\"APP_NAME\" id=\"1Xt-HY-uBw\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"APP_NAME\" systemMenu=\"apple\" id=\"uQy-DD-JDr\">\n                        <items>\n                            <menuItem title=\"About APP_NAME\" id=\"5kV-Vb-QxS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"orderFrontStandardAboutPanel:\" target=\"-1\" id=\"Exp-CZ-Vem\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"VOq-y0-SEH\"/>\n                            <menuItem title=\"Preferences…\" keyEquivalent=\",\" id=\"BOF-NM-1cW\"/>\n                            <menuItem isSeparatorItem=\"YES\" id=\"wFC-TO-SCJ\"/>\n                            <menuItem title=\"Services\" id=\"NMo-om-nkz\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Services\" systemMenu=\"services\" id=\"hz9-B4-Xy5\"/>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"4je-JR-u6R\"/>\n                            <menuItem title=\"Hide APP_NAME\" keyEquivalent=\"h\" id=\"Olw-nP-bQN\">\n                                <connections>\n                                    <action selector=\"hide:\" target=\"-1\" id=\"PnN-Uc-m68\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Hide Others\" keyEquivalent=\"h\" id=\"Vdr-fp-XzO\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"hideOtherApplications:\" target=\"-1\" id=\"VT4-aY-XCT\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Show All\" id=\"Kd2-mp-pUS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"unhideAllApplications:\" target=\"-1\" id=\"Dhg-Le-xox\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"kCx-OE-vgT\"/>\n                            <menuItem title=\"Quit APP_NAME\" keyEquivalent=\"q\" id=\"4sb-4s-VLi\">\n                                <connections>\n                                    <action selector=\"terminate:\" target=\"-1\" id=\"Te7-pn-YzF\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Edit\" id=\"5QF-Oa-p0T\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Edit\" id=\"W48-6f-4Dl\">\n                        <items>\n                            <menuItem title=\"Undo\" keyEquivalent=\"z\" id=\"dRJ-4n-Yzg\">\n                                <connections>\n                                    <action selector=\"undo:\" target=\"-1\" id=\"M6e-cu-g7V\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Redo\" keyEquivalent=\"Z\" id=\"6dh-zS-Vam\">\n                                <connections>\n                                    <action selector=\"redo:\" target=\"-1\" id=\"oIA-Rs-6OD\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"WRV-NI-Exz\"/>\n                            <menuItem title=\"Cut\" keyEquivalent=\"x\" id=\"uRl-iY-unG\">\n                                <connections>\n                                    <action selector=\"cut:\" target=\"-1\" id=\"YJe-68-I9s\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Copy\" keyEquivalent=\"c\" id=\"x3v-GG-iWU\">\n                                <connections>\n                                    <action selector=\"copy:\" target=\"-1\" id=\"G1f-GL-Joy\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Paste\" keyEquivalent=\"v\" id=\"gVA-U4-sdL\">\n                                <connections>\n                                    <action selector=\"paste:\" target=\"-1\" id=\"UvS-8e-Qdg\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Paste and Match Style\" keyEquivalent=\"V\" id=\"WeT-3V-zwk\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"pasteAsPlainText:\" target=\"-1\" id=\"cEh-KX-wJQ\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Delete\" id=\"pa3-QI-u2k\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"delete:\" target=\"-1\" id=\"0Mk-Ml-PaM\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Select All\" keyEquivalent=\"a\" id=\"Ruw-6m-B2m\">\n                                <connections>\n                                    <action selector=\"selectAll:\" target=\"-1\" id=\"VNm-Mi-diN\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"uyl-h8-XO2\"/>\n                            <menuItem title=\"Find\" id=\"4EN-yA-p0u\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Find\" id=\"1b7-l0-nxx\">\n                                    <items>\n                                        <menuItem title=\"Find…\" tag=\"1\" keyEquivalent=\"f\" id=\"Xz5-n4-O0W\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"cD7-Qs-BN4\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find and Replace…\" tag=\"12\" keyEquivalent=\"f\" id=\"YEy-JH-Tfz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"WD3-Gg-5AJ\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find Next\" tag=\"2\" keyEquivalent=\"g\" id=\"q09-fT-Sye\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"NDo-RZ-v9R\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find Previous\" tag=\"3\" keyEquivalent=\"G\" id=\"OwM-mh-QMV\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"HOh-sY-3ay\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Use Selection for Find\" tag=\"7\" keyEquivalent=\"e\" id=\"buJ-ug-pKt\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"U76-nv-p5D\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Jump to Selection\" keyEquivalent=\"j\" id=\"S0p-oC-mLd\">\n                                            <connections>\n                                                <action selector=\"centerSelectionInVisibleArea:\" target=\"-1\" id=\"IOG-6D-g5B\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Spelling and Grammar\" id=\"Dv1-io-Yv7\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Spelling\" id=\"3IN-sU-3Bg\">\n                                    <items>\n                                        <menuItem title=\"Show Spelling and Grammar\" keyEquivalent=\":\" id=\"HFo-cy-zxI\">\n                                            <connections>\n                                                <action selector=\"showGuessPanel:\" target=\"-1\" id=\"vFj-Ks-hy3\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Check Document Now\" keyEquivalent=\";\" id=\"hz2-CU-CR7\">\n                                            <connections>\n                                                <action selector=\"checkSpelling:\" target=\"-1\" id=\"fz7-VC-reM\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"bNw-od-mp5\"/>\n                                        <menuItem title=\"Check Spelling While Typing\" id=\"rbD-Rh-wIN\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleContinuousSpellChecking:\" target=\"-1\" id=\"7w6-Qz-0kB\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Check Grammar With Spelling\" id=\"mK6-2p-4JG\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleGrammarChecking:\" target=\"-1\" id=\"muD-Qn-j4w\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Correct Spelling Automatically\" id=\"78Y-hA-62v\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticSpellingCorrection:\" target=\"-1\" id=\"2lM-Qi-WAP\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Substitutions\" id=\"9ic-FL-obx\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Substitutions\" id=\"FeM-D8-WVr\">\n                                    <items>\n                                        <menuItem title=\"Show Substitutions\" id=\"z6F-FW-3nz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"orderFrontSubstitutionsPanel:\" target=\"-1\" id=\"oku-mr-iSq\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"gPx-C9-uUO\"/>\n                                        <menuItem title=\"Smart Copy/Paste\" id=\"9yt-4B-nSM\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleSmartInsertDelete:\" target=\"-1\" id=\"3IJ-Se-DZD\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Quotes\" id=\"hQb-2v-fYv\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticQuoteSubstitution:\" target=\"-1\" id=\"ptq-xd-QOA\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Dashes\" id=\"rgM-f4-ycn\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticDashSubstitution:\" target=\"-1\" id=\"oCt-pO-9gS\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Links\" id=\"cwL-P1-jid\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticLinkDetection:\" target=\"-1\" id=\"Gip-E3-Fov\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Data Detectors\" id=\"tRr-pd-1PS\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticDataDetection:\" target=\"-1\" id=\"R1I-Nq-Kbl\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Text Replacement\" id=\"HFQ-gK-NFA\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticTextReplacement:\" target=\"-1\" id=\"DvP-Fe-Py6\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Transformations\" id=\"2oI-Rn-ZJC\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Transformations\" id=\"c8a-y6-VQd\">\n                                    <items>\n                                        <menuItem title=\"Make Upper Case\" id=\"vmV-6d-7jI\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"uppercaseWord:\" target=\"-1\" id=\"sPh-Tk-edu\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Make Lower Case\" id=\"d9M-CD-aMd\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"lowercaseWord:\" target=\"-1\" id=\"iUZ-b5-hil\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Capitalize\" id=\"UEZ-Bs-lqG\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"capitalizeWord:\" target=\"-1\" id=\"26H-TL-nsh\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Speech\" id=\"xrE-MZ-jX0\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Speech\" id=\"3rS-ZA-NoH\">\n                                    <items>\n                                        <menuItem title=\"Start Speaking\" id=\"Ynk-f8-cLZ\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"startSpeaking:\" target=\"-1\" id=\"654-Ng-kyl\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Stop Speaking\" id=\"Oyz-dy-DGm\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"stopSpeaking:\" target=\"-1\" id=\"dX8-6p-jy9\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"View\" id=\"H8h-7b-M4v\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"View\" id=\"HyV-fh-RgO\">\n                        <items>\n                            <menuItem title=\"Enter Full Screen\" keyEquivalent=\"f\" id=\"4J7-dP-txa\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"toggleFullScreen:\" target=\"-1\" id=\"dU3-MA-1Rq\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Window\" id=\"aUF-d1-5bR\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Window\" systemMenu=\"window\" id=\"Td7-aD-5lo\">\n                        <items>\n                            <menuItem title=\"Minimize\" keyEquivalent=\"m\" id=\"OY7-WF-poV\">\n                                <connections>\n                                    <action selector=\"performMiniaturize:\" target=\"-1\" id=\"VwT-WD-YPe\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Zoom\" id=\"R4o-n2-Eq4\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"performZoom:\" target=\"-1\" id=\"DIl-cC-cCs\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"eu3-7i-yIM\"/>\n                            <menuItem title=\"Bring All to Front\" id=\"LE2-aR-0XJ\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"arrangeInFront:\" target=\"-1\" id=\"DRN-fu-gQh\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Help\" id=\"EPT-qC-fAb\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Help\" systemMenu=\"help\" id=\"rJ0-wn-3NY\"/>\n                </menuItem>\n            </items>\n            <point key=\"canvasLocation\" x=\"142\" y=\"-258\"/>\n        </menu>\n        <window title=\"APP_NAME\" allowsToolTipsWhenApplicationIsInactive=\"NO\" autorecalculatesKeyViewLoop=\"NO\" releasedWhenClosed=\"NO\" animationBehavior=\"default\" titlebarAppearsTransparent=\"YES\" titleVisibility=\"hidden\" id=\"QvC-M9-y7g\" customClass=\"MainFlutterWindow\" customModule=\"AIdea\" customModuleProvider=\"target\">\n            <windowStyleMask key=\"styleMask\" titled=\"YES\" closable=\"YES\" miniaturizable=\"YES\" resizable=\"YES\" fullSizeContentView=\"YES\"/>\n            <rect key=\"contentRect\" x=\"335\" y=\"390\" width=\"800\" height=\"600\"/>\n            <rect key=\"screenRect\" x=\"0.0\" y=\"0.0\" width=\"3440\" height=\"1415\"/>\n            <view key=\"contentView\" wantsLayer=\"YES\" id=\"EiT-Mj-1SZ\">\n                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"800\" height=\"600\"/>\n                <autoresizingMask key=\"autoresizingMask\"/>\n            </view>\n            <point key=\"canvasLocation\" x=\"21\" y=\"143\"/>\n        </window>\n    </objects>\n</document>\n"
  },
  {
    "path": "macos/Runner/Configs/AppInfo.xcconfig",
    "content": "// Application-level settings for the Runner target.\n//\n// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the\n// future. If not, the values below would default to using the project name when this becomes a\n// 'flutter create' template.\n\n// The application's name. By default this is also the title of the Flutter window.\nPRODUCT_NAME = AIdea\n\n// The application's bundle identifier\nPRODUCT_BUNDLE_IDENTIFIER = cc.aicode.flutter.askaide.askaide\n\n// The copyright displayed in application information\nPRODUCT_COPYRIGHT = Copyright © 2023 cc.aicode.flutter.askaide. All rights reserved.\n"
  },
  {
    "path": "macos/Runner/Configs/Debug.xcconfig",
    "content": "#include \"../../Flutter/Flutter-Debug.xcconfig\"\n#include \"Warnings.xcconfig\"\n"
  },
  {
    "path": "macos/Runner/Configs/Release.xcconfig",
    "content": "#include \"../../Flutter/Flutter-Release.xcconfig\"\n#include \"Warnings.xcconfig\"\n"
  },
  {
    "path": "macos/Runner/Configs/Warnings.xcconfig",
    "content": "WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings\nGCC_WARN_UNDECLARED_SELECTOR = YES\nCLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES\nCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE\nCLANG_WARN__DUPLICATE_METHOD_MATCH = YES\nCLANG_WARN_PRAGMA_PACK = YES\nCLANG_WARN_STRICT_PROTOTYPES = YES\nCLANG_WARN_COMMA = YES\nGCC_WARN_STRICT_SELECTOR_MATCH = YES\nCLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES\nCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES\nGCC_WARN_SHADOW = YES\nCLANG_WARN_UNREACHABLE_CODE = YES\n"
  },
  {
    "path": "macos/Runner/DebugProfile.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.developer.applesignin</key>\n\t<array>\n\t\t<string>Default</string>\n\t</array>\n\t<key>com.apple.security.app-sandbox</key>\n\t<true/>\n\t<key>com.apple.security.cs.allow-jit</key>\n\t<true/>\n\t<key>com.apple.security.device.audio-input</key>\n\t<true/>\n\t<key>com.apple.security.files.downloads.read-write</key>\n\t<true/>\n\t<key>com.apple.security.network.client</key>\n\t<true/>\n\t<key>com.apple.security.network.server</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "macos/Runner/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIconFile</key>\n\t<string></string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(FLUTTER_BUILD_NAME)</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(FLUTTER_BUILD_NUMBER)</string>\n\t<key>LSMinimumSystemVersion</key>\n\t<string>$(MACOSX_DEPLOYMENT_TARGET)</string>\n\t<key>NSHumanReadableCopyright</key>\n\t<string>$(PRODUCT_COPYRIGHT)</string>\n\t<key>NSMainNibFile</key>\n\t<string>MainMenu</string>\n\t<key>NSPrincipalClass</key>\n\t<string>NSApplication</string>\n\t<key>NSMicrophoneUsageDescription</key>\n\t<string>We need to access to the microphone to record audio file</string>\n\t<key>NSPhotoLibraryUsageDescription</key>\n\t<string>We need to access to the photo library to pick files for upload</string>\n\t<key>NSAppTransportSecurity</key>\n\t<dict>\n\t\t<key>NSAllowsArbitraryLoads</key>\n\t\t<true/>\n\t\t<key>NSAllowsArbitraryLoadsForMedia</key>\n\t\t<true/>\n\t</dict>\n</dict>\n</plist>\n"
  },
  {
    "path": "macos/Runner/MainFlutterWindow.swift",
    "content": "import Cocoa\nimport FlutterMacOS\nimport bitsdojo_window_macos\n\nclass MainFlutterWindow: BitsdojoWindow {\n  override func bitsdojo_window_configure() -> UInt {\n     return BDW_CUSTOM_FRAME | BDW_HIDE_ON_STARTUP\n  }\n  \n  override func awakeFromNib() {\n    let flutterViewController = FlutterViewController.init()\n    let windowFrame = self.frame\n    self.contentViewController = flutterViewController\n    self.setFrame(windowFrame, display: true)\n\n    // 设置窗口大小\n    self.setContentSize(NSSize(width: 850, height: 750))\n\n    // 设置窗口禁止缩放\n    // let window: NSWindow! = self.contentView?.window\n    // window.styleMask.remove(.resizable)\n\n    RegisterGeneratedPlugins(registry: flutterViewController)\n\n    super.awakeFromNib()\n  }\n}\n"
  },
  {
    "path": "macos/Runner/Release.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.developer.applesignin</key>\n\t<array>\n\t\t<string>Default</string>\n\t</array>\n\t<key>com.apple.security.app-sandbox</key>\n\t<true/>\n\t<key>com.apple.security.device.audio-input</key>\n\t<true/>\n\t<key>com.apple.security.files.downloads.read-write</key>\n\t<true/>\n\t<key>com.apple.security.network.client</key>\n\t<true/>\n\t<key>com.apple.security.network.server</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "macos/Runner.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXAggregateTarget section */\n\t\t33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {\n\t\t\tisa = PBXAggregateTarget;\n\t\t\tbuildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget \"Flutter Assemble\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t33CC111E2044C6BF0003C045 /* ShellScript */,\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = \"Flutter Assemble\";\n\t\t\tproductName = FLX;\n\t\t};\n/* End PBXAggregateTarget section */\n\n/* Begin PBXBuildFile section */\n\t\t335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };\n\t\t33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };\n\t\t33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };\n\t\t33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };\n\t\t33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };\n\t\t5A29A95C06583FFBDD292907 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43240CB6A8AF2E981A3B5D25 /* Pods_Runner.framework */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\t33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 33CC10E52044A3C60003C045 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 33CC111A2044C6BA0003C045;\n\t\t\tremoteInfo = FLX;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t33CC110E2044A8840003C045 /* Bundle Framework */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tname = \"Bundle Framework\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t03A3A2E26C91E71A9326CB79 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.debug.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t107E9B87B247CD30AF3B0493 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.profile.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = \"<group>\"; };\n\t\t335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = \"<group>\"; };\n\t\t33CC10ED2044A3C60003C045 /* AIdea.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AIdea.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = \"<group>\"; };\n\t\t33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = \"<group>\"; };\n\t\t33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = \"<group>\"; };\n\t\t33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = \"Flutter-Debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = \"Flutter-Release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = \"Flutter-Generated.xcconfig\"; path = \"ephemeral/Flutter-Generated.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = \"<group>\"; };\n\t\t33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = \"<group>\"; };\n\t\t33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = \"<group>\"; };\n\t\t43240CB6A8AF2E981A3B5D25 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = \"<group>\"; };\n\t\tC5938F4B7199953F9DBA617A /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.release.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t33CC10EA2044A3C60003C045 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t5A29A95C06583FFBDD292907 /* Pods_Runner.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t33BA886A226E78AF003329D5 /* Configs */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33E5194F232828860026EE4D /* AppInfo.xcconfig */,\n\t\t\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */,\n\t\t\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */,\n\t\t\t\t333000ED22D3DE5D00554162 /* Warnings.xcconfig */,\n\t\t\t);\n\t\t\tpath = Configs;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC10E42044A3C60003C045 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33FAB671232836740065AC1E /* Runner */,\n\t\t\t\t33CEB47122A05771004F2AC0 /* Flutter */,\n\t\t\t\t33CC10EE2044A3C60003C045 /* Products */,\n\t\t\t\tD73912EC22F37F3D000D13A0 /* Frameworks */,\n\t\t\t\t729C48A7C1C7FFEDEFF13553 /* Pods */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC10EE2044A3C60003C045 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10ED2044A3C60003C045 /* AIdea.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC11242044D66E0003C045 /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F22044A3C60003C045 /* Assets.xcassets */,\n\t\t\t\t33CC10F42044A3C60003C045 /* MainMenu.xib */,\n\t\t\t\t33CC10F72044A3C60003C045 /* Info.plist */,\n\t\t\t);\n\t\t\tname = Resources;\n\t\t\tpath = ..;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CEB47122A05771004F2AC0 /* Flutter */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,\n\t\t\t\t33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,\n\t\t\t\t33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,\n\t\t\t\t33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,\n\t\t\t);\n\t\t\tpath = Flutter;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33FAB671232836740065AC1E /* Runner */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F02044A3C60003C045 /* AppDelegate.swift */,\n\t\t\t\t33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,\n\t\t\t\t33E51913231747F40026EE4D /* DebugProfile.entitlements */,\n\t\t\t\t33E51914231749380026EE4D /* Release.entitlements */,\n\t\t\t\t33CC11242044D66E0003C045 /* Resources */,\n\t\t\t\t33BA886A226E78AF003329D5 /* Configs */,\n\t\t\t);\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t729C48A7C1C7FFEDEFF13553 /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t03A3A2E26C91E71A9326CB79 /* Pods-Runner.debug.xcconfig */,\n\t\t\t\tC5938F4B7199953F9DBA617A /* Pods-Runner.release.xcconfig */,\n\t\t\t\t107E9B87B247CD30AF3B0493 /* Pods-Runner.profile.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD73912EC22F37F3D000D13A0 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t43240CB6A8AF2E981A3B5D25 /* Pods_Runner.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t33CC10EC2044A3C60003C045 /* Runner */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget \"Runner\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t67CB0A1AC8A57E2A6033912B /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t33CC10E92044A3C60003C045 /* Sources */,\n\t\t\t\t33CC10EA2044A3C60003C045 /* Frameworks */,\n\t\t\t\t33CC10EB2044A3C60003C045 /* Resources */,\n\t\t\t\t33CC110E2044A8840003C045 /* Bundle Framework */,\n\t\t\t\t3399D490228B24CF009A79C7 /* ShellScript */,\n\t\t\t\t5126F90D8DF54E4B9681052E /* [CP] Embed Pods Frameworks */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t33CC11202044C79F0003C045 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = Runner;\n\t\t\tproductName = Runner;\n\t\t\tproductReference = 33CC10ED2044A3C60003C045 /* AIdea.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t33CC10E52044A3C60003C045 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 0920;\n\t\t\t\tLastUpgradeCheck = 1510;\n\t\t\t\tORGANIZATIONNAME = \"\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t33CC10EC2044A3C60003C045 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tLastSwiftMigration = 1100;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t\tSystemCapabilities = {\n\t\t\t\t\t\t\tcom.apple.Sandbox = {\n\t\t\t\t\t\t\t\tenabled = 1;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\t\t\t\t\t33CC111A2044C6BA0003C045 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tProvisioningStyle = Manual;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject \"Runner\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 33CC10E42044A3C60003C045;\n\t\t\tproductRefGroup = 33CC10EE2044A3C60003C045 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t33CC10EC2044A3C60003C045 /* Runner */,\n\t\t\t\t33CC111A2044C6BA0003C045 /* Flutter Assemble */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t33CC10EB2044A3C60003C045 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,\n\t\t\t\t33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t3399D490228B24CF009A79C7 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"echo \\\"$PRODUCT_NAME.app\\\" > \\\"$PROJECT_DIR\\\"/Flutter/ephemeral/.app_filename && \\\"$FLUTTER_ROOT\\\"/packages/flutter_tools/bin/macos_assemble.sh embed\\n\";\n\t\t};\n\t\t33CC111E2044C6BF0003C045 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\tFlutter/ephemeral/FlutterInputs.xcfilelist,\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\tFlutter/ephemeral/tripwire,\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t\tFlutter/ephemeral/FlutterOutputs.xcfilelist,\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"$FLUTTER_ROOT\\\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire\";\n\t\t};\n\t\t5126F90D8DF54E4B9681052E /* [CP] Embed Pods Frameworks */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist\",\n\t\t\t);\n\t\t\tname = \"[CP] Embed Pods Frameworks\";\n\t\t\toutputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t67CB0A1AC8A57E2A6033912B /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t33CC10E92044A3C60003C045 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,\n\t\t\t\t33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,\n\t\t\t\t335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\t33CC11202044C79F0003C045 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;\n\t\t\ttargetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin PBXVariantGroup section */\n\t\t33CC10F42044A3C60003C045 /* MainMenu.xib */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F52044A3C60003C045 /* Base */,\n\t\t\t);\n\t\t\tname = MainMenu.xib;\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t338D0CE9231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CEA231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=macosx*]\" = \"Apple Development\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_TEAM = N95437SZ2A;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CEB231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t33CC10F92044A3C60003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC10FA2044A3C60003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t33CC10FC2044A3C60003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=macosx*]\" = \"Apple Development\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_TEAM = N95437SZ2A;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC10FD2044A3C60003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=macosx*]\" = \"Apple Development\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_TEAM = N95437SZ2A;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t33CC111C2044C6BA0003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC111D2044C6BA0003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t33CC10E82044A3C60003C045 /* Build configuration list for PBXProject \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC10F92044A3C60003C045 /* Debug */,\n\t\t\t\t33CC10FA2044A3C60003C045 /* Release */,\n\t\t\t\t338D0CE9231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC10FC2044A3C60003C045 /* Debug */,\n\t\t\t\t33CC10FD2044A3C60003C045 /* Release */,\n\t\t\t\t338D0CEA231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget \"Flutter Assemble\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC111C2044C6BA0003C045 /* Debug */,\n\t\t\t\t33CC111D2044C6BA0003C045 /* Release */,\n\t\t\t\t338D0CEB231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 33CC10E52044A3C60003C045 /* Project object */;\n}\n"
  },
  {
    "path": "macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1510\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n               BuildableName = \"AIdea.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"AIdea.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"AIdea.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"AIdea.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "macos/Runner.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Runner.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:Pods/Pods.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "nginx.conf",
    "content": "server {\n    listen 80;\n\n    gzip on;\n    gzip_static on;\n    gzip_vary on;\n    gzip_types   text/plain application/x-javascript text/css application/xml text/xml application/javascript;\n\n    root /data/webroot;\n\n    location / {\n        index index.html;\n        try_files $uri $uri/ =404;\n    }\n}"
  },
  {
    "path": "pubspec.yaml",
    "content": "name: askaide\ndescription: 一款支持 GPT 以及国产大语言模型通义千问、文心一言等，支持 Stable Diffusion 文生图、图生图、 SDXL1.0、超分辨率、图片上色的全能型 APP。\n# The following line prevents the package from being accidentally published to\n# pub.dev using `flutter pub publish`. This is preferred for private packages.\npublish_to: 'none' # Remove this line if you wish to publish to pub.dev\n\n# The following defines the version and build number for your application.\n# A version number is three numbers separated by dots, like 1.2.43\n# followed by an optional build number separated by a +.\n# Both the version and the builder number may be overridden in flutter\n# build by specifying --build-name and --build-number, respectively.\n# In Android, build-name is used as versionName while build-number used as versionCode.\n# Read more about Android versioning at https://developer.android.com/studio/publish/versioning\n# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.\n# Read more about iOS versioning at\n# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html\n# In Windows, build-name is used as the major, minor, and patch parts\n# of the product and file versions while build-number is used as the build suffix.\n# 应用正式发布时，需要同步修改 lib/helper/constant.dart 中的 VERSION 值\nversion: 2.0.0+1\n\nenvironment:\n  sdk: '>=3.0.0 <4.0.0'\n\n# Dependencies specify other packages that your package needs in order to work.\n# To automatically upgrade your package dependencies to the latest versions\n# consider running `flutter pub upgrade --major-versions`. Alternatively,\n# dependencies can be manually updated by changing the version numbers below to\n# the latest version available on pub.dev. To see which dependencies have newer\n# versions available, run `flutter pub outdated`.\ndependencies:\n  flutter:\n    sdk: flutter\n\n\n  # The following adds the Cupertino Icons font to your application.\n  # Use with the CupertinoIcons class for iOS style icons.\n  cupertino_icons: ^1.0.2\n  intl: ^0.19.0\n  flutter_highlight: ^0.7.0\n  flutter_bloc: ^8.1.2\n  path_provider: ^2.0.14\n  dart_openai: \n    git: \n      url: https://github.com/mylxsw/openai.git\n  uuid: ^4.4.2\n  settings_ui: ^2.0.2\n  clipboard: ^0.1.3\n  bot_toast: ^4.0.3\n  loading_animation_widget: ^1.2.0+4\n  flip_card: ^0.7.0\n  flutter_slidable: ^3.0.1\n  provider: ^6.0.5\n  flutter_iconpicker: \n    git:\n      url: https://github.com/mylxsw/FlutterIconPicker\n      ref: hotfix\n  share_plus: ^7.2.1\n  go_router: ^8.2.0\n  sqflite: ^2.2.6\n  file_picker: ^5.2.8\n  http: ^1.1.0\n  cached_network_image: ^3.2.3\n  photo_view: ^0.14.0\n  flutter_image_compress: ^2.3.0\n  isolate_image_compress: \n    git:\n      url: https://github.com/mylxsw/isolate_flutter\n      ref: customized\n      path: isolate_image_compress\n  image: 4.2.0\n  flutter_cache_manager: ^3.3.0\n  file_saver: \n    git:\n      url: https://github.com/mylxsw/file_saver\n      ref: customized\n  image_gallery_saver: ^2.0.3\n  tiktoken: ^1.0.3\n  flutter_colorpicker: ^1.0.3\n  singleton: ^0.0.2\n  url_launcher: ^6.1.10\n  crypto: ^3.0.2\n  dio: ^5.5.0\n  dio_smart_retry: ^6.0.0\n  qiniu_flutter_sdk: ^0.6.0\n  sign_in_with_apple: ^4.3.0\n  record: ^5.0.4\n  animations: ^2.0.7\n  animated_text_kit: ^4.2.2\n  flutter_localization: ^0.1.11\n  random_avatar: ^0.0.8\n  in_app_purchase: ^3.1.7\n  fluwx: ^3.13.1\n  quickalert:\n    git:\n      url: https://github.com/mylxsw/QuickAlert.git\n      ref: customized\n  sign_in_button: ^3.2.0\n  animated_button_bar: ^1.0.0\n  circular_countdown_timer: ^0.2.3\n  sizer: ^2.0.15\n  logger: \n    git:\n      url: https://github.com/Bungeefan/logger.git\n  before_after: \n    git:\n      url: https://github.com/mylxsw/before_after.git\n  flutter_native_splash: ^2.2.19\n  flutter_drawing_board: ^0.4.4+2\n  loading_more_list: ^5.0.3\n  tobias: ^2.4.2\n  flutter_sticky_header: ^0.6.5\n  audioplayers: ^5.0.0\n  flutter_tts: ^3.7.0\n  widgets_to_image: ^0.0.2\n  custom_sliding_segmented_control: ^1.7.5\n  sqflite_common_ffi: ^2.2.5\n  sqflite_common_ffi_web: ^0.4.0\n  flutter_markdown: ^0.7.6+2 \n  markdown: ^7.3.0\n  fetch_api: 1.0.1\n  web_socket_channel: ^2.4.0\n  flutter_initicon: ^3.0.0+1\n  markdown_widget: ^2.3.1\n  flutter_math_fork: ^0.7.3\n  media_kit: ^1.1.10\n  media_kit_video: ^1.2.4\n  media_kit_libs_video: ^1.0.4\n  path: ^1.8.3\n  autoscale_tabbarview: ^1.0.2\n  flutter_stripe: ^11.0.0\n  flutter_stripe_web: ^6.0.0\n  stripe_js: ^6.0.0\n  qr_flutter: ^4.1.0\n  flutter_phoenix: ^1.1.1\n  dio_cache_interceptor: ^3.5.0\n  camera: ^0.11.0+2\n  camerawesome: ^2.1.0\n  auto_size_text: ^3.0.0\n  animated_list_plus: ^0.5.2\n  lottie: ^3.2.0\n  bitsdojo_window: ^0.1.6\n  \ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n\n  # The \"flutter_lints\" package below contains a set of recommended lints to\n  # encourage good coding practices. The lint set provided by the package is\n  # activated in the `analysis_options.yaml` file located at the root of your\n  # package. See that file for information about deactivating specific lint\n  # rules and activating additional ones.\n  flutter_lints: ^2.0.0\n  flutter_launcher_icons: \"^0.14.2\"\n  build_runner: ^2.3.3\n  msix: ^3.9.1\n\n# For information on the generic Dart part of this file, see the\n# following page: https://dart.dev/tools/pub/pubspec\n\n# The following section is specific to Flutter packages.\nflutter:\n\n  # The following line ensures that the Material Icons font is\n  # included with your application, so that you can use the icons in\n  # the material Icons class.\n  uses-material-design: true\n\n  # To add assets to your application, add an assets section, like this:\n  assets:\n    - assets/app.png\n    - assets/app-macos.png\n    - assets/image-broken.png\n    - assets/wechat.png\n    - assets/friendroom.png\n    - assets/share.png\n    - assets/app-256-transparent.png\n    - assets/light-dark-auto.png\n    - assets/openai.png\n    - assets/transport.png\n    - assets/weibo.png\n    - assets/github.png\n    - assets/x.png\n    - assets/xiaohongshu.png\n    - assets/play.png\n    - assets/text-to-video.gif\n    - assets/wechat-pay.png\n    - assets/zhifubao.png\n    - assets/stripe.png\n    - assets/apple.webp\n    - assets/icons/doc.png\n    - assets/icons/file.png\n    - assets/icons/mp3.png\n    - assets/icons/pdf.png\n    - assets/icons/txt.png\n    - assets/lottie/empty_status.json\n\n  \n  #   - images/a_dot_ham.jpeg\n\n  # An image asset can refer to one or more resolution-specific \"variants\", see\n  # https://flutter.dev/assets-and-images/#resolution-aware\n\n  # For details regarding adding assets from package dependencies, see\n  # https://flutter.dev/assets-and-images/#from-packages\n\n  # To add custom fonts to your application, add a fonts section here,\n  # in this \"flutter\" section. Each entry in this list should have a\n  # \"family\" key with the font family name, and a \"fonts\" key with a\n  # list giving the asset and other descriptors for the font. For\n  # example:\n  fonts:\n    - family: AlibabaPuHuiTi\n      fonts:\n        - asset: assets/fonts/AlibabaPuHuiTi-3-55-Regular.ttf\n        - asset: assets/fonts/AlibabaPuHuiTi-3-85-Bold.ttf\n          weight: 700\n  #\n  # For details regarding fonts from package dependencies,\n  # see https://flutter.dev/custom-fonts/#from-packages\n\nflutter_native_splash:\n  image: assets/app-splash.png\n  color: \"#ffffff\"\n  fullscreen: true\n\ntobias:\n  url_scheme: alipay2021004101661425\n\nmsix_config:\n  display_name: AIdea\n  publisher_display_name: Shenzhen Gulu Artificial Intelligence Technology Co., Ltd.\n  identity_name: cc.aicode.flutter.askaide.askaide\n  logo_path: assets/app.png\n  execution_alias: AIdea\n"
  },
  {
    "path": "scripts/go.mod",
    "content": "module github.com/mylxsw/aidea/scripts\n\ngo 1.20\n\nrequire (\n\tgithub.com/mylxsw/asteria v1.0.1\n\tgithub.com/mylxsw/go-utils v1.0.3\n)\n\nrequire (\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect\n\tgithub.com/modern-go/reflect2 v1.0.2 // indirect\n\tgolang.org/x/text v0.3.4 // indirect\n)\n"
  },
  {
    "path": "scripts/go.sum",
    "content": "github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=\ngithub.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/mylxsw/asteria v1.0.1 h1:M+RLL/0R0CkeRLwiaikBlLkEqO6rTpqqaMUhDVsZRqQ=\ngithub.com/mylxsw/asteria v1.0.1/go.mod h1:pmMRQjiOk1ZndmWnk7fDb4iIVrPhWCaWl6wV0R51zws=\ngithub.com/mylxsw/go-utils v1.0.3 h1:kL1n25xVzEDCjhtNx32dXXixFvuslCE5RGKMEUxeeJI=\ngithub.com/mylxsw/go-utils v1.0.3/go.mod h1:F5pQ/vTAgccZxQA7jsIBXM6m2INAbqPKfzbNwQgqhzY=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=\ngolang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=\ngolang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=\n"
  },
  {
    "path": "scripts/macos-icon-replace.sh",
    "content": "mv 16x16.png icon-16.png\ncp 32x32.png icon-16@2x.png\nmv 32x32.png icon-32.png\nmv 64x64.png icon-32@2x.png\nmv 128x128.png icon-128.png\ncp 256x256.png icon-128@2x.png\nmv 256x256.png icon-256.png\ncp 512x512.png icon-256@2x.png\nmv 512x512.png icon-512.png\nmv 1024x1024.png icon-512@2x.png"
  },
  {
    "path": "scripts/main.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/mylxsw/asteria/log\"\n\t\"github.com/mylxsw/go-utils/must\"\n)\n\n// 用于替换 Web 端的字体文件地址，将字体文件下载到本地，解决无法访问 Google 字体的问题\nfunc main() {\n\n\tmainDartJSPath := os.Args[1]\n\n\tdata := string(must.Must(os.ReadFile(mainDartJSPath)))\n\t// 替换字体为本地 CDN\n\t// fontRegex := regexp.MustCompile(`https://fonts\\.gstatic\\.com/(.*?)\\.(ttf|otf|woff|woff2)`)\n\n\t// for _, u := range fontRegex.FindAllString(data, -1) {\n\t// \tsavePath := must.Must(download(u))\n\t// \tdata = strings.ReplaceAll(data, u, \"https://resources.aicode.cc/fonts/\"+savePath)\n\t// }\n\n\t// 替换字体为国内镜像\n\tdata = strings.ReplaceAll(data, \"fonts.gstatic.com\", \"global-cdn.aicode.cc\")\n\tdata = strings.ReplaceAll(data, \"www.gstatic.com\", \"global-cdn.aicode.cc\")\n\n\t// 替换字体为国内镜像\n\tdata = strings.ReplaceAll(data, \"fonts.gstatic.com\", \"fonts-gstatic.lug.ustc.edu.cn\")\n\n\tmust.NoError(os.WriteFile(mainDartJSPath, []byte(data), 0755))\n\n\tlog.Debugf(\"replace font url success\")\n}\n\n// func download(remoteURL string) (string, error) {\n// \tsavePath := strings.TrimPrefix(remoteURL, \"https://fonts.gstatic.com/\")\n// \t//  检查目录是否存在，不存在则创建\n// \tif err := os.MkdirAll(filepath.Dir(savePath), 0755); err != nil {\n// \t\treturn \"\", err\n// \t}\n\n// \t// 检查文件是否存在\n// \tif _, err := os.Stat(savePath); err == nil {\n// \t\treturn savePath, nil\n// \t}\n\n// \tlog.Debugf(\"download %s to %s\", remoteURL, savePath)\n\n// \t// 下载文件到本地\n// \tresp, err := http.Get(remoteURL)\n// \tif err != nil {\n// \t\treturn \"\", err\n// \t}\n// \tdefer resp.Body.Close()\n\n// \tf, err := os.Create(savePath)\n// \tif err != nil {\n// \t\treturn \"\", err\n// \t}\n// \tdefer f.Close()\n\n// \tif _, err := io.Copy(f, resp.Body); err != nil {\n// \t\treturn \"\", err\n// \t}\n\n// \treturn savePath, nil\n// }\n"
  },
  {
    "path": "web/index.html",
    "content": "<!DOCTYPE html><html><head>\n  <!--\n    If you are serving your web app in a path other than the root, change the\n    href value below to reflect the base path you are serving from.\n\n    The path provided below has to start and end with a slash \"/\" in order for\n    it to work correctly.\n\n    For more details:\n    * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base\n\n    This is a placeholder for base href that will be replaced by the value of\n    the `--base-href` argument provided to `flutter build`.\n  -->\n  <base href=\"$FLUTTER_BASE_HREF\">\n\n  <meta charset=\"UTF-8\">\n  <meta content=\"IE=Edge\" http-equiv=\"X-UA-Compatible\">\n  <meta name=\"description\" content=\"一款强大的AI应用，涵盖当下最流行的人工智能模型\">\n\n  <!-- iOS meta tags & icons -->\n  <meta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n  <meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black\">\n  <meta name=\"apple-mobile-web-app-title\" content=\"AIdea\">\n  <link rel=\"apple-touch-icon\" href=\"icons/Icon-192.png\">\n\n  <!-- Favicon -->\n  <link rel=\"icon\" type=\"image/png\" href=\"favicon.png\">\n\n  <title>AIdea</title>\n  <link rel=\"manifest\" href=\"manifest.json\">\n\n  <script>\n    // The value below is injected by flutter build, do not touch.\n    var serviceWorkerVersion = null;\n  </script>\n  <!-- This script adds the flutter initialization JS code -->\n  <script src=\"flutter.js\" defer=\"\"></script>\n  <script type=\"text/javascript\" src=\"https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js\"></script>\n  \n  <meta content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\" name=\"viewport\">\n  \n  <style id=\"splash-screen-style\">\n    html {\n      height: 100%\n    }\n\n    body {\n      margin: 0;\n      min-height: 100%;\n      background-color: #ffffff;\n          background-size: 100% 100%;\n    }\n\n    .center {\n      margin: 0;\n      position: absolute;\n      top: 50%;\n      left: 50%;\n      -ms-transform: translate(-50%, -50%);\n      transform: translate(-50%, -50%);\n    }\n\n    .contain {\n      display:block;\n      width:100%; height:100%;\n      object-fit: contain;\n    }\n\n    .stretch {\n      display:block;\n      width:100%; height:100%;\n    }\n\n    .cover {\n      display:block;\n      width:100%; height:100%;\n      object-fit: cover;\n    }\n\n    .bottom {\n      position: absolute;\n      bottom: 0;\n      left: 50%;\n      -ms-transform: translate(-50%, 0);\n      transform: translate(-50%, 0);\n    }\n\n    .bottomLeft {\n      position: absolute;\n      bottom: 0;\n      left: 0;\n    }\n\n    .bottomRight {\n      position: absolute;\n      bottom: 0;\n      right: 0;\n    }\n  </style>\n  <script id=\"splash-screen-script\">\n    function removeSplashFromWeb() {\n      document.getElementById(\"splash\")?.remove();\n      document.getElementById(\"splash-branding\")?.remove();\n      document.body.style.background = \"transparent\";\n    }\n  </script>\n</head>\n<body>\n  <picture id=\"splash\">\n      <source srcset=\"splash/img/light-1x.png 1x, splash/img/light-2x.png 2x, splash/img/light-3x.png 3x, splash/img/light-4x.png 4x\" media=\"(prefers-color-scheme: light)\">\n      <source srcset=\"splash/img/dark-1x.png 1x, splash/img/dark-2x.png 2x, splash/img/dark-3x.png 3x, splash/img/dark-4x.png 4x\" media=\"(prefers-color-scheme: dark)\">\n      <img class=\"center\" aria-hidden=\"true\" src=\"splash/img/light-1x.png\" alt=\"\">\n  </picture>    \n  <script>\n    window.addEventListener('load', function(ev) {\n      // Download main.dart.js\n      _flutter.loader.loadEntrypoint({\n        serviceWorker: {\n          serviceWorkerVersion: serviceWorkerVersion,\n        },\n        onEntrypointLoaded: function(engineInitializer) {\n          engineInitializer.initializeEngine().then(function(appRunner) {\n            appRunner.runApp();\n          });\n        }\n      });\n    });\n  </script>\n\n\n</body></html>"
  },
  {
    "path": "web/manifest.json",
    "content": "{\n    \"name\": \"AIdea\",\n    \"short_name\": \"aidea\",\n    \"start_url\": \".\",\n    \"display\": \"standalone\",\n    \"background_color\": \"#hexcode\",\n    \"theme_color\": \"#hexcode\",\n    \"description\": \"一款强大的AI应用，涵盖当下最流行的人工智能模型\",\n    \"orientation\": \"portrait-primary\",\n    \"prefer_related_applications\": false,\n    \"icons\": [\n        {\n            \"src\": \"icons/Icon-192.png\",\n            \"sizes\": \"192x192\",\n            \"type\": \"image/png\"\n        },\n        {\n            \"src\": \"icons/Icon-512.png\",\n            \"sizes\": \"512x512\",\n            \"type\": \"image/png\"\n        },\n        {\n            \"src\": \"icons/Icon-maskable-192.png\",\n            \"sizes\": \"192x192\",\n            \"type\": \"image/png\",\n            \"purpose\": \"maskable\"\n        },\n        {\n            \"src\": \"icons/Icon-maskable-512.png\",\n            \"sizes\": \"512x512\",\n            \"type\": \"image/png\",\n            \"purpose\": \"maskable\"\n        }\n    ]\n}"
  },
  {
    "path": "web/splash/splash.js",
    "content": "function removeSplashFromWeb() {\n  document.getElementById(\"splash\")?.remove();\n  document.getElementById(\"splash-branding\")?.remove();\n  document.body.style.background = \"transparent\";\n}\n"
  },
  {
    "path": "web/splash/style.css",
    "content": "html {\n  height: 100%\n}\n\nbody {\n  margin: 0;\n  min-height: 100%;\n  background-color: #ffffff;\n  background-size: 100% 100%;\n}\n\n.center {\n  margin: 0;\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  -ms-transform: translate(-50%, -50%);\n  transform: translate(-50%, -50%);\n}\n\n.contain {\n  display:block;\n  width:100%; height:100%;\n  object-fit: contain;\n}\n\n.stretch {\n  display:block;\n  width:100%; height:100%;\n}\n\n.cover {\n  display:block;\n  width:100%; height:100%;\n  object-fit: cover;\n}\n\n.bottom {\n  position: absolute;\n  bottom: 0;\n  left: 50%;\n  -ms-transform: translate(-50%, 0);\n  transform: translate(-50%, 0);\n}\n\n.bottomLeft {\n  position: absolute;\n  bottom: 0;\n  left: 0;\n}\n\n.bottomRight {\n  position: absolute;\n  bottom: 0;\n  right: 0;\n}\n"
  },
  {
    "path": "web/sqflite_sw.js",
    "content": "(function dartProgram(){function copyProperties(a,b){var s=Object.keys(a)\nfor(var r=0;r<s.length;r++){var q=s[r]\nb[q]=a[q]}}function mixinPropertiesHard(a,b){var s=Object.keys(a)\nfor(var r=0;r<s.length;r++){var q=s[r]\nif(!b.hasOwnProperty(q))b[q]=a[q]}}function mixinPropertiesEasy(a,b){Object.assign(b,a)}var z=function(){var s=function(){}\ns.prototype={p:{}}\nvar r=new s()\nif(!(r.__proto__&&r.__proto__.p===s.prototype.p))return false\ntry{if(typeof navigator!=\"undefined\"&&typeof navigator.userAgent==\"string\"&&navigator.userAgent.indexOf(\"Chrome/\")>=0)return true\nif(typeof version==\"function\"&&version.length==0){var q=version()\nif(/^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(q))return true}}catch(p){}return false}()\nfunction inherit(a,b){a.prototype.constructor=a\na.prototype[\"$i\"+a.name]=a\nif(b!=null){if(z){a.prototype.__proto__=b.prototype\nreturn}var s=Object.create(b.prototype)\ncopyProperties(a.prototype,s)\na.prototype=s}}function inheritMany(a,b){for(var s=0;s<b.length;s++)inherit(b[s],a)}function mixinEasy(a,b){mixinPropertiesEasy(b.prototype,a.prototype)\na.prototype.constructor=a}function mixinHard(a,b){mixinPropertiesHard(b.prototype,a.prototype)\na.prototype.constructor=a}function lazyOld(a,b,c,d){var s=a\na[b]=s\na[c]=function(){a[c]=function(){A.vw(b)}\nvar r\nvar q=d\ntry{if(a[b]===s){r=a[b]=q\nr=a[b]=d()}else r=a[b]}finally{if(r===q)a[b]=null\na[c]=function(){return this[b]}}return r}}function lazy(a,b,c,d){var s=a\na[b]=s\na[c]=function(){if(a[b]===s)a[b]=d()\na[c]=function(){return this[b]}\nreturn a[b]}}function lazyFinal(a,b,c,d){var s=a\na[b]=s\na[c]=function(){if(a[b]===s){var r=d()\nif(a[b]!==s)A.nm(b)\na[b]=r}var q=a[b]\na[c]=function(){return q}\nreturn q}}function makeConstList(a){a.immutable$list=Array\na.fixed$length=Array\nreturn a}function convertToFastObject(a){function t(){}t.prototype=a\nnew t()\nreturn a}function convertAllToFastObject(a){for(var s=0;s<a.length;++s)convertToFastObject(a[s])}var y=0\nfunction instanceTearOffGetter(a,b){var s=null\nreturn a?function(c){if(s===null)s=A.od(b)\nreturn new s(c,this)}:function(){if(s===null)s=A.od(b)\nreturn new s(this,null)}}function staticTearOffGetter(a){var s=null\nreturn function(){if(s===null)s=A.od(a).prototype\nreturn s}}var x=0\nfunction tearOffParameters(a,b,c,d,e,f,g,h,i,j){if(typeof h==\"number\")h+=x\nreturn{co:a,iS:b,iI:c,rC:d,dV:e,cs:f,fs:g,fT:h,aI:i||0,nDA:j}}function installStaticTearOff(a,b,c,d,e,f,g,h){var s=tearOffParameters(a,true,false,c,d,e,f,g,h,false)\nvar r=staticTearOffGetter(s)\na[b]=r}function installInstanceTearOff(a,b,c,d,e,f,g,h,i,j){c=!!c\nvar s=tearOffParameters(a,false,c,d,e,f,g,h,i,!!j)\nvar r=instanceTearOffGetter(c,s)\na[b]=r}function setOrUpdateInterceptorsByTag(a){var s=v.interceptorsByTag\nif(!s){v.interceptorsByTag=a\nreturn}copyProperties(a,s)}function setOrUpdateLeafTags(a){var s=v.leafTags\nif(!s){v.leafTags=a\nreturn}copyProperties(a,s)}function updateTypes(a){var s=v.types\nvar r=s.length\ns.push.apply(s,a)\nreturn r}function updateHolder(a,b){copyProperties(b,a)\nreturn a}var hunkHelpers=function(){var s=function(a,b,c,d,e){return function(f,g,h,i){return installInstanceTearOff(f,g,a,b,c,d,[h],i,e,false)}},r=function(a,b,c,d){return function(e,f,g,h){return installStaticTearOff(e,f,a,b,c,[g],h,d)}}\nreturn{inherit:inherit,inheritMany:inheritMany,mixin:mixinEasy,mixinHard:mixinHard,installStaticTearOff:installStaticTearOff,installInstanceTearOff:installInstanceTearOff,_instance_0u:s(0,0,null,[\"$0\"],0),_instance_1u:s(0,1,null,[\"$1\"],0),_instance_2u:s(0,2,null,[\"$2\"],0),_instance_0i:s(1,0,null,[\"$0\"],0),_instance_1i:s(1,1,null,[\"$1\"],0),_instance_2i:s(1,2,null,[\"$2\"],0),_static_0:r(0,null,[\"$0\"],0),_static_1:r(1,null,[\"$1\"],0),_static_2:r(2,null,[\"$2\"],0),makeConstList:makeConstList,lazy:lazy,lazyFinal:lazyFinal,lazyOld:lazyOld,updateHolder:updateHolder,convertToFastObject:convertToFastObject,updateTypes:updateTypes,setOrUpdateInterceptorsByTag:setOrUpdateInterceptorsByTag,setOrUpdateLeafTags:setOrUpdateLeafTags}}()\nfunction initializeDeferredHunk(a){x=v.types.length\na(hunkHelpers,v,w,$)}var A={nz:function nz(){},\nfe(a,b,c){if(b.h(\"k<0>\").b(a))return new A.ep(a,b.h(\"@<0>\").q(c).h(\"ep<1,2>\"))\nreturn new A.cb(a,b.h(\"@<0>\").q(c).h(\"cb<1,2>\"))},\nrv(a){return new A.cQ(\"Field '\"+a+\"' has been assigned during initialization.\")},\noN(a){return new A.cQ(\"Field '\"+a+\"' has not been initialized.\")},\nn9(a){var s,r=a^48\nif(r<=9)return r\ns=a|32\nif(97<=s&&s<=102)return s-87\nreturn-1},\nbZ(a,b){a=a+b&536870911\na=a+((a&524287)<<10)&536870911\nreturn a^a>>>6},\nnO(a){a=a+((a&67108863)<<3)&536870911\na^=a>>>11\nreturn a+((a&16383)<<15)&536870911},\nc7(a,b,c){return a},\neb(a,b,c,d){A.aT(b,\"start\")\nif(c!=null){A.aT(c,\"end\")\nif(b>c)A.J(A.a1(b,0,c,\"start\",null))}return new A.cn(a,b,c,d.h(\"cn<0>\"))},\nnD(a,b,c,d){if(t.V.b(a))return new A.ce(a,b,c.h(\"@<0>\").q(d).h(\"ce<1,2>\"))\nreturn new A.bw(a,b,c.h(\"@<0>\").q(d).h(\"bw<1,2>\"))},\np0(a,b,c){var s=\"count\"\nif(t.V.b(a)){A.jg(b,s,t.S)\nA.aT(b,s)\nreturn new A.cG(a,b,c.h(\"cG<0>\"))}A.jg(b,s,t.S)\nA.aT(b,s)\nreturn new A.bA(a,b,c.h(\"bA<0>\"))},\nbt(){return new A.bB(\"No element\")},\noJ(){return new A.bB(\"Too few elements\")},\nry(a,b){return new A.dO(a,b.h(\"dO<0>\"))},\nt0(a,b,c){A.he(a,0,J.Y(a)-1,b,c)},\nhe(a,b,c,d,e){if(c-b<=32)A.t_(a,b,c,d,e)\nelse A.rZ(a,b,c,d,e)},\nt_(a,b,c,d,e){var s,r,q,p,o,n\nfor(s=b+1,r=J.T(a);s<=c;++s){q=r.i(a,s)\np=s\nwhile(!0){if(p>b){o=d.$2(r.i(a,p-1),q)\nif(typeof o!==\"number\")return o.a5()\no=o>0}else o=!1\nif(!o)break\nn=p-1\nr.k(a,p,r.i(a,n))\np=n}r.k(a,p,q)}},\nrZ(a3,a4,a5,a6,a7){var s,r,q,p,o,n,m,l,k,j=B.c.R(a5-a4+1,6),i=a4+j,h=a5-j,g=B.c.R(a4+a5,2),f=g-j,e=g+j,d=J.T(a3),c=d.i(a3,i),b=d.i(a3,f),a=d.i(a3,g),a0=d.i(a3,e),a1=d.i(a3,h),a2=a6.$2(c,b)\nif(typeof a2!==\"number\")return a2.a5()\nif(a2>0){s=b\nb=c\nc=s}a2=a6.$2(a0,a1)\nif(typeof a2!==\"number\")return a2.a5()\nif(a2>0){s=a1\na1=a0\na0=s}a2=a6.$2(c,a)\nif(typeof a2!==\"number\")return a2.a5()\nif(a2>0){s=a\na=c\nc=s}a2=a6.$2(b,a)\nif(typeof a2!==\"number\")return a2.a5()\nif(a2>0){s=a\na=b\nb=s}a2=a6.$2(c,a0)\nif(typeof a2!==\"number\")return a2.a5()\nif(a2>0){s=a0\na0=c\nc=s}a2=a6.$2(a,a0)\nif(typeof a2!==\"number\")return a2.a5()\nif(a2>0){s=a0\na0=a\na=s}a2=a6.$2(b,a1)\nif(typeof a2!==\"number\")return a2.a5()\nif(a2>0){s=a1\na1=b\nb=s}a2=a6.$2(b,a)\nif(typeof a2!==\"number\")return a2.a5()\nif(a2>0){s=a\na=b\nb=s}a2=a6.$2(a0,a1)\nif(typeof a2!==\"number\")return a2.a5()\nif(a2>0){s=a1\na1=a0\na0=s}d.k(a3,i,c)\nd.k(a3,g,a)\nd.k(a3,h,a1)\nd.k(a3,f,d.i(a3,a4))\nd.k(a3,e,d.i(a3,a5))\nr=a4+1\nq=a5-1\nif(J.a7(a6.$2(b,a0),0)){for(p=r;p<=q;++p){o=d.i(a3,p)\nn=a6.$2(o,b)\nif(n===0)continue\nif(n<0){if(p!==r){d.k(a3,p,d.i(a3,r))\nd.k(a3,r,o)}++r}else for(;!0;){n=a6.$2(d.i(a3,q),b)\nif(n>0){--q\ncontinue}else{m=q-1\nif(n<0){d.k(a3,p,d.i(a3,r))\nl=r+1\nd.k(a3,r,d.i(a3,q))\nd.k(a3,q,o)\nq=m\nr=l\nbreak}else{d.k(a3,p,d.i(a3,q))\nd.k(a3,q,o)\nq=m\nbreak}}}}k=!0}else{for(p=r;p<=q;++p){o=d.i(a3,p)\nif(a6.$2(o,b)<0){if(p!==r){d.k(a3,p,d.i(a3,r))\nd.k(a3,r,o)}++r}else if(a6.$2(o,a0)>0)for(;!0;)if(a6.$2(d.i(a3,q),a0)>0){--q\nif(q<p)break\ncontinue}else{m=q-1\nif(a6.$2(d.i(a3,q),b)<0){d.k(a3,p,d.i(a3,r))\nl=r+1\nd.k(a3,r,d.i(a3,q))\nd.k(a3,q,o)\nr=l}else{d.k(a3,p,d.i(a3,q))\nd.k(a3,q,o)}q=m\nbreak}}k=!1}a2=r-1\nd.k(a3,a4,d.i(a3,a2))\nd.k(a3,a2,b)\na2=q+1\nd.k(a3,a5,d.i(a3,a2))\nd.k(a3,a2,a0)\nA.he(a3,a4,r-2,a6,a7)\nA.he(a3,q+2,a5,a6,a7)\nif(k)return\nif(r<i&&q>h){for(;J.a7(a6.$2(d.i(a3,r),b),0);)++r\nfor(;J.a7(a6.$2(d.i(a3,q),a0),0);)--q\nfor(p=r;p<=q;++p){o=d.i(a3,p)\nif(a6.$2(o,b)===0){if(p!==r){d.k(a3,p,d.i(a3,r))\nd.k(a3,r,o)}++r}else if(a6.$2(o,a0)===0)for(;!0;)if(a6.$2(d.i(a3,q),a0)===0){--q\nif(q<p)break\ncontinue}else{m=q-1\nif(a6.$2(d.i(a3,q),b)<0){d.k(a3,p,d.i(a3,r))\nl=r+1\nd.k(a3,r,d.i(a3,q))\nd.k(a3,q,o)\nr=l}else{d.k(a3,p,d.i(a3,q))\nd.k(a3,q,o)}q=m\nbreak}}A.he(a3,r,q,a6,a7)}else A.he(a3,r,q,a6,a7)},\nc3:function c3(){},\ndu:function du(a,b){this.a=a\nthis.$ti=b},\ncb:function cb(a,b){this.a=a\nthis.$ti=b},\nep:function ep(a,b){this.a=a\nthis.$ti=b},\nek:function ek(){},\nba:function ba(a,b){this.a=a\nthis.$ti=b},\ndv:function dv(a,b){this.a=a\nthis.$ti=b},\nju:function ju(a,b){this.a=a\nthis.b=b},\njt:function jt(a){this.a=a},\ncQ:function cQ(a){this.a=a},\nfh:function fh(a){this.a=a},\nni:function ni(){},\nkn:function kn(){},\nk:function k(){},\na3:function a3(){},\ncn:function cn(a,b,c,d){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.$ti=d},\naP:function aP(a,b,c){var _=this\n_.a=a\n_.b=b\n_.c=0\n_.d=null\n_.$ti=c},\nbw:function bw(a,b,c){this.a=a\nthis.b=b\nthis.$ti=c},\nce:function ce(a,b,c){this.a=a\nthis.b=b\nthis.$ti=c},\ndQ:function dQ(a,b,c){var _=this\n_.a=null\n_.b=a\n_.c=b\n_.$ti=c},\naf:function af(a,b,c){this.a=a\nthis.b=b\nthis.$ti=c},\nlv:function lv(a,b,c){this.a=a\nthis.b=b\nthis.$ti=c},\ncq:function cq(a,b,c){this.a=a\nthis.b=b\nthis.$ti=c},\nbA:function bA(a,b,c){this.a=a\nthis.b=b\nthis.$ti=c},\ncG:function cG(a,b,c){this.a=a\nthis.b=b\nthis.$ti=c},\ne2:function e2(a,b,c){this.a=a\nthis.b=b\nthis.$ti=c},\ncf:function cf(a){this.$ti=a},\ndB:function dB(a){this.$ti=a},\nef:function ef(a,b){this.a=a\nthis.$ti=b},\neg:function eg(a,b){this.a=a\nthis.$ti=b},\nar:function ar(){},\nc0:function c0(){},\nd2:function d2(){},\nic:function ic(a){this.a=a},\ndO:function dO(a,b){this.a=a\nthis.$ti=b},\ne0:function e0(a,b){this.a=a\nthis.$ti=b},\nd1:function d1(a){this.a=a},\neU:function eU(){},\nrd(){throw A.b(A.x(\"Cannot modify unmodifiable Map\"))},\nqo(a){var s=v.mangledGlobalNames[a]\nif(s!=null)return s\nreturn\"minified:\"+a},\nvn(a,b){var s\nif(b!=null){s=b.x\nif(s!=null)return s}return t.dX.b(a)},\nq(a){var s\nif(typeof a==\"string\")return a\nif(typeof a==\"number\"){if(a!==0)return\"\"+a}else if(!0===a)return\"true\"\nelse if(!1===a)return\"false\"\nelse if(a==null)return\"null\"\ns=J.bp(a)\nreturn s},\ndZ(a){var s,r=$.oR\nif(r==null)r=$.oR=Symbol(\"identityHashCode\")\ns=a[r]\nif(s==null){s=Math.random()*0x3fffffff|0\na[r]=s}return s},\nnE(a,b){var s,r,q,p,o,n=null,m=/^\\s*[+-]?((0x[a-f0-9]+)|(\\d+)|([a-z0-9]+))\\s*$/i.exec(a)\nif(m==null)return n\nif(3>=m.length)return A.d(m,3)\ns=m[3]\nif(b==null){if(s!=null)return parseInt(a,10)\nif(m[2]!=null)return parseInt(a,16)\nreturn n}if(b<2||b>36)throw A.b(A.a1(b,2,36,\"radix\",n))\nif(b===10&&s!=null)return parseInt(a,10)\nif(b<10||s==null){r=b<=10?47+b:86+b\nq=m[1]\nfor(p=q.length,o=0;o<p;++o)if((B.a.t(q,o)|32)>r)return n}return parseInt(a,b)},\nkb(a){return A.rH(a)},\nrH(a){var s,r,q,p\nif(a instanceof A.r)return A.aJ(A.a0(a),null)\ns=J.bL(a)\nif(s===B.W||s===B.Z||t.cx.b(a)){r=B.v(a)\nif(r!==\"Object\"&&r!==\"\")return r\nq=a.constructor\nif(typeof q==\"function\"){p=q.name\nif(typeof p==\"string\"&&p!==\"Object\"&&p!==\"\")return p}}return A.aJ(A.a0(a),null)},\nrJ(){if(!!self.location)return self.location.href\nreturn null},\noQ(a){var s,r,q,p,o=a.length\nif(o<=500)return String.fromCharCode.apply(null,a)\nfor(s=\"\",r=0;r<o;r=q){q=r+500\np=q<o?q:o\ns+=String.fromCharCode.apply(null,a.slice(r,p))}return s},\nrS(a){var s,r,q,p=A.t([],t.t)\nfor(s=a.length,r=0;r<a.length;a.length===s||(0,A.aM)(a),++r){q=a[r]\nif(!A.dl(q))throw A.b(A.cA(q))\nif(q<=65535)B.b.m(p,q)\nelse if(q<=1114111){B.b.m(p,55296+(B.c.M(q-65536,10)&1023))\nB.b.m(p,56320+(q&1023))}else throw A.b(A.cA(q))}return A.oQ(p)},\nrR(a){var s,r,q\nfor(s=a.length,r=0;r<s;++r){q=a[r]\nif(!A.dl(q))throw A.b(A.cA(q))\nif(q<0)throw A.b(A.cA(q))\nif(q>65535)return A.rS(a)}return A.oQ(a)},\nrT(a,b,c){var s,r,q,p\nif(c<=500&&b===0&&c===a.length)return String.fromCharCode.apply(null,a)\nfor(s=b,r=\"\";s<c;s=q){q=s+500\np=q<c?q:c\nr+=String.fromCharCode.apply(null,a.subarray(s,p))}return r},\nbx(a){var s\nif(0<=a){if(a<=65535)return String.fromCharCode(a)\nif(a<=1114111){s=a-65536\nreturn String.fromCharCode((B.c.M(s,10)|55296)>>>0,s&1023|56320)}}throw A.b(A.a1(a,0,1114111,null,null))},\naS(a){if(a.date===void 0)a.date=new Date(a.a)\nreturn a.date},\nrQ(a){return a.b?A.aS(a).getUTCFullYear()+0:A.aS(a).getFullYear()+0},\nrO(a){return a.b?A.aS(a).getUTCMonth()+1:A.aS(a).getMonth()+1},\nrK(a){return a.b?A.aS(a).getUTCDate()+0:A.aS(a).getDate()+0},\nrL(a){return a.b?A.aS(a).getUTCHours()+0:A.aS(a).getHours()+0},\nrN(a){return a.b?A.aS(a).getUTCMinutes()+0:A.aS(a).getMinutes()+0},\nrP(a){return a.b?A.aS(a).getUTCSeconds()+0:A.aS(a).getSeconds()+0},\nrM(a){return a.b?A.aS(a).getUTCMilliseconds()+0:A.aS(a).getMilliseconds()+0},\nbY(a,b,c){var s,r,q={}\nq.a=0\ns=[]\nr=[]\nq.a=b.length\nB.b.b4(s,b)\nq.b=\"\"\nif(c!=null&&c.a!==0)c.D(0,new A.ka(q,r,s))\nreturn J.r_(a,new A.fI(B.a3,0,s,r,0))},\nrI(a,b,c){var s,r,q\nif(Array.isArray(b))s=c==null||c.a===0\nelse s=!1\nif(s){r=b.length\nif(r===0){if(!!a.$0)return a.$0()}else if(r===1){if(!!a.$1)return a.$1(b[0])}else if(r===2){if(!!a.$2)return a.$2(b[0],b[1])}else if(r===3){if(!!a.$3)return a.$3(b[0],b[1],b[2])}else if(r===4){if(!!a.$4)return a.$4(b[0],b[1],b[2],b[3])}else if(r===5)if(!!a.$5)return a.$5(b[0],b[1],b[2],b[3],b[4])\nq=a[\"\"+\"$\"+r]\nif(q!=null)return q.apply(a,b)}return A.rG(a,b,c)},\nrG(a,b,c){var s,r,q,p,o,n,m,l,k,j,i,h,g=Array.isArray(b)?b:A.fM(b,!0,t.z),f=g.length,e=a.$R\nif(f<e)return A.bY(a,g,c)\ns=a.$D\nr=s==null\nq=!r?s():null\np=J.bL(a)\no=p.$C\nif(typeof o==\"string\")o=p[o]\nif(r){if(c!=null&&c.a!==0)return A.bY(a,g,c)\nif(f===e)return o.apply(a,g)\nreturn A.bY(a,g,c)}if(Array.isArray(q)){if(c!=null&&c.a!==0)return A.bY(a,g,c)\nn=e+q.length\nif(f>n)return A.bY(a,g,null)\nif(f<n){m=q.slice(f-e)\nif(g===b)g=A.fM(g,!0,t.z)\nB.b.b4(g,m)}return o.apply(a,g)}else{if(f>e)return A.bY(a,g,c)\nif(g===b)g=A.fM(g,!0,t.z)\nl=Object.keys(q)\nif(c==null)for(r=l.length,k=0;k<l.length;l.length===r||(0,A.aM)(l),++k){j=q[A.S(l[k])]\nif(B.z===j)return A.bY(a,g,c)\nB.b.m(g,j)}else{for(r=l.length,i=0,k=0;k<l.length;l.length===r||(0,A.aM)(l),++k){h=A.S(l[k])\nif(c.F(0,h)){++i\nB.b.m(g,c.i(0,h))}else{j=q[h]\nif(B.z===j)return A.bY(a,g,c)\nB.b.m(g,j)}}if(i!==c.a)return A.bY(a,g,c)}return o.apply(a,g)}},\nvf(a){throw A.b(A.cA(a))},\nd(a,b){if(a==null)J.Y(a)\nthrow A.b(A.dp(a,b))},\ndp(a,b){var s,r=\"index\"\nif(!A.dl(b))return new A.bh(!0,b,r,null)\ns=A.j(J.Y(a))\nif(b<0||b>=s)return A.V(b,s,a,null,r)\nreturn A.oS(b,r)},\nva(a,b,c){if(a<0||a>c)return A.a1(a,0,c,\"start\",null)\nif(b!=null)if(b<a||b>c)return A.a1(b,a,c,\"end\",null)\nreturn new A.bh(!0,b,\"end\",null)},\ncA(a){return new A.bh(!0,a,null,null)},\nb(a){var s,r\nif(a==null)a=new A.h1()\ns=new Error()\ns.dartException=a\nr=A.vx\nif(\"defineProperty\" in Object){Object.defineProperty(s,\"message\",{get:r})\ns.name=\"\"}else s.toString=r\nreturn s},\nvx(){return J.bp(this.dartException)},\nJ(a){throw A.b(a)},\naM(a){throw A.b(A.ap(a))},\nbC(a){var s,r,q,p,o,n\na=A.vt(a.replace(String({}),\"$receiver$\"))\ns=a.match(/\\\\\\$[a-zA-Z]+\\\\\\$/g)\nif(s==null)s=A.t([],t.s)\nr=s.indexOf(\"\\\\$arguments\\\\$\")\nq=s.indexOf(\"\\\\$argumentsExpr\\\\$\")\np=s.indexOf(\"\\\\$expr\\\\$\")\no=s.indexOf(\"\\\\$method\\\\$\")\nn=s.indexOf(\"\\\\$receiver\\\\$\")\nreturn new A.ld(a.replace(new RegExp(\"\\\\\\\\\\\\$arguments\\\\\\\\\\\\$\",\"g\"),\"((?:x|[^x])*)\").replace(new RegExp(\"\\\\\\\\\\\\$argumentsExpr\\\\\\\\\\\\$\",\"g\"),\"((?:x|[^x])*)\").replace(new RegExp(\"\\\\\\\\\\\\$expr\\\\\\\\\\\\$\",\"g\"),\"((?:x|[^x])*)\").replace(new RegExp(\"\\\\\\\\\\\\$method\\\\\\\\\\\\$\",\"g\"),\"((?:x|[^x])*)\").replace(new RegExp(\"\\\\\\\\\\\\$receiver\\\\\\\\\\\\$\",\"g\"),\"((?:x|[^x])*)\"),r,q,p,o,n)},\nle(a){return function($expr$){var $argumentsExpr$=\"$arguments$\"\ntry{$expr$.$method$($argumentsExpr$)}catch(s){return s.message}}(a)},\np7(a){return function($expr$){try{$expr$.$method$}catch(s){return s.message}}(a)},\nnA(a,b){var s=b==null,r=s?null:b.method\nreturn new A.fK(a,r,s?null:b.receiver)},\nM(a){var s\nif(a==null)return new A.h2(a)\nif(a instanceof A.dC){s=a.a\nreturn A.c9(a,s==null?t.K.a(s):s)}if(typeof a!==\"object\")return a\nif(\"dartException\" in a)return A.c9(a,a.dartException)\nreturn A.uY(a)},\nc9(a,b){if(t.W.b(b))if(b.$thrownJsError==null)b.$thrownJsError=a\nreturn b},\nuY(a){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e=null\nif(!(\"message\" in a))return a\ns=a.message\nif(\"number\" in a&&typeof a.number==\"number\"){r=a.number\nq=r&65535\nif((B.c.M(r,16)&8191)===10)switch(q){case 438:return A.c9(a,A.nA(A.q(s)+\" (Error \"+q+\")\",e))\ncase 445:case 5007:p=A.q(s)\nreturn A.c9(a,new A.dW(p+\" (Error \"+q+\")\",e))}}if(a instanceof TypeError){o=$.qs()\nn=$.qt()\nm=$.qu()\nl=$.qv()\nk=$.qy()\nj=$.qz()\ni=$.qx()\n$.qw()\nh=$.qB()\ng=$.qA()\nf=o.a4(s)\nif(f!=null)return A.c9(a,A.nA(A.S(s),f))\nelse{f=n.a4(s)\nif(f!=null){f.method=\"call\"\nreturn A.c9(a,A.nA(A.S(s),f))}else{f=m.a4(s)\nif(f==null){f=l.a4(s)\nif(f==null){f=k.a4(s)\nif(f==null){f=j.a4(s)\nif(f==null){f=i.a4(s)\nif(f==null){f=l.a4(s)\nif(f==null){f=h.a4(s)\nif(f==null){f=g.a4(s)\np=f!=null}else p=!0}else p=!0}else p=!0}else p=!0}else p=!0}else p=!0}else p=!0\nif(p){A.S(s)\nreturn A.c9(a,new A.dW(s,f==null?e:f.method))}}}return A.c9(a,new A.hx(typeof s==\"string\"?s:\"\"))}if(a instanceof RangeError){if(typeof s==\"string\"&&s.indexOf(\"call stack\")!==-1)return new A.e9()\ns=function(b){try{return String(b)}catch(d){}return null}(a)\nreturn A.c9(a,new A.bh(!1,e,e,typeof s==\"string\"?s.replace(/^RangeError:\\s*/,\"\"):s))}if(typeof InternalError==\"function\"&&a instanceof InternalError)if(typeof s==\"string\"&&s===\"too much recursion\")return new A.e9()\nreturn a},\na_(a){var s\nif(a instanceof A.dC)return a.b\nif(a==null)return new A.eH(a)\ns=a.$cachedTrace\nif(s!=null)return s\nreturn a.$cachedTrace=new A.eH(a)},\nj7(a){if(a==null||typeof a!=\"object\")return J.ax(a)\nelse return A.dZ(a)},\nvb(a,b){var s,r,q,p=a.length\nfor(s=0;s<p;s=q){r=s+1\nq=r+1\nb.k(0,a[s],a[r])}return b},\nvl(a,b,c,d,e,f){t.Y.a(a)\nswitch(A.j(b)){case 0:return a.$0()\ncase 1:return a.$1(c)\ncase 2:return a.$2(c,d)\ncase 3:return a.$3(c,d,e)\ncase 4:return a.$4(c,d,e,f)}throw A.b(A.oF(\"Unsupported number of arguments for wrapped closure\"))},\nc8(a,b){var s\nif(a==null)return null\ns=a.$identity\nif(!!s)return s\ns=function(c,d,e){return function(f,g,h,i){return e(c,d,f,g,h,i)}}(a,b,A.vl)\na.$identity=s\nreturn s},\nrb(a2){var s,r,q,p,o,n,m,l,k,j,i=a2.co,h=a2.iS,g=a2.iI,f=a2.nDA,e=a2.aI,d=a2.fs,c=a2.cs,b=d[0],a=c[0],a0=i[b],a1=a2.fT\na1.toString\ns=h?Object.create(new A.hk().constructor.prototype):Object.create(new A.cD(null,null).constructor.prototype)\ns.$initialize=s.constructor\nif(h)r=function static_tear_off(){this.$initialize()}\nelse r=function tear_off(a3,a4){this.$initialize(a3,a4)}\ns.constructor=r\nr.prototype=s\ns.$_name=b\ns.$_target=a0\nq=!h\nif(q)p=A.oD(b,a0,g,f)\nelse{s.$static_name=b\np=a0}s.$S=A.r7(a1,h,g)\ns[a]=p\nfor(o=p,n=1;n<d.length;++n){m=d[n]\nif(typeof m==\"string\"){l=i[m]\nk=m\nm=l}else k=\"\"\nj=c[n]\nif(j!=null){if(q)m=A.oD(k,m,g,f)\ns[j]=m}if(n===e)o=m}s.$C=o\ns.$R=a2.rC\ns.$D=a2.dV\nreturn r},\nr7(a,b,c){if(typeof a==\"number\")return a\nif(typeof a==\"string\"){if(b)throw A.b(\"Cannot compute signature for static tearoff.\")\nreturn function(d,e){return function(){return e(this,d)}}(a,A.r5)}throw A.b(\"Error in functionType of tearoff\")},\nr8(a,b,c,d){var s=A.oB\nswitch(b?-1:a){case 0:return function(e,f){return function(){return f(this)[e]()}}(c,s)\ncase 1:return function(e,f){return function(g){return f(this)[e](g)}}(c,s)\ncase 2:return function(e,f){return function(g,h){return f(this)[e](g,h)}}(c,s)\ncase 3:return function(e,f){return function(g,h,i){return f(this)[e](g,h,i)}}(c,s)\ncase 4:return function(e,f){return function(g,h,i,j){return f(this)[e](g,h,i,j)}}(c,s)\ncase 5:return function(e,f){return function(g,h,i,j,k){return f(this)[e](g,h,i,j,k)}}(c,s)\ndefault:return function(e,f){return function(){return e.apply(f(this),arguments)}}(d,s)}},\noD(a,b,c,d){var s,r\nif(c)return A.ra(a,b,d)\ns=b.length\nr=A.r8(s,d,a,b)\nreturn r},\nr9(a,b,c,d){var s=A.oB,r=A.r6\nswitch(b?-1:a){case 0:throw A.b(new A.hc(\"Intercepted function with no arguments.\"))\ncase 1:return function(e,f,g){return function(){return f(this)[e](g(this))}}(c,r,s)\ncase 2:return function(e,f,g){return function(h){return f(this)[e](g(this),h)}}(c,r,s)\ncase 3:return function(e,f,g){return function(h,i){return f(this)[e](g(this),h,i)}}(c,r,s)\ncase 4:return function(e,f,g){return function(h,i,j){return f(this)[e](g(this),h,i,j)}}(c,r,s)\ncase 5:return function(e,f,g){return function(h,i,j,k){return f(this)[e](g(this),h,i,j,k)}}(c,r,s)\ncase 6:return function(e,f,g){return function(h,i,j,k,l){return f(this)[e](g(this),h,i,j,k,l)}}(c,r,s)\ndefault:return function(e,f,g){return function(){var q=[g(this)]\nArray.prototype.push.apply(q,arguments)\nreturn e.apply(f(this),q)}}(d,r,s)}},\nra(a,b,c){var s,r\nif($.oz==null)$.oz=A.oy(\"interceptor\")\nif($.oA==null)$.oA=A.oy(\"receiver\")\ns=b.length\nr=A.r9(s,c,a,b)\nreturn r},\nod(a){return A.rb(a)},\nr5(a,b){return A.mF(v.typeUniverse,A.a0(a.a),b)},\noB(a){return a.a},\nr6(a){return a.b},\noy(a){var s,r,q,p=new A.cD(\"receiver\",\"interceptor\"),o=J.jP(Object.getOwnPropertyNames(p),t.X)\nfor(s=o.length,r=0;r<s;++r){q=o[r]\nif(p[q]===a)return q}throw A.b(A.ao(\"Field name \"+a+\" not found.\",null))},\naK(a){if(a==null)A.v_(\"boolean expression must not be null\")\nreturn a},\nv_(a){throw A.b(new A.hO(a))},\nvw(a){throw A.b(new A.fp(a))},\nvd(a){return v.getIsolateTag(a)},\nv5(a){var s,r=A.t([],t.s)\nif(a==null)return r\nif(Array.isArray(a)){for(s=0;s<a.length;++s)r.push(String(a[s]))\nreturn r}r.push(String(a))\nreturn r},\nvy(a,b){var s=$.D\nif(s===B.d)return a\nreturn s.dM(a,b)},\nwM(a,b,c){Object.defineProperty(a,b,{value:c,enumerable:false,writable:true,configurable:true})},\nvp(a){var s,r,q,p,o,n=A.S($.qe.$1(a)),m=$.n6[n]\nif(m!=null){Object.defineProperty(a,v.dispatchPropertyName,{value:m,enumerable:false,writable:true,configurable:true})\nreturn m.i}s=$.ne[n]\nif(s!=null)return s\nr=v.interceptorsByTag[n]\nif(r==null){q=A.o6($.q5.$2(a,n))\nif(q!=null){m=$.n6[q]\nif(m!=null){Object.defineProperty(a,v.dispatchPropertyName,{value:m,enumerable:false,writable:true,configurable:true})\nreturn m.i}s=$.ne[q]\nif(s!=null)return s\nr=v.interceptorsByTag[q]\nn=q}}if(r==null)return null\ns=r.prototype\np=n[0]\nif(p===\"!\"){m=A.nh(s)\n$.n6[n]=m\nObject.defineProperty(a,v.dispatchPropertyName,{value:m,enumerable:false,writable:true,configurable:true})\nreturn m.i}if(p===\"~\"){$.ne[n]=s\nreturn s}if(p===\"-\"){o=A.nh(s)\nObject.defineProperty(Object.getPrototypeOf(a),v.dispatchPropertyName,{value:o,enumerable:false,writable:true,configurable:true})\nreturn o.i}if(p===\"+\")return A.qi(a,s)\nif(p===\"*\")throw A.b(A.hw(n))\nif(v.leafTags[n]===true){o=A.nh(s)\nObject.defineProperty(Object.getPrototypeOf(a),v.dispatchPropertyName,{value:o,enumerable:false,writable:true,configurable:true})\nreturn o.i}else return A.qi(a,s)},\nqi(a,b){var s=Object.getPrototypeOf(a)\nObject.defineProperty(s,v.dispatchPropertyName,{value:J.oj(b,s,null,null),enumerable:false,writable:true,configurable:true})\nreturn b},\nnh(a){return J.oj(a,!1,null,!!a.$iF)},\nvs(a,b,c){var s=b.prototype\nif(v.leafTags[a]===true)return A.nh(s)\nelse return J.oj(s,c,null,null)},\nvj(){if(!0===$.oi)return\n$.oi=!0\nA.vk()},\nvk(){var s,r,q,p,o,n,m,l\n$.n6=Object.create(null)\n$.ne=Object.create(null)\nA.vi()\ns=v.interceptorsByTag\nr=Object.getOwnPropertyNames(s)\nif(typeof window!=\"undefined\"){window\nq=function(){}\nfor(p=0;p<r.length;++p){o=r[p]\nn=$.ql.$1(o)\nif(n!=null){m=A.vs(o,s[o],n)\nif(m!=null){Object.defineProperty(n,v.dispatchPropertyName,{value:m,enumerable:false,writable:true,configurable:true})\nq.prototype=n}}}}for(p=0;p<r.length;++p){o=r[p]\nif(/^[A-Za-z_]/.test(o)){l=s[o]\ns[\"!\"+o]=l\ns[\"~\"+o]=l\ns[\"-\"+o]=l\ns[\"+\"+o]=l\ns[\"*\"+o]=l}}},\nvi(){var s,r,q,p,o,n,m=B.L()\nm=A.dn(B.M,A.dn(B.N,A.dn(B.w,A.dn(B.w,A.dn(B.O,A.dn(B.P,A.dn(B.Q(B.v),m)))))))\nif(typeof dartNativeDispatchHooksTransformer!=\"undefined\"){s=dartNativeDispatchHooksTransformer\nif(typeof s==\"function\")s=[s]\nif(s.constructor==Array)for(r=0;r<s.length;++r){q=s[r]\nif(typeof q==\"function\")m=q(m)||m}}p=m.getTag\no=m.getUnknownTag\nn=m.prototypeForTag\n$.qe=new A.na(p)\n$.q5=new A.nb(o)\n$.ql=new A.nc(n)},\ndn(a,b){return a(b)||b},\noM(a,b,c,d,e,f){var s=b?\"m\":\"\",r=c?\"\":\"i\",q=d?\"u\":\"\",p=e?\"s\":\"\",o=f?\"g\":\"\",n=function(g,h){try{return new RegExp(g,h)}catch(m){return m}}(a,s+r+q+p+o)\nif(n instanceof RegExp)return n\nthrow A.b(A.ad(\"Illegal RegExp pattern (\"+String(n)+\")\",a,null))},\nvu(a,b,c){var s\nif(typeof b==\"string\")return a.indexOf(b,c)>=0\nelse if(b instanceof A.dL){s=B.a.O(a,c)\nreturn b.b.test(s)}else{s=J.qR(b,B.a.O(a,c))\nreturn!s.gC(s)}},\nvt(a){if(/[[\\]{}()*+?.\\\\^$|]/.test(a))return a.replace(/[[\\]{}()*+?.\\\\^$|]/g,\"\\\\$&\")\nreturn a},\nvv(a,b,c,d){return a.substring(0,b)+d+a.substring(c)},\ndx:function dx(a,b){this.a=a\nthis.$ti=b},\ndw:function dw(){},\ncc:function cc(a,b,c,d){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.$ti=d},\njv:function jv(a){this.a=a},\nem:function em(a,b){this.a=a\nthis.$ti=b},\nfI:function fI(a,b,c,d,e){var _=this\n_.a=a\n_.c=b\n_.d=c\n_.e=d\n_.f=e},\nka:function ka(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nld:function ld(a,b,c,d,e,f){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d\n_.e=e\n_.f=f},\ndW:function dW(a,b){this.a=a\nthis.b=b},\nfK:function fK(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nhx:function hx(a){this.a=a},\nh2:function h2(a){this.a=a},\ndC:function dC(a,b){this.a=a\nthis.b=b},\neH:function eH(a){this.a=a\nthis.b=null},\nbS:function bS(){},\nff:function ff(){},\nfg:function fg(){},\nho:function ho(){},\nhk:function hk(){},\ncD:function cD(a,b){this.a=a\nthis.b=b},\nhc:function hc(a){this.a=a},\nhO:function hO(a){this.a=a},\nmu:function mu(){},\nas:function as(a){var _=this\n_.a=0\n_.f=_.e=_.d=_.c=_.b=null\n_.r=0\n_.$ti=a},\njT:function jT(a){this.a=a},\njS:function jS(a){this.a=a},\njV:function jV(a,b){var _=this\n_.a=a\n_.b=b\n_.d=_.c=null},\nbv:function bv(a,b){this.a=a\nthis.$ti=b},\ndM:function dM(a,b,c){var _=this\n_.a=a\n_.b=b\n_.d=_.c=null\n_.$ti=c},\nna:function na(a){this.a=a},\nnb:function nb(a){this.a=a},\nnc:function nc(a){this.a=a},\ndL:function dL(a,b){var _=this\n_.a=a\n_.b=b\n_.d=_.c=null},\ney:function ey(a){this.b=a},\nhM:function hM(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nhN:function hN(a,b,c){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=null},\nea:function ea(a,b){this.a=a\nthis.c=b},\niE:function iE(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\niF:function iF(a,b,c){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=null},\naZ(a){return A.J(A.oN(a))},\nnm(a){return A.J(A.rv(a))},\nel(a){var s=new A.lI(a)\nreturn s.b=s},\nlI:function lI(a){this.a=a\nthis.b=null},\nup(a){return a},\npJ(a,b,c){},\nuv(a){return a},\nrB(a){return new Int8Array(a)},\ndS(a,b,c){A.pJ(a,b,c)\nc=B.c.R(a.byteLength-b,4)\nreturn new Uint32Array(a,b,c)},\nb_(a,b,c){A.pJ(a,b,c)\nreturn c==null?new Uint8Array(a,b):new Uint8Array(a,b,c)},\nbK(a,b,c){if(a>>>0!==a||a>=c)throw A.b(A.dp(b,a))},\nuq(a,b,c){var s\nif(!(a>>>0!==a))s=b>>>0!==b||a>b||b>c\nelse s=!0\nif(s)throw A.b(A.va(a,b,c))\nreturn b},\ncW:function cW(){},\na5:function a5(){},\ndR:function dR(){},\nag:function ag(){},\nbX:function bX(){},\naQ:function aQ(){},\nfT:function fT(){},\nfU:function fU(){},\nfV:function fV(){},\nfW:function fW(){},\nfX:function fX(){},\nfY:function fY(){},\nfZ:function fZ(){},\ndT:function dT(){},\ncl:function cl(){},\neA:function eA(){},\neB:function eB(){},\neC:function eC(){},\neD:function eD(){},\noY(a,b){var s=b.c\nreturn s==null?b.c=A.o1(a,b.y,!0):s},\noX(a,b){var s=b.c\nreturn s==null?b.c=A.eP(a,\"H\",[b.y]):s},\noZ(a){var s=a.x\nif(s===6||s===7||s===8)return A.oZ(a.y)\nreturn s===12||s===13},\nrY(a){return a.at},\naL(a){return A.iS(v.typeUniverse,a,!1)},\nc6(a,b,a0,a1){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c=b.x\nswitch(c){case 5:case 1:case 2:case 3:case 4:return b\ncase 6:s=b.y\nr=A.c6(a,s,a0,a1)\nif(r===s)return b\nreturn A.ps(a,r,!0)\ncase 7:s=b.y\nr=A.c6(a,s,a0,a1)\nif(r===s)return b\nreturn A.o1(a,r,!0)\ncase 8:s=b.y\nr=A.c6(a,s,a0,a1)\nif(r===s)return b\nreturn A.pr(a,r,!0)\ncase 9:q=b.z\np=A.f_(a,q,a0,a1)\nif(p===q)return b\nreturn A.eP(a,b.y,p)\ncase 10:o=b.y\nn=A.c6(a,o,a0,a1)\nm=b.z\nl=A.f_(a,m,a0,a1)\nif(n===o&&l===m)return b\nreturn A.o_(a,n,l)\ncase 12:k=b.y\nj=A.c6(a,k,a0,a1)\ni=b.z\nh=A.uV(a,i,a0,a1)\nif(j===k&&h===i)return b\nreturn A.pq(a,j,h)\ncase 13:g=b.z\na1+=g.length\nf=A.f_(a,g,a0,a1)\no=b.y\nn=A.c6(a,o,a0,a1)\nif(f===g&&n===o)return b\nreturn A.o0(a,n,f,!0)\ncase 14:e=b.y\nif(e<a1)return b\nd=a0[e-a1]\nif(d==null)return b\nreturn d\ndefault:throw A.b(A.f7(\"Attempted to substitute unexpected RTI kind \"+c))}},\nf_(a,b,c,d){var s,r,q,p,o=b.length,n=A.mJ(o)\nfor(s=!1,r=0;r<o;++r){q=b[r]\np=A.c6(a,q,c,d)\nif(p!==q)s=!0\nn[r]=p}return s?n:b},\nuW(a,b,c,d){var s,r,q,p,o,n,m=b.length,l=A.mJ(m)\nfor(s=!1,r=0;r<m;r+=3){q=b[r]\np=b[r+1]\no=b[r+2]\nn=A.c6(a,o,c,d)\nif(n!==o)s=!0\nl.splice(r,3,q,p,n)}return s?l:b},\nuV(a,b,c,d){var s,r=b.a,q=A.f_(a,r,c,d),p=b.b,o=A.f_(a,p,c,d),n=b.c,m=A.uW(a,n,c,d)\nif(q===r&&o===p&&m===n)return b\ns=new A.i4()\ns.a=q\ns.b=o\ns.c=m\nreturn s},\nt(a,b){a[v.arrayRti]=b\nreturn a},\nq8(a){var s,r=a.$S\nif(r!=null){if(typeof r==\"number\")return A.ve(r)\ns=a.$S()\nreturn s}return null},\nqf(a,b){var s\nif(A.oZ(b))if(a instanceof A.bS){s=A.q8(a)\nif(s!=null)return s}return A.a0(a)},\na0(a){var s\nif(a instanceof A.r){s=a.$ti\nreturn s!=null?s:A.o9(a)}if(Array.isArray(a))return A.av(a)\nreturn A.o9(J.bL(a))},\nav(a){var s=a[v.arrayRti],r=t.b\nif(s==null)return r\nif(s.constructor!==r.constructor)return r\nreturn s},\nv(a){var s=a.$ti\nreturn s!=null?s:A.o9(a)},\no9(a){var s=a.constructor,r=s.$ccache\nif(r!=null)return r\nreturn A.uD(a,s)},\nuD(a,b){var s=a instanceof A.bS?a.__proto__.__proto__.constructor:b,r=A.u2(v.typeUniverse,s.name)\nb.$ccache=r\nreturn r},\nve(a){var s,r=v.types,q=r[a]\nif(typeof q==\"string\"){s=A.iS(v.typeUniverse,q,!1)\nr[a]=s\nreturn s}return q},\noh(a){var s=a instanceof A.bS?A.q8(a):null\nreturn A.qb(s==null?A.a0(a):s)},\nqb(a){var s,r,q,p=a.w\nif(p!=null)return p\ns=a.at\nr=s.replace(/\\*/g,\"\")\nif(r===s)return a.w=new A.iR(a)\nq=A.iS(v.typeUniverse,r,!0)\np=q.w\nreturn a.w=p==null?q.w=new A.iR(q):p},\nai(a){return A.qb(A.iS(v.typeUniverse,a,!1))},\nuC(a){var s,r,q,p,o=this\nif(o===t.K)return A.dk(o,a,A.uI)\nif(!A.bM(o))if(!(o===t._))s=!1\nelse s=!0\nelse s=!0\nif(s)return A.dk(o,a,A.uM)\ns=o.x\nr=s===6?o.y:o\nif(r===t.S)q=A.dl\nelse if(r===t.dx||r===t.cZ)q=A.uH\nelse if(r===t.N)q=A.uK\nelse q=r===t.y?A.cz:null\nif(q!=null)return A.dk(o,a,q)\nif(r.x===9){p=r.y\nif(r.z.every(A.vo)){o.r=\"$i\"+p\nif(p===\"m\")return A.dk(o,a,A.uG)\nreturn A.dk(o,a,A.uL)}}else if(s===7)return A.dk(o,a,A.uz)\nreturn A.dk(o,a,A.ux)},\ndk(a,b,c){a.b=c\nreturn a.b(b)},\nuB(a){var s,r=this,q=A.uw\nif(!A.bM(r))if(!(r===t._))s=!1\nelse s=!0\nelse s=!0\nif(s)q=A.uk\nelse if(r===t.K)q=A.uj\nelse{s=A.f0(r)\nif(s)q=A.uy}r.a=q\nreturn r.a(a)},\nj5(a){var s,r=a.x\nif(!A.bM(a))if(!(a===t._))if(!(a===t.eK))if(r!==7)if(!(r===6&&A.j5(a.y)))s=r===8&&A.j5(a.y)||a===t.P||a===t.T\nelse s=!0\nelse s=!0\nelse s=!0\nelse s=!0\nelse s=!0\nreturn s},\nux(a){var s=this\nif(a==null)return A.j5(s)\nreturn A.Z(v.typeUniverse,A.qf(a,s),null,s,null)},\nuz(a){if(a==null)return!0\nreturn this.y.b(a)},\nuL(a){var s,r=this\nif(a==null)return A.j5(r)\ns=r.r\nif(a instanceof A.r)return!!a[s]\nreturn!!J.bL(a)[s]},\nuG(a){var s,r=this\nif(a==null)return A.j5(r)\nif(typeof a!=\"object\")return!1\nif(Array.isArray(a))return!0\ns=r.r\nif(a instanceof A.r)return!!a[s]\nreturn!!J.bL(a)[s]},\nuw(a){var s,r=this\nif(a==null){s=A.f0(r)\nif(s)return a}else if(r.b(a))return a\nA.pO(a,r)},\nuy(a){var s=this\nif(a==null)return a\nelse if(s.b(a))return a\nA.pO(a,s)},\npO(a,b){throw A.b(A.tS(A.pj(a,A.qf(a,b),A.aJ(b,null))))},\npj(a,b,c){var s=A.cg(a)\nreturn s+\": type '\"+A.aJ(b==null?A.a0(a):b,null)+\"' is not a subtype of type '\"+c+\"'\"},\ntS(a){return new A.eN(\"TypeError: \"+a)},\nau(a,b){return new A.eN(\"TypeError: \"+A.pj(a,null,b))},\nuI(a){return a!=null},\nuj(a){if(a!=null)return a\nthrow A.b(A.au(a,\"Object\"))},\nuM(a){return!0},\nuk(a){return a},\ncz(a){return!0===a||!1===a},\nwx(a){if(!0===a)return!0\nif(!1===a)return!1\nthrow A.b(A.au(a,\"bool\"))},\nwy(a){if(!0===a)return!0\nif(!1===a)return!1\nif(a==null)return a\nthrow A.b(A.au(a,\"bool\"))},\neW(a){if(!0===a)return!0\nif(!1===a)return!1\nif(a==null)return a\nthrow A.b(A.au(a,\"bool?\"))},\npI(a){if(typeof a==\"number\")return a\nthrow A.b(A.au(a,\"double\"))},\nwA(a){if(typeof a==\"number\")return a\nif(a==null)return a\nthrow A.b(A.au(a,\"double\"))},\nwz(a){if(typeof a==\"number\")return a\nif(a==null)return a\nthrow A.b(A.au(a,\"double?\"))},\ndl(a){return typeof a==\"number\"&&Math.floor(a)===a},\nj(a){if(typeof a==\"number\"&&Math.floor(a)===a)return a\nthrow A.b(A.au(a,\"int\"))},\nwB(a){if(typeof a==\"number\"&&Math.floor(a)===a)return a\nif(a==null)return a\nthrow A.b(A.au(a,\"int\"))},\ndj(a){if(typeof a==\"number\"&&Math.floor(a)===a)return a\nif(a==null)return a\nthrow A.b(A.au(a,\"int?\"))},\nuH(a){return typeof a==\"number\"},\nuh(a){if(typeof a==\"number\")return a\nthrow A.b(A.au(a,\"num\"))},\nwC(a){if(typeof a==\"number\")return a\nif(a==null)return a\nthrow A.b(A.au(a,\"num\"))},\nui(a){if(typeof a==\"number\")return a\nif(a==null)return a\nthrow A.b(A.au(a,\"num?\"))},\nuK(a){return typeof a==\"string\"},\nS(a){if(typeof a==\"string\")return a\nthrow A.b(A.au(a,\"String\"))},\nwD(a){if(typeof a==\"string\")return a\nif(a==null)return a\nthrow A.b(A.au(a,\"String\"))},\no6(a){if(typeof a==\"string\")return a\nif(a==null)return a\nthrow A.b(A.au(a,\"String?\"))},\npY(a,b){var s,r,q\nfor(s=\"\",r=\"\",q=0;q<a.length;++q,r=\", \")s+=r+A.aJ(a[q],b)\nreturn s},\nuR(a,b){var s,r,q,p,o,n,m=a.y,l=a.z\nif(\"\"===m)return\"(\"+A.pY(l,b)+\")\"\ns=l.length\nr=m.split(\",\")\nq=r.length-s\nfor(p=\"(\",o=\"\",n=0;n<s;++n,o=\", \"){p+=o\nif(q===0)p+=\"{\"\np+=A.aJ(l[n],b)\nif(q>=0)p+=\" \"+r[q];++q}return p+\"})\"},\npP(a4,a5,a6){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a,a0,a1,a2,a3=\", \"\nif(a6!=null){s=a6.length\nif(a5==null){a5=A.t([],t.s)\nr=null}else r=a5.length\nq=a5.length\nfor(p=s;p>0;--p)B.b.m(a5,\"T\"+(q+p))\nfor(o=t.X,n=t._,m=\"<\",l=\"\",p=0;p<s;++p,l=a3){k=a5.length\nj=k-1-p\nif(!(j>=0))return A.d(a5,j)\nm=B.a.bf(m+l,a5[j])\ni=a6[p]\nh=i.x\nif(!(h===2||h===3||h===4||h===5||i===o))if(!(i===n))k=!1\nelse k=!0\nelse k=!0\nif(!k)m+=\" extends \"+A.aJ(i,a5)}m+=\">\"}else{m=\"\"\nr=null}o=a4.y\ng=a4.z\nf=g.a\ne=f.length\nd=g.b\nc=d.length\nb=g.c\na=b.length\na0=A.aJ(o,a5)\nfor(a1=\"\",a2=\"\",p=0;p<e;++p,a2=a3)a1+=a2+A.aJ(f[p],a5)\nif(c>0){a1+=a2+\"[\"\nfor(a2=\"\",p=0;p<c;++p,a2=a3)a1+=a2+A.aJ(d[p],a5)\na1+=\"]\"}if(a>0){a1+=a2+\"{\"\nfor(a2=\"\",p=0;p<a;p+=3,a2=a3){a1+=a2\nif(b[p+1])a1+=\"required \"\na1+=A.aJ(b[p+2],a5)+\" \"+b[p]}a1+=\"}\"}if(r!=null){a5.toString\na5.length=r}return m+\"(\"+a1+\") => \"+a0},\naJ(a,b){var s,r,q,p,o,n,m,l=a.x\nif(l===5)return\"erased\"\nif(l===2)return\"dynamic\"\nif(l===3)return\"void\"\nif(l===1)return\"Never\"\nif(l===4)return\"any\"\nif(l===6){s=A.aJ(a.y,b)\nreturn s}if(l===7){r=a.y\ns=A.aJ(r,b)\nq=r.x\nreturn(q===12||q===13?\"(\"+s+\")\":s)+\"?\"}if(l===8)return\"FutureOr<\"+A.aJ(a.y,b)+\">\"\nif(l===9){p=A.uX(a.y)\no=a.z\nreturn o.length>0?p+(\"<\"+A.pY(o,b)+\">\"):p}if(l===11)return A.uR(a,b)\nif(l===12)return A.pP(a,b,null)\nif(l===13)return A.pP(a.y,b,a.z)\nif(l===14){n=a.y\nm=b.length\nn=m-1-n\nif(!(n>=0&&n<m))return A.d(b,n)\nreturn b[n]}return\"?\"},\nuX(a){var s=v.mangledGlobalNames[a]\nif(s!=null)return s\nreturn\"minified:\"+a},\nu3(a,b){var s=a.tR[b]\nfor(;typeof s==\"string\";)s=a.tR[s]\nreturn s},\nu2(a,b){var s,r,q,p,o,n=a.eT,m=n[b]\nif(m==null)return A.iS(a,b,!1)\nelse if(typeof m==\"number\"){s=m\nr=A.eQ(a,5,\"#\")\nq=A.mJ(s)\nfor(p=0;p<s;++p)q[p]=r\no=A.eP(a,b,q)\nn[b]=o\nreturn o}else return m},\nu0(a,b){return A.pG(a.tR,b)},\nu_(a,b){return A.pG(a.eT,b)},\niS(a,b,c){var s,r=a.eC,q=r.get(b)\nif(q!=null)return q\ns=A.pn(A.pl(a,null,b,c))\nr.set(b,s)\nreturn s},\nmF(a,b,c){var s,r,q=b.Q\nif(q==null)q=b.Q=new Map()\ns=q.get(c)\nif(s!=null)return s\nr=A.pn(A.pl(a,b,c,!0))\nq.set(c,r)\nreturn r},\nu1(a,b,c){var s,r,q,p=b.as\nif(p==null)p=b.as=new Map()\ns=c.at\nr=p.get(s)\nif(r!=null)return r\nq=A.o_(a,b,c.x===10?c.z:[c])\np.set(s,q)\nreturn q},\nbI(a,b){b.a=A.uB\nb.b=A.uC\nreturn b},\neQ(a,b,c){var s,r,q=a.eC.get(c)\nif(q!=null)return q\ns=new A.b2(null,null)\ns.x=b\ns.at=c\nr=A.bI(a,s)\na.eC.set(c,r)\nreturn r},\nps(a,b,c){var s,r=b.at+\"*\",q=a.eC.get(r)\nif(q!=null)return q\ns=A.tX(a,b,r,c)\na.eC.set(r,s)\nreturn s},\ntX(a,b,c,d){var s,r,q\nif(d){s=b.x\nif(!A.bM(b))r=b===t.P||b===t.T||s===7||s===6\nelse r=!0\nif(r)return b}q=new A.b2(null,null)\nq.x=6\nq.y=b\nq.at=c\nreturn A.bI(a,q)},\no1(a,b,c){var s,r=b.at+\"?\",q=a.eC.get(r)\nif(q!=null)return q\ns=A.tW(a,b,r,c)\na.eC.set(r,s)\nreturn s},\ntW(a,b,c,d){var s,r,q,p\nif(d){s=b.x\nif(!A.bM(b))if(!(b===t.P||b===t.T))if(s!==7)r=s===8&&A.f0(b.y)\nelse r=!0\nelse r=!0\nelse r=!0\nif(r)return b\nelse if(s===1||b===t.eK)return t.P\nelse if(s===6){q=b.y\nif(q.x===8&&A.f0(q.y))return q\nelse return A.oY(a,b)}}p=new A.b2(null,null)\np.x=7\np.y=b\np.at=c\nreturn A.bI(a,p)},\npr(a,b,c){var s,r=b.at+\"/\",q=a.eC.get(r)\nif(q!=null)return q\ns=A.tU(a,b,r,c)\na.eC.set(r,s)\nreturn s},\ntU(a,b,c,d){var s,r,q\nif(d){s=b.x\nif(!A.bM(b))if(!(b===t._))r=!1\nelse r=!0\nelse r=!0\nif(r||b===t.K)return b\nelse if(s===1)return A.eP(a,\"H\",[b])\nelse if(b===t.P||b===t.T)return t.gK}q=new A.b2(null,null)\nq.x=8\nq.y=b\nq.at=c\nreturn A.bI(a,q)},\ntY(a,b){var s,r,q=\"\"+b+\"^\",p=a.eC.get(q)\nif(p!=null)return p\ns=new A.b2(null,null)\ns.x=14\ns.y=b\ns.at=q\nr=A.bI(a,s)\na.eC.set(q,r)\nreturn r},\neO(a){var s,r,q,p=a.length\nfor(s=\"\",r=\"\",q=0;q<p;++q,r=\",\")s+=r+a[q].at\nreturn s},\ntT(a){var s,r,q,p,o,n=a.length\nfor(s=\"\",r=\"\",q=0;q<n;q+=3,r=\",\"){p=a[q]\no=a[q+1]?\"!\":\":\"\ns+=r+p+o+a[q+2].at}return s},\neP(a,b,c){var s,r,q,p=b\nif(c.length>0)p+=\"<\"+A.eO(c)+\">\"\ns=a.eC.get(p)\nif(s!=null)return s\nr=new A.b2(null,null)\nr.x=9\nr.y=b\nr.z=c\nif(c.length>0)r.c=c[0]\nr.at=p\nq=A.bI(a,r)\na.eC.set(p,q)\nreturn q},\no_(a,b,c){var s,r,q,p,o,n\nif(b.x===10){s=b.y\nr=b.z.concat(c)}else{r=c\ns=b}q=s.at+(\";<\"+A.eO(r)+\">\")\np=a.eC.get(q)\nif(p!=null)return p\no=new A.b2(null,null)\no.x=10\no.y=s\no.z=r\no.at=q\nn=A.bI(a,o)\na.eC.set(q,n)\nreturn n},\ntZ(a,b,c){var s,r,q=\"+\"+(b+\"(\"+A.eO(c)+\")\"),p=a.eC.get(q)\nif(p!=null)return p\ns=new A.b2(null,null)\ns.x=11\ns.y=b\ns.z=c\ns.at=q\nr=A.bI(a,s)\na.eC.set(q,r)\nreturn r},\npq(a,b,c){var s,r,q,p,o,n=b.at,m=c.a,l=m.length,k=c.b,j=k.length,i=c.c,h=i.length,g=\"(\"+A.eO(m)\nif(j>0){s=l>0?\",\":\"\"\ng+=s+\"[\"+A.eO(k)+\"]\"}if(h>0){s=l>0?\",\":\"\"\ng+=s+\"{\"+A.tT(i)+\"}\"}r=n+(g+\")\")\nq=a.eC.get(r)\nif(q!=null)return q\np=new A.b2(null,null)\np.x=12\np.y=b\np.z=c\np.at=r\no=A.bI(a,p)\na.eC.set(r,o)\nreturn o},\no0(a,b,c,d){var s,r=b.at+(\"<\"+A.eO(c)+\">\"),q=a.eC.get(r)\nif(q!=null)return q\ns=A.tV(a,b,c,r,d)\na.eC.set(r,s)\nreturn s},\ntV(a,b,c,d,e){var s,r,q,p,o,n,m,l\nif(e){s=c.length\nr=A.mJ(s)\nfor(q=0,p=0;p<s;++p){o=c[p]\nif(o.x===1){r[p]=o;++q}}if(q>0){n=A.c6(a,b,r,0)\nm=A.f_(a,c,r,0)\nreturn A.o0(a,n,m,c!==m)}}l=new A.b2(null,null)\nl.x=13\nl.y=b\nl.z=c\nl.at=d\nreturn A.bI(a,l)},\npl(a,b,c,d){return{u:a,e:b,r:c,s:[],p:0,n:d}},\npn(a){var s,r,q,p,o,n,m,l,k,j=a.r,i=a.s\nfor(s=j.length,r=0;r<s;){q=j.charCodeAt(r)\nif(q>=48&&q<=57)r=A.tN(r+1,q,j,i)\nelse if((((q|32)>>>0)-97&65535)<26||q===95||q===36||q===124)r=A.pm(a,r,j,i,!1)\nelse if(q===46)r=A.pm(a,r,j,i,!0)\nelse{++r\nswitch(q){case 44:break\ncase 58:i.push(!1)\nbreak\ncase 33:i.push(!0)\nbreak\ncase 59:i.push(A.c4(a.u,a.e,i.pop()))\nbreak\ncase 94:i.push(A.tY(a.u,i.pop()))\nbreak\ncase 35:i.push(A.eQ(a.u,5,\"#\"))\nbreak\ncase 64:i.push(A.eQ(a.u,2,\"@\"))\nbreak\ncase 126:i.push(A.eQ(a.u,3,\"~\"))\nbreak\ncase 60:i.push(a.p)\na.p=i.length\nbreak\ncase 62:p=a.u\no=i.splice(a.p)\nA.nZ(a.u,a.e,o)\na.p=i.pop()\nn=i.pop()\nif(typeof n==\"string\")i.push(A.eP(p,n,o))\nelse{m=A.c4(p,a.e,n)\nswitch(m.x){case 12:i.push(A.o0(p,m,o,a.n))\nbreak\ndefault:i.push(A.o_(p,m,o))\nbreak}}break\ncase 38:A.tO(a,i)\nbreak\ncase 42:p=a.u\ni.push(A.ps(p,A.c4(p,a.e,i.pop()),a.n))\nbreak\ncase 63:p=a.u\ni.push(A.o1(p,A.c4(p,a.e,i.pop()),a.n))\nbreak\ncase 47:p=a.u\ni.push(A.pr(p,A.c4(p,a.e,i.pop()),a.n))\nbreak\ncase 40:i.push(-3)\ni.push(a.p)\na.p=i.length\nbreak\ncase 41:A.tM(a,i)\nbreak\ncase 91:i.push(a.p)\na.p=i.length\nbreak\ncase 93:o=i.splice(a.p)\nA.nZ(a.u,a.e,o)\na.p=i.pop()\ni.push(o)\ni.push(-1)\nbreak\ncase 123:i.push(a.p)\na.p=i.length\nbreak\ncase 125:o=i.splice(a.p)\nA.tQ(a.u,a.e,o)\na.p=i.pop()\ni.push(o)\ni.push(-2)\nbreak\ncase 43:l=j.indexOf(\"(\",r)\ni.push(j.substring(r,l))\ni.push(-4)\ni.push(a.p)\na.p=i.length\nr=l+1\nbreak\ndefault:throw\"Bad character \"+q}}}k=i.pop()\nreturn A.c4(a.u,a.e,k)},\ntN(a,b,c,d){var s,r,q=b-48\nfor(s=c.length;a<s;++a){r=c.charCodeAt(a)\nif(!(r>=48&&r<=57))break\nq=q*10+(r-48)}d.push(q)\nreturn a},\npm(a,b,c,d,e){var s,r,q,p,o,n,m=b+1\nfor(s=c.length;m<s;++m){r=c.charCodeAt(m)\nif(r===46){if(e)break\ne=!0}else{if(!((((r|32)>>>0)-97&65535)<26||r===95||r===36||r===124))q=r>=48&&r<=57\nelse q=!0\nif(!q)break}}p=c.substring(b,m)\nif(e){s=a.u\no=a.e\nif(o.x===10)o=o.y\nn=A.u3(s,o.y)[p]\nif(n==null)A.J('No \"'+p+'\" in \"'+A.rY(o)+'\"')\nd.push(A.mF(s,o,n))}else d.push(p)\nreturn m},\ntM(a,b){var s,r,q,p,o,n=null,m=a.u,l=b.pop()\nif(typeof l==\"number\")switch(l){case-1:s=b.pop()\nr=n\nbreak\ncase-2:r=b.pop()\ns=n\nbreak\ndefault:b.push(l)\nr=n\ns=r\nbreak}else{b.push(l)\nr=n\ns=r}q=A.tL(a,b)\nl=b.pop()\nswitch(l){case-3:l=b.pop()\nif(s==null)s=m.sEA\nif(r==null)r=m.sEA\np=A.c4(m,a.e,l)\no=new A.i4()\no.a=q\no.b=s\no.c=r\nb.push(A.pq(m,p,o))\nreturn\ncase-4:b.push(A.tZ(m,b.pop(),q))\nreturn\ndefault:throw A.b(A.f7(\"Unexpected state under `()`: \"+A.q(l)))}},\ntO(a,b){var s=b.pop()\nif(0===s){b.push(A.eQ(a.u,1,\"0&\"))\nreturn}if(1===s){b.push(A.eQ(a.u,4,\"1&\"))\nreturn}throw A.b(A.f7(\"Unexpected extended operation \"+A.q(s)))},\ntL(a,b){var s=b.splice(a.p)\nA.nZ(a.u,a.e,s)\na.p=b.pop()\nreturn s},\nc4(a,b,c){if(typeof c==\"string\")return A.eP(a,c,a.sEA)\nelse if(typeof c==\"number\"){b.toString\nreturn A.tP(a,b,c)}else return c},\nnZ(a,b,c){var s,r=c.length\nfor(s=0;s<r;++s)c[s]=A.c4(a,b,c[s])},\ntQ(a,b,c){var s,r=c.length\nfor(s=2;s<r;s+=3)c[s]=A.c4(a,b,c[s])},\ntP(a,b,c){var s,r,q=b.x\nif(q===10){if(c===0)return b.y\ns=b.z\nr=s.length\nif(c<=r)return s[c-1]\nc-=r\nb=b.y\nq=b.x}else if(c===0)return b\nif(q!==9)throw A.b(A.f7(\"Indexed base must be an interface type\"))\ns=b.z\nif(c<=s.length)return s[c-1]\nthrow A.b(A.f7(\"Bad index \"+c+\" for \"+b.l(0)))},\nZ(a,b,c,d,e){var s,r,q,p,o,n,m,l,k,j\nif(b===d)return!0\nif(!A.bM(d))if(!(d===t._))s=!1\nelse s=!0\nelse s=!0\nif(s)return!0\nr=b.x\nif(r===4)return!0\nif(A.bM(b))return!1\nif(b.x!==1)s=!1\nelse s=!0\nif(s)return!0\nq=r===14\nif(q)if(A.Z(a,c[b.y],c,d,e))return!0\np=d.x\ns=b===t.P||b===t.T\nif(s){if(p===8)return A.Z(a,b,c,d.y,e)\nreturn d===t.P||d===t.T||p===7||p===6}if(d===t.K){if(r===8)return A.Z(a,b.y,c,d,e)\nif(r===6)return A.Z(a,b.y,c,d,e)\nreturn r!==7}if(r===6)return A.Z(a,b.y,c,d,e)\nif(p===6){s=A.oY(a,d)\nreturn A.Z(a,b,c,s,e)}if(r===8){if(!A.Z(a,b.y,c,d,e))return!1\nreturn A.Z(a,A.oX(a,b),c,d,e)}if(r===7){s=A.Z(a,t.P,c,d,e)\nreturn s&&A.Z(a,b.y,c,d,e)}if(p===8){if(A.Z(a,b,c,d.y,e))return!0\nreturn A.Z(a,b,c,A.oX(a,d),e)}if(p===7){s=A.Z(a,b,c,t.P,e)\nreturn s||A.Z(a,b,c,d.y,e)}if(q)return!1\ns=r!==12\nif((!s||r===13)&&d===t.Y)return!0\nif(p===13){if(b===t.et)return!0\nif(r!==13)return!1\no=b.z\nn=d.z\nm=o.length\nif(m!==n.length)return!1\nc=c==null?o:o.concat(c)\ne=e==null?n:n.concat(e)\nfor(l=0;l<m;++l){k=o[l]\nj=n[l]\nif(!A.Z(a,k,c,j,e)||!A.Z(a,j,e,k,c))return!1}return A.pR(a,b.y,c,d.y,e)}if(p===12){if(b===t.et)return!0\nif(s)return!1\nreturn A.pR(a,b,c,d,e)}if(r===9){if(p!==9)return!1\nreturn A.uF(a,b,c,d,e)}s=r===11\nif(s&&d===t.lZ)return!0\nif(s&&p===11)return A.uJ(a,b,c,d,e)\nreturn!1},\npR(a3,a4,a5,a6,a7){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a,a0,a1,a2\nif(!A.Z(a3,a4.y,a5,a6.y,a7))return!1\ns=a4.z\nr=a6.z\nq=s.a\np=r.a\no=q.length\nn=p.length\nif(o>n)return!1\nm=n-o\nl=s.b\nk=r.b\nj=l.length\ni=k.length\nif(o+j<n+i)return!1\nfor(h=0;h<o;++h){g=q[h]\nif(!A.Z(a3,p[h],a7,g,a5))return!1}for(h=0;h<m;++h){g=l[h]\nif(!A.Z(a3,p[o+h],a7,g,a5))return!1}for(h=0;h<i;++h){g=l[m+h]\nif(!A.Z(a3,k[h],a7,g,a5))return!1}f=s.c\ne=r.c\nd=f.length\nc=e.length\nfor(b=0,a=0;a<c;a+=3){a0=e[a]\nfor(;!0;){if(b>=d)return!1\na1=f[b]\nb+=3\nif(a0<a1)return!1\na2=f[b-2]\nif(a1<a0){if(a2)return!1\ncontinue}g=e[a+1]\nif(a2&&!g)return!1\ng=f[b-1]\nif(!A.Z(a3,e[a+2],a7,g,a5))return!1\nbreak}}for(;b<d;){if(f[b+1])return!1\nb+=3}return!0},\nuF(a,b,c,d,e){var s,r,q,p,o,n,m,l=b.y,k=d.y\nfor(;l!==k;){s=a.tR[l]\nif(s==null)return!1\nif(typeof s==\"string\"){l=s\ncontinue}r=s[k]\nif(r==null)return!1\nq=r.length\np=q>0?new Array(q):v.typeUniverse.sEA\nfor(o=0;o<q;++o)p[o]=A.mF(a,b,r[o])\nreturn A.pH(a,p,null,c,d.z,e)}n=b.z\nm=d.z\nreturn A.pH(a,n,null,c,m,e)},\npH(a,b,c,d,e,f){var s,r,q,p=b.length\nfor(s=0;s<p;++s){r=b[s]\nq=e[s]\nif(!A.Z(a,r,d,q,f))return!1}return!0},\nuJ(a,b,c,d,e){var s,r=b.z,q=d.z,p=r.length\nif(p!==q.length)return!1\nif(b.y!==d.y)return!1\nfor(s=0;s<p;++s)if(!A.Z(a,r[s],c,q[s],e))return!1\nreturn!0},\nf0(a){var s,r=a.x\nif(!(a===t.P||a===t.T))if(!A.bM(a))if(r!==7)if(!(r===6&&A.f0(a.y)))s=r===8&&A.f0(a.y)\nelse s=!0\nelse s=!0\nelse s=!0\nelse s=!0\nreturn s},\nvo(a){var s\nif(!A.bM(a))if(!(a===t._))s=!1\nelse s=!0\nelse s=!0\nreturn s},\nbM(a){var s=a.x\nreturn s===2||s===3||s===4||s===5||a===t.X},\npG(a,b){var s,r,q=Object.keys(b),p=q.length\nfor(s=0;s<p;++s){r=q[s]\na[r]=b[r]}},\nmJ(a){return a>0?new Array(a):v.typeUniverse.sEA},\nb2:function b2(a,b){var _=this\n_.a=a\n_.b=b\n_.w=_.r=_.c=null\n_.x=0\n_.at=_.as=_.Q=_.z=_.y=null},\ni4:function i4(){this.c=this.b=this.a=null},\niR:function iR(a){this.a=a},\ni_:function i_(){},\neN:function eN(a){this.a=a},\ntv(){var s,r,q={}\nif(self.scheduleImmediate!=null)return A.v0()\nif(self.MutationObserver!=null&&self.document!=null){s=self.document.createElement(\"div\")\nr=self.document.createElement(\"span\")\nq.a=null\nnew self.MutationObserver(A.c8(new A.lz(q),1)).observe(s,{childList:true})\nreturn new A.ly(q,s,r)}else if(self.setImmediate!=null)return A.v1()\nreturn A.v2()},\ntw(a){self.scheduleImmediate(A.c8(new A.lA(t.M.a(a)),0))},\ntx(a){self.setImmediate(A.c8(new A.lB(t.M.a(a)),0))},\nty(a){A.p6(B.u,t.M.a(a))},\np6(a,b){return A.tR(0,b)},\ntR(a,b){var s=new A.mD(!0)\ns.eC(a,b)\nreturn s},\nB(a){return new A.eh(new A.E($.D,a.h(\"E<0>\")),a.h(\"eh<0>\"))},\nA(a,b){a.$2(0,null)\nb.b=!0\nreturn b.a},\np(a,b){A.ul(a,b)},\nz(a,b){b.a0(0,a)},\ny(a,b){b.bA(A.M(a),A.a_(a))},\nul(a,b){var s,r,q=new A.mM(b),p=new A.mN(b)\nif(a instanceof A.E)a.dE(q,p,t.z)\nelse{s=t.z\nif(t.c.b(a))a.bP(q,p,s)\nelse{r=new A.E($.D,t.g)\nr.a=8\nr.c=a\nr.dE(q,p,s)}}},\nC(a){var s=function(b,c){return function(d,e){while(true)try{b(d,e)\nbreak}catch(r){e=r\nd=c}}}(a,1)\nreturn $.D.cN(new A.n2(s),t.H,t.S,t.z)},\nwt(a){return new A.db(a,1)},\ntI(){return B.am},\ntJ(a){return new A.db(a,3)},\nuO(a,b){return new A.eK(a,b.h(\"eK<0>\"))},\njh(a,b){var s=A.c7(a,\"error\",t.K)\nreturn new A.dt(s,b==null?A.f8(a):b)},\nf8(a){var s\nif(t.W.b(a)){s=a.gaW()\nif(s!=null)return s}return B.T},\nrl(a,b){var s=new A.E($.D,b.h(\"E<0>\"))\nA.tn(B.u,new A.jI(s,a))\nreturn s},\noG(a,b){var s,r,q,p,o,n,m,l\ntry{s=a.$0()\nif(b.h(\"H<0>\").b(s))return s\nelse{n=b.a(s)\nm=new A.E($.D,b.h(\"E<0>\"))\nm.a=8\nm.c=n\nreturn m}}catch(l){r=A.M(l)\nq=A.a_(l)\nn=$.D\np=new A.E(n,b.h(\"E<0>\"))\no=n.b7(r,q)\nif(o!=null)p.aC(o.a,o.b)\nelse p.aC(r,q)\nreturn p}},\noH(a,b){var s,r\nb.a(a)\ns=a\nr=new A.E($.D,b.h(\"E<0>\"))\nr.bk(s)\nreturn r},\ndE(a,b,c){var s,r\nA.c7(a,\"error\",t.K)\ns=$.D\nif(s!==B.d){r=s.b7(a,b)\nif(r!=null){a=r.a\nb=r.b}}if(b==null)b=A.f8(a)\ns=new A.E($.D,c.h(\"E<0>\"))\ns.aC(a,b)\nreturn s},\nnv(a,b){var s,r,q,p,o,n,m,l,k,j,i={},h=null,g=!1,f=new A.E($.D,b.h(\"E<m<0>>\"))\ni.a=null\ni.b=0\ns=A.el(\"error\")\nr=A.el(\"stackTrace\")\nq=new A.jK(i,h,g,f,s,r)\ntry{for(l=J.an(a),k=t.P;l.p();){p=l.gu(l)\no=i.b\np.bP(new A.jJ(i,o,f,h,g,s,r,b),q,k);++i.b}l=i.b\nif(l===0){l=f\nl.b0(A.t([],b.h(\"O<0>\")))\nreturn l}i.a=A.jX(l,null,!1,b.h(\"0?\"))}catch(j){n=A.M(j)\nm=A.a_(j)\nif(i.b===0||A.aK(g))return A.dE(n,m,b.h(\"m<0>\"))\nelse{s.b=n\nr.b=m}}return f},\npK(a,b,c){var s=$.D.b7(b,c)\nif(s!=null){b=s.a\nc=s.b}else if(c==null)c=A.f8(b)\na.V(b,c)},\nlU(a,b){var s,r,q\nfor(s=t.g;r=a.a,(r&4)!==0;)a=s.a(a.c)\nif((r&24)!==0){q=b.br()\nb.c2(a)\nA.da(b,q)}else{q=t.e.a(b.c)\nb.a=b.a&1|4\nb.c=a\na.dt(q)}},\nda(a,a0){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c={},b=c.a=a\nfor(s=t.n,r=t.e,q=t.c;!0;){p={}\no=b.a\nn=(o&16)===0\nm=!n\nif(a0==null){if(m&&(o&1)===0){l=s.a(b.c)\nb.b.dU(l.a,l.b)}return}p.a=a0\nk=a0.a\nfor(b=a0;k!=null;b=k,k=j){b.a=null\nA.da(c.a,b)\np.a=k\nj=k.a}o=c.a\ni=o.c\np.b=m\np.c=i\nif(n){h=b.c\nh=(h&1)!==0||(h&15)===8}else h=!0\nif(h){g=b.b.b\nif(m){b=o.b\nb=!(b===g||b.gaI()===g.gaI())}else b=!1\nif(b){b=c.a\nl=s.a(b.c)\nb.b.dU(l.a,l.b)\nreturn}f=$.D\nif(f!==g)$.D=g\nelse f=null\nb=p.a.c\nif((b&15)===8)new A.m1(p,c,m).$0()\nelse if(n){if((b&1)!==0)new A.m0(p,i).$0()}else if((b&2)!==0)new A.m_(c,p).$0()\nif(f!=null)$.D=f\nb=p.c\nif(q.b(b)){o=p.a.$ti\no=o.h(\"H<2>\").b(b)||!o.z[1].b(b)}else o=!1\nif(o){q.a(b)\ne=p.a.b\nif((b.a&24)!==0){d=r.a(e.c)\ne.c=null\na0=e.bt(d)\ne.a=b.a&30|e.a&1\ne.c=b.c\nc.a=b\ncontinue}else A.lU(b,e)\nreturn}}e=p.a.b\nd=r.a(e.c)\ne.c=null\na0=e.bt(d)\nb=p.b\no=p.c\nif(!b){e.$ti.c.a(o)\ne.a=8\ne.c=o}else{s.a(o)\ne.a=e.a&1|16\ne.c=o}c.a=e\nb=e}},\nuS(a,b){if(t.Q.b(a))return b.cN(a,t.z,t.K,t.l)\nif(t.v.b(a))return b.bN(a,t.z,t.K)\nthrow A.b(A.bq(a,\"onError\",u.c))},\nuP(){var s,r\nfor(s=$.dm;s!=null;s=$.dm){$.eY=null\nr=s.b\n$.dm=r\nif(r==null)$.eX=null\ns.a.$0()}},\nuU(){$.oa=!0\ntry{A.uP()}finally{$.eY=null\n$.oa=!1\nif($.dm!=null)$.om().$1(A.q7())}},\nq_(a){var s=new A.hP(a),r=$.eX\nif(r==null){$.dm=$.eX=s\nif(!$.oa)$.om().$1(A.q7())}else $.eX=r.b=s},\nuT(a){var s,r,q,p=$.dm\nif(p==null){A.q_(a)\n$.eY=$.eX\nreturn}s=new A.hP(a)\nr=$.eY\nif(r==null){s.b=p\n$.dm=$.eY=s}else{q=r.b\ns.b=q\n$.eY=r.b=s\nif(q==null)$.eX=s}},\nqm(a){var s,r=null,q=$.D\nif(B.d===q){A.n0(r,r,B.d,a)\nreturn}if(B.d===q.gfi().a)s=B.d.gaI()===q.gaI()\nelse s=!1\nif(s){A.n0(r,r,q,q.cO(a,t.H))\nreturn}s=$.D\ns.aB(s.cr(a))},\nw3(a,b){return new A.iD(A.c7(a,\"stream\",t.K),b.h(\"iD<0>\"))},\noc(a){return},\npi(a,b,c){var s=b==null?A.v3():b\nreturn a.bN(s,t.H,c)},\ntG(a,b){if(t.b9.b(b))return a.cN(b,t.z,t.K,t.l)\nif(t.i6.b(b))return a.bN(b,t.z,t.K)\nthrow A.b(A.ao(\"handleError callback must take either an Object (the error), or both an Object (the error) and a StackTrace.\",null))},\nuQ(a){},\nun(a,b,c){var s=a.Y(0),r=$.f2()\nif(s!==r)s.aS(new A.mO(b,c))\nelse b.b_(c)},\ntn(a,b){var s=$.D\nif(s===B.d)return s.dQ(a,b)\nreturn s.dQ(a,s.cr(b))},\nmZ(a,b){A.uT(new A.n_(a,b))},\npV(a,b,c,d,e){var s,r\nt.J.a(a)\nt.r.a(b)\nt.x.a(c)\ne.h(\"0()\").a(d)\nr=$.D\nif(r===c)return d.$0()\n$.D=c\ns=r\ntry{r=d.$0()\nreturn r}finally{$.D=s}},\npX(a,b,c,d,e,f,g){var s,r\nt.J.a(a)\nt.r.a(b)\nt.x.a(c)\nf.h(\"@<0>\").q(g).h(\"1(2)\").a(d)\ng.a(e)\nr=$.D\nif(r===c)return d.$1(e)\n$.D=c\ns=r\ntry{r=d.$1(e)\nreturn r}finally{$.D=s}},\npW(a,b,c,d,e,f,g,h,i){var s,r\nt.J.a(a)\nt.r.a(b)\nt.x.a(c)\ng.h(\"@<0>\").q(h).q(i).h(\"1(2,3)\").a(d)\nh.a(e)\ni.a(f)\nr=$.D\nif(r===c)return d.$2(e,f)\n$.D=c\ns=r\ntry{r=d.$2(e,f)\nreturn r}finally{$.D=s}},\nn0(a,b,c,d){var s,r\nt.M.a(d)\nif(B.d!==c){s=B.d.gaI()\nr=c.gaI()\nd=s!==r?c.cr(d):c.fD(d,t.H)}A.q_(d)},\nlz:function lz(a){this.a=a},\nly:function ly(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nlA:function lA(a){this.a=a},\nlB:function lB(a){this.a=a},\nmD:function mD(a){this.a=a\nthis.b=null\nthis.c=0},\nmE:function mE(a,b){this.a=a\nthis.b=b},\neh:function eh(a,b){this.a=a\nthis.b=!1\nthis.$ti=b},\nmM:function mM(a){this.a=a},\nmN:function mN(a){this.a=a},\nn2:function n2(a){this.a=a},\ndb:function db(a,b){this.a=a\nthis.b=b},\nde:function de(a,b){var _=this\n_.a=a\n_.d=_.c=_.b=null\n_.$ti=b},\neK:function eK(a,b){this.a=a\nthis.$ti=b},\ndt:function dt(a,b){this.a=a\nthis.b=b},\njI:function jI(a,b){this.a=a\nthis.b=b},\njK:function jK(a,b,c,d,e,f){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d\n_.e=e\n_.f=f},\njJ:function jJ(a,b,c,d,e,f,g,h){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d\n_.e=e\n_.f=f\n_.r=g\n_.w=h},\ncs:function cs(){},\ncr:function cr(a,b){this.a=a\nthis.$ti=b},\naa:function aa(a,b){this.a=a\nthis.$ti=b},\nbH:function bH(a,b,c,d,e){var _=this\n_.a=null\n_.b=a\n_.c=b\n_.d=c\n_.e=d\n_.$ti=e},\nE:function E(a,b){var _=this\n_.a=0\n_.b=a\n_.c=null\n_.$ti=b},\nlR:function lR(a,b){this.a=a\nthis.b=b},\nlZ:function lZ(a,b){this.a=a\nthis.b=b},\nlV:function lV(a){this.a=a},\nlW:function lW(a){this.a=a},\nlX:function lX(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nlT:function lT(a,b){this.a=a\nthis.b=b},\nlY:function lY(a,b){this.a=a\nthis.b=b},\nlS:function lS(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nm1:function m1(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nm2:function m2(a){this.a=a},\nm0:function m0(a,b){this.a=a\nthis.b=b},\nm_:function m_(a,b){this.a=a\nthis.b=b},\nhP:function hP(a){this.a=a\nthis.b=null},\naV:function aV(){},\nl9:function l9(a,b){this.a=a\nthis.b=b},\nla:function la(a,b){this.a=a\nthis.b=b},\nl7:function l7(a){this.a=a},\nl8:function l8(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nbm:function bm(){},\nhm:function hm(){},\ndd:function dd(){},\nmz:function mz(a){this.a=a},\nmy:function my(a){this.a=a},\niK:function iK(){},\ndf:function df(a,b,c,d,e){var _=this\n_.a=null\n_.b=0\n_.c=null\n_.d=a\n_.e=b\n_.f=c\n_.r=d\n_.$ti=e},\nd5:function d5(a,b){this.a=a\nthis.$ti=b},\nd6:function d6(a,b,c,d,e,f,g){var _=this\n_.w=a\n_.a=b\n_.b=c\n_.c=d\n_.d=e\n_.e=f\n_.r=_.f=null\n_.$ti=g},\nej:function ej(){},\nlH:function lH(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nlG:function lG(a){this.a=a},\neJ:function eJ(){},\nbG:function bG(){},\ncu:function cu(a,b){this.b=a\nthis.a=null\nthis.$ti=b},\nen:function en(a,b){this.b=a\nthis.c=b\nthis.a=null},\nhV:function hV(){},\nb4:function b4(a){var _=this\n_.a=0\n_.c=_.b=null\n_.$ti=a},\nms:function ms(a,b){this.a=a\nthis.b=b},\niD:function iD(a,b){var _=this\n_.a=null\n_.b=a\n_.c=!1\n_.$ti=b},\nmO:function mO(a,b){this.a=a\nthis.b=b},\niT:function iT(a,b,c){this.a=a\nthis.b=b\nthis.$ti=c},\neT:function eT(){},\nn_:function n_(a,b){this.a=a\nthis.b=b},\niu:function iu(){},\nmw:function mw(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nmv:function mv(a,b){this.a=a\nthis.b=b},\nmx:function mx(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nrw(a,b,c,d,e){if(c==null)if(b==null){if(a==null)return new A.as(d.h(\"@<0>\").q(e).h(\"as<1,2>\"))\nb=A.qa()}else{if(A.v8()===b&&A.v7()===a)return new A.et(d.h(\"@<0>\").q(e).h(\"et<1,2>\"))\nif(a==null)a=A.q9()}else{if(b==null)b=A.qa()\nif(a==null)a=A.q9()}return A.tK(a,b,c,d,e)},\naO(a,b,c){return b.h(\"@<0>\").q(c).h(\"jU<1,2>\").a(A.vb(a,new A.as(b.h(\"@<0>\").q(c).h(\"as<1,2>\"))))},\nX(a,b){return new A.as(a.h(\"@<0>\").q(b).h(\"as<1,2>\"))},\ntK(a,b,c,d,e){var s=c!=null?c:new A.mq(d)\nreturn new A.er(a,b,s,d.h(\"@<0>\").q(e).h(\"er<1,2>\"))},\nrx(a){return new A.es(a.h(\"es<0>\"))},\nnY(){var s=Object.create(null)\ns[\"<non-identifier-key>\"]=s\ndelete s[\"<non-identifier-key>\"]\nreturn s},\npk(a,b,c){var s=new A.cw(a,b,c.h(\"cw<0>\"))\ns.c=a.e\nreturn s},\nut(a,b){return J.a7(a,b)},\nuu(a){return J.ax(a)},\nrp(a,b,c){var s,r\nif(A.ob(a)){if(b===\"(\"&&c===\")\")return\"(...)\"\nreturn b+\"...\"+c}s=A.t([],t.s)\nB.b.m($.aY,a)\ntry{A.uN(a,s)}finally{if(0>=$.aY.length)return A.d($.aY,-1)\n$.aY.pop()}r=A.lb(b,t.e7.a(s),\", \")+c\nreturn r.charCodeAt(0)==0?r:r},\nnw(a,b,c){var s,r\nif(A.ob(a))return b+\"...\"+c\ns=new A.ah(b)\nB.b.m($.aY,a)\ntry{r=s\nr.a=A.lb(r.a,a,\", \")}finally{if(0>=$.aY.length)return A.d($.aY,-1)\n$.aY.pop()}s.a+=c\nr=s.a\nreturn r.charCodeAt(0)==0?r:r},\nob(a){var s,r\nfor(s=$.aY.length,r=0;r<s;++r)if(a===$.aY[r])return!0\nreturn!1},\nuN(a,b){var s,r,q,p,o,n,m,l=a.gE(a),k=0,j=0\nwhile(!0){if(!(k<80||j<3))break\nif(!l.p())return\ns=A.q(l.gu(l))\nB.b.m(b,s)\nk+=s.length+2;++j}if(!l.p()){if(j<=5)return\nif(0>=b.length)return A.d(b,-1)\nr=b.pop()\nif(0>=b.length)return A.d(b,-1)\nq=b.pop()}else{p=l.gu(l);++j\nif(!l.p()){if(j<=4){B.b.m(b,A.q(p))\nreturn}r=A.q(p)\nif(0>=b.length)return A.d(b,-1)\nq=b.pop()\nk+=r.length+2}else{o=l.gu(l);++j\nfor(;l.p();p=o,o=n){n=l.gu(l);++j\nif(j>100){while(!0){if(!(k>75&&j>3))break\nif(0>=b.length)return A.d(b,-1)\nk-=b.pop().length+2;--j}B.b.m(b,\"...\")\nreturn}}q=A.q(p)\nr=A.q(o)\nk+=r.length+q.length+4}}if(j>b.length+2){k+=5\nm=\"...\"}else m=null\nwhile(!0){if(!(k>80&&b.length>3))break\nif(0>=b.length)return A.d(b,-1)\nk-=b.pop().length+2\nif(m==null){k+=5\nm=\"...\"}}if(m!=null)B.b.m(b,m)\nB.b.m(b,q)\nB.b.m(b,r)},\nnB(a,b,c){var s=A.rw(null,null,null,b,c)\nJ.bo(a,new A.jW(s,b,c))\nreturn s},\njZ(a){var s,r={}\nif(A.ob(a))return\"{...}\"\ns=new A.ah(\"\")\ntry{B.b.m($.aY,a)\ns.a+=\"{\"\nr.a=!0\nJ.bo(a,new A.k_(r,s))\ns.a+=\"}\"}finally{if(0>=$.aY.length)return A.d($.aY,-1)\n$.aY.pop()}r=s.a\nreturn r.charCodeAt(0)==0?r:r},\net:function et(a){var _=this\n_.a=0\n_.f=_.e=_.d=_.c=_.b=null\n_.r=0\n_.$ti=a},\ner:function er(a,b,c,d){var _=this\n_.w=a\n_.x=b\n_.y=c\n_.a=0\n_.f=_.e=_.d=_.c=_.b=null\n_.r=0\n_.$ti=d},\nmq:function mq(a){this.a=a},\nes:function es(a){var _=this\n_.a=0\n_.f=_.e=_.d=_.c=_.b=null\n_.r=0\n_.$ti=a},\nib:function ib(a){this.a=a\nthis.c=this.b=null},\ncw:function cw(a,b,c){var _=this\n_.a=a\n_.b=b\n_.d=_.c=null\n_.$ti=c},\ndH:function dH(){},\njW:function jW(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\ncR:function cR(a){var _=this\n_.b=_.a=0\n_.c=null\n_.$ti=a},\neu:function eu(a,b,c,d){var _=this\n_.a=a\n_.b=b\n_.c=null\n_.d=c\n_.e=!1\n_.$ti=d},\nae:function ae(){},\ndN:function dN(){},\nh:function h(){},\ndP:function dP(){},\nk_:function k_(a,b){this.a=a\nthis.b=b},\nw:function w(){},\nk0:function k0(a){this.a=a},\nd3:function d3(){},\new:function ew(a,b){this.a=a\nthis.$ti=b},\nex:function ex(a,b,c){var _=this\n_.a=a\n_.b=b\n_.c=null\n_.$ti=c},\nc5:function c5(){},\ncS:function cS(){},\ned:function ed(){},\ne1:function e1(){},\neE:function eE(){},\nev:function ev(){},\ndh:function dh(){},\neV:function eV(){},\ntt(a,b,c,d){var s,r\nif(b instanceof Uint8Array){s=b\nif(d==null)d=s.length\nif(d-c<15)return null\nr=A.tu(a,s,c,d)\nif(r!=null&&a)if(r.indexOf(\"\\ufffd\")>=0)return null\nreturn r}return null},\ntu(a,b,c,d){var s=a?$.qD():$.qC()\nif(s==null)return null\nif(0===c&&d===b.length)return A.pa(s,b)\nreturn A.pa(s,b.subarray(c,A.by(c,d,b.length)))},\npa(a,b){var s,r\ntry{s=a.decode(b)\nreturn s}catch(r){}return null},\now(a,b,c,d,e,f){if(B.c.ab(f,4)!==0)throw A.b(A.ad(\"Invalid base64 padding, padded length must be multiple of four, is \"+f,a,c))\nif(d+e!==f)throw A.b(A.ad(\"Invalid base64 padding, '=' not at the end\",a,b))\nif(e>2)throw A.b(A.ad(\"Invalid base64 padding, more than two '=' characters\",a,b))},\nuf(a){switch(a){case 65:return\"Missing extension byte\"\ncase 67:return\"Unexpected extension byte\"\ncase 69:return\"Invalid UTF-8 byte\"\ncase 71:return\"Overlong encoding\"\ncase 73:return\"Out of unicode range\"\ncase 75:return\"Encoded surrogate\"\ncase 77:return\"Unfinished UTF-8 octet sequence\"\ndefault:return\"\"}},\nue(a,b,c){var s,r,q,p=c-b,o=new Uint8Array(p)\nfor(s=J.T(a),r=0;r<p;++r){q=s.i(a,b+r)\nif((q&4294967040)>>>0!==0)q=255\nif(!(r<p))return A.d(o,r)\no[r]=q}return o},\nln:function ln(){},\nlm:function lm(){},\nfc:function fc(){},\njr:function jr(){},\nay:function ay(){},\nfl:function fl(){},\nfw:function fw(){},\nee:function ee(){},\nlo:function lo(){},\nmI:function mI(a){this.b=0\nthis.c=a},\nll:function ll(a){this.a=a},\nmH:function mH(a){this.a=a\nthis.b=16\nthis.c=0},\nvh(a){return A.j7(a)},\nnd(a,b){var s=A.nE(a,b)\nif(s!=null)return s\nthrow A.b(A.ad(a,null,null))},\nox(a){var s=A.nX(a,null)\nif(s==null)A.J(A.ad(\"Could not parse BigInt\",a,null))\nreturn s},\nrg(a){if(a instanceof A.bS)return a.l(0)\nreturn\"Instance of '\"+A.kb(a)+\"'\"},\nrh(a,b){a=A.b(a)\nif(a==null)a=t.K.a(a)\na.stack=b.l(0)\nthrow a\nthrow A.b(\"unreachable\")},\njX(a,b,c,d){var s,r=c?J.rq(a,d):J.nx(a,d)\nif(a!==0&&b!=null)for(s=0;s<r.length;++s)r[s]=b\nreturn r},\njY(a,b,c){var s,r=A.t([],c.h(\"O<0>\"))\nfor(s=J.an(a);s.p();)B.b.m(r,c.a(s.gu(s)))\nif(b)return r\nreturn J.jP(r,c)},\nfM(a,b,c){var s\nif(b)return A.oO(a,c)\ns=J.jP(A.oO(a,c),c)\nreturn s},\noO(a,b){var s,r\nif(Array.isArray(a))return A.t(a.slice(0),b.h(\"O<0>\"))\ns=A.t([],b.h(\"O<0>\"))\nfor(r=J.an(a);r.p();)B.b.m(s,r.gu(r))\nreturn s},\nfN(a,b){return J.oK(A.jY(a,!1,b))},\np5(a,b,c){if(t.hD.b(a))return A.rT(a,b,A.by(b,c,a.length))\nreturn A.tl(a,b,c)},\ntk(a){return A.bx(a)},\ntl(a,b,c){var s,r,q,p,o,n=null\nif(b<0)throw A.b(A.a1(b,0,a.length,n,n))\ns=c==null\nif(!s&&c<b)throw A.b(A.a1(c,b,a.length,n,n))\nr=A.a0(a)\nq=new A.aP(a,a.length,r.h(\"aP<h.E>\"))\nfor(p=0;p<b;++p)if(!q.p())throw A.b(A.a1(b,0,p,n,n))\no=[]\nif(s)for(s=r.h(\"h.E\");q.p();){r=q.d\no.push(r==null?s.a(r):r)}else for(s=r.h(\"h.E\"),p=b;p<c;++p){if(!q.p())throw A.b(A.a1(c,b,p,n,n))\nr=q.d\no.push(r==null?s.a(r):r)}return A.rR(o)},\nb1(a,b){return new A.dL(a,A.oM(a,!1,b,!1,!1,!1))},\nvg(a,b){return a==null?b==null:a===b},\nlb(a,b,c){var s=J.an(b)\nif(!s.p())return a\nif(c.length===0){do a+=A.q(s.gu(s))\nwhile(s.p())}else{a+=A.q(s.gu(s))\nfor(;s.p();)a=a+c+A.q(s.gu(s))}return a},\nrC(a,b,c,d,e){return new A.dU(a,b,c,d,e)},\nnQ(){var s=A.rJ()\nif(s!=null)return A.li(s)\nthrow A.b(A.x(\"'Uri.base' is not supported\"))},\ntF(a,b){var s=A.nX(a,b)\nif(s==null)throw A.b(A.ad(\"Could not parse BigInt\",a,null))\nreturn s},\ntC(a,b){var s,r,q=$.bN(),p=a.length,o=4-p%4\nif(o===4)o=0\nfor(s=0,r=0;r<p;++r){s=s*10+B.a.t(a,r)-48;++o\nif(o===4){q=q.bg(0,$.on()).bf(0,A.lC(s))\ns=0\no=0}}if(b)return q.ac(0)\nreturn q},\npb(a){if(48<=a&&a<=57)return a-48\nreturn(a|32)-97+10},\ntD(a,b,c){var s,r,q,p,o,n,m,l=a.length,k=l-b,j=B.X.fF(k/4),i=new Uint16Array(j),h=j-1,g=k-h*4\nfor(s=b,r=0,q=0;q<g;++q,s=p){p=s+1\no=A.pb(B.a.t(a,s))\nif(o>=16)return null\nr=r*16+o}n=h-1\nif(!(h>=0&&h<j))return A.d(i,h)\ni[h]=r\nfor(;s<l;n=m){for(r=0,q=0;q<4;++q,s=p){p=s+1\no=A.pb(B.a.t(a,s))\nif(o>=16)return null\nr=r*16+o}m=n-1\nif(!(n>=0&&n<j))return A.d(i,n)\ni[n]=r}if(j===1){if(0>=j)return A.d(i,0)\nl=i[0]===0}else l=!1\nif(l)return $.bN()\nl=A.b3(j,i)\nreturn new A.a8(l===0?!1:c,i,l)},\nnX(a,b){var s,r,q,p,o,n\nif(a===\"\")return null\ns=$.qF().fS(a)\nif(s==null)return null\nr=s.b\nq=r.length\nif(1>=q)return A.d(r,1)\np=r[1]===\"-\"\nif(4>=q)return A.d(r,4)\no=r[4]\nn=r[3]\nif(5>=q)return A.d(r,5)\nif(o!=null)return A.tC(o,p)\nif(n!=null)return A.tD(n,2,p)\nreturn null},\nb3(a,b){var s,r=b.length\nwhile(!0){if(a>0){s=a-1\nif(!(s<r))return A.d(b,s)\ns=b[s]===0}else s=!1\nif(!s)break;--a}return a},\nnV(a,b,c,d){var s,r,q,p=new Uint16Array(d),o=c-b\nfor(s=a.length,r=0;r<o;++r){q=b+r\nif(!(q>=0&&q<s))return A.d(a,q)\nq=a[q]\nif(!(r<d))return A.d(p,r)\np[r]=q}return p},\nlC(a){var s,r,q,p,o=a<0\nif(o){if(a===-9223372036854776e3){s=new Uint16Array(4)\ns[3]=32768\nr=A.b3(4,s)\nreturn new A.a8(r!==0||!1,s,r)}a=-a}if(a<65536){s=new Uint16Array(1)\ns[0]=a\nr=A.b3(1,s)\nreturn new A.a8(r===0?!1:o,s,r)}if(a<=4294967295){s=new Uint16Array(2)\ns[0]=a&65535\ns[1]=B.c.M(a,16)\nr=A.b3(2,s)\nreturn new A.a8(r===0?!1:o,s,r)}r=B.c.R(B.c.gdN(a)-1,16)+1\ns=new Uint16Array(r)\nfor(q=0;a!==0;q=p){p=q+1\nif(!(q<r))return A.d(s,q)\ns[q]=a&65535\na=B.c.R(a,65536)}r=A.b3(r,s)\nreturn new A.a8(r===0?!1:o,s,r)},\nnW(a,b,c,d){var s,r,q,p,o\nif(b===0)return 0\nif(c===0&&d===a)return b\nfor(s=b-1,r=a.length,q=d.length;s>=0;--s){p=s+c\nif(!(s<r))return A.d(a,s)\no=a[s]\nif(!(p>=0&&p<q))return A.d(d,p)\nd[p]=o}for(s=c-1;s>=0;--s){if(!(s<q))return A.d(d,s)\nd[s]=0}return b+c},\ntB(a,b,c,d){var s,r,q,p,o,n,m,l=B.c.R(c,16),k=B.c.ab(c,16),j=16-k,i=B.c.aU(1,j)-1\nfor(s=b-1,r=a.length,q=d.length,p=0;s>=0;--s){if(!(s<r))return A.d(a,s)\no=a[s]\nn=s+l+1\nm=B.c.aV(o,j)\nif(!(n>=0&&n<q))return A.d(d,n)\nd[n]=(m|p)>>>0\np=B.c.aU((o&i)>>>0,k)}if(!(l>=0&&l<q))return A.d(d,l)\nd[l]=p},\npc(a,b,c,d){var s,r,q,p,o=B.c.R(c,16)\nif(B.c.ab(c,16)===0)return A.nW(a,b,o,d)\ns=b+o+1\nA.tB(a,b,c,d)\nfor(r=d.length,q=o;--q,q>=0;){if(!(q<r))return A.d(d,q)\nd[q]=0}p=s-1\nif(!(p>=0&&p<r))return A.d(d,p)\nif(d[p]===0)s=p\nreturn s},\ntE(a,b,c,d){var s,r,q,p,o,n,m=B.c.R(c,16),l=B.c.ab(c,16),k=16-l,j=B.c.aU(1,l)-1,i=a.length\nif(!(m>=0&&m<i))return A.d(a,m)\ns=B.c.aV(a[m],l)\nr=b-m-1\nfor(q=d.length,p=0;p<r;++p){o=p+m+1\nif(!(o<i))return A.d(a,o)\nn=a[o]\no=B.c.aU((n&j)>>>0,k)\nif(!(p<q))return A.d(d,p)\nd[p]=(o|s)>>>0\ns=B.c.aV(n,l)}if(!(r>=0&&r<q))return A.d(d,r)\nd[r]=s},\nlD(a,b,c,d){var s,r,q,p,o=b-d\nif(o===0)for(s=b-1,r=a.length,q=c.length;s>=0;--s){if(!(s<r))return A.d(a,s)\np=a[s]\nif(!(s<q))return A.d(c,s)\no=p-c[s]\nif(o!==0)return o}return o},\ntz(a,b,c,d,e){var s,r,q,p,o,n\nfor(s=a.length,r=c.length,q=e.length,p=0,o=0;o<d;++o){if(!(o<s))return A.d(a,o)\nn=a[o]\nif(!(o<r))return A.d(c,o)\np+=n+c[o]\nif(!(o<q))return A.d(e,o)\ne[o]=p&65535\np=B.c.M(p,16)}for(o=d;o<b;++o){if(!(o>=0&&o<s))return A.d(a,o)\np+=a[o]\nif(!(o<q))return A.d(e,o)\ne[o]=p&65535\np=B.c.M(p,16)}if(!(b>=0&&b<q))return A.d(e,b)\ne[b]=p},\nhR(a,b,c,d,e){var s,r,q,p,o,n\nfor(s=a.length,r=c.length,q=e.length,p=0,o=0;o<d;++o){if(!(o<s))return A.d(a,o)\nn=a[o]\nif(!(o<r))return A.d(c,o)\np+=n-c[o]\nif(!(o<q))return A.d(e,o)\ne[o]=p&65535\np=0-(B.c.M(p,16)&1)}for(o=d;o<b;++o){if(!(o>=0&&o<s))return A.d(a,o)\np+=a[o]\nif(!(o<q))return A.d(e,o)\ne[o]=p&65535\np=0-(B.c.M(p,16)&1)}},\nph(a,b,c,d,e,f){var s,r,q,p,o,n,m,l\nif(a===0)return\nfor(s=b.length,r=d.length,q=0;--f,f>=0;e=m,c=p){p=c+1\nif(!(c<s))return A.d(b,c)\no=b[c]\nif(!(e>=0&&e<r))return A.d(d,e)\nn=a*o+d[e]+q\nm=e+1\nd[e]=n&65535\nq=B.c.R(n,65536)}for(;q!==0;e=m){if(!(e>=0&&e<r))return A.d(d,e)\nl=d[e]+q\nm=e+1\nd[e]=l&65535\nq=B.c.R(l,65536)}},\ntA(a,b,c){var s,r,q,p=b.length\nif(!(c>=0&&c<p))return A.d(b,c)\ns=b[c]\nif(s===a)return 65535\nr=c-1\nif(!(r>=0&&r<p))return A.d(b,r)\nq=B.c.ex((s<<16|b[r])>>>0,a)\nif(q>65535)return 65535\nreturn q},\nre(a){var s=Math.abs(a),r=a<0?\"-\":\"\"\nif(s>=1000)return\"\"+a\nif(s>=100)return r+\"0\"+s\nif(s>=10)return r+\"00\"+s\nreturn r+\"000\"+s},\nrf(a){if(a>=100)return\"\"+a\nif(a>=10)return\"0\"+a\nreturn\"00\"+a},\nfs(a){if(a>=10)return\"\"+a\nreturn\"0\"+a},\ncg(a){if(typeof a==\"number\"||A.cz(a)||a==null)return J.bp(a)\nif(typeof a==\"string\")return JSON.stringify(a)\nreturn A.rg(a)},\nf7(a){return new A.ds(a)},\nao(a,b){return new A.bh(!1,null,b,a)},\nbq(a,b,c){return new A.bh(!0,a,b,c)},\njg(a,b,c){return a},\nrV(a){var s=null\nreturn new A.cX(s,s,!1,s,s,a)},\noS(a,b){return new A.cX(null,null,!0,a,b,\"Value not in range\")},\na1(a,b,c,d,e){return new A.cX(b,c,!0,a,d,\"Invalid value\")},\nby(a,b,c){if(0>a||a>c)throw A.b(A.a1(a,0,c,\"start\",null))\nif(b!=null){if(a>b||b>c)throw A.b(A.a1(b,a,c,\"end\",null))\nreturn b}return c},\naT(a,b){if(a<0)throw A.b(A.a1(a,0,null,b,null))\nreturn a},\nV(a,b,c,d,e){return new A.fE(b,!0,a,e,\"Index out of range\")},\nx(a){return new A.hz(a)},\nhw(a){return new A.hv(a)},\nK(a){return new A.bB(a)},\nap(a){return new A.fj(a)},\noF(a){return new A.i0(a)},\nad(a,b,c){return new A.fC(a,b,c)},\nrA(a,b,c,d,e){return new A.dv(a,b.h(\"@<0>\").q(c).q(d).q(e).h(\"dv<1,2,3,4>\"))},\noP(a,b,c,d){var s,r\nif(B.x===c){s=J.ax(a)\nb=J.ax(b)\nreturn A.nO(A.bZ(A.bZ($.no(),s),b))}if(B.x===d){s=J.ax(a)\nb=J.ax(b)\nc=J.ax(c)\nreturn A.nO(A.bZ(A.bZ(A.bZ($.no(),s),b),c))}s=J.ax(a)\nb=J.ax(b)\nc=J.ax(c)\nd=J.ax(d)\nr=$.no()\nreturn A.nO(A.bZ(A.bZ(A.bZ(A.bZ(r,s),b),c),d))},\nb9(a){var s=$.qk\nif(s==null)A.qj(a)\nelse s.$1(a)},\nli(a5){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a,a0,a1,a2,a3=null,a4=a5.length\nif(a4>=5){s=((B.a.t(a5,4)^58)*3|B.a.t(a5,0)^100|B.a.t(a5,1)^97|B.a.t(a5,2)^116|B.a.t(a5,3)^97)>>>0\nif(s===0)return A.p8(a4<a4?B.a.n(a5,0,a4):a5,5,a3).ged()\nelse if(s===32)return A.p8(B.a.n(a5,5,a4),0,a3).ged()}r=A.jX(8,0,!1,t.S)\nB.b.k(r,0,0)\nB.b.k(r,1,-1)\nB.b.k(r,2,-1)\nB.b.k(r,7,-1)\nB.b.k(r,3,0)\nB.b.k(r,4,0)\nB.b.k(r,5,a4)\nB.b.k(r,6,a4)\nif(A.pZ(a5,0,a4,0,r)>=14)B.b.k(r,7,a4)\nq=r[1]\nif(q>=0)if(A.pZ(a5,0,q,20,r)===20)r[7]=q\np=r[2]+1\no=r[3]\nn=r[4]\nm=r[5]\nl=r[6]\nif(l<m)m=l\nif(n<p)n=m\nelse if(n<=q)n=q+1\nif(o<p)o=n\nk=r[7]<0\nif(k)if(p>q+3){j=a3\nk=!1}else{i=o>0\nif(i&&o+1===n){j=a3\nk=!1}else{if(!B.a.H(a5,\"\\\\\",n))if(p>0)h=B.a.H(a5,\"\\\\\",p-1)||B.a.H(a5,\"\\\\\",p-2)\nelse h=!1\nelse h=!0\nif(h){j=a3\nk=!1}else{if(!(m<a4&&m===n+2&&B.a.H(a5,\"..\",n)))h=m>n+2&&B.a.H(a5,\"/..\",m-3)\nelse h=!0\nif(h){j=a3\nk=!1}else{if(q===4)if(B.a.H(a5,\"file\",0)){if(p<=0){if(!B.a.H(a5,\"/\",n)){g=\"file:///\"\ns=3}else{g=\"file://\"\ns=2}a5=g+B.a.n(a5,n,a4)\nq-=0\ni=s-0\nm+=i\nl+=i\na4=a5.length\np=7\no=7\nn=7}else if(n===m){++l\nf=m+1\na5=B.a.az(a5,n,m,\"/\");++a4\nm=f}j=\"file\"}else if(B.a.H(a5,\"http\",0)){if(i&&o+3===n&&B.a.H(a5,\"80\",o+1)){l-=3\ne=n-3\nm-=3\na5=B.a.az(a5,o,n,\"\")\na4-=3\nn=e}j=\"http\"}else j=a3\nelse if(q===5&&B.a.H(a5,\"https\",0)){if(i&&o+4===n&&B.a.H(a5,\"443\",o+1)){l-=4\ne=n-4\nm-=4\na5=B.a.az(a5,o,n,\"\")\na4-=3\nn=e}j=\"https\"}else j=a3\nk=!0}}}}else j=a3\nif(k){if(a4<a5.length){a5=B.a.n(a5,0,a4)\nq-=0\np-=0\no-=0\nn-=0\nm-=0\nl-=0}return new A.b5(a5,q,p,o,n,m,l,j)}if(j==null)if(q>0)j=A.u9(a5,0,q)\nelse{if(q===0)A.di(a5,0,\"Invalid empty scheme\")\nj=\"\"}if(p>0){d=q+3\nc=d<p?A.pB(a5,d,p-1):\"\"\nb=A.py(a5,p,o,!1)\ni=o+1\nif(i<n){a=A.nE(B.a.n(a5,i,n),a3)\na0=A.o3(a==null?A.J(A.ad(\"Invalid port\",a5,i)):a,j)}else a0=a3}else{a0=a3\nb=a0\nc=\"\"}a1=A.pz(a5,n,m,a3,j,b!=null)\na2=m<l?A.pA(a5,m+1,l,a3):a3\nreturn A.mG(j,c,b,a0,a1,a2,l<a4?A.px(a5,l+1,a4):a3)},\nts(a){A.S(a)\nreturn A.ud(a,0,a.length,B.f,!1)},\ntr(a,b,c){var s,r,q,p,o,n,m=\"IPv4 address should contain exactly 4 parts\",l=\"each part must be in the range 0..255\",k=new A.lh(a),j=new Uint8Array(4)\nfor(s=b,r=s,q=0;s<c;++s){p=B.a.B(a,s)\nif(p!==46){if((p^48)>9)k.$2(\"invalid character\",s)}else{if(q===3)k.$2(m,s)\no=A.nd(B.a.n(a,r,s),null)\nif(o>255)k.$2(l,r)\nn=q+1\nif(!(q<4))return A.d(j,q)\nj[q]=o\nr=s+1\nq=n}}if(q!==3)k.$2(m,c)\no=A.nd(B.a.n(a,r,c),null)\nif(o>255)k.$2(l,r)\nif(!(q<4))return A.d(j,q)\nj[q]=o\nreturn j},\np9(a,a0,a1){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d=null,c=new A.lj(a),b=new A.lk(c,a)\nif(a.length<2)c.$2(\"address is too short\",d)\ns=A.t([],t.t)\nfor(r=a0,q=r,p=!1,o=!1;r<a1;++r){n=B.a.B(a,r)\nif(n===58){if(r===a0){++r\nif(B.a.B(a,r)!==58)c.$2(\"invalid start colon.\",r)\nq=r}if(r===q){if(p)c.$2(\"only one wildcard `::` is allowed\",r)\nB.b.m(s,-1)\np=!0}else B.b.m(s,b.$2(q,r))\nq=r+1}else if(n===46)o=!0}if(s.length===0)c.$2(\"too few parts\",d)\nm=q===a1\nl=B.b.gai(s)\nif(m&&l!==-1)c.$2(\"expected a part after last `:`\",a1)\nif(!m)if(!o)B.b.m(s,b.$2(q,a1))\nelse{k=A.tr(a,q,a1)\nB.b.m(s,(k[0]<<8|k[1])>>>0)\nB.b.m(s,(k[2]<<8|k[3])>>>0)}if(p){if(s.length>7)c.$2(\"an address with a wildcard must have less than 7 parts\",d)}else if(s.length!==8)c.$2(\"an address without a wildcard must contain exactly 8 parts\",d)\nj=new Uint8Array(16)\nfor(l=s.length,i=9-l,r=0,h=0;r<l;++r){g=s[r]\nif(g===-1)for(f=0;f<i;++f){if(!(h>=0&&h<16))return A.d(j,h)\nj[h]=0\ne=h+1\nif(!(e<16))return A.d(j,e)\nj[e]=0\nh+=2}else{e=B.c.M(g,8)\nif(!(h>=0&&h<16))return A.d(j,h)\nj[h]=e\ne=h+1\nif(!(e<16))return A.d(j,e)\nj[e]=g&255\nh+=2}}return j},\nmG(a,b,c,d,e,f,g){return new A.eR(a,b,c,d,e,f,g)},\npu(a){if(a===\"http\")return 80\nif(a===\"https\")return 443\nreturn 0},\ndi(a,b,c){throw A.b(A.ad(c,a,b))},\nu5(a,b){var s,r,q\nfor(s=a.length,r=0;r<s;++r){q=a[r]\nif(J.nr(q,\"/\")){s=A.x(\"Illegal path character \"+A.q(q))\nthrow A.b(s)}}},\npt(a,b,c){var s,r,q\nfor(s=A.eb(a,c,null,A.av(a).c),r=s.$ti,s=new A.aP(s,s.gj(s),r.h(\"aP<a3.E>\")),r=r.h(\"a3.E\");s.p();){q=s.d\nif(q==null)q=r.a(q)\nif(B.a.S(q,A.b1('[\"*/:<>?\\\\\\\\|]',!0))){s=A.x(\"Illegal character in path: \"+q)\nthrow A.b(s)}}},\nu6(a,b){var s\nif(!(65<=a&&a<=90))s=97<=a&&a<=122\nelse s=!0\nif(s)return\ns=A.x(\"Illegal drive letter \"+A.tk(a))\nthrow A.b(s)},\no3(a,b){if(a!=null&&a===A.pu(b))return null\nreturn a},\npy(a,b,c,d){var s,r,q,p,o,n\nif(a==null)return null\nif(b===c)return\"\"\nif(B.a.B(a,b)===91){s=c-1\nif(B.a.B(a,s)!==93)A.di(a,b,\"Missing end `]` to match `[` in host\")\nr=b+1\nq=A.u7(a,r,s)\nif(q<s){p=q+1\no=A.pE(a,B.a.H(a,\"25\",p)?q+3:p,s,\"%25\")}else o=\"\"\nA.p9(a,r,q)\nreturn B.a.n(a,b,q).toLowerCase()+o+\"]\"}for(n=b;n<c;++n)if(B.a.B(a,n)===58){q=B.a.aq(a,\"%\",b)\nq=q>=b&&q<c?q:c\nif(q<c){p=q+1\no=A.pE(a,B.a.H(a,\"25\",p)?q+3:p,c,\"%25\")}else o=\"\"\nA.p9(a,b,q)\nreturn\"[\"+B.a.n(a,b,q)+o+\"]\"}return A.ub(a,b,c)},\nu7(a,b,c){var s=B.a.aq(a,\"%\",b)\nreturn s>=b&&s<c?s:c},\npE(a,b,c,d){var s,r,q,p,o,n,m,l,k,j,i=d!==\"\"?new A.ah(d):null\nfor(s=b,r=s,q=!0;s<c;){p=B.a.B(a,s)\nif(p===37){o=A.o4(a,s,!0)\nn=o==null\nif(n&&q){s+=3\ncontinue}if(i==null)i=new A.ah(\"\")\nm=i.a+=B.a.n(a,r,s)\nif(n)o=B.a.n(a,s,s+3)\nelse if(o===\"%\")A.di(a,s,\"ZoneID should not contain % anymore\")\ni.a=m+o\ns+=3\nr=s\nq=!0}else{if(p<127){n=p>>>4\nif(!(n<8))return A.d(B.o,n)\nn=(B.o[n]&1<<(p&15))!==0}else n=!1\nif(n){if(q&&65<=p&&90>=p){if(i==null)i=new A.ah(\"\")\nif(r<s){i.a+=B.a.n(a,r,s)\nr=s}q=!1}++s}else{if((p&64512)===55296&&s+1<c){l=B.a.B(a,s+1)\nif((l&64512)===56320){p=(p&1023)<<10|l&1023|65536\nk=2}else k=1}else k=1\nj=B.a.n(a,r,s)\nif(i==null){i=new A.ah(\"\")\nn=i}else n=i\nn.a+=j\nn.a+=A.o2(p)\ns+=k\nr=s}}}if(i==null)return B.a.n(a,b,c)\nif(r<c)i.a+=B.a.n(a,r,c)\nn=i.a\nreturn n.charCodeAt(0)==0?n:n},\nub(a,b,c){var s,r,q,p,o,n,m,l,k,j,i\nfor(s=b,r=s,q=null,p=!0;s<c;){o=B.a.B(a,s)\nif(o===37){n=A.o4(a,s,!0)\nm=n==null\nif(m&&p){s+=3\ncontinue}if(q==null)q=new A.ah(\"\")\nl=B.a.n(a,r,s)\nk=q.a+=!p?l.toLowerCase():l\nif(m){n=B.a.n(a,s,s+3)\nj=3}else if(n===\"%\"){n=\"%25\"\nj=1}else j=3\nq.a=k+n\ns+=j\nr=s\np=!0}else{if(o<127){m=o>>>4\nif(!(m<8))return A.d(B.A,m)\nm=(B.A[m]&1<<(o&15))!==0}else m=!1\nif(m){if(p&&65<=o&&90>=o){if(q==null)q=new A.ah(\"\")\nif(r<s){q.a+=B.a.n(a,r,s)\nr=s}p=!1}++s}else{if(o<=93){m=o>>>4\nif(!(m<8))return A.d(B.j,m)\nm=(B.j[m]&1<<(o&15))!==0}else m=!1\nif(m)A.di(a,s,\"Invalid character\")\nelse{if((o&64512)===55296&&s+1<c){i=B.a.B(a,s+1)\nif((i&64512)===56320){o=(o&1023)<<10|i&1023|65536\nj=2}else j=1}else j=1\nl=B.a.n(a,r,s)\nif(!p)l=l.toLowerCase()\nif(q==null){q=new A.ah(\"\")\nm=q}else m=q\nm.a+=l\nm.a+=A.o2(o)\ns+=j\nr=s}}}}if(q==null)return B.a.n(a,b,c)\nif(r<c){l=B.a.n(a,r,c)\nq.a+=!p?l.toLowerCase():l}m=q.a\nreturn m.charCodeAt(0)==0?m:m},\nu9(a,b,c){var s,r,q,p\nif(b===c)return\"\"\nif(!A.pw(B.a.t(a,b)))A.di(a,b,\"Scheme not starting with alphabetic character\")\nfor(s=b,r=!1;s<c;++s){q=B.a.t(a,s)\nif(q<128){p=q>>>4\nif(!(p<8))return A.d(B.l,p)\np=(B.l[p]&1<<(q&15))!==0}else p=!1\nif(!p)A.di(a,s,\"Illegal scheme character\")\nif(65<=q&&q<=90)r=!0}a=B.a.n(a,b,c)\nreturn A.u4(r?a.toLowerCase():a)},\nu4(a){if(a===\"http\")return\"http\"\nif(a===\"file\")return\"file\"\nif(a===\"https\")return\"https\"\nif(a===\"package\")return\"package\"\nreturn a},\npB(a,b,c){if(a==null)return\"\"\nreturn A.eS(a,b,c,B.a0,!1,!1)},\npz(a,b,c,d,e,f){var s=e===\"file\",r=s||f,q=A.eS(a,b,c,B.B,!0,!0)\nif(q.length===0){if(s)return\"/\"}else if(r&&!B.a.J(q,\"/\"))q=\"/\"+q\nreturn A.ua(q,e,f)},\nua(a,b,c){var s=b.length===0\nif(s&&!c&&!B.a.J(a,\"/\")&&!B.a.J(a,\"\\\\\"))return A.o5(a,!s||c)\nreturn A.bJ(a)},\npA(a,b,c,d){if(a!=null)return A.eS(a,b,c,B.k,!0,!1)\nreturn null},\npx(a,b,c){if(a==null)return null\nreturn A.eS(a,b,c,B.k,!0,!1)},\no4(a,b,c){var s,r,q,p,o,n=b+2\nif(n>=a.length)return\"%\"\ns=B.a.B(a,b+1)\nr=B.a.B(a,n)\nq=A.n9(s)\np=A.n9(r)\nif(q<0||p<0)return\"%\"\no=q*16+p\nif(o<127){n=B.c.M(o,4)\nif(!(n<8))return A.d(B.o,n)\nn=(B.o[n]&1<<(o&15))!==0}else n=!1\nif(n)return A.bx(c&&65<=o&&90>=o?(o|32)>>>0:o)\nif(s>=97||r>=97)return B.a.n(a,b,b+3).toUpperCase()\nreturn null},\no2(a){var s,r,q,p,o,n,m,l,k=\"0123456789ABCDEF\"\nif(a<128){s=new Uint8Array(3)\ns[0]=37\ns[1]=B.a.t(k,a>>>4)\ns[2]=B.a.t(k,a&15)}else{if(a>2047)if(a>65535){r=240\nq=4}else{r=224\nq=3}else{r=192\nq=2}p=3*q\ns=new Uint8Array(p)\nfor(o=0;--q,q>=0;r=128){n=B.c.fn(a,6*q)&63|r\nif(!(o<p))return A.d(s,o)\ns[o]=37\nm=o+1\nl=B.a.t(k,n>>>4)\nif(!(m<p))return A.d(s,m)\ns[m]=l\nl=o+2\nm=B.a.t(k,n&15)\nif(!(l<p))return A.d(s,l)\ns[l]=m\no+=3}}return A.p5(s,0,null)},\neS(a,b,c,d,e,f){var s=A.pD(a,b,c,d,e,f)\nreturn s==null?B.a.n(a,b,c):s},\npD(a,b,c,d,e,f){var s,r,q,p,o,n,m,l,k,j,i=null\nfor(s=!e,r=b,q=r,p=i;r<c;){o=B.a.B(a,r)\nif(o<127){n=o>>>4\nif(!(n<8))return A.d(d,n)\nn=(d[n]&1<<(o&15))!==0}else n=!1\nif(n)++r\nelse{if(o===37){m=A.o4(a,r,!1)\nif(m==null){r+=3\ncontinue}if(\"%\"===m){m=\"%25\"\nl=1}else l=3}else if(o===92&&f){m=\"/\"\nl=1}else{if(s)if(o<=93){n=o>>>4\nif(!(n<8))return A.d(B.j,n)\nn=(B.j[n]&1<<(o&15))!==0}else n=!1\nelse n=!1\nif(n){A.di(a,r,\"Invalid character\")\nl=i\nm=l}else{if((o&64512)===55296){n=r+1\nif(n<c){k=B.a.B(a,n)\nif((k&64512)===56320){o=(o&1023)<<10|k&1023|65536\nl=2}else l=1}else l=1}else l=1\nm=A.o2(o)}}if(p==null){p=new A.ah(\"\")\nn=p}else n=p\nj=n.a+=B.a.n(a,q,r)\nn.a=j+A.q(m)\nif(typeof l!==\"number\")return A.vf(l)\nr+=l\nq=r}}if(p==null)return i\nif(q<c)p.a+=B.a.n(a,q,c)\ns=p.a\nreturn s.charCodeAt(0)==0?s:s},\npC(a){if(B.a.J(a,\".\"))return!0\nreturn B.a.cB(a,\"/.\")!==-1},\nbJ(a){var s,r,q,p,o,n,m\nif(!A.pC(a))return a\ns=A.t([],t.s)\nfor(r=a.split(\"/\"),q=r.length,p=!1,o=0;o<q;++o){n=r[o]\nif(J.a7(n,\"..\")){m=s.length\nif(m!==0){if(0>=m)return A.d(s,-1)\ns.pop()\nif(s.length===0)B.b.m(s,\"\")}p=!0}else if(\".\"===n)p=!0\nelse{B.b.m(s,n)\np=!1}}if(p)B.b.m(s,\"\")\nreturn B.b.au(s,\"/\")},\no5(a,b){var s,r,q,p,o,n\nif(!A.pC(a))return!b?A.pv(a):a\ns=A.t([],t.s)\nfor(r=a.split(\"/\"),q=r.length,p=!1,o=0;o<q;++o){n=r[o]\nif(\"..\"===n)if(s.length!==0&&B.b.gai(s)!==\"..\"){if(0>=s.length)return A.d(s,-1)\ns.pop()\np=!0}else{B.b.m(s,\"..\")\np=!1}else if(\".\"===n)p=!0\nelse{B.b.m(s,n)\np=!1}}r=s.length\nif(r!==0)if(r===1){if(0>=r)return A.d(s,0)\nr=s[0].length===0}else r=!1\nelse r=!0\nif(r)return\"./\"\nif(p||B.b.gai(s)===\"..\")B.b.m(s,\"\")\nif(!b){if(0>=s.length)return A.d(s,0)\nB.b.k(s,0,A.pv(s[0]))}return B.b.au(s,\"/\")},\npv(a){var s,r,q,p=a.length\nif(p>=2&&A.pw(B.a.t(a,0)))for(s=1;s<p;++s){r=B.a.t(a,s)\nif(r===58)return B.a.n(a,0,s)+\"%3A\"+B.a.O(a,s+1)\nif(r<=127){q=r>>>4\nif(!(q<8))return A.d(B.l,q)\nq=(B.l[q]&1<<(r&15))===0}else q=!0\nif(q)break}return a},\nuc(a,b){if(a.h6(\"package\")&&a.c==null)return A.q0(b,0,b.length)\nreturn-1},\npF(a){var s,r,q,p=a.gcJ(),o=p.length\nif(o>0&&J.Y(p[0])===2&&J.os(p[0],1)===58){if(0>=o)return A.d(p,0)\nA.u6(J.os(p[0],0),!1)\nA.pt(p,!1,1)\ns=!0}else{A.pt(p,!1,0)\ns=!1}r=a.gbG()&&!s?\"\"+\"\\\\\":\"\"\nif(a.gb9()){q=a.gah(a)\nif(q.length!==0)r=r+\"\\\\\"+q+\"\\\\\"}r=A.lb(r,p,\"\\\\\")\no=s&&o===1?r+\"\\\\\":r\nreturn o.charCodeAt(0)==0?o:o},\nu8(a,b){var s,r,q\nfor(s=0,r=0;r<2;++r){q=B.a.t(a,b+r)\nif(48<=q&&q<=57)s=s*16+q-48\nelse{q|=32\nif(97<=q&&q<=102)s=s*16+q-87\nelse throw A.b(A.ao(\"Invalid URL encoding\",null))}}return s},\nud(a,b,c,d,e){var s,r,q,p,o=b\nwhile(!0){if(!(o<c)){s=!0\nbreak}r=B.a.t(a,o)\nif(r<=127)if(r!==37)q=!1\nelse q=!0\nelse q=!0\nif(q){s=!1\nbreak}++o}if(s){if(B.f!==d)q=!1\nelse q=!0\nif(q)return B.a.n(a,b,c)\nelse p=new A.fh(B.a.n(a,b,c))}else{p=A.t([],t.t)\nfor(q=a.length,o=b;o<c;++o){r=B.a.t(a,o)\nif(r>127)throw A.b(A.ao(\"Illegal percent encoding in URI\",null))\nif(r===37){if(o+3>q)throw A.b(A.ao(\"Truncated URI\",null))\nB.b.m(p,A.u8(a,o+1))\no+=2}else B.b.m(p,r)}}return d.b5(0,p)},\npw(a){var s=a|32\nreturn 97<=s&&s<=122},\np8(a,b,c){var s,r,q,p,o,n,m,l,k=\"Invalid MIME type\",j=A.t([b-1],t.t)\nfor(s=a.length,r=b,q=-1,p=null;r<s;++r){p=B.a.t(a,r)\nif(p===44||p===59)break\nif(p===47){if(q<0){q=r\ncontinue}throw A.b(A.ad(k,a,r))}}if(q<0&&r>b)throw A.b(A.ad(k,a,r))\nfor(;p!==44;){B.b.m(j,r);++r\nfor(o=-1;r<s;++r){p=B.a.t(a,r)\nif(p===61){if(o<0)o=r}else if(p===59||p===44)break}if(o>=0)B.b.m(j,o)\nelse{n=B.b.gai(j)\nif(p!==44||r!==n+7||!B.a.H(a,\"base64\",n+1))throw A.b(A.ad(\"Expecting '='\",a,r))\nbreak}}B.b.m(j,r)\nm=r+1\nif((j.length&1)===1)a=B.I.he(0,a,m,s)\nelse{l=A.pD(a,m,s,B.k,!0,!1)\nif(l!=null)a=B.a.az(a,m,s,l)}return new A.lg(a,j,c)},\nus(){var s,r,q,p,o,n,m=\"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~!$&'()*+,;=\",l=\".\",k=\":\",j=\"/\",i=\"\\\\\",h=\"?\",g=\"#\",f=\"/\\\\\",e=A.t(new Array(22),t.bs)\nfor(s=0;s<22;++s)e[s]=new Uint8Array(96)\nr=new A.mR(e)\nq=new A.mS()\np=new A.mT()\no=t.p.a(r.$2(0,225))\nq.$3(o,m,1)\nq.$3(o,l,14)\nq.$3(o,k,34)\nq.$3(o,j,3)\nq.$3(o,i,227)\nq.$3(o,h,172)\nq.$3(o,g,205)\nn=r.$2(14,225)\nq.$3(n,m,1)\nq.$3(n,l,15)\nq.$3(n,k,34)\nq.$3(n,f,234)\nq.$3(n,h,172)\nq.$3(n,g,205)\nn=r.$2(15,225)\nq.$3(n,m,1)\nq.$3(n,\"%\",225)\nq.$3(n,k,34)\nq.$3(n,j,9)\nq.$3(n,i,233)\nq.$3(n,h,172)\nq.$3(n,g,205)\nn=r.$2(1,225)\nq.$3(n,m,1)\nq.$3(n,k,34)\nq.$3(n,j,10)\nq.$3(n,i,234)\nq.$3(n,h,172)\nq.$3(n,g,205)\nn=r.$2(2,235)\nq.$3(n,m,139)\nq.$3(n,j,131)\nq.$3(n,i,131)\nq.$3(n,l,146)\nq.$3(n,h,172)\nq.$3(n,g,205)\nn=r.$2(3,235)\nq.$3(n,m,11)\nq.$3(n,j,68)\nq.$3(n,i,68)\nq.$3(n,l,18)\nq.$3(n,h,172)\nq.$3(n,g,205)\nn=r.$2(4,229)\nq.$3(n,m,5)\np.$3(n,\"AZ\",229)\nq.$3(n,k,102)\nq.$3(n,\"@\",68)\nq.$3(n,\"[\",232)\nq.$3(n,j,138)\nq.$3(n,i,138)\nq.$3(n,h,172)\nq.$3(n,g,205)\nn=r.$2(5,229)\nq.$3(n,m,5)\np.$3(n,\"AZ\",229)\nq.$3(n,k,102)\nq.$3(n,\"@\",68)\nq.$3(n,j,138)\nq.$3(n,i,138)\nq.$3(n,h,172)\nq.$3(n,g,205)\nn=r.$2(6,231)\np.$3(n,\"19\",7)\nq.$3(n,\"@\",68)\nq.$3(n,j,138)\nq.$3(n,i,138)\nq.$3(n,h,172)\nq.$3(n,g,205)\nn=r.$2(7,231)\np.$3(n,\"09\",7)\nq.$3(n,\"@\",68)\nq.$3(n,j,138)\nq.$3(n,i,138)\nq.$3(n,h,172)\nq.$3(n,g,205)\nq.$3(r.$2(8,8),\"]\",5)\nn=r.$2(9,235)\nq.$3(n,m,11)\nq.$3(n,l,16)\nq.$3(n,f,234)\nq.$3(n,h,172)\nq.$3(n,g,205)\nn=r.$2(16,235)\nq.$3(n,m,11)\nq.$3(n,l,17)\nq.$3(n,f,234)\nq.$3(n,h,172)\nq.$3(n,g,205)\nn=r.$2(17,235)\nq.$3(n,m,11)\nq.$3(n,j,9)\nq.$3(n,i,233)\nq.$3(n,h,172)\nq.$3(n,g,205)\nn=r.$2(10,235)\nq.$3(n,m,11)\nq.$3(n,l,18)\nq.$3(n,j,10)\nq.$3(n,i,234)\nq.$3(n,h,172)\nq.$3(n,g,205)\nn=r.$2(18,235)\nq.$3(n,m,11)\nq.$3(n,l,19)\nq.$3(n,f,234)\nq.$3(n,h,172)\nq.$3(n,g,205)\nn=r.$2(19,235)\nq.$3(n,m,11)\nq.$3(n,f,234)\nq.$3(n,h,172)\nq.$3(n,g,205)\nn=r.$2(11,235)\nq.$3(n,m,11)\nq.$3(n,j,10)\nq.$3(n,i,234)\nq.$3(n,h,172)\nq.$3(n,g,205)\nn=r.$2(12,236)\nq.$3(n,m,12)\nq.$3(n,h,12)\nq.$3(n,g,205)\nn=r.$2(13,237)\nq.$3(n,m,13)\nq.$3(n,h,13)\np.$3(r.$2(20,245),\"az\",21)\nn=r.$2(21,245)\np.$3(n,\"az\",21)\np.$3(n,\"09\",21)\nq.$3(n,\"+-.\",21)\nreturn e},\npZ(a,b,c,d,e){var s,r,q,p,o=$.qK()\nfor(s=b;s<c;++s){if(!(d>=0&&d<o.length))return A.d(o,d)\nr=o[d]\nq=B.a.t(a,s)^96\np=r[q>95?31:q]\nd=p&31\nB.b.k(e,p>>>5,s)}return d},\npo(a){if(a.b===7&&B.a.J(a.a,\"package\")&&a.c<=0)return A.q0(a.a,a.e,a.f)\nreturn-1},\nq0(a,b,c){var s,r,q\nfor(s=b,r=0;s<c;++s){q=B.a.B(a,s)\nif(q===47)return r!==0?s:-1\nif(q===37||q===58)return-1\nr|=q^46}return-1},\nuo(a,b,c){var s,r,q,p,o,n,m\nfor(s=a.length,r=0,q=0;q<s;++q){p=B.a.t(a,q)\no=B.a.t(b,c+q)\nn=p^o\nif(n!==0){if(n===32){m=o|n\nif(97<=m&&m<=122){r=32\ncontinue}}return-1}}return r},\ni3:function i3(a,b){this.a=a\nthis.$ti=b},\nk6:function k6(a,b){this.a=a\nthis.b=b},\na8:function a8(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nlE:function lE(){},\nlF:function lF(){},\nbU:function bU(a,b){this.a=a\nthis.b=b},\ncd:function cd(){},\nlL:function lL(){},\nQ:function Q(){},\nds:function ds(a){this.a=a},\nbn:function bn(){},\nh1:function h1(){},\nbh:function bh(a,b,c,d){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d},\ncX:function cX(a,b,c,d,e,f){var _=this\n_.e=a\n_.f=b\n_.a=c\n_.b=d\n_.c=e\n_.d=f},\nfE:function fE(a,b,c,d,e){var _=this\n_.f=a\n_.a=b\n_.b=c\n_.c=d\n_.d=e},\ndU:function dU(a,b,c,d,e){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d\n_.e=e},\nhz:function hz(a){this.a=a},\nhv:function hv(a){this.a=a},\nbB:function bB(a){this.a=a},\nfj:function fj(a){this.a=a},\nh5:function h5(){},\ne9:function e9(){},\nfp:function fp(a){this.a=a},\ni0:function i0(a){this.a=a},\nfC:function fC(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nfG:function fG(){},\ne:function e(){},\nL:function L(){},\na4:function a4(a,b,c){this.a=a\nthis.b=b\nthis.$ti=c},\nR:function R(){},\nr:function r(){},\niI:function iI(){},\nah:function ah(a){this.a=a},\nlh:function lh(a){this.a=a},\nlj:function lj(a){this.a=a},\nlk:function lk(a,b){this.a=a\nthis.b=b},\neR:function eR(a,b,c,d,e,f,g){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d\n_.e=e\n_.f=f\n_.r=g\n_.y=_.x=_.w=$},\nlg:function lg(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nmR:function mR(a){this.a=a},\nmS:function mS(){},\nmT:function mT(){},\nb5:function b5(a,b,c,d,e,f,g,h){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d\n_.e=e\n_.f=f\n_.r=g\n_.w=h\n_.x=null},\nhU:function hU(a,b,c,d,e,f,g){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d\n_.e=e\n_.f=f\n_.r=g\n_.y=_.x=_.w=$},\nr4(a){var s=new self.Blob(a)\nreturn s},\nbf(a,b,c,d,e){var s=c==null?null:A.q4(new A.lN(c),t.A)\ns=new A.eq(a,b,s,!1,e.h(\"eq<0>\"))\ns.dG()\nreturn s},\nq4(a,b){var s=$.D\nif(s===B.d)return a\nreturn s.dM(a,b)},\no:function o(){},\nf4:function f4(){},\nf5:function f5(){},\nf6:function f6(){},\nbR:function bR(){},\nbi:function bi(){},\nfm:function fm(){},\nP:function P(){},\ncE:function cE(){},\njx:function jx(){},\naq:function aq(){},\nbb:function bb(){},\nfn:function fn(){},\nfo:function fo(){},\nfq:function fq(){},\nft:function ft(){},\ndz:function dz(){},\ndA:function dA(){},\nfu:function fu(){},\nfv:function fv(){},\nn:function n(){},\nl:function l(){},\nf:function f(){},\naz:function az(){},\ncI:function cI(){},\nfz:function fz(){},\nfB:function fB(){},\naA:function aA(){},\nfD:function fD(){},\nci:function ci(){},\ncL:function cL(){},\nfO:function fO(){},\nfP:function fP(){},\ncV:function cV(){},\nck:function ck(){},\nfQ:function fQ(){},\nk2:function k2(a){this.a=a},\nk3:function k3(a){this.a=a},\nfR:function fR(){},\nk4:function k4(a){this.a=a},\nk5:function k5(a){this.a=a},\naB:function aB(){},\nfS:function fS(){},\nG:function G(){},\ndV:function dV(){},\naC:function aC(){},\nh7:function h7(){},\nhb:function hb(){},\nkl:function kl(a){this.a=a},\nkm:function km(a){this.a=a},\nhd:function hd(){},\ncY:function cY(){},\ncZ:function cZ(){},\naD:function aD(){},\nhf:function hf(){},\naE:function aE(){},\nhg:function hg(){},\naF:function aF(){},\nhl:function hl(){},\nl5:function l5(a){this.a=a},\nl6:function l6(a){this.a=a},\nal:function al(){},\naH:function aH(){},\nam:function am(){},\nhp:function hp(){},\nhq:function hq(){},\nhr:function hr(){},\naI:function aI(){},\nhs:function hs(){},\nht:function ht(){},\nhB:function hB(){},\nhD:function hD(){},\nc1:function c1(){},\nhS:function hS(){},\neo:function eo(){},\ni5:function i5(){},\nez:function ez(){},\niA:function iA(){},\niJ:function iJ(){},\nnu:function nu(a,b){this.a=a\nthis.$ti=b},\nlM:function lM(a,b,c,d){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.$ti=d},\neq:function eq(a,b,c,d,e){var _=this\n_.a=0\n_.b=a\n_.c=b\n_.d=c\n_.e=d\n_.$ti=e},\nlN:function lN(a){this.a=a},\nlO:function lO(a){this.a=a},\nu:function u(){},\ndD:function dD(a,b,c){var _=this\n_.a=a\n_.b=b\n_.c=-1\n_.d=null\n_.$ti=c},\nhT:function hT(){},\nhW:function hW(){},\nhX:function hX(){},\nhY:function hY(){},\nhZ:function hZ(){},\ni1:function i1(){},\ni2:function i2(){},\ni6:function i6(){},\ni7:function i7(){},\nid:function id(){},\nie:function ie(){},\nig:function ig(){},\nih:function ih(){},\nii:function ii(){},\nij:function ij(){},\nio:function io(){},\nip:function ip(){},\nix:function ix(){},\neF:function eF(){},\neG:function eG(){},\niy:function iy(){},\niz:function iz(){},\niC:function iC(){},\niL:function iL(){},\niM:function iM(){},\neL:function eL(){},\neM:function eM(){},\niN:function iN(){},\niO:function iO(){},\niU:function iU(){},\niV:function iV(){},\niW:function iW(){},\niX:function iX(){},\niY:function iY(){},\niZ:function iZ(){},\nj_:function j_(){},\nj0:function j0(){},\nj1:function j1(){},\nj2:function j2(){},\npM(a){var s,r,q\nif(a==null)return a\nif(typeof a==\"string\"||typeof a==\"number\"||A.cz(a))return a\nif(A.qh(a))return A.b7(a)\ns=Array.isArray(a)\ns.toString\nif(s){r=[]\nq=0\nwhile(!0){s=a.length\ns.toString\nif(!(q<s))break\nr.push(A.pM(a[q]));++q}return r}return a},\nb7(a){var s,r,q,p,o,n\nif(a==null)return null\ns=A.X(t.N,t.z)\nr=Object.getOwnPropertyNames(a)\nfor(q=r.length,p=0;p<r.length;r.length===q||(0,A.aM)(r),++p){o=r[p]\nn=o\nn.toString\ns.k(0,n,A.pM(a[o]))}return s},\npL(a){var s\nif(a==null)return a\nif(typeof a==\"string\"||typeof a==\"number\"||A.cz(a))return a\nif(t.f.b(a))return A.oe(a)\nif(t.j.b(a)){s=[]\nJ.bo(a,new A.mQ(s))\na=s}return a},\noe(a){var s={}\nJ.bo(a,new A.n5(s))\nreturn s},\nqh(a){var s=Object.getPrototypeOf(a),r=s===Object.prototype\nr.toString\nif(!r){r=s===null\nr.toString}else r=!0\nreturn r},\nmA:function mA(){},\nmB:function mB(a,b){this.a=a\nthis.b=b},\nmC:function mC(a,b){this.a=a\nthis.b=b},\nlw:function lw(){},\nlx:function lx(a,b){this.a=a\nthis.b=b},\nmQ:function mQ(a){this.a=a},\nn5:function n5(a){this.a=a},\ncx:function cx(a,b){this.a=a\nthis.b=b},\nc2:function c2(a,b){this.a=a\nthis.b=b\nthis.c=!1},\nj3(a,b){var s,r=new A.E($.D,b.h(\"E<0>\")),q=new A.aa(r,b.h(\"aa<0>\")),p=t.a,o=p.a(new A.mP(a,q,b))\nt.Z.a(null)\ns=t.A\nA.bf(a,\"success\",o,!1,s)\nA.bf(a,\"error\",p.a(q.gfI()),!1,s)\nreturn r},\nrE(a,b,c){var s,r=null,q=c.h(\"df<0>\"),p=new A.df(r,r,r,r,q),o=t.a,n=o.a(p.gfz())\nt.Z.a(null)\ns=t.A\nA.bf(a,\"error\",n,!1,s)\nA.bf(a,\"success\",o.a(new A.k7(a,p,b,c)),!1,s)\nreturn new A.d5(p,q.h(\"d5<1>\"))},\nbT:function bT(){},\nbr:function br(){},\nbj:function bj(){},\ncj:function cj(){},\nmP:function mP(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\ndG:function dG(){},\ndX:function dX(){},\nk7:function k7(a,b,c,d){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d},\nbz:function bz(){},\nec:function ec(){},\nbD:function bD(){},\nn4(a,b,c,d){return d.a(a[b].apply(a,c))},\nj8(a,b){var s=new A.E($.D,b.h(\"E<0>\")),r=new A.cr(s,b.h(\"cr<0>\"))\na.then(A.c8(new A.nj(r,b),1),A.c8(new A.nk(r),1))\nreturn s},\nnj:function nj(a,b){this.a=a\nthis.b=b},\nnk:function nk(a){this.a=a},\nh0:function h0(a){this.a=a},\ni8:function i8(a){this.a=a},\naN:function aN(){},\nfL:function fL(){},\naR:function aR(){},\nh3:function h3(){},\nh8:function h8(){},\nhn:function hn(){},\naW:function aW(){},\nhu:function hu(){},\ni9:function i9(){},\nia:function ia(){},\nik:function ik(){},\nil:function il(){},\niG:function iG(){},\niH:function iH(){},\niP:function iP(){},\niQ:function iQ(){},\nf9:function f9(){},\nfa:function fa(){},\njp:function jp(a){this.a=a},\njq:function jq(a){this.a=a},\nfb:function fb(){},\nbQ:function bQ(){},\nh4:function h4(){},\nhQ:function hQ(){},\ntq(){throw A.b(A.x(\"Cannot modify an unmodifiable Map\"))},\nh_:function h_(){},\nhy:function hy(){},\nq3(a,b){var s,r,q,p,o,n,m,l\nfor(s=b.length,r=1;r<s;++r){if(b[r]==null||b[r-1]!=null)continue\nfor(;s>=1;s=q){q=s-1\nif(b[q]!=null)break}p=new A.ah(\"\")\no=\"\"+(a+\"(\")\np.a=o\nn=A.av(b)\nm=n.h(\"cn<1>\")\nl=new A.cn(b,0,s,m)\nl.ey(b,0,s,n.c)\nm=o+new A.af(l,m.h(\"i(a3.E)\").a(new A.n1()),m.h(\"af<a3.E,i>\")).au(0,\", \")\np.a=m\np.a=m+(\"): part \"+(r-1)+\" was null, but part \"+r+\" was not.\")\nthrow A.b(A.ao(p.l(0),null))}},\nfk:function fk(a,b){this.a=a\nthis.b=b},\njw:function jw(){},\nn1:function n1(){},\nbV:function bV(){},\nrF(a,b){var s,r,q,p,o,n=b.eg(a)\nb.ar(a)\nif(n!=null)a=B.a.O(a,n.length)\ns=t.s\nr=A.t([],s)\nq=A.t([],s)\ns=a.length\nif(s!==0&&b.bI(B.a.t(a,0))){if(0>=s)return A.d(a,0)\nB.b.m(q,a[0])\np=1}else{B.b.m(q,\"\")\np=0}for(o=p;o<s;++o)if(b.bI(B.a.t(a,o))){B.b.m(r,B.a.n(a,p,o))\nB.b.m(q,a[o])\np=o+1}if(p<s){B.b.m(r,B.a.O(a,p))\nB.b.m(q,\"\")}return new A.k8(b,n,r,q)},\nk8:function k8(a,b,c,d){var _=this\n_.a=a\n_.b=b\n_.d=c\n_.e=d},\ntm(){var s,r,q,p,o,n,m,l,k=null\nif(A.nQ().gal()!==\"file\")return $.ja()\ns=A.nQ()\nif(!B.a.dR(s.gX(s),\"/\"))return $.ja()\nr=A.pB(k,0,0)\nq=A.py(k,0,0,!1)\np=A.pA(k,0,0,k)\no=A.px(k,0,0)\nn=A.o3(k,\"\")\nif(q==null)s=r.length!==0||n!=null||!1\nelse s=!1\nif(s)q=\"\"\ns=q==null\nm=!s\nl=A.pz(\"a/b\",0,3,k,\"\",m)\nif(s&&!B.a.J(l,\"/\"))l=A.o5(l,m)\nelse l=A.bJ(l)\nif(A.mG(\"\",r,s&&B.a.J(l,\"//\")?\"\":q,n,l,p,o).cS()===\"a\\\\b\")return $.qr()\nreturn $.qq()},\nlc:function lc(){},\nh9:function h9(a,b,c){this.d=a\nthis.e=b\nthis.f=c},\nhC:function hC(a,b,c,d){var _=this\n_.d=a\n_.e=b\n_.f=c\n_.r=d},\nhK:function hK(a,b,c,d){var _=this\n_.d=a\n_.e=b\n_.f=c\n_.r=d},\nug(a){var s\nif(a==null)return null\ns=J.bp(a)\nif(s.length>50)return B.a.n(s,0,50)+\"...\"\nreturn s},\nuZ(a){if(t.p.b(a))return\"Blob(\"+a.length+\")\"\nreturn A.ug(a)},\nq6(a){var s=a.$ti\nreturn\"[\"+new A.af(a,s.h(\"i?(h.E)\").a(new A.n3()),s.h(\"af<h.E,i?>\")).au(0,\", \")+\"]\"},\nn3:function n3(){},\ndy:function dy(){},\ne3:function e3(){},\nko:function ko(a){this.a=a},\nkp:function kp(a){this.a=a},\njB:function jB(){},\nri(a){var s=J.T(a),r=s.i(a,\"method\"),q=s.i(a,\"arguments\")\nif(r!=null)return new A.fx(A.S(r),q)\nreturn null},\nfx:function fx(a,b){this.a=a\nthis.b=b},\ncH:function cH(a,b){this.a=a\nthis.b=b},\nhh(a,b,c,d){var s=new A.bl(a,b,b,c)\ns.b=d\nreturn s},\nbl:function bl(a,b,c,d){var _=this\n_.r=_.f=_.e=null\n_.w=a\n_.x=b\n_.b=null\n_.c=c\n_.a=d},\nmX(a,b,c,d){var s,r,q,p\nif(a instanceof A.bl){s=a.e\nif(s==null)s=a.e=b\nr=a.f\nif(r==null)r=a.f=c\nq=a.r\nif(q==null)q=a.r=d\np=s==null\nif(!p||r!=null||q!=null)if(a.x==null){r=A.X(t.N,t.X)\nif(!p)r.k(0,\"database\",s.ec())\ns=a.f\nif(s!=null)r.k(0,\"sql\",s)\ns=a.r\nif(s!=null)r.k(0,\"arguments\",s)\na.sfP(0,r)}return a}else if(a instanceof A.d_){s=a.l(0)\nreturn A.mX(A.hh(\"sqlite_error\",null,s,a.c),b,c,d)}else return A.mX(A.hh(\"error\",null,J.bp(a),null),b,c,d)},\nkX(a){return A.tf(a)},\ntf(a){var s=0,r=A.B(t.z),q,p=2,o,n,m,l,k,j,i\nvar $async$kX=A.C(function(b,c){if(b===1){o=c\ns=p}while(true)switch(s){case 0:p=4\ns=7\nreturn A.p(A.at(a),$async$kX)\ncase 7:n=c\nq=n\ns=1\nbreak\np=2\ns=6\nbreak\ncase 4:p=3\ni=o\nm=A.M(i)\nl=A.a_(i)\nk=A.mX(m,A.p1(a),A.cm(a,\"sql\",t.N),A.hi(a))\nthrow A.b(k)\ns=6\nbreak\ncase 3:s=2\nbreak\ncase 6:case 1:return A.z(q,r)\ncase 2:return A.y(o,r)}})\nreturn A.A($async$kX,r)},\ne5(a,b){var s=A.kH(a)\nreturn s.b8(A.dj(J.ab(t.f.a(a.b),\"transactionId\")),new A.kG(b,s))},\ne4(a,b){return $.qJ().a7(new A.kF(b),t.z)},\nat(a){var s=0,r=A.B(t.z),q,p\nvar $async$at=A.C(function(b,c){if(b===1)return A.y(c,r)\nwhile(true)switch(s){case 0:p=a.a\ncase 3:switch(p){case\"openDatabase\":s=5\nbreak\ncase\"closeDatabase\":s=6\nbreak\ncase\"query\":s=7\nbreak\ncase\"queryCursorNext\":s=8\nbreak\ncase\"execute\":s=9\nbreak\ncase\"insert\":s=10\nbreak\ncase\"update\":s=11\nbreak\ncase\"batch\":s=12\nbreak\ncase\"getDatabasesPath\":s=13\nbreak\ncase\"deleteDatabase\":s=14\nbreak\ncase\"databaseExists\":s=15\nbreak\ncase\"options\":s=16\nbreak\ncase\"debugMode\":s=17\nbreak\ndefault:s=18\nbreak}break\ncase 5:s=19\nreturn A.p(A.e4(a,A.t9(a)),$async$at)\ncase 19:q=c\ns=1\nbreak\ncase 6:s=20\nreturn A.p(A.e4(a,A.t3(a)),$async$at)\ncase 20:q=c\ns=1\nbreak\ncase 7:s=21\nreturn A.p(A.e5(a,A.tb(a)),$async$at)\ncase 21:q=c\ns=1\nbreak\ncase 8:s=22\nreturn A.p(A.e5(a,A.tc(a)),$async$at)\ncase 22:q=c\ns=1\nbreak\ncase 9:s=23\nreturn A.p(A.e5(a,A.t6(a)),$async$at)\ncase 23:q=c\ns=1\nbreak\ncase 10:s=24\nreturn A.p(A.e5(a,A.t8(a)),$async$at)\ncase 24:q=c\ns=1\nbreak\ncase 11:s=25\nreturn A.p(A.e5(a,A.td(a)),$async$at)\ncase 25:q=c\ns=1\nbreak\ncase 12:s=26\nreturn A.p(A.e5(a,A.t2(a)),$async$at)\ncase 26:q=c\ns=1\nbreak\ncase 13:s=27\nreturn A.p(A.e4(a,A.t7(a)),$async$at)\ncase 27:q=c\ns=1\nbreak\ncase 14:s=28\nreturn A.p(A.e4(a,A.t5(a)),$async$at)\ncase 28:q=c\ns=1\nbreak\ncase 15:s=29\nreturn A.p(A.e4(a,A.t4(a)),$async$at)\ncase 29:q=c\ns=1\nbreak\ncase 16:s=30\nreturn A.p(A.e4(a,A.ta(a)),$async$at)\ncase 30:q=c\ns=1\nbreak\ncase 17:s=31\nreturn A.p(A.nI(a),$async$at)\ncase 31:q=c\ns=1\nbreak\ncase 18:throw A.b(A.ao(\"Invalid method \"+p+\" \"+a.l(0),null))\ncase 4:case 1:return A.z(q,r)}})\nreturn A.A($async$at,r)},\nt9(a){return new A.kQ(a)},\nkY(a){return A.tg(a)},\ntg(a){var s=0,r=A.B(t.f),q,p=2,o,n,m,l,k,j,i,h,g,f,e,d,c\nvar $async$kY=A.C(function(b,a0){if(b===1){o=a0\ns=p}while(true)switch(s){case 0:i=t.f.a(a.b)\nh=J.T(i)\ng=A.S(h.i(i,\"path\"))\nf=new A.kZ()\ne=A.eW(h.i(i,\"singleInstance\"))\nd=e===!0\nh=A.eW(h.i(i,\"readOnly\"))\nif(d){l=$.j6.i(0,g)\nif(l!=null){i=$.nf\nif(typeof i!==\"number\"){q=i.hq()\ns=1\nbreak}if(i>=2)l.av(\"Reopening existing single database \"+l.l(0))\nq=f.$1(l.e)\ns=1\nbreak}}n=null\np=4\ne=$.b6\ns=7\nreturn A.p((e==null?$.b6=A.f1():e).bM(i),$async$kY)\ncase 7:n=a0\np=2\ns=6\nbreak\ncase 4:p=3\nc=o\ni=A.M(c)\nif(i instanceof A.d_){m=i\ni=m\nh=i.l(0)\nthrow A.b(A.hh(\"sqlite_error\",null,\"open_failed: \"+h,i.c))}else throw c\ns=6\nbreak\ncase 3:s=2\nbreak\ncase 6:j=$.pT=$.pT+1\ni=n\ne=$.nf\nl=new A.aU(A.t([],t.it),A.nC(),j,d,g,h===!0,i,e,A.X(t.S,t.lz),A.nC())\n$.qc.k(0,j,l)\nl.av(\"Opening database \"+l.l(0))\nif(d)$.j6.k(0,g,l)\nq=f.$1(j)\ns=1\nbreak\ncase 1:return A.z(q,r)\ncase 2:return A.y(o,r)}})\nreturn A.A($async$kY,r)},\nt3(a){return new A.kK(a)},\nnG(a){var s=0,r=A.B(t.z),q\nvar $async$nG=A.C(function(b,c){if(b===1)return A.y(c,r)\nwhile(true)switch(s){case 0:q=A.kH(a)\nif(q.f){$.j6.G(0,q.r)\nif($.q2==null)$.q2=new A.jB()}q.af(0)\nreturn A.z(null,r)}})\nreturn A.A($async$nG,r)},\nkH(a){var s=A.p1(a)\nif(s==null)throw A.b(A.K(\"Database \"+A.q(A.p2(a))+\" not found\"))\nreturn s},\np1(a){var s=A.p2(a)\nif(s!=null)return $.qc.i(0,s)\nreturn null},\np2(a){var s=a.b\nif(t.f.b(s))return A.dj(J.ab(s,\"id\"))\nreturn null},\ncm(a,b,c){var s=a.b\nif(t.f.b(s))return c.h(\"0?\").a(J.ab(s,b))\nreturn null},\nth(a){var s,r=\"transactionId\",q=a.b\nif(t.f.b(q)){s=J.a2(q)\nreturn s.F(q,r)&&s.i(q,r)==null}return!1},\np3(a){var s=null,r=A.cm(a,\"path\",t.N)\nif(r!=null&&r!==\":memory:\"&&$.or().a.ak(r)<=0){if($.b6==null)$.b6=A.f1()\nr=$.or().dZ(0,\"/\",r,s,s,s,s,s,s,s,s,s,s,s,s,s,s)}return r},\nhi(a){var s,r,q,p,o=A.cm(a,\"arguments\",t.j)\nif(o!=null)for(s=J.an(o),r=t.k,q=t.p;s.p();){p=s.gu(s)\nif(p!=null)if(typeof p!=\"number\")if(typeof p!=\"string\")if(!q.b(p))if(!r.b(p))throw A.b(A.ao(\"Invalid sql argument type '\"+J.jf(p).l(0)+\"': \"+A.q(p),null))}return o==null?null:J.jd(o,t.X)},\nt1(a){var s=A.t([],t.bw),r=t.f\nr=J.jd(t.j.a(J.ab(r.a(a.b),\"operations\")),r)\nr.D(r,new A.kI(s))\nreturn s},\ntb(a){return new A.kT(a)},\nnL(a,b){var s=0,r=A.B(t.z),q,p,o\nvar $async$nL=A.C(function(c,d){if(c===1)return A.y(d,r)\nwhile(true)switch(s){case 0:o=A.cm(a,\"sql\",t.N)\no.toString\np=A.hi(a)\nq=b.h0(A.dj(J.ab(t.f.a(a.b),\"cursorPageSize\")),o,p)\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$nL,r)},\ntc(a){return new A.kS(a)},\nnM(a,b){var s=0,r=A.B(t.z),q,p,o,n\nvar $async$nM=A.C(function(c,d){if(c===1)return A.y(d,r)\nwhile(true)switch(s){case 0:b=A.kH(a)\np=t.f.a(a.b)\no=J.T(p)\nn=A.j(o.i(p,\"cursorId\"))\nq=b.h1(A.eW(o.i(p,\"cancel\")),n)\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$nM,r)},\nkE(a,b){var s=0,r=A.B(t.X),q,p\nvar $async$kE=A.C(function(c,d){if(c===1)return A.y(d,r)\nwhile(true)switch(s){case 0:b=A.kH(a)\np=A.cm(a,\"sql\",t.N)\np.toString\ns=3\nreturn A.p(b.fZ(p,A.hi(a)),$async$kE)\ncase 3:q=null\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$kE,r)},\nt6(a){return new A.kN(a)},\nkW(a,b){return A.te(a,b)},\nte(a,b){var s=0,r=A.B(t.X),q,p=2,o,n,m,l,k\nvar $async$kW=A.C(function(c,d){if(c===1){o=d\ns=p}while(true)switch(s){case 0:m=A.cm(a,\"inTransaction\",t.y)\nl=m===!0&&A.th(a)\nif(A.aK(l))b.b=++b.a\np=4\ns=7\nreturn A.p(A.kE(a,b),$async$kW)\ncase 7:p=2\ns=6\nbreak\ncase 4:p=3\nk=o\nif(A.aK(l))b.b=null\nthrow k\ns=6\nbreak\ncase 3:s=2\nbreak\ncase 6:if(A.aK(l)){q=A.aO([\"transactionId\",b.b],t.N,t.X)\ns=1\nbreak}else if(m===!1)b.b=null\nq=null\ns=1\nbreak\ncase 1:return A.z(q,r)\ncase 2:return A.y(o,r)}})\nreturn A.A($async$kW,r)},\nta(a){return new A.kR(a)},\nl_(a){var s=0,r=A.B(t.z),q,p,o\nvar $async$l_=A.C(function(b,c){if(b===1)return A.y(c,r)\nwhile(true)switch(s){case 0:o=a.b\ns=t.f.b(o)?3:4\nbreak\ncase 3:p=J.a2(o)\nif(p.F(o,\"logLevel\")){p=A.dj(p.i(o,\"logLevel\"))\n$.nf=p==null?0:p}p=$.b6\ns=5\nreturn A.p((p==null?$.b6=A.f1():p).cz(o),$async$l_)\ncase 5:case 4:q=null\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$l_,r)},\nnI(a){var s=0,r=A.B(t.z),q\nvar $async$nI=A.C(function(b,c){if(b===1)return A.y(c,r)\nwhile(true)switch(s){case 0:if(J.a7(a.b,!0))$.nf=2\nq=null\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$nI,r)},\nt8(a){return new A.kP(a)},\nnK(a,b){var s=0,r=A.B(t.I),q,p\nvar $async$nK=A.C(function(c,d){if(c===1)return A.y(d,r)\nwhile(true)switch(s){case 0:p=A.cm(a,\"sql\",t.N)\np.toString\nq=b.h_(p,A.hi(a))\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$nK,r)},\ntd(a){return new A.kU(a)},\nnN(a,b){var s=0,r=A.B(t.S),q,p\nvar $async$nN=A.C(function(c,d){if(c===1)return A.y(d,r)\nwhile(true)switch(s){case 0:p=A.cm(a,\"sql\",t.N)\np.toString\nq=b.h3(p,A.hi(a))\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$nN,r)},\nt2(a){return new A.kJ(a)},\nt7(a){return new A.kO(a)},\nnJ(a){var s=0,r=A.B(t.z),q\nvar $async$nJ=A.C(function(b,c){if(b===1)return A.y(c,r)\nwhile(true)switch(s){case 0:if($.b6==null)$.b6=A.f1()\nq=\"/\"\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$nJ,r)},\nt5(a){return new A.kM(a)},\nkV(a){var s=0,r=A.B(t.H),q=1,p,o,n,m,l,k,j\nvar $async$kV=A.C(function(b,c){if(b===1){p=c\ns=q}while(true)switch(s){case 0:l=A.p3(a)\nk=$.j6.i(0,l)\nif(k!=null){k.af(0)\n$.j6.G(0,l)}q=3\no=$.b6\nif(o==null)o=$.b6=A.f1()\nn=l\nn.toString\ns=6\nreturn A.p(o.b6(n),$async$kV)\ncase 6:q=1\ns=5\nbreak\ncase 3:q=2\nj=p\ns=5\nbreak\ncase 2:s=1\nbreak\ncase 5:return A.z(null,r)\ncase 1:return A.y(p,r)}})\nreturn A.A($async$kV,r)},\nt4(a){return new A.kL(a)},\nnH(a){var s=0,r=A.B(t.y),q,p,o\nvar $async$nH=A.C(function(b,c){if(b===1)return A.y(c,r)\nwhile(true)switch(s){case 0:p=A.p3(a)\no=$.b6\nif(o==null)o=$.b6=A.f1()\np.toString\nq=o.bF(p)\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$nH,r)},\nkC:function kC(){},\ne6:function e6(){this.c=this.b=this.a=null},\niB:function iB(a,b,c,d){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d\n_.e=!1},\niq:function iq(a,b){this.a=a\nthis.b=b},\naU:function aU(a,b,c,d,e,f,g,h,i,j){var _=this\n_.a=0\n_.b=null\n_.c=a\n_.d=b\n_.e=c\n_.f=d\n_.r=e\n_.w=f\n_.x=g\n_.y=h\n_.z=i\n_.Q=0\n_.as=j},\nkx:function kx(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nkv:function kv(a){this.a=a},\nkq:function kq(a){this.a=a},\nky:function ky(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nkB:function kB(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nkA:function kA(a,b,c,d){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d},\nkz:function kz(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nkw:function kw(a,b,c,d){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d},\nku:function ku(){},\nkt:function kt(a,b){this.a=a\nthis.b=b},\nkr:function kr(a,b,c,d,e,f){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d\n_.e=e\n_.f=f},\nks:function ks(a,b){this.a=a\nthis.b=b},\nkG:function kG(a,b){this.a=a\nthis.b=b},\nkF:function kF(a){this.a=a},\nkQ:function kQ(a){this.a=a},\nkZ:function kZ(){},\nkK:function kK(a){this.a=a},\nkI:function kI(a){this.a=a},\nkT:function kT(a){this.a=a},\nkS:function kS(a){this.a=a},\nkN:function kN(a){this.a=a},\nkR:function kR(a){this.a=a},\nkP:function kP(a){this.a=a},\nkU:function kU(a){this.a=a},\nkJ:function kJ(a){this.a=a},\nkO:function kO(a){this.a=a},\nkM:function kM(a){this.a=a},\nkL:function kL(a){this.a=a},\nkD:function kD(a){this.a=a\nthis.c=this.b=null},\nj4(a){return A.uA(t.A.a(a))},\nuA(a8){var s=0,r=A.B(t.H),q=1,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a,a0,a1,a2,a3,a4,a5,a6,a7\nvar $async$j4=A.C(function(a9,b0){if(a9===1){p=b0\ns=q}while(true)switch(s){case 0:t.hy.a(a8)\no=new A.c2([],[]).aF(a8.data,!0)\na1=a8.ports\na1.toString\nn=J.bP(a1)\nq=3\ns=typeof o==\"string\"?6:8\nbreak\ncase 6:J.cB(n,o)\ns=7\nbreak\ncase 8:s=t.j.b(o)?9:11\nbreak\ncase 9:m=J.ab(o,0)\nif(J.a7(m,\"varSet\")){l=t.f.a(J.ab(o,1))\nk=A.S(J.ab(l,\"key\"))\nj=J.ab(l,\"value\")\nA.b9($.eZ+\" \"+A.q(m)+\" \"+A.q(k)+\": \"+A.q(j))\n$.qn.k(0,k,j)\nJ.cB(n,null)}else if(J.a7(m,\"varGet\")){i=t.f.a(J.ab(o,1))\nh=A.S(J.ab(i,\"key\"))\ng=$.qn.i(0,h)\nA.b9($.eZ+\" \"+A.q(m)+\" \"+A.q(h)+\": \"+A.q(g))\na1=t.N\nJ.cB(n,A.aO([\"result\",A.aO([\"key\",h,\"value\",g],a1,t.X)],a1,t.lb))}else{A.b9($.eZ+\" \"+A.q(m)+\" unknown\")\nJ.cB(n,null)}s=10\nbreak\ncase 11:s=t.f.b(o)?12:14\nbreak\ncase 12:f=A.ri(o)\ns=f!=null?15:17\nbreak\ncase 15:f=new A.fx(f.a,A.o7(f.b))\ns=$.q1==null?18:19\nbreak\ncase 18:s=20\nreturn A.p(A.dq(new A.l0(),!0),$async$j4)\ncase 20:a1=b0\n$.q1=a1\na1.toString\n$.b6=new A.kD(a1)\ncase 19:e=new A.mY(n)\nq=22\ns=25\nreturn A.p(A.kX(f),$async$j4)\ncase 25:d=b0\nd=A.o8(d)\ne.$1(new A.cH(d,null))\nq=3\ns=24\nbreak\ncase 22:q=21\na6=p\nc=A.M(a6)\nb=A.a_(a6)\na1=c\na3=b\na4=new A.cH($,$)\na5=A.X(t.N,t.X)\nif(a1 instanceof A.bl){a5.k(0,\"code\",a1.w)\na5.k(0,\"details\",a1.x)\na5.k(0,\"message\",a1.a)\na5.k(0,\"resultCode\",a1.bS())}else a5.k(0,\"message\",J.bp(a1))\na1=$.pS\nif(!(a1==null?$.pS=!0:a1)&&a3!=null)a5.k(0,\"stackTrace\",a3.l(0))\na4.b=a5\na4.a=null\ne.$1(a4)\ns=24\nbreak\ncase 21:s=3\nbreak\ncase 24:s=16\nbreak\ncase 17:A.b9($.eZ+\" \"+A.q(o)+\" unknown\")\nJ.cB(n,null)\ncase 16:s=13\nbreak\ncase 14:A.b9($.eZ+\" \"+A.q(o)+\" map unknown\")\nJ.cB(n,null)\ncase 13:case 10:case 7:q=1\ns=5\nbreak\ncase 3:q=2\na7=p\na=A.M(a7)\na0=A.a_(a7)\nA.b9($.eZ+\" error caught \"+A.q(a)+\" \"+A.q(a0))\nJ.cB(n,null)\ns=5\nbreak\ncase 2:s=1\nbreak\ncase 5:return A.z(null,r)\ncase 1:return A.y(p,r)}})\nreturn A.A($async$j4,r)},\nvr(a){var s,r,q\ntry{s=self\ns.toString\nt.aD.a(s)\nr=t.a.a(new A.ng())\nt.Z.a(null)\nA.bf(s,\"connect\",r,!1,t.A)}catch(q){try{s=self\ns.toString\nJ.qP(s,\"message\",A.ok())}catch(q){}}},\nmY:function mY(a){this.a=a},\nng:function ng(){},\npQ(a){if(a==null)return!0\nelse if(typeof a==\"number\"||typeof a==\"string\"||A.cz(a))return!0\nreturn!1},\npU(a){var s,r=J.T(a)\nif(r.gj(a)===1){s=J.bP(r.gK(a))\nif(typeof s==\"string\")return B.a.J(s,\"@\")\nthrow A.b(A.bq(s,null,null))}return!1},\no8(a){var s,r,q,p,o,n,m,l,k={}\nif(A.pQ(a))return a\na.toString\nfor(s=$.oq(),r=0;r<1;++r){q=s[r]\np=A.v(q).h(\"dg.T\")\nif(p.b(a))return A.aO([\"@\"+q.a,t.k.a(p.a(a)).l(0)],t.N,t.X)}if(t.f.b(a)){if(A.pU(a))return A.aO([\"@\",a],t.N,t.X)\nk.a=null\nJ.bo(a,new A.mW(k,a))\ns=k.a\nif(s==null)s=a\nreturn s}else if(t.j.b(a)){for(s=J.T(a),p=t.z,o=null,n=0;n<s.gj(a);++n){m=s.i(a,n)\nl=A.o8(m)\nif(l==null?m!=null:l!==m){if(o==null)o=A.jY(a,!0,p)\nB.b.k(o,n,l)}}if(o==null)s=a\nelse s=o\nreturn s}else throw A.b(A.x(\"Unsupported value type \"+J.jf(a).l(0)+\" for \"+A.q(a)))},\no7(a){var s,r,q,p,o,n,m,l,k,j,i,h={}\nif(A.pQ(a))return a\na.toString\nif(t.f.b(a)){if(A.pU(a)){p=J.a2(a)\no=B.a.O(A.S(J.bP(p.gK(a))),1)\nif(o===\"\"){p=J.bP(p.gU(a))\nreturn p==null?t.K.a(p):p}s=$.qG().i(0,o)\nif(s!=null){r=J.bP(p.gU(a))\nif(r==null)return null\ntry{p=J.qU(s,r)\nif(p==null)p=t.K.a(p)\nreturn p}catch(n){q=A.M(n)\nA.b9(A.q(q)+\" - ignoring \"+A.q(r)+\" \"+J.jf(r).l(0))}}}h.a=null\nJ.bo(a,new A.mV(h,a))\np=h.a\nif(p==null)p=a\nreturn p}else if(t.j.b(a)){for(p=J.T(a),m=t.z,l=null,k=0;k<p.gj(a);++k){j=p.i(a,k)\ni=A.o7(j)\nif(i==null?j!=null:i!==j){if(l==null)l=A.jY(a,!0,m)\nB.b.k(l,k,i)}}if(l==null)p=a\nelse p=l\nreturn p}else throw A.b(A.x(\"Unsupported value type \"+J.jf(a).l(0)+\" for \"+A.q(a)))},\ndg:function dg(){},\nbe:function be(a){this.a=a},\nmL:function mL(){},\nmW:function mW(a,b){this.a=a\nthis.b=b},\nmV:function mV(a,b){this.a=a\nthis.b=b},\nl0:function l0(){},\ne7:function e7(){},\nnl(a){var s=0,r=A.B(t.cE),q,p\nvar $async$nl=A.C(function(b,c){if(b===1)return A.y(c,r)\nwhile(true)switch(s){case 0:p=A\ns=3\nreturn A.p(A.fF(\"sqflite_databases\"),$async$nl)\ncase 3:q=p.p4(c,a,null)\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$nl,r)},\ndq(a,b){var s=0,r=A.B(t.cE),q,p,o,n,m,l,k,j,i,h,g,f\nvar $async$dq=A.C(function(c,d){if(c===1)return A.y(d,r)\nwhile(true)switch(s){case 0:s=3\nreturn A.p(A.nl(a),$async$dq)\ncase 3:i=d\ni=i\np=$.qI()\no=self\no.toString\nn=p.l(0)\no=o.fetch(n,null)\no.toString\ns=4\nreturn A.p(A.j8(o,t.z),$async$dq)\ncase 4:m=d\nif(m==null)m=t.K.a(m)\nh=A\ng=t.U\ns=5\nreturn A.p(A.j8(t.K.a(m.arrayBuffer()),t.X),$async$dq)\ncase 5:l=h.b_(g.a(d),0,null)\nk=t.db.a(i).b\no=A.ti(k)\nj={}\nj[\"content-type\"]=\"application/wasm\"\nh=A\ng=k\nf=a\ns=6\nreturn A.p(A.lt(t.d9.a(new self.Response(l,{headers:j})),o),$async$dq)\ncase 6:q=h.p4(g,f,d)\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$dq,r)},\np4(a,b,c){return new A.e8(a,c)},\ne8:function e8(a,b){this.b=a\nthis.c=b\nthis.f=$},\nd_:function d_(a,b,c,d,e,f){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d\n_.e=e\n_.f=f},\nl3:function l3(){},\nhj:function hj(a,b,c){this.a=a\nthis.b=b\nthis.$ti=c},\nkc:function kc(){},\nkd:function kd(){},\nfA:function fA(a,b,c){var _=this\n_.b=a\n_.c=b\n_.d=c\n_.a=null},\nfr:function fr(a,b,c,d){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d\n_.e=!1},\njz:function jz(a,b){this.a=a\nthis.b=b},\nbs:function bs(){},\nn7:function n7(){},\nl1:function l1(){},\ncJ:function cJ(a){var _=this\n_.b=a\n_.c=!0\n_.d=!1\n_.a=null},\nd0:function d0(a,b,c,d){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d\n_.f=_.e=null},\nhL:function hL(a,b,c){var _=this\n_.r=a\n_.w=-1\n_.x=$\n_.y=!1\n_.a=b\n_.c=c},\ncF:function cF(){},\ndI:function dI(){},\nha:function ha(a,b,c){this.d=a\nthis.a=b\nthis.c=c},\nak:function ak(a,b){this.a=a\nthis.b=b},\nir:function ir(a){this.a=a\nthis.b=-1},\nis:function is(){},\nit:function it(){},\niv:function iv(){},\niw:function iw(){},\ndY:function dY(a){this.b=a},\nhI:function hI(a){this.a=a},\nhF:function hF(a,b){this.a=a\nthis.b=b},\nlu:function lu(a,b,c,d){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d},\nhJ:function hJ(a,b,c){this.b=a\nthis.c=b\nthis.d=c},\ncp:function cp(){},\nbE:function bE(){},\nd4:function d4(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nti(a){var s=$.qp()\ns=s\nreturn new A.l2(s,a==null?new A.dF(A.X(t.N,t.nh)):a)},\nl2:function l2(a,b){this.a=a\nthis.b=b},\nbd(a,b){return new A.bc(a,b)},\nbc:function bc(a,b){this.a=a\nthis.b=b},\nfF(a){var s=0,r=A.B(t.cF),q,p,o,n\nvar $async$fF=A.C(function(b,c){if(b===1)return A.y(c,r)\nwhile(true)switch(s){case 0:p=t.N\no=new A.ji(a)\nn=new A.cM(o,new A.dF(A.X(p,t.nh)),new A.cR(t.h),A.rx(p),A.X(p,t.S))\ns=3\nreturn A.p(o.bL(0),$async$fF)\ncase 3:s=4\nreturn A.p(n.b2(),$async$fF)\ncase 4:q=n\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$fF,r)},\nji:function ji(a){this.a=null\nthis.b=a},\njn:function jn(){},\njm:function jm(a){this.a=a},\njj:function jj(a){this.a=a},\njo:function jo(a,b,c,d){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d},\njl:function jl(a,b){this.a=a\nthis.b=b},\njk:function jk(a,b){this.a=a\nthis.b=b},\nbg:function bg(){},\nlP:function lP(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nlQ:function lQ(a,b){this.a=a\nthis.b=b},\nim:function im(a,b){this.a=a\nthis.b=b},\ncM:function cM(a,b,c,d,e){var _=this\n_.a=a\n_.c=null\n_.d=b\n_.e=c\n_.f=d\n_.r=e},\njM:function jM(a){this.a=a},\njN:function jN(){},\njO:function jO(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\na9:function a9(){},\nd9:function d9(a,b){var _=this\n_.w=a\n_.d=b\n_.c=_.b=_.a=null},\nd8:function d8(a,b,c){var _=this\n_.w=a\n_.x=b\n_.d=c\n_.c=_.b=_.a=null},\nct:function ct(a,b,c){var _=this\n_.w=a\n_.x=b\n_.d=c\n_.c=_.b=_.a=null},\ncy:function cy(a,b,c,d,e){var _=this\n_.w=a\n_.x=b\n_.y=c\n_.z=d\n_.d=e\n_.c=_.b=_.a=null},\ndF:function dF(a){this.a=a},\njL:function jL(){},\njR:function jR(){},\ncP:function cP(a){this.a=a},\nlf:function lf(){},\njC:function jC(){},\nkk:function kk(){},\nkj:function kj(){},\nmr:function mr(){},\nl4:function l4(){},\nfy:function fy(){},\njE:function jE(){},\njF:function jF(){},\njH:function jH(){},\nm3:function m3(){},\nmt:function mt(){},\njG:function jG(){},\nrD(a,b){return A.n4(a,\"put\",[b],t.B)},\nnF(a,b,c){var s,r,q,p,o={},n=new A.E($.D,c.h(\"E<0>\")),m=new A.aa(n,c.h(\"aa<0>\"))\no.a=o.b=null\ns=new A.kg(o)\nr=t.a\nq=r.a(new A.kh(s,m,b,a,c))\nt.Z.a(null)\np=t.A\no.b=A.bf(a,\"success\",q,!1,p)\no.a=A.bf(a,\"error\",r.a(new A.ki(o,s,m)),!1,p)\nreturn n},\nkg:function kg(a){this.a=a},\nkh:function kh(a,b,c,d,e){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d\n_.e=e},\nkf:function kf(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nki:function ki(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nd7:function d7(a,b){var _=this\n_.c=_.b=_.a=null\n_.d=a\n_.$ti=b},\nlJ:function lJ(a,b){this.a=a\nthis.b=b},\nlK:function lK(a,b){this.a=a\nthis.b=b},\njA:function jA(){},\nlq(a,b){var s=0,r=A.B(t.ax),q,p,o,n,m\nvar $async$lq=A.C(function(c,d){if(c===1)return A.y(d,r)\nwhile(true)switch(s){case 0:o={}\nb.D(0,new A.ls(o))\np=t.N\np=new A.hG(A.X(p,t.Y),A.X(p,t.ng))\nn=p\nm=J\ns=3\nreturn A.p(A.j8(self.WebAssembly.instantiateStreaming(a,o),t.ot),$async$lq)\ncase 3:n.ez(m.qW(d))\nq=p\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$lq,r)},\nmK:function mK(){},\ndc:function dc(){},\nhG:function hG(a,b){this.a=a\nthis.b=b},\nls:function ls(a){this.a=a},\nlr:function lr(a){this.a=a},\nk1:function k1(){},\ncU:function cU(){},\ncK:function cK(){},\nlt(a,b){var s=0,r=A.B(t.es),q,p,o\nvar $async$lt=A.C(function(c,d){if(c===1)return A.y(d,r)\nwhile(true)switch(s){case 0:p=A\no=A\ns=3\nreturn A.p(A.lp(a,b),$async$lt)\ncase 3:q=new p.hH(new o.hI(d))\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$lt,r)},\nhH:function hH(a){this.a=a},\nlp(b9,c0){var s=0,r=A.B(t.n0),q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,b0,b1,b2,b3,b4,b5,b6,b7,b8\nvar $async$lp=A.C(function(c1,c2){if(c1===1)return A.y(c2,r)\nwhile(true)switch(s){case 0:b7=A.tH(c0)\nb8=b7.b\nb8===$&&A.aZ(\"injectedValues\")\ns=3\nreturn A.p(A.lq(b9,b8),$async$lp)\ncase 3:p=c2\nb8=b7.c\nb8===$&&A.aZ(\"memory\")\no=p.a\nn=o.i(0,\"dart_sqlite3_malloc\")\nn.toString\nm=o.i(0,\"dart_sqlite3_free\")\nm.toString\no.i(0,\"dart_sqlite3_create_scalar_function\").toString\no.i(0,\"dart_sqlite3_create_aggregate_function\").toString\no.i(0,\"dart_sqlite3_create_window_function\")\no.i(0,\"dart_sqlite3_create_collation\")\nl=o.i(0,\"dart_sqlite3_updates\")\nl.toString\no.i(0,\"sqlite3_libversion\").toString\no.i(0,\"sqlite3_sourceid\").toString\no.i(0,\"sqlite3_libversion_number\").toString\nk=o.i(0,\"sqlite3_open_v2\")\nk.toString\nj=o.i(0,\"sqlite3_close_v2\")\nj.toString\ni=o.i(0,\"sqlite3_extended_errcode\")\ni.toString\nh=o.i(0,\"sqlite3_errmsg\")\nh.toString\ng=o.i(0,\"sqlite3_errstr\")\ng.toString\nf=o.i(0,\"sqlite3_extended_result_codes\")\nf.toString\ne=o.i(0,\"sqlite3_exec\")\ne.toString\no.i(0,\"sqlite3_free\").toString\nd=o.i(0,\"sqlite3_prepare_v3\")\nd.toString\nc=o.i(0,\"sqlite3_bind_parameter_count\")\nc.toString\nb=o.i(0,\"sqlite3_column_count\")\nb.toString\na=o.i(0,\"sqlite3_column_name\")\na.toString\na0=o.i(0,\"sqlite3_reset\")\na0.toString\na1=o.i(0,\"sqlite3_step\")\na1.toString\na2=o.i(0,\"sqlite3_finalize\")\na2.toString\na3=o.i(0,\"sqlite3_column_type\")\na3.toString\na4=o.i(0,\"sqlite3_column_int64\")\na4.toString\na5=o.i(0,\"sqlite3_column_double\")\na5.toString\na6=o.i(0,\"sqlite3_column_bytes\")\na6.toString\na7=o.i(0,\"sqlite3_column_blob\")\na7.toString\na8=o.i(0,\"sqlite3_column_text\")\na8.toString\na9=o.i(0,\"sqlite3_bind_null\")\na9.toString\nb0=o.i(0,\"sqlite3_bind_int64\")\nb0.toString\nb1=o.i(0,\"sqlite3_bind_double\")\nb1.toString\nb2=o.i(0,\"sqlite3_bind_text\")\nb2.toString\nb3=o.i(0,\"sqlite3_bind_blob64\")\nb3.toString\no.i(0,\"sqlite3_bind_parameter_index\").toString\nb4=o.i(0,\"sqlite3_changes\")\nb4.toString\nb5=o.i(0,\"sqlite3_last_insert_rowid\")\nb5.toString\nb6=o.i(0,\"sqlite3_user_data\")\nb6.toString\no.i(0,\"sqlite3_result_null\").toString\no.i(0,\"sqlite3_result_int64\").toString\no.i(0,\"sqlite3_result_double\").toString\no.i(0,\"sqlite3_result_text\").toString\no.i(0,\"sqlite3_result_blob64\").toString\no.i(0,\"sqlite3_result_error\").toString\no.i(0,\"sqlite3_value_type\").toString\no.i(0,\"sqlite3_value_int64\").toString\no.i(0,\"sqlite3_value_double\").toString\no.i(0,\"sqlite3_value_bytes\").toString\no.i(0,\"sqlite3_value_text\").toString\no.i(0,\"sqlite3_value_blob\").toString\no.i(0,\"sqlite3_aggregate_context\").toString\np.b.i(0,\"sqlite3_temp_directory\").toString\nq=b7.a=new A.hE(b8,b7.d,n,m,l,k,j,i,h,g,f,e,d,c,b,a,a0,a1,a3,a4,a5,a6,a8,a7,a9,b0,b1,b2,b3,a2,b4,b5,b6)\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$lp,r)},\noV(a,b){var s,r=A.b_(J.bO(a),0,null),q=r.length,p=0\nwhile(!0){s=b+p\nif(!(s>=0&&s<q))return A.d(r,s)\nif(!(r[s]!==0))break;++p}return p},\nb0(a,b){var s=J.bO(a),r=A.oV(a,b)\nreturn B.f.b5(0,A.b_(s,b,r))},\noU(a,b,c){var s\nif(b===0)return null\ns=J.bO(a)\nreturn B.f.b5(0,A.b_(s,b,c))},\ntH(a){var s=t.S\ns=new A.m4(new A.jy(A.X(s,t.lq),A.X(s,t.ie)))\ns.eA(a)\nreturn s},\nhE:function hE(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,b0,b1,b2,b3){var _=this\n_.b=a\n_.c=b\n_.d=c\n_.e=d\n_.w=e\n_.Q=f\n_.as=g\n_.at=h\n_.ax=i\n_.ay=j\n_.ch=k\n_.CW=l\n_.cy=m\n_.db=n\n_.dx=o\n_.dy=p\n_.fr=q\n_.fx=r\n_.fy=s\n_.go=a0\n_.id=a1\n_.k1=a2\n_.k2=a3\n_.k3=a4\n_.k4=a5\n_.ok=a6\n_.p1=a7\n_.p2=a8\n_.p3=a9\n_.R8=b0\n_.RG=b1\n_.rx=b2\n_.ry=b3},\nm4:function m4(a){var _=this\n_.c=_.b=_.a=$\n_.d=a},\nm5:function m5(a,b){this.a=a\nthis.b=b},\nm6:function m6(a){this.a=a},\nm7:function m7(){},\nmh:function mh(a){this.a=a},\nmi:function mi(a){this.a=a},\nmj:function mj(a){this.a=a},\nmk:function mk(a){this.a=a},\nml:function ml(a){this.a=a},\nmm:function mm(a){this.a=a},\nmn:function mn(a){this.a=a},\nmo:function mo(a,b){this.a=a\nthis.b=b},\nm8:function m8(a,b){this.a=a\nthis.b=b},\nm9:function m9(a,b){this.a=a\nthis.b=b},\nma:function ma(a,b){this.a=a\nthis.b=b},\nmb:function mb(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nmc:function mc(a,b){this.a=a\nthis.b=b},\nmd:function md(a,b){this.a=a\nthis.b=b},\nme:function me(a,b){this.a=a\nthis.b=b},\nmf:function mf(a,b){this.a=a\nthis.b=b},\nmg:function mg(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\njy:function jy(a,b){this.b=a\nthis.d=b\nthis.e=null},\nfd:function fd(){this.a=null},\njs:function js(a,b){this.a=a\nthis.b=b},\nqj(a){if(typeof dartPrint==\"function\"){dartPrint(a)\nreturn}if(typeof console==\"object\"&&typeof console.log!=\"undefined\"){console.log(a)\nreturn}if(typeof window==\"object\")return\nif(typeof print==\"function\"){print(a)\nreturn}throw\"Unable to print message: \"+String(a)},\nur(a){var s,r=a.$dart_jsFunction\nif(r!=null)return r\ns=function(b,c){return function(){return b(c,Array.prototype.slice.apply(arguments))}}(A.um,a)\ns[$.ol()]=a\na.$dart_jsFunction=s\nreturn s},\num(a,b){t.j.a(b)\nt.Y.a(a)\nreturn A.rI(a,b,null)},\na6(a,b){if(typeof a==\"function\")return a\nelse return b.a(A.ur(a))},\nv9(){var s,r,q,p,o=null\ntry{o=A.nQ()}catch(s){if(t.mA.b(A.M(s))){r=$.mU\nif(r!=null)return r\nthrow s}else throw s}if(J.a7(o,$.pN)){r=$.mU\nr.toString\nreturn r}$.pN=o\nif($.nn()==$.ja())r=$.mU=o.e9(\".\").l(0)\nelse{q=o.cS()\np=q.length-1\nr=$.mU=p===0?q:B.a.n(q,0,p)}return r},\nqg(a){var s\nif(!(a>=65&&a<=90))s=a>=97&&a<=122\nelse s=!0\nreturn s},\nvm(a,b){var s=a.length,r=b+2\nif(s<r)return!1\nif(!A.qg(B.a.B(a,b)))return!1\nif(B.a.B(a,b+1)!==58)return!1\nif(s===r)return!0\nreturn B.a.B(a,r)===47},\nf1(){return A.J(A.x(\"sqfliteFfiHandlerIo Web not supported\"))},\nof(a,b,c,d,e,f){var s=b.a,r=b.b,q=A.j(s.at.$1(r)),p=a.a\nreturn new A.d_(A.b0(s.b,A.j(s.ax.$1(r))),A.b0(p.b,A.j(p.ay.$1(q)))+\" (code \"+q+\")\",c,d,e,f)},\nj9(a,b,c,d,e){throw A.b(A.of(a.a,a.b,b,c,d,e))},\nke(a){var s=0,r=A.B(t.p),q,p\nvar $async$ke=A.C(function(b,c){if(b===1)return A.y(c,r)\nwhile(true)switch(s){case 0:p=A\ns=3\nreturn A.p(A.j8(t.K.a(a.arrayBuffer()),t.U),$async$ke)\ncase 3:q=p.b_(c,0,null)\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$ke,r)},\nnC(){return new A.fd()},\nvq(a){A.vr(a)}},J={\noj(a,b,c,d){return{i:a,p:b,e:c,x:d}},\nn8(a){var s,r,q,p,o,n=a[v.dispatchPropertyName]\nif(n==null)if($.oi==null){A.vj()\nn=a[v.dispatchPropertyName]}if(n!=null){s=n.p\nif(!1===s)return n.i\nif(!0===s)return a\nr=Object.getPrototypeOf(a)\nif(s===r)return n.i\nif(n.e===r)throw A.b(A.hw(\"Return interceptor for \"+A.q(s(a,n))))}q=a.constructor\nif(q==null)p=null\nelse{o=$.mp\nif(o==null)o=$.mp=v.getIsolateTag(\"_$dart_js\")\np=q[o]}if(p!=null)return p\np=A.vp(a)\nif(p!=null)return p\nif(typeof a==\"function\")return B.Y\ns=Object.getPrototypeOf(a)\nif(s==null)return B.H\nif(s===Object.prototype)return B.H\nif(typeof q==\"function\"){o=$.mp\nif(o==null)o=$.mp=v.getIsolateTag(\"_$dart_js\")\nObject.defineProperty(q,o,{value:B.r,enumerable:false,writable:true,configurable:true})\nreturn B.r}return B.r},\nnx(a,b){if(a<0||a>4294967295)throw A.b(A.a1(a,0,4294967295,\"length\",null))\nreturn J.rr(new Array(a),b)},\nrq(a,b){if(a<0)throw A.b(A.ao(\"Length must be a non-negative integer: \"+a,null))\nreturn A.t(new Array(a),b.h(\"O<0>\"))},\nrr(a,b){return J.jP(A.t(a,b.h(\"O<0>\")),b)},\njP(a,b){a.fixed$length=Array\nreturn a},\noK(a){a.fixed$length=Array\na.immutable$list=Array\nreturn a},\nrs(a,b){var s=t.bP\nreturn J.qS(s.a(a),s.a(b))},\noL(a){if(a<256)switch(a){case 9:case 10:case 11:case 12:case 13:case 32:case 133:case 160:return!0\ndefault:return!1}switch(a){case 5760:case 8192:case 8193:case 8194:case 8195:case 8196:case 8197:case 8198:case 8199:case 8200:case 8201:case 8202:case 8232:case 8233:case 8239:case 8287:case 12288:case 65279:return!0\ndefault:return!1}},\nrt(a,b){var s,r\nfor(s=a.length;b<s;){r=B.a.t(a,b)\nif(r!==32&&r!==13&&!J.oL(r))break;++b}return b},\nru(a,b){var s,r\nfor(;b>0;b=s){s=b-1\nr=B.a.B(a,s)\nif(r!==32&&r!==13&&!J.oL(r))break}return b},\nbL(a){if(typeof a==\"number\"){if(Math.floor(a)==a)return J.dJ.prototype\nreturn J.fJ.prototype}if(typeof a==\"string\")return J.bW.prototype\nif(a==null)return J.dK.prototype\nif(typeof a==\"boolean\")return J.fH.prototype\nif(a.constructor==Array)return J.O.prototype\nif(typeof a!=\"object\"){if(typeof a==\"function\")return J.bu.prototype\nreturn a}if(a instanceof A.r)return a\nreturn J.n8(a)},\nT(a){if(typeof a==\"string\")return J.bW.prototype\nif(a==null)return a\nif(a.constructor==Array)return J.O.prototype\nif(typeof a!=\"object\"){if(typeof a==\"function\")return J.bu.prototype\nreturn a}if(a instanceof A.r)return a\nreturn J.n8(a)},\nb8(a){if(a==null)return a\nif(a.constructor==Array)return J.O.prototype\nif(typeof a!=\"object\"){if(typeof a==\"function\")return J.bu.prototype\nreturn a}if(a instanceof A.r)return a\nreturn J.n8(a)},\nvc(a){if(typeof a==\"number\")return J.cO.prototype\nif(typeof a==\"string\")return J.bW.prototype\nif(a==null)return a\nif(!(a instanceof A.r))return J.c_.prototype\nreturn a},\nog(a){if(typeof a==\"string\")return J.bW.prototype\nif(a==null)return a\nif(!(a instanceof A.r))return J.c_.prototype\nreturn a},\na2(a){if(a==null)return a\nif(typeof a!=\"object\"){if(typeof a==\"function\")return J.bu.prototype\nreturn a}if(a instanceof A.r)return a\nreturn J.n8(a)},\nqd(a){if(a==null)return a\nif(!(a instanceof A.r))return J.c_.prototype\nreturn a},\na7(a,b){if(a==null)return b==null\nif(typeof a!=\"object\")return b!=null&&a===b\nreturn J.bL(a).W(a,b)},\nab(a,b){if(typeof b===\"number\")if(a.constructor==Array||typeof a==\"string\"||A.vn(a,a[v.dispatchPropertyName]))if(b>>>0===b&&b<a.length)return a[b]\nreturn J.T(a).i(a,b)},\nnq(a,b,c){return J.b8(a).k(a,b,c)},\nqN(a,b,c,d){return J.a2(a).ff(a,b,c,d)},\nqO(a,b){return J.b8(a).m(a,b)},\nqP(a,b,c){return J.a2(a).fB(a,b,c)},\nqQ(a,b,c,d){return J.a2(a).co(a,b,c,d)},\nqR(a,b){return J.og(a).dL(a,b)},\njd(a,b){return J.b8(a).bz(a,b)},\nos(a,b){return J.og(a).B(a,b)},\nqS(a,b){return J.vc(a).a8(a,b)},\nnr(a,b){return J.T(a).S(a,b)},\nqT(a,b){return J.a2(a).F(a,b)},\nqU(a,b){return J.qd(a).b5(a,b)},\nje(a,b){return J.b8(a).v(a,b)},\nqV(a){return J.qd(a).fT(a)},\nbo(a,b){return J.b8(a).D(a,b)},\nbO(a){return J.a2(a).gaE(a)},\not(a){return J.a2(a).gaH(a)},\nbP(a){return J.b8(a).gA(a)},\nax(a){return J.bL(a).gI(a)},\nqW(a){return J.a2(a).gh5(a)},\ndr(a){return J.T(a).gC(a)},\nf3(a){return J.T(a).gP(a)},\nan(a){return J.b8(a).gE(a)},\nou(a){return J.a2(a).gK(a)},\nY(a){return J.T(a).gj(a)},\njf(a){return J.bL(a).gN(a)},\nqX(a){return J.a2(a).gU(a)},\nqY(a,b){return J.T(a).cB(a,b)},\nov(a,b,c){return J.b8(a).aj(a,b,c)},\nqZ(a){return J.a2(a).hc(a)},\nr_(a,b){return J.bL(a).e2(a,b)},\ncB(a,b){return J.a2(a).e6(a,b)},\nr0(a,b){return J.b8(a).G(a,b)},\nr1(a,b,c,d,e){return J.b8(a).T(a,b,c,d,e)},\nns(a,b){return J.b8(a).a2(a,b)},\nr2(a,b,c){return J.og(a).n(a,b,c)},\nbp(a){return J.bL(a).l(a)},\ncN:function cN(){},\nfH:function fH(){},\ndK:function dK(){},\na:function a(){},\nU:function U(){},\nh6:function h6(){},\nc_:function c_(){},\nbu:function bu(){},\nO:function O(a){this.$ti=a},\njQ:function jQ(a){this.$ti=a},\nca:function ca(a,b,c){var _=this\n_.a=a\n_.b=b\n_.c=0\n_.d=null\n_.$ti=c},\ncO:function cO(){},\ndJ:function dJ(){},\nfJ:function fJ(){},\nbW:function bW(){}},B={}\nvar w=[A,J,B]\nvar $={}\nA.nz.prototype={}\nJ.cN.prototype={\nW(a,b){return a===b},\ngI(a){return A.dZ(a)},\nl(a){return\"Instance of '\"+A.kb(a)+\"'\"},\ne2(a,b){t.bg.a(b)\nthrow A.b(new A.dU(a,b.ge0(),b.ge5(),b.ge1(),null))},\ngN(a){return A.oh(a)}}\nJ.fH.prototype={\nl(a){return String(a)},\ngI(a){return a?519018:218159},\ngN(a){return B.ai},\n$iaw:1}\nJ.dK.prototype={\nW(a,b){return null==b},\nl(a){return\"null\"},\ngI(a){return 0},\n$iR:1}\nJ.a.prototype={}\nJ.U.prototype={\ngI(a){return 0},\ngN(a){return B.ab},\nl(a){return String(a)},\n$iny:1,\n$ibg:1,\n$idc:1,\n$icU:1,\n$icK:1,\ngaO(a){return a.name},\ngj(a){return a.length},\ngdS(a){return a.exports},\ngh5(a){return a.instance},\ngaE(a){return a.buffer}}\nJ.h6.prototype={}\nJ.c_.prototype={}\nJ.bu.prototype={\nl(a){var s=a[$.ol()]\nif(s==null)return this.eu(a)\nreturn\"JavaScript function for \"+J.bp(s)},\n$ich:1}\nJ.O.prototype={\nbz(a,b){return new A.ba(a,A.av(a).h(\"@<1>\").q(b).h(\"ba<1,2>\"))},\nm(a,b){A.av(a).c.a(b)\nif(!!a.fixed$length)A.J(A.x(\"add\"))\na.push(b)},\nhi(a,b){var s\nif(!!a.fixed$length)A.J(A.x(\"removeAt\"))\ns=a.length\nif(b>=s)throw A.b(A.oS(b,null))\nreturn a.splice(b,1)[0]},\nG(a,b){var s\nif(!!a.fixed$length)A.J(A.x(\"remove\"))\nfor(s=0;s<a.length;++s)if(J.a7(a[s],b)){a.splice(s,1)\nreturn!0}return!1},\nb4(a,b){var s\nA.av(a).h(\"e<1>\").a(b)\nif(!!a.fixed$length)A.J(A.x(\"addAll\"))\nif(Array.isArray(b)){this.eF(a,b)\nreturn}for(s=J.an(b);s.p();)a.push(s.gu(s))},\neF(a,b){var s,r\nt.b.a(b)\ns=b.length\nif(s===0)return\nif(a===b)throw A.b(A.ap(a))\nfor(r=0;r<s;++r)a.push(b[r])},\nfG(a){if(!!a.fixed$length)A.J(A.x(\"clear\"))\na.length=0},\nD(a,b){var s,r\nA.av(a).h(\"~(1)\").a(b)\ns=a.length\nfor(r=0;r<s;++r){b.$1(a[r])\nif(a.length!==s)throw A.b(A.ap(a))}},\naj(a,b,c){var s=A.av(a)\nreturn new A.af(a,s.q(c).h(\"1(2)\").a(b),s.h(\"@<1>\").q(c).h(\"af<1,2>\"))},\nau(a,b){var s,r=A.jX(a.length,\"\",!1,t.N)\nfor(s=0;s<a.length;++s)this.k(r,s,A.q(a[s]))\nreturn r.join(b)},\na2(a,b){return A.eb(a,b,null,A.av(a).c)},\nv(a,b){if(!(b>=0&&b<a.length))return A.d(a,b)\nreturn a[b]},\ngA(a){if(a.length>0)return a[0]\nthrow A.b(A.bt())},\ngai(a){var s=a.length\nif(s>0)return a[s-1]\nthrow A.b(A.bt())},\nT(a,b,c,d,e){var s,r,q,p,o\nA.av(a).h(\"e<1>\").a(d)\nif(!!a.immutable$list)A.J(A.x(\"setRange\"))\nA.by(b,c,a.length)\ns=c-b\nif(s===0)return\nA.aT(e,\"skipCount\")\nif(t.j.b(d)){r=d\nq=e}else{r=J.ns(d,e).bQ(0,!1)\nq=0}p=J.T(r)\nif(q+s>p.gj(r))throw A.b(A.oJ())\nif(q<b)for(o=s-1;o>=0;--o)a[b+o]=p.i(r,q+o)\nelse for(o=0;o<s;++o)a[b+o]=p.i(r,q+o)},\nei(a,b){var s,r=A.av(a)\nr.h(\"c(1,1)?\").a(b)\nif(!!a.immutable$list)A.J(A.x(\"sort\"))\ns=b==null?J.uE():b\nA.t0(a,s,r.c)},\neh(a){return this.ei(a,null)},\ncG(a,b){var s,r=a.length,q=r-1\nif(q<0)return-1\nq>=r\nfor(s=q;s>=0;--s){if(!(s<a.length))return A.d(a,s)\nif(J.a7(a[s],b))return s}return-1},\nS(a,b){var s\nfor(s=0;s<a.length;++s)if(J.a7(a[s],b))return!0\nreturn!1},\ngC(a){return a.length===0},\ngP(a){return a.length!==0},\nl(a){return A.nw(a,\"[\",\"]\")},\ngE(a){return new J.ca(a,a.length,A.av(a).h(\"ca<1>\"))},\ngI(a){return A.dZ(a)},\ngj(a){return a.length},\ni(a,b){if(!(b>=0&&b<a.length))throw A.b(A.dp(a,b))\nreturn a[b]},\nk(a,b,c){A.av(a).c.a(c)\nif(!!a.immutable$list)A.J(A.x(\"indexed set\"))\nif(!(b>=0&&b<a.length))throw A.b(A.dp(a,b))\na[b]=c},\n$ik:1,\n$ie:1,\n$im:1}\nJ.jQ.prototype={}\nJ.ca.prototype={\ngu(a){var s=this.d\nreturn s==null?this.$ti.c.a(s):s},\np(){var s,r=this,q=r.a,p=q.length\nif(r.b!==p)throw A.b(A.aM(q))\ns=r.c\nif(s>=p){r.sd_(null)\nreturn!1}r.sd_(q[s]);++r.c\nreturn!0},\nsd_(a){this.d=this.$ti.h(\"1?\").a(a)},\n$iL:1}\nJ.cO.prototype={\na8(a,b){var s\nA.uh(b)\nif(a<b)return-1\nelse if(a>b)return 1\nelse if(a===b){if(a===0){s=this.gcF(b)\nif(this.gcF(a)===s)return 0\nif(this.gcF(a))return-1\nreturn 1}return 0}else if(isNaN(a)){if(isNaN(b))return 0\nreturn 1}else return-1},\ngcF(a){return a===0?1/a<0:a<0},\nfF(a){var s,r\nif(a>=0){if(a<=2147483647){s=a|0\nreturn a===s?s:s+1}}else if(a>=-2147483648)return a|0\nr=Math.ceil(a)\nif(isFinite(r))return r\nthrow A.b(A.x(\"\"+a+\".ceil()\"))},\nl(a){if(a===0&&1/a<0)return\"-0.0\"\nelse return\"\"+a},\ngI(a){var s,r,q,p,o=a|0\nif(a===o)return o&536870911\ns=Math.abs(a)\nr=Math.log(s)/0.6931471805599453|0\nq=Math.pow(2,r)\np=s<1?s/q:q/s\nreturn((p*9007199254740992|0)+(p*3542243181176521|0))*599197+r*1259&536870911},\nab(a,b){var s=a%b\nif(s===0)return 0\nif(s>0)return s\nreturn s+b},\nex(a,b){if((a|0)===a)if(b>=1||b<-1)return a/b|0\nreturn this.dC(a,b)},\nR(a,b){return(a|0)===a?a/b|0:this.dC(a,b)},\ndC(a,b){var s=a/b\nif(s>=-2147483648&&s<=2147483647)return s|0\nif(s>0){if(s!==1/0)return Math.floor(s)}else if(s>-1/0)return Math.ceil(s)\nthrow A.b(A.x(\"Result of truncating division is \"+A.q(s)+\": \"+A.q(a)+\" ~/ \"+b))},\naU(a,b){if(b<0)throw A.b(A.cA(b))\nreturn b>31?0:a<<b>>>0},\naV(a,b){var s\nif(b<0)throw A.b(A.cA(b))\nif(a>0)s=this.ck(a,b)\nelse{s=b>31?31:b\ns=a>>s>>>0}return s},\nM(a,b){var s\nif(a>0)s=this.ck(a,b)\nelse{s=b>31?31:b\ns=a>>s>>>0}return s},\nfn(a,b){if(0>b)throw A.b(A.cA(b))\nreturn this.ck(a,b)},\nck(a,b){return b>31?0:a>>>b},\ngN(a){return B.al},\n$iaj:1,\n$iN:1,\n$iW:1}\nJ.dJ.prototype={\ngdN(a){var s,r=a<0?-a-1:a,q=r\nfor(s=32;q>=4294967296;){q=this.R(q,4294967296)\ns+=32}return s-Math.clz32(q)},\ngN(a){return B.ak},\n$ic:1}\nJ.fJ.prototype={\ngN(a){return B.aj}}\nJ.bW.prototype={\nB(a,b){if(b<0)throw A.b(A.dp(a,b))\nif(b>=a.length)A.J(A.dp(a,b))\nreturn a.charCodeAt(b)},\nt(a,b){if(b>=a.length)throw A.b(A.dp(a,b))\nreturn a.charCodeAt(b)},\ncp(a,b,c){var s=b.length\nif(c>s)throw A.b(A.a1(c,0,s,null,null))\nreturn new A.iE(b,a,c)},\ndL(a,b){return this.cp(a,b,0)},\nbf(a,b){return a+b},\ndR(a,b){var s=b.length,r=a.length\nif(s>r)return!1\nreturn b===this.O(a,r-s)},\naz(a,b,c,d){var s=A.by(b,c,a.length)\nreturn A.vv(a,b,s,d)},\nH(a,b,c){var s\nif(c<0||c>a.length)throw A.b(A.a1(c,0,a.length,null,null))\ns=c+b.length\nif(s>a.length)return!1\nreturn b===a.substring(c,s)},\nJ(a,b){return this.H(a,b,0)},\nn(a,b,c){return a.substring(b,A.by(b,c,a.length))},\nO(a,b){return this.n(a,b,null)},\nhn(a){var s,r,q,p=a.trim(),o=p.length\nif(o===0)return p\nif(this.t(p,0)===133){s=J.rt(p,1)\nif(s===o)return\"\"}else s=0\nr=o-1\nq=this.B(p,r)===133?J.ru(p,r):o\nif(s===0&&q===o)return p\nreturn p.substring(s,q)},\nbg(a,b){var s,r\nif(0>=b)return\"\"\nif(b===1||a.length===0)return a\nif(b!==b>>>0)throw A.b(B.R)\nfor(s=a,r=\"\";!0;){if((b&1)===1)r=s+r\nb=b>>>1\nif(b===0)break\ns+=s}return r},\nhg(a,b,c){var s=b-a.length\nif(s<=0)return a\nreturn this.bg(c,s)+a},\naq(a,b,c){var s\nif(c<0||c>a.length)throw A.b(A.a1(c,0,a.length,null,null))\ns=a.indexOf(b,c)\nreturn s},\ncB(a,b){return this.aq(a,b,0)},\ne_(a,b,c){var s,r\nif(c==null)c=a.length\nelse if(c<0||c>a.length)throw A.b(A.a1(c,0,a.length,null,null))\ns=b.length\nr=a.length\nif(c+s>r)c=r-s\nreturn a.lastIndexOf(b,c)},\ncG(a,b){return this.e_(a,b,null)},\nS(a,b){return A.vu(a,b,0)},\na8(a,b){var s\nA.S(b)\nif(a===b)s=0\nelse s=a<b?-1:1\nreturn s},\nl(a){return a},\ngI(a){var s,r,q\nfor(s=a.length,r=0,q=0;q<s;++q){r=r+a.charCodeAt(q)&536870911\nr=r+((r&524287)<<10)&536870911\nr^=r>>6}r=r+((r&67108863)<<3)&536870911\nr^=r>>11\nreturn r+((r&16383)<<15)&536870911},\ngN(a){return B.ad},\ngj(a){return a.length},\n$iaj:1,\n$ik9:1,\n$ii:1}\nA.c3.prototype={\ngE(a){var s=A.v(this)\nreturn new A.du(J.an(this.ga3()),s.h(\"@<1>\").q(s.z[1]).h(\"du<1,2>\"))},\ngj(a){return J.Y(this.ga3())},\ngC(a){return J.dr(this.ga3())},\ngP(a){return J.f3(this.ga3())},\na2(a,b){var s=A.v(this)\nreturn A.fe(J.ns(this.ga3(),b),s.c,s.z[1])},\nv(a,b){return A.v(this).z[1].a(J.je(this.ga3(),b))},\ngA(a){return A.v(this).z[1].a(J.bP(this.ga3()))},\nS(a,b){return J.nr(this.ga3(),b)},\nl(a){return J.bp(this.ga3())}}\nA.du.prototype={\np(){return this.a.p()},\ngu(a){var s=this.a\nreturn this.$ti.z[1].a(s.gu(s))},\n$iL:1}\nA.cb.prototype={\nga3(){return this.a}}\nA.ep.prototype={$ik:1}\nA.ek.prototype={\ni(a,b){return this.$ti.z[1].a(J.ab(this.a,b))},\nk(a,b,c){var s=this.$ti\nJ.nq(this.a,b,s.c.a(s.z[1].a(c)))},\nT(a,b,c,d,e){var s=this.$ti\nJ.r1(this.a,b,c,A.fe(s.h(\"e<2>\").a(d),s.z[1],s.c),e)},\na6(a,b,c,d){return this.T(a,b,c,d,0)},\n$ik:1,\n$im:1}\nA.ba.prototype={\nbz(a,b){return new A.ba(this.a,this.$ti.h(\"@<1>\").q(b).h(\"ba<1,2>\"))},\nga3(){return this.a}}\nA.dv.prototype={\nF(a,b){return J.qT(this.a,b)},\ni(a,b){return this.$ti.h(\"4?\").a(J.ab(this.a,b))},\nG(a,b){return this.$ti.h(\"4?\").a(J.r0(this.a,b))},\nD(a,b){J.bo(this.a,new A.ju(this,this.$ti.h(\"~(3,4)\").a(b)))},\ngK(a){var s=this.$ti\nreturn A.fe(J.ou(this.a),s.c,s.z[2])},\ngU(a){var s=this.$ti\nreturn A.fe(J.qX(this.a),s.z[1],s.z[3])},\ngj(a){return J.Y(this.a)},\ngC(a){return J.dr(this.a)},\ngP(a){return J.f3(this.a)},\ngaH(a){return J.ot(this.a).aj(0,new A.jt(this),this.$ti.h(\"a4<3,4>\"))}}\nA.ju.prototype={\n$2(a,b){var s=this.a.$ti\ns.c.a(a)\ns.z[1].a(b)\nthis.b.$2(s.z[2].a(a),s.z[3].a(b))},\n$S(){return this.a.$ti.h(\"~(1,2)\")}}\nA.jt.prototype={\n$1(a){var s,r=this.a.$ti\nr.h(\"a4<1,2>\").a(a)\ns=r.z[3]\nreturn new A.a4(r.z[2].a(a.a),s.a(a.b),r.h(\"@<3>\").q(s).h(\"a4<1,2>\"))},\n$S(){return this.a.$ti.h(\"a4<3,4>(a4<1,2>)\")}}\nA.cQ.prototype={\nl(a){return\"LateInitializationError: \"+this.a}}\nA.fh.prototype={\ngj(a){return this.a.length},\ni(a,b){return B.a.B(this.a,b)}}\nA.ni.prototype={\n$0(){return A.oH(null,t.P)},\n$S:10}\nA.kn.prototype={}\nA.k.prototype={}\nA.a3.prototype={\ngE(a){var s=this\nreturn new A.aP(s,s.gj(s),A.v(s).h(\"aP<a3.E>\"))},\ngC(a){return this.gj(this)===0},\ngA(a){if(this.gj(this)===0)throw A.b(A.bt())\nreturn this.v(0,0)},\nS(a,b){var s,r=this,q=r.gj(r)\nfor(s=0;s<q;++s){if(J.a7(r.v(0,s),b))return!0\nif(q!==r.gj(r))throw A.b(A.ap(r))}return!1},\nau(a,b){var s,r,q,p=this,o=p.gj(p)\nif(b.length!==0){if(o===0)return\"\"\ns=A.q(p.v(0,0))\nif(o!==p.gj(p))throw A.b(A.ap(p))\nfor(r=s,q=1;q<o;++q){r=r+b+A.q(p.v(0,q))\nif(o!==p.gj(p))throw A.b(A.ap(p))}return r.charCodeAt(0)==0?r:r}else{for(q=0,r=\"\";q<o;++q){r+=A.q(p.v(0,q))\nif(o!==p.gj(p))throw A.b(A.ap(p))}return r.charCodeAt(0)==0?r:r}},\nh7(a){return this.au(a,\"\")},\naj(a,b,c){var s=A.v(this)\nreturn new A.af(this,s.q(c).h(\"1(a3.E)\").a(b),s.h(\"@<a3.E>\").q(c).h(\"af<1,2>\"))},\na2(a,b){return A.eb(this,b,null,A.v(this).h(\"a3.E\"))}}\nA.cn.prototype={\ney(a,b,c,d){var s,r=this.b\nA.aT(r,\"start\")\ns=this.c\nif(s!=null){A.aT(s,\"end\")\nif(r>s)throw A.b(A.a1(r,0,s,\"start\",null))}},\ngeW(){var s=J.Y(this.a),r=this.c\nif(r==null||r>s)return s\nreturn r},\ngfq(){var s=J.Y(this.a),r=this.b\nif(r>s)return s\nreturn r},\ngj(a){var s,r=J.Y(this.a),q=this.b\nif(q>=r)return 0\ns=this.c\nif(s==null||s>=r)return r-q\nif(typeof s!==\"number\")return s.aX()\nreturn s-q},\nv(a,b){var s=this,r=s.gfq()+b\nif(b<0||r>=s.geW())throw A.b(A.V(b,s.gj(s),s,null,\"index\"))\nreturn J.je(s.a,r)},\na2(a,b){var s,r,q=this\nA.aT(b,\"count\")\ns=q.b+b\nr=q.c\nif(r!=null&&s>=r)return new A.cf(q.$ti.h(\"cf<1>\"))\nreturn A.eb(q.a,s,r,q.$ti.c)},\nbQ(a,b){var s,r,q,p=this,o=p.b,n=p.a,m=J.T(n),l=m.gj(n),k=p.c\nif(k!=null&&k<l)l=k\ns=l-o\nif(s<=0){n=J.nx(0,p.$ti.c)\nreturn n}r=A.jX(s,m.v(n,o),!1,p.$ti.c)\nfor(q=1;q<s;++q){B.b.k(r,q,m.v(n,o+q))\nif(m.gj(n)<l)throw A.b(A.ap(p))}return r}}\nA.aP.prototype={\ngu(a){var s=this.d\nreturn s==null?this.$ti.c.a(s):s},\np(){var s,r=this,q=r.a,p=J.T(q),o=p.gj(q)\nif(r.b!==o)throw A.b(A.ap(q))\ns=r.c\nif(s>=o){r.saZ(null)\nreturn!1}r.saZ(p.v(q,s));++r.c\nreturn!0},\nsaZ(a){this.d=this.$ti.h(\"1?\").a(a)},\n$iL:1}\nA.bw.prototype={\ngE(a){var s=A.v(this)\nreturn new A.dQ(J.an(this.a),this.b,s.h(\"@<1>\").q(s.z[1]).h(\"dQ<1,2>\"))},\ngj(a){return J.Y(this.a)},\ngC(a){return J.dr(this.a)},\ngA(a){return this.b.$1(J.bP(this.a))},\nv(a,b){return this.b.$1(J.je(this.a,b))}}\nA.ce.prototype={$ik:1}\nA.dQ.prototype={\np(){var s=this,r=s.b\nif(r.p()){s.saZ(s.c.$1(r.gu(r)))\nreturn!0}s.saZ(null)\nreturn!1},\ngu(a){var s=this.a\nreturn s==null?this.$ti.z[1].a(s):s},\nsaZ(a){this.a=this.$ti.h(\"2?\").a(a)}}\nA.af.prototype={\ngj(a){return J.Y(this.a)},\nv(a,b){return this.b.$1(J.je(this.a,b))}}\nA.lv.prototype={\ngE(a){return new A.cq(J.an(this.a),this.b,this.$ti.h(\"cq<1>\"))},\naj(a,b,c){var s=this.$ti\nreturn new A.bw(this,s.q(c).h(\"1(2)\").a(b),s.h(\"@<1>\").q(c).h(\"bw<1,2>\"))}}\nA.cq.prototype={\np(){var s,r\nfor(s=this.a,r=this.b;s.p();)if(A.aK(r.$1(s.gu(s))))return!0\nreturn!1},\ngu(a){var s=this.a\nreturn s.gu(s)}}\nA.bA.prototype={\na2(a,b){A.jg(b,\"count\",t.S)\nA.aT(b,\"count\")\nreturn new A.bA(this.a,this.b+b,A.v(this).h(\"bA<1>\"))},\ngE(a){return new A.e2(J.an(this.a),this.b,A.v(this).h(\"e2<1>\"))}}\nA.cG.prototype={\ngj(a){var s=J.Y(this.a)-this.b\nif(s>=0)return s\nreturn 0},\na2(a,b){A.jg(b,\"count\",t.S)\nA.aT(b,\"count\")\nreturn new A.cG(this.a,this.b+b,this.$ti)},\n$ik:1}\nA.e2.prototype={\np(){var s,r\nfor(s=this.a,r=0;r<this.b;++r)s.p()\nthis.b=0\nreturn s.p()},\ngu(a){var s=this.a\nreturn s.gu(s)}}\nA.cf.prototype={\ngE(a){return B.J},\ngC(a){return!0},\ngj(a){return 0},\ngA(a){throw A.b(A.bt())},\nv(a,b){throw A.b(A.a1(b,0,0,\"index\",null))},\nS(a,b){return!1},\naj(a,b,c){this.$ti.q(c).h(\"1(2)\").a(b)\nreturn new A.cf(c.h(\"cf<0>\"))},\na2(a,b){A.aT(b,\"count\")\nreturn this},\nbQ(a,b){var s=J.nx(0,this.$ti.c)\nreturn s}}\nA.dB.prototype={\np(){return!1},\ngu(a){throw A.b(A.bt())},\n$iL:1}\nA.ef.prototype={\ngE(a){return new A.eg(J.an(this.a),this.$ti.h(\"eg<1>\"))}}\nA.eg.prototype={\np(){var s,r\nfor(s=this.a,r=this.$ti.c;s.p();)if(r.b(s.gu(s)))return!0\nreturn!1},\ngu(a){var s=this.a\nreturn this.$ti.c.a(s.gu(s))},\n$iL:1}\nA.ar.prototype={}\nA.c0.prototype={\nk(a,b,c){A.v(this).h(\"c0.E\").a(c)\nthrow A.b(A.x(\"Cannot modify an unmodifiable list\"))},\nT(a,b,c,d,e){A.v(this).h(\"e<c0.E>\").a(d)\nthrow A.b(A.x(\"Cannot modify an unmodifiable list\"))},\na6(a,b,c,d){return this.T(a,b,c,d,0)}}\nA.d2.prototype={}\nA.ic.prototype={\ngj(a){return J.Y(this.a)},\nv(a,b){var s=J.Y(this.a)\nif(0>b||b>=s)A.J(A.V(b,s,this,null,\"index\"))\nreturn b}}\nA.dO.prototype={\ni(a,b){return this.F(0,b)?J.ab(this.a,A.j(b)):null},\ngj(a){return J.Y(this.a)},\ngU(a){return A.eb(this.a,0,null,this.$ti.c)},\ngK(a){return new A.ic(this.a)},\ngC(a){return J.dr(this.a)},\ngP(a){return J.f3(this.a)},\nF(a,b){return A.dl(b)&&b>=0&&b<J.Y(this.a)},\nD(a,b){var s,r,q,p\nthis.$ti.h(\"~(c,1)\").a(b)\ns=this.a\nr=J.T(s)\nq=r.gj(s)\nfor(p=0;p<q;++p){b.$2(p,r.i(s,p))\nif(q!==r.gj(s))throw A.b(A.ap(s))}}}\nA.e0.prototype={\ngj(a){return J.Y(this.a)},\nv(a,b){var s=this.a,r=J.T(s)\nreturn r.v(s,r.gj(s)-1-b)}}\nA.d1.prototype={\ngI(a){var s=this._hashCode\nif(s!=null)return s\ns=664597*J.ax(this.a)&536870911\nthis._hashCode=s\nreturn s},\nl(a){return'Symbol(\"'+A.q(this.a)+'\")'},\nW(a,b){if(b==null)return!1\nreturn b instanceof A.d1&&this.a==b.a},\n$ico:1}\nA.eU.prototype={}\nA.dx.prototype={}\nA.dw.prototype={\ngC(a){return this.gj(this)===0},\ngP(a){return this.gj(this)!==0},\nl(a){return A.jZ(this)},\nG(a,b){A.rd()},\ngaH(a){return this.fR(0,A.v(this).h(\"a4<1,2>\"))},\nfR(a,b){var s=this\nreturn A.uO(function(){var r=a\nvar q=0,p=1,o,n,m,l,k,j\nreturn function $async$gaH(c,d){if(c===1){o=d\nq=p}while(true)switch(q){case 0:n=s.gK(s),n=n.gE(n),m=A.v(s),l=m.z[1],m=m.h(\"@<1>\").q(l).h(\"a4<1,2>\")\ncase 2:if(!n.p()){q=3\nbreak}k=n.gu(n)\nj=s.i(0,k)\nq=4\nreturn new A.a4(k,j==null?l.a(j):j,m)\ncase 4:q=2\nbreak\ncase 3:return A.tI()\ncase 1:return A.tJ(o)}}},b)},\n$iI:1}\nA.cc.prototype={\ngj(a){return this.a},\nF(a,b){if(typeof b!=\"string\")return!1\nif(\"__proto__\"===b)return!1\nreturn this.b.hasOwnProperty(b)},\ni(a,b){if(!this.F(0,b))return null\nreturn this.b[A.S(b)]},\nD(a,b){var s,r,q,p,o,n=this.$ti\nn.h(\"~(1,2)\").a(b)\ns=this.c\nfor(r=s.length,q=this.b,n=n.z[1],p=0;p<r;++p){o=A.S(s[p])\nb.$2(o,n.a(q[o]))}},\ngK(a){return new A.em(this,this.$ti.h(\"em<1>\"))},\ngU(a){var s=this.$ti\nreturn A.nD(this.c,new A.jv(this),s.c,s.z[1])}}\nA.jv.prototype={\n$1(a){var s=this.a,r=s.$ti\nreturn r.z[1].a(s.b[A.S(r.c.a(a))])},\n$S(){return this.a.$ti.h(\"2(1)\")}}\nA.em.prototype={\ngE(a){var s=this.a.c\nreturn new J.ca(s,s.length,A.av(s).h(\"ca<1>\"))},\ngj(a){return this.a.c.length}}\nA.fI.prototype={\nge0(){var s=this.a\nreturn s},\nge5(){var s,r,q,p,o=this\nif(o.c===1)return B.m\ns=o.d\nr=s.length-o.e.length-o.f\nif(r===0)return B.m\nq=[]\nfor(p=0;p<r;++p){if(!(p<s.length))return A.d(s,p)\nq.push(s[p])}return J.oK(q)},\nge1(){var s,r,q,p,o,n,m,l,k=this\nif(k.c!==0)return B.D\ns=k.e\nr=s.length\nq=k.d\np=q.length-r-k.f\nif(r===0)return B.D\no=new A.as(t.bX)\nfor(n=0;n<r;++n){if(!(n<s.length))return A.d(s,n)\nm=s[n]\nl=p+n\nif(!(l>=0&&l<q.length))return A.d(q,l)\no.k(0,new A.d1(m),q[l])}return new A.dx(o,t.i9)},\n$ioI:1}\nA.ka.prototype={\n$2(a,b){var s\nA.S(a)\ns=this.a\ns.b=s.b+\"$\"+a\nB.b.m(this.b,a)\nB.b.m(this.c,b);++s.a},\n$S:1}\nA.ld.prototype={\na4(a){var s,r,q=this,p=new RegExp(q.a).exec(a)\nif(p==null)return null\ns=Object.create(null)\nr=q.b\nif(r!==-1)s.arguments=p[r+1]\nr=q.c\nif(r!==-1)s.argumentsExpr=p[r+1]\nr=q.d\nif(r!==-1)s.expr=p[r+1]\nr=q.e\nif(r!==-1)s.method=p[r+1]\nr=q.f\nif(r!==-1)s.receiver=p[r+1]\nreturn s}}\nA.dW.prototype={\nl(a){var s=this.b\nif(s==null)return\"NoSuchMethodError: \"+this.a\nreturn\"NoSuchMethodError: method not found: '\"+s+\"' on null\"}}\nA.fK.prototype={\nl(a){var s,r=this,q=\"NoSuchMethodError: method not found: '\",p=r.b\nif(p==null)return\"NoSuchMethodError: \"+r.a\ns=r.c\nif(s==null)return q+p+\"' (\"+r.a+\")\"\nreturn q+p+\"' on '\"+s+\"' (\"+r.a+\")\"}}\nA.hx.prototype={\nl(a){var s=this.a\nreturn s.length===0?\"Error\":\"Error: \"+s}}\nA.h2.prototype={\nl(a){return\"Throw of null ('\"+(this.a===null?\"null\":\"undefined\")+\"' from JavaScript)\"},\n$iac:1}\nA.dC.prototype={}\nA.eH.prototype={\nl(a){var s,r=this.b\nif(r!=null)return r\nr=this.a\ns=r!==null&&typeof r===\"object\"?r.stack:null\nreturn this.b=s==null?\"\":s},\n$iaG:1}\nA.bS.prototype={\nl(a){var s=this.constructor,r=s==null?null:s.name\nreturn\"Closure '\"+A.qo(r==null?\"unknown\":r)+\"'\"},\n$ich:1,\nghp(){return this},\n$C:\"$1\",\n$R:1,\n$D:null}\nA.ff.prototype={$C:\"$0\",$R:0}\nA.fg.prototype={$C:\"$2\",$R:2}\nA.ho.prototype={}\nA.hk.prototype={\nl(a){var s=this.$static_name\nif(s==null)return\"Closure of unknown static method\"\nreturn\"Closure '\"+A.qo(s)+\"'\"}}\nA.cD.prototype={\nW(a,b){if(b==null)return!1\nif(this===b)return!0\nif(!(b instanceof A.cD))return!1\nreturn this.$_target===b.$_target&&this.a===b.a},\ngI(a){return(A.j7(this.a)^A.dZ(this.$_target))>>>0},\nl(a){return\"Closure '\"+this.$_name+\"' of \"+(\"Instance of '\"+A.kb(this.a)+\"'\")}}\nA.hc.prototype={\nl(a){return\"RuntimeError: \"+this.a}}\nA.hO.prototype={\nl(a){return\"Assertion failed: \"+A.cg(this.a)}}\nA.mu.prototype={}\nA.as.prototype={\ngj(a){return this.a},\ngC(a){return this.a===0},\ngP(a){return this.a!==0},\ngK(a){return new A.bv(this,A.v(this).h(\"bv<1>\"))},\ngU(a){var s=A.v(this)\nreturn A.nD(new A.bv(this,s.h(\"bv<1>\")),new A.jT(this),s.c,s.z[1])},\nF(a,b){var s,r\nif(typeof b==\"string\"){s=this.b\nif(s==null)return!1\nreturn s[b]!=null}else if(typeof b==\"number\"&&(b&0x3fffffff)===b){r=this.c\nif(r==null)return!1\nreturn r[b]!=null}else return this.dV(b)},\ndV(a){var s=this.d\nif(s==null)return!1\nreturn this.aN(s[this.aM(a)],a)>=0},\nb4(a,b){J.bo(A.v(this).h(\"I<1,2>\").a(b),new A.jS(this))},\ni(a,b){var s,r,q,p,o=null\nif(typeof b==\"string\"){s=this.b\nif(s==null)return o\nr=s[b]\nq=r==null?o:r.b\nreturn q}else if(typeof b==\"number\"&&(b&0x3fffffff)===b){p=this.c\nif(p==null)return o\nr=p[b]\nq=r==null?o:r.b\nreturn q}else return this.dW(b)},\ndW(a){var s,r,q=this.d\nif(q==null)return null\ns=q[this.aM(a)]\nr=this.aN(s,a)\nif(r<0)return null\nreturn s[r].b},\nk(a,b,c){var s,r,q=this,p=A.v(q)\np.c.a(b)\np.z[1].a(c)\nif(typeof b==\"string\"){s=q.b\nq.d1(s==null?q.b=q.cg():s,b,c)}else if(typeof b==\"number\"&&(b&0x3fffffff)===b){r=q.c\nq.d1(r==null?q.c=q.cg():r,b,c)}else q.dY(b,c)},\ndY(a,b){var s,r,q,p,o=this,n=A.v(o)\nn.c.a(a)\nn.z[1].a(b)\ns=o.d\nif(s==null)s=o.d=o.cg()\nr=o.aM(a)\nq=s[r]\nif(q==null)s[r]=[o.ci(a,b)]\nelse{p=o.aN(q,a)\nif(p>=0)q[p].b=b\nelse q.push(o.ci(a,b))}},\ne8(a,b,c){var s,r,q=this,p=A.v(q)\np.c.a(b)\np.h(\"2()\").a(c)\nif(q.F(0,b)){s=q.i(0,b)\nreturn s==null?p.z[1].a(s):s}r=c.$0()\nq.k(0,b,r)\nreturn r},\nG(a,b){var s=this\nif(typeof b==\"string\")return s.dv(s.b,b)\nelse if(typeof b==\"number\"&&(b&0x3fffffff)===b)return s.dv(s.c,b)\nelse return s.dX(b)},\ndX(a){var s,r,q,p,o=this,n=o.d\nif(n==null)return null\ns=o.aM(a)\nr=n[s]\nq=o.aN(r,a)\nif(q<0)return null\np=r.splice(q,1)[0]\no.dH(p)\nif(r.length===0)delete n[s]\nreturn p.b},\nD(a,b){var s,r,q=this\nA.v(q).h(\"~(1,2)\").a(b)\ns=q.e\nr=q.r\nfor(;s!=null;){b.$2(s.a,s.b)\nif(r!==q.r)throw A.b(A.ap(q))\ns=s.c}},\nd1(a,b,c){var s,r=A.v(this)\nr.c.a(b)\nr.z[1].a(c)\ns=a[b]\nif(s==null)a[b]=this.ci(b,c)\nelse s.b=c},\ndv(a,b){var s\nif(a==null)return null\ns=a[b]\nif(s==null)return null\nthis.dH(s)\ndelete a[b]\nreturn s.b},\ndl(){this.r=this.r+1&1073741823},\nci(a,b){var s=this,r=A.v(s),q=new A.jV(r.c.a(a),r.z[1].a(b))\nif(s.e==null)s.e=s.f=q\nelse{r=s.f\nr.toString\nq.d=r\ns.f=r.c=q}++s.a\ns.dl()\nreturn q},\ndH(a){var s=this,r=a.d,q=a.c\nif(r==null)s.e=q\nelse r.c=q\nif(q==null)s.f=r\nelse q.d=r;--s.a\ns.dl()},\naM(a){return J.ax(a)&0x3fffffff},\naN(a,b){var s,r\nif(a==null)return-1\ns=a.length\nfor(r=0;r<s;++r)if(J.a7(a[r].a,b))return r\nreturn-1},\nl(a){return A.jZ(this)},\ncg(){var s=Object.create(null)\ns[\"<non-identifier-key>\"]=s\ndelete s[\"<non-identifier-key>\"]\nreturn s},\n$ijU:1}\nA.jT.prototype={\n$1(a){var s=this.a,r=A.v(s)\ns=s.i(0,r.c.a(a))\nreturn s==null?r.z[1].a(s):s},\n$S(){return A.v(this.a).h(\"2(1)\")}}\nA.jS.prototype={\n$2(a,b){var s=this.a,r=A.v(s)\ns.k(0,r.c.a(a),r.z[1].a(b))},\n$S(){return A.v(this.a).h(\"~(1,2)\")}}\nA.jV.prototype={}\nA.bv.prototype={\ngj(a){return this.a.a},\ngC(a){return this.a.a===0},\ngE(a){var s=this.a,r=new A.dM(s,s.r,this.$ti.h(\"dM<1>\"))\nr.c=s.e\nreturn r},\nS(a,b){return this.a.F(0,b)}}\nA.dM.prototype={\ngu(a){return this.d},\np(){var s,r=this,q=r.a\nif(r.b!==q.r)throw A.b(A.ap(q))\ns=r.c\nif(s==null){r.sd0(null)\nreturn!1}else{r.sd0(s.a)\nr.c=s.c\nreturn!0}},\nsd0(a){this.d=this.$ti.h(\"1?\").a(a)},\n$iL:1}\nA.na.prototype={\n$1(a){return this.a(a)},\n$S:77}\nA.nb.prototype={\n$2(a,b){return this.a(a,b)},\n$S:76}\nA.nc.prototype={\n$1(a){return this.a(A.S(a))},\n$S:74}\nA.dL.prototype={\nl(a){return\"RegExp/\"+this.a+\"/\"+this.b.flags},\ngf6(){var s=this,r=s.c\nif(r!=null)return r\nr=s.b\nreturn s.c=A.oM(s.a,r.multiline,!r.ignoreCase,r.unicode,r.dotAll,!0)},\nfS(a){var s=this.b.exec(a)\nif(s==null)return null\nreturn new A.ey(s)},\ncp(a,b,c){var s=b.length\nif(c>s)throw A.b(A.a1(c,0,s,null,null))\nreturn new A.hM(this,b,c)},\ndL(a,b){return this.cp(a,b,0)},\neY(a,b){var s,r=this.gf6()\nif(r==null)r=t.K.a(r)\nr.lastIndex=b\ns=r.exec(a)\nif(s==null)return null\nreturn new A.ey(s)},\n$ik9:1,\n$ioW:1}\nA.ey.prototype={\ngfQ(a){var s=this.b\nreturn s.index+s[0].length},\n$icT:1,\n$ie_:1}\nA.hM.prototype={\ngE(a){return new A.hN(this.a,this.b,this.c)}}\nA.hN.prototype={\ngu(a){var s=this.d\nreturn s==null?t.lu.a(s):s},\np(){var s,r,q,p,o,n=this,m=n.b\nif(m==null)return!1\ns=n.c\nr=m.length\nif(s<=r){q=n.a\np=q.eY(m,s)\nif(p!=null){n.d=p\no=p.gfQ(p)\nif(p.b.index===o){if(q.b.unicode){s=n.c\nq=s+1\nif(q<r){s=B.a.B(m,s)\nif(s>=55296&&s<=56319){s=B.a.B(m,q)\ns=s>=56320&&s<=57343}else s=!1}else s=!1}else s=!1\no=(s?o+1:o)+1}n.c=o\nreturn!0}}n.b=n.d=null\nreturn!1},\n$iL:1}\nA.ea.prototype={$icT:1}\nA.iE.prototype={\ngE(a){return new A.iF(this.a,this.b,this.c)},\ngA(a){var s=this.b,r=this.a.indexOf(s,this.c)\nif(r>=0)return new A.ea(r,s)\nthrow A.b(A.bt())}}\nA.iF.prototype={\np(){var s,r,q=this,p=q.c,o=q.b,n=o.length,m=q.a,l=m.length\nif(p+n>l){q.d=null\nreturn!1}s=m.indexOf(o,p)\nif(s<0){q.c=l+1\nq.d=null\nreturn!1}r=s+n\nq.d=new A.ea(s,o)\nq.c=r===q.c?r+1:r\nreturn!0},\ngu(a){var s=this.d\ns.toString\nreturn s},\n$iL:1}\nA.lI.prototype={\nbq(){var s=this.b\nif(s===this)throw A.b(new A.cQ(\"Local '\"+this.a+\"' has not been initialized.\"))\nreturn s},\na_(){var s=this.b\nif(s===this)throw A.b(A.oN(this.a))\nreturn s}}\nA.cW.prototype={\ngN(a){return B.a4},\n$icW:1,\n$int:1}\nA.a5.prototype={\nf5(a,b,c,d){var s=A.a1(b,0,c,d,null)\nthrow A.b(s)},\nd5(a,b,c,d){if(b>>>0!==b||b>c)this.f5(a,b,c,d)},\n$ia5:1}\nA.dR.prototype={\ngN(a){return B.a5},\nf0(a,b,c){return a.getUint32(b,c)},\nfm(a,b,c,d){return a.setUint32(b,c,d)},\n$ioC:1}\nA.ag.prototype={\ngj(a){return a.length},\ndz(a,b,c,d,e){var s,r,q=a.length\nthis.d5(a,b,q,\"start\")\nthis.d5(a,c,q,\"end\")\nif(b>c)throw A.b(A.a1(b,0,c,null,null))\ns=c-b\nif(e<0)throw A.b(A.ao(e,null))\nr=d.length\nif(r-e<s)throw A.b(A.K(\"Not enough elements\"))\nif(e!==0||r!==s)d=d.subarray(e,e+s)\na.set(d,b)},\n$iF:1}\nA.bX.prototype={\ni(a,b){A.bK(b,a,a.length)\nreturn a[b]},\nk(a,b,c){A.pI(c)\nA.bK(b,a,a.length)\na[b]=c},\nT(a,b,c,d,e){t.id.a(d)\nif(t.dQ.b(d)){this.dz(a,b,c,d,e)\nreturn}this.cZ(a,b,c,d,e)},\na6(a,b,c,d){return this.T(a,b,c,d,0)},\n$ik:1,\n$ie:1,\n$im:1}\nA.aQ.prototype={\nk(a,b,c){A.j(c)\nA.bK(b,a,a.length)\na[b]=c},\nT(a,b,c,d,e){t.fm.a(d)\nif(t.aj.b(d)){this.dz(a,b,c,d,e)\nreturn}this.cZ(a,b,c,d,e)},\na6(a,b,c,d){return this.T(a,b,c,d,0)},\n$ik:1,\n$ie:1,\n$im:1}\nA.fT.prototype={\ngN(a){return B.a6}}\nA.fU.prototype={\ngN(a){return B.a7}}\nA.fV.prototype={\ngN(a){return B.a8},\ni(a,b){A.bK(b,a,a.length)\nreturn a[b]}}\nA.fW.prototype={\ngN(a){return B.a9},\ni(a,b){A.bK(b,a,a.length)\nreturn a[b]}}\nA.fX.prototype={\ngN(a){return B.aa},\ni(a,b){A.bK(b,a,a.length)\nreturn a[b]}}\nA.fY.prototype={\ngN(a){return B.ae},\ni(a,b){A.bK(b,a,a.length)\nreturn a[b]},\n$inP:1}\nA.fZ.prototype={\ngN(a){return B.af},\ni(a,b){A.bK(b,a,a.length)\nreturn a[b]}}\nA.dT.prototype={\ngN(a){return B.ag},\ngj(a){return a.length},\ni(a,b){A.bK(b,a,a.length)\nreturn a[b]}}\nA.cl.prototype={\ngN(a){return B.ah},\ngj(a){return a.length},\ni(a,b){A.bK(b,a,a.length)\nreturn a[b]},\nel(a,b,c){return new Uint8Array(a.subarray(b,A.uq(b,c,a.length)))},\n$icl:1,\n$iaX:1}\nA.eA.prototype={}\nA.eB.prototype={}\nA.eC.prototype={}\nA.eD.prototype={}\nA.b2.prototype={\nh(a){return A.mF(v.typeUniverse,this,a)},\nq(a){return A.u1(v.typeUniverse,this,a)}}\nA.i4.prototype={}\nA.iR.prototype={\nl(a){return A.aJ(this.a,null)}}\nA.i_.prototype={\nl(a){return this.a}}\nA.eN.prototype={$ibn:1}\nA.lz.prototype={\n$1(a){var s=this.a,r=s.a\ns.a=null\nr.$0()},\n$S:16}\nA.ly.prototype={\n$1(a){var s,r\nthis.a.a=t.M.a(a)\ns=this.b\nr=this.c\ns.firstChild?s.removeChild(r):s.appendChild(r)},\n$S:72}\nA.lA.prototype={\n$0(){this.a.$0()},\n$S:6}\nA.lB.prototype={\n$0(){this.a.$0()},\n$S:6}\nA.mD.prototype={\neC(a,b){if(self.setTimeout!=null)this.b=self.setTimeout(A.c8(new A.mE(this,b),0),a)\nelse throw A.b(A.x(\"`setTimeout()` not found.\"))}}\nA.mE.prototype={\n$0(){var s=this.a\ns.b=null\ns.c=1\nthis.b.$0()},\n$S:0}\nA.eh.prototype={\na0(a,b){var s,r=this,q=r.$ti\nq.h(\"1/?\").a(b)\nif(b==null)q.c.a(b)\nif(!r.b)r.a.bk(b)\nelse{s=r.a\nif(q.h(\"H<1>\").b(b))s.d4(b)\nelse s.b0(q.c.a(b))}},\nbA(a,b){var s=this.a\nif(this.b)s.V(a,b)\nelse s.aC(a,b)},\n$ifi:1}\nA.mM.prototype={\n$1(a){return this.a.$2(0,a)},\n$S:4}\nA.mN.prototype={\n$2(a,b){this.a.$2(1,new A.dC(a,t.l.a(b)))},\n$S:68}\nA.n2.prototype={\n$2(a,b){this.a(A.j(a),b)},\n$S:67}\nA.db.prototype={\nl(a){return\"IterationMarker(\"+this.b+\", \"+A.q(this.a)+\")\"}}\nA.de.prototype={\ngu(a){var s,r=this.c\nif(r==null){s=this.b\nreturn s==null?this.$ti.c.a(s):s}return r.gu(r)},\np(){var s,r,q,p,o,n,m=this\nfor(s=m.$ti.h(\"L<1>\");!0;){r=m.c\nif(r!=null)if(r.p())return!0\nelse m.sdm(null)\nq=function(a,b,c){var l,k=b\nwhile(true)try{return a(k,l)}catch(j){l=j\nk=c}}(m.a,0,1)\nif(q instanceof A.db){p=q.b\nif(p===2){o=m.d\nif(o==null||o.length===0){m.sd2(null)\nreturn!1}if(0>=o.length)return A.d(o,-1)\nm.a=o.pop()\ncontinue}else{r=q.a\nif(p===3)throw r\nelse{n=s.a(J.an(r))\nif(n instanceof A.de){r=m.d\nif(r==null)r=m.d=[]\nB.b.m(r,m.a)\nm.a=n.a\ncontinue}else{m.sdm(n)\ncontinue}}}}else{m.sd2(q)\nreturn!0}}return!1},\nsd2(a){this.b=this.$ti.h(\"1?\").a(a)},\nsdm(a){this.c=this.$ti.h(\"L<1>?\").a(a)},\n$iL:1}\nA.eK.prototype={\ngE(a){return new A.de(this.a(),this.$ti.h(\"de<1>\"))}}\nA.dt.prototype={\nl(a){return A.q(this.a)},\n$iQ:1,\ngaW(){return this.b}}\nA.jI.prototype={\n$0(){var s,r,q\ntry{this.a.b_(this.b.$0())}catch(q){s=A.M(q)\nr=A.a_(q)\nA.pK(this.a,s,r)}},\n$S:0}\nA.jK.prototype={\n$2(a,b){var s,r,q=this\nt.K.a(a)\nt.l.a(b)\ns=q.a\nr=--s.b\nif(s.a!=null){s.a=null\nif(s.b===0||q.c)q.d.V(a,b)\nelse{q.e.b=a\nq.f.b=b}}else if(r===0&&!q.c)q.d.V(q.e.bq(),q.f.bq())},\n$S:21}\nA.jJ.prototype={\n$1(a){var s,r,q=this,p=q.w\np.a(a)\nr=q.a;--r.b\ns=r.a\nif(s!=null){J.nq(s,q.b,a)\nif(r.b===0)q.c.b0(A.jY(s,!0,p))}else if(r.b===0&&!q.e)q.c.V(q.f.bq(),q.r.bq())},\n$S(){return this.w.h(\"R(0)\")}}\nA.cs.prototype={\nbA(a,b){var s,r=t.K\nr.a(a)\nt.fw.a(b)\nA.c7(a,\"error\",r)\nif((this.a.a&30)!==0)throw A.b(A.K(\"Future already completed\"))\ns=$.D.b7(a,b)\nif(s!=null){a=s.a\nb=s.b}else if(b==null)b=A.f8(a)\nthis.V(a,b)},\nag(a){return this.bA(a,null)},\n$ifi:1}\nA.cr.prototype={\na0(a,b){var s,r=this.$ti\nr.h(\"1/?\").a(b)\ns=this.a\nif((s.a&30)!==0)throw A.b(A.K(\"Future already completed\"))\ns.bk(r.h(\"1/\").a(b))},\nV(a,b){this.a.aC(a,b)}}\nA.aa.prototype={\na0(a,b){var s,r=this.$ti\nr.h(\"1/?\").a(b)\ns=this.a\nif((s.a&30)!==0)throw A.b(A.K(\"Future already completed\"))\ns.b_(r.h(\"1/\").a(b))},\nfH(a){return this.a0(a,null)},\nV(a,b){this.a.V(a,b)}}\nA.bH.prototype={\nha(a){if((this.c&15)!==6)return!0\nreturn this.b.b.cQ(t.iW.a(this.d),a.a,t.y,t.K)},\nfY(a){var s,r=this,q=r.e,p=null,o=t.z,n=t.K,m=a.a,l=r.b.b\nif(t.Q.b(q))p=l.hl(q,m,a.b,o,n,t.l)\nelse p=l.cQ(t.v.a(q),m,o,n)\ntry{o=r.$ti.h(\"2/\").a(p)\nreturn o}catch(s){if(t.do.b(A.M(s))){if((r.c&1)!==0)throw A.b(A.ao(\"The error handler of Future.then must return a value of the returned future's type\",\"onError\"))\nthrow A.b(A.ao(\"The error handler of Future.catchError must return a value of the future's type\",\"onError\"))}else throw s}}}\nA.E.prototype={\nbP(a,b,c){var s,r,q,p=this.$ti\np.q(c).h(\"1/(2)\").a(a)\ns=$.D\nif(s===B.d){if(b!=null&&!t.Q.b(b)&&!t.v.b(b))throw A.b(A.bq(b,\"onError\",u.c))}else{a=s.bN(a,c.h(\"0/\"),p.c)\nif(b!=null)b=A.uS(b,s)}r=new A.E($.D,c.h(\"E<0>\"))\nq=b==null?1:3\nthis.bj(new A.bH(r,q,a,b,p.h(\"@<1>\").q(c).h(\"bH<1,2>\")))\nreturn r},\neb(a,b){return this.bP(a,null,b)},\ndE(a,b,c){var s,r=this.$ti\nr.q(c).h(\"1/(2)\").a(a)\ns=new A.E($.D,c.h(\"E<0>\"))\nthis.bj(new A.bH(s,3,a,b,r.h(\"@<1>\").q(c).h(\"bH<1,2>\")))\nreturn s},\naS(a){var s,r,q\nt.mY.a(a)\ns=this.$ti\nr=$.D\nq=new A.E(r,s)\nif(r!==B.d)a=r.cO(a,t.z)\nthis.bj(new A.bH(q,8,a,null,s.h(\"@<1>\").q(s.c).h(\"bH<1,2>\")))\nreturn q},\nfk(a){this.a=this.a&1|16\nthis.c=a},\nc2(a){this.a=a.a&30|this.a&1\nthis.c=a.c},\nbj(a){var s,r=this,q=r.a\nif(q<=3){a.a=t.e.a(r.c)\nr.c=a}else{if((q&4)!==0){s=t.g.a(r.c)\nif((s.a&24)===0){s.bj(a)\nreturn}r.c2(s)}r.b.aB(new A.lR(r,a))}},\ndt(a){var s,r,q,p,o,n,m=this,l={}\nl.a=a\nif(a==null)return\ns=m.a\nif(s<=3){r=t.e.a(m.c)\nm.c=a\nif(r!=null){q=a.a\nfor(p=a;q!=null;p=q,q=o)o=q.a\np.a=r}}else{if((s&4)!==0){n=t.g.a(m.c)\nif((n.a&24)===0){n.dt(a)\nreturn}m.c2(n)}l.a=m.bt(a)\nm.b.aB(new A.lZ(l,m))}},\nbr(){var s=t.e.a(this.c)\nthis.c=null\nreturn this.bt(s)},\nbt(a){var s,r,q\nfor(s=a,r=null;s!=null;r=s,s=q){q=s.a\ns.a=r}return r},\nd3(a){var s,r,q,p=this\np.a^=2\ntry{a.bP(new A.lV(p),new A.lW(p),t.P)}catch(q){s=A.M(q)\nr=A.a_(q)\nA.qm(new A.lX(p,s,r))}},\nb_(a){var s,r=this,q=r.$ti\nq.h(\"1/\").a(a)\nif(q.h(\"H<1>\").b(a))if(q.b(a))A.lU(a,r)\nelse r.d3(a)\nelse{s=r.br()\nq.c.a(a)\nr.a=8\nr.c=a\nA.da(r,s)}},\nb0(a){var s,r=this\nr.$ti.c.a(a)\ns=r.br()\nr.a=8\nr.c=a\nA.da(r,s)},\nV(a,b){var s\nt.K.a(a)\nt.l.a(b)\ns=this.br()\nthis.fk(A.jh(a,b))\nA.da(this,s)},\nbk(a){var s=this.$ti\ns.h(\"1/\").a(a)\nif(s.h(\"H<1>\").b(a)){this.d4(a)\nreturn}this.eJ(s.c.a(a))},\neJ(a){var s=this\ns.$ti.c.a(a)\ns.a^=2\ns.b.aB(new A.lT(s,a))},\nd4(a){var s=this,r=s.$ti\nr.h(\"H<1>\").a(a)\nif(r.b(a)){if((a.a&16)!==0){s.a^=2\ns.b.aB(new A.lY(s,a))}else A.lU(a,s)\nreturn}s.d3(a)},\naC(a,b){t.l.a(b)\nthis.a^=2\nthis.b.aB(new A.lS(this,a,b))},\n$iH:1}\nA.lR.prototype={\n$0(){A.da(this.a,this.b)},\n$S:0}\nA.lZ.prototype={\n$0(){A.da(this.b,this.a.a)},\n$S:0}\nA.lV.prototype={\n$1(a){var s,r,q,p=this.a\np.a^=2\ntry{p.b0(p.$ti.c.a(a))}catch(q){s=A.M(q)\nr=A.a_(q)\np.V(s,r)}},\n$S:16}\nA.lW.prototype={\n$2(a,b){this.a.V(t.K.a(a),t.l.a(b))},\n$S:53}\nA.lX.prototype={\n$0(){this.a.V(this.b,this.c)},\n$S:0}\nA.lT.prototype={\n$0(){this.a.b0(this.b)},\n$S:0}\nA.lY.prototype={\n$0(){A.lU(this.b,this.a)},\n$S:0}\nA.lS.prototype={\n$0(){this.a.V(this.b,this.c)},\n$S:0}\nA.m1.prototype={\n$0(){var s,r,q,p,o,n,m=this,l=null\ntry{q=m.a.a\nl=q.b.b.cP(t.mY.a(q.d),t.z)}catch(p){s=A.M(p)\nr=A.a_(p)\nq=m.c&&t.n.a(m.b.a.c).a===s\no=m.a\nif(q)o.c=t.n.a(m.b.a.c)\nelse o.c=A.jh(s,r)\no.b=!0\nreturn}if(l instanceof A.E&&(l.a&24)!==0){if((l.a&16)!==0){q=m.a\nq.c=t.n.a(l.c)\nq.b=!0}return}if(t.c.b(l)){n=m.b.a\nq=m.a\nq.c=l.eb(new A.m2(n),t.z)\nq.b=!1}},\n$S:0}\nA.m2.prototype={\n$1(a){return this.a},\n$S:47}\nA.m0.prototype={\n$0(){var s,r,q,p,o,n,m,l\ntry{q=this.a\np=q.a\no=p.$ti\nn=o.c\nm=n.a(this.b)\nq.c=p.b.b.cQ(o.h(\"2/(1)\").a(p.d),m,o.h(\"2/\"),n)}catch(l){s=A.M(l)\nr=A.a_(l)\nq=this.a\nq.c=A.jh(s,r)\nq.b=!0}},\n$S:0}\nA.m_.prototype={\n$0(){var s,r,q,p,o,n,m=this\ntry{s=t.n.a(m.a.a.c)\np=m.b\nif(p.a.ha(s)&&p.a.e!=null){p.c=p.a.fY(s)\np.b=!1}}catch(o){r=A.M(o)\nq=A.a_(o)\np=t.n.a(m.a.a.c)\nn=m.b\nif(p.a===r)n.c=p\nelse n.c=A.jh(r,q)\nn.b=!0}},\n$S:0}\nA.hP.prototype={}\nA.aV.prototype={\ngj(a){var s={},r=new A.E($.D,t.g_)\ns.a=0\nthis.cH(new A.l9(s,this),!0,new A.la(s,r),r.gda())\nreturn r},\ngA(a){var s=new A.E($.D,A.v(this).h(\"E<aV.T>\")),r=this.cH(null,!0,new A.l7(s),s.gda())\nr.e3(new A.l8(this,r,s))\nreturn s}}\nA.l9.prototype={\n$1(a){A.v(this.b).h(\"aV.T\").a(a);++this.a.a},\n$S(){return A.v(this.b).h(\"~(aV.T)\")}}\nA.la.prototype={\n$0(){this.b.b_(this.a.a)},\n$S:0}\nA.l7.prototype={\n$0(){var s,r,q,p\ntry{q=A.bt()\nthrow A.b(q)}catch(p){s=A.M(p)\nr=A.a_(p)\nA.pK(this.a,s,r)}},\n$S:0}\nA.l8.prototype={\n$1(a){A.un(this.b,this.c,A.v(this.a).h(\"aV.T\").a(a))},\n$S(){return A.v(this.a).h(\"~(aV.T)\")}}\nA.bm.prototype={}\nA.hm.prototype={}\nA.dd.prototype={\ngf9(){var s,r=this\nif((r.b&8)===0)return A.v(r).h(\"b4<1>?\").a(r.a)\ns=A.v(r)\nreturn s.h(\"b4<1>?\").a(s.h(\"eI<1>\").a(r.a).gcW())},\nc7(){var s,r,q=this\nif((q.b&8)===0){s=q.a\nif(s==null)s=q.a=new A.b4(A.v(q).h(\"b4<1>\"))\nreturn A.v(q).h(\"b4<1>\").a(s)}r=A.v(q)\ns=r.h(\"eI<1>\").a(q.a).gcW()\nreturn r.h(\"b4<1>\").a(s)},\ngcl(){var s=this.a\nif((this.b&8)!==0)s=t.gL.a(s).gcW()\nreturn A.v(this).h(\"d6<1>\").a(s)},\nbY(){if((this.b&4)!==0)return new A.bB(\"Cannot add event after closing\")\nreturn new A.bB(\"Cannot add event while adding a stream\")},\nde(){var s=this.c\nif(s==null)s=this.c=(this.b&2)!==0?$.f2():new A.E($.D,t.D)\nreturn s},\ndK(a,b){var s,r,q=this\nA.c7(a,\"error\",t.K)\nif(q.b>=4)throw A.b(q.bY())\ns=$.D.b7(a,b)\nif(s!=null){a=s.a\nb=s.b}else b=A.f8(a)\nr=q.b\nif((r&1)!==0)q.bx(a,b)\nelse if((r&3)===0)q.c7().m(0,new A.en(a,b))},\nfA(a){return this.dK(a,null)},\naf(a){var s=this,r=s.b\nif((r&4)!==0)return s.de()\nif(r>=4)throw A.b(s.bY())\ns.eM()\nreturn s.de()},\neM(){var s=this.b|=4\nif((s&1)!==0)this.bw()\nelse if((s&3)===0)this.c7().m(0,B.y)},\nbW(a,b){var s,r=this,q=A.v(r)\nq.c.a(b)\ns=r.b\nif((s&1)!==0)r.bv(b)\nelse if((s&3)===0)r.c7().m(0,new A.cu(b,q.h(\"cu<1>\")))},\nfs(a,b,c,d){var s,r,q,p,o,n,m,l=this,k=A.v(l)\nk.h(\"~(1)?\").a(a)\nt.Z.a(c)\nif((l.b&3)!==0)throw A.b(A.K(\"Stream has already been listened to.\"))\ns=$.D\nr=d?1:0\nq=A.pi(s,a,k.c)\np=A.tG(s,b)\no=new A.d6(l,q,p,s.cO(c,t.H),s,r,k.h(\"d6<1>\"))\nn=l.gf9()\ns=l.b|=1\nif((s&8)!==0){m=k.h(\"eI<1>\").a(l.a)\nm.scW(o)\nm.hk(0)}else l.a=o\no.fl(n)\no.f1(new A.mz(l))\nreturn o},\nfc(a){var s,r,q,p,o,n,m,l=this,k=A.v(l)\nk.h(\"bm<1>\").a(a)\ns=null\nif((l.b&8)!==0)s=k.h(\"eI<1>\").a(l.a).Y(0)\nl.a=null\nl.b=l.b&4294967286|2\nr=l.r\nif(r!=null)if(s==null)try{q=r.$0()\nif(t.p8.b(q))s=q}catch(n){p=A.M(n)\no=A.a_(n)\nm=new A.E($.D,t.D)\nm.aC(p,o)\ns=m}else s=s.aS(r)\nk=new A.my(l)\nif(s!=null)s=s.aS(k)\nelse k.$0()\nreturn s},\n$ipp:1,\n$icv:1}\nA.mz.prototype={\n$0(){A.oc(this.a.d)},\n$S:0}\nA.my.prototype={\n$0(){var s=this.a.c\nif(s!=null&&(s.a&30)===0)s.bk(null)},\n$S:0}\nA.iK.prototype={\nbv(a){this.$ti.c.a(a)\nthis.gcl().bW(0,a)},\nbx(a,b){this.gcl().eG(a,b)},\nbw(){this.gcl().eL()}}\nA.df.prototype={}\nA.d5.prototype={\ngI(a){return(A.dZ(this.a)^892482866)>>>0},\nW(a,b){if(b==null)return!1\nif(this===b)return!0\nreturn b instanceof A.d5&&b.a===this.a}}\nA.d6.prototype={\ndn(){return this.w.fc(this)},\ndr(){var s=this.w,r=A.v(s)\nr.h(\"bm<1>\").a(this)\nif((s.b&8)!==0)r.h(\"eI<1>\").a(s.a).ht(0)\nA.oc(s.e)},\nds(){var s=this.w,r=A.v(s)\nr.h(\"bm<1>\").a(this)\nif((s.b&8)!==0)r.h(\"eI<1>\").a(s.a).hk(0)\nA.oc(s.f)}}\nA.ej.prototype={\nfl(a){var s=this\nA.v(s).h(\"b4<1>?\").a(a)\nif(a==null)return\ns.sbp(a)\nif(a.c!=null){s.e=(s.e|64)>>>0\na.bT(s)}},\ne3(a){var s=A.v(this)\nthis.seI(A.pi(this.d,s.h(\"~(1)?\").a(a),s.c))},\nY(a){var s=this,r=(s.e&4294967279)>>>0\ns.e=r\nif((r&8)===0)s.c0()\nr=s.f\nreturn r==null?$.f2():r},\nc0(){var s,r=this,q=r.e=(r.e|8)>>>0\nif((q&64)!==0){s=r.r\nif(s.a===1)s.a=3}if((q&32)===0)r.sbp(null)\nr.f=r.dn()},\nbW(a,b){var s,r=this,q=A.v(r)\nq.c.a(b)\ns=r.e\nif((s&8)!==0)return\nif(s<32)r.bv(b)\nelse r.bX(new A.cu(b,q.h(\"cu<1>\")))},\neG(a,b){var s=this.e\nif((s&8)!==0)return\nif(s<32)this.bx(a,b)\nelse this.bX(new A.en(a,b))},\neL(){var s=this,r=s.e\nif((r&8)!==0)return\nr=(r|2)>>>0\ns.e=r\nif(r<32)s.bw()\nelse s.bX(B.y)},\ndr(){},\nds(){},\ndn(){return null},\nbX(a){var s,r=this,q=r.r\nif(q==null){q=new A.b4(A.v(r).h(\"b4<1>\"))\nr.sbp(q)}q.m(0,a)\ns=r.e\nif((s&64)===0){s=(s|64)>>>0\nr.e=s\nif(s<128)q.bT(r)}},\nbv(a){var s,r=this,q=A.v(r).c\nq.a(a)\ns=r.e\nr.e=(s|32)>>>0\nr.d.cR(r.a,a,q)\nr.e=(r.e&4294967263)>>>0\nr.c1((s&4)!==0)},\nbx(a,b){var s,r=this,q=r.e,p=new A.lH(r,a,b)\nif((q&1)!==0){r.e=(q|16)>>>0\nr.c0()\ns=r.f\nif(s!=null&&s!==$.f2())s.aS(p)\nelse p.$0()}else{p.$0()\nr.c1((q&4)!==0)}},\nbw(){var s,r=this,q=new A.lG(r)\nr.c0()\nr.e=(r.e|16)>>>0\ns=r.f\nif(s!=null&&s!==$.f2())s.aS(q)\nelse q.$0()},\nf1(a){var s,r=this\nt.M.a(a)\ns=r.e\nr.e=(s|32)>>>0\na.$0()\nr.e=(r.e&4294967263)>>>0\nr.c1((s&4)!==0)},\nc1(a){var s,r,q=this,p=q.e\nif((p&64)!==0&&q.r.c==null){p=q.e=(p&4294967231)>>>0\nif((p&4)!==0)if(p<128){s=q.r\ns=s==null?null:s.c==null\ns=s!==!1}else s=!1\nelse s=!1\nif(s){p=(p&4294967291)>>>0\nq.e=p}}for(;!0;a=r){if((p&8)!==0){q.sbp(null)\nreturn}r=(p&4)!==0\nif(a===r)break\nq.e=(p^32)>>>0\nif(r)q.dr()\nelse q.ds()\np=(q.e&4294967263)>>>0\nq.e=p}if((p&64)!==0&&p<128)q.r.bT(q)},\nseI(a){this.a=A.v(this).h(\"~(1)\").a(a)},\nsbp(a){this.r=A.v(this).h(\"b4<1>?\").a(a)},\n$ibm:1,\n$icv:1}\nA.lH.prototype={\n$0(){var s,r,q,p=this.a,o=p.e\nif((o&8)!==0&&(o&16)===0)return\np.e=(o|32)>>>0\ns=p.b\no=this.b\nr=t.K\nq=p.d\nif(t.b9.b(s))q.hm(s,o,this.c,r,t.l)\nelse q.cR(t.i6.a(s),o,r)\np.e=(p.e&4294967263)>>>0},\n$S:0}\nA.lG.prototype={\n$0(){var s=this.a,r=s.e\nif((r&16)===0)return\ns.e=(r|42)>>>0\ns.d.ea(s.c)\ns.e=(s.e&4294967263)>>>0},\n$S:0}\nA.eJ.prototype={\ncH(a,b,c,d){var s=this.$ti\ns.h(\"~(1)?\").a(a)\nt.Z.a(c)\nreturn this.a.fs(s.h(\"~(1)?\").a(a),d,c,!0)}}\nA.bG.prototype={\nsbb(a,b){this.a=t.lT.a(b)},\ngbb(a){return this.a}}\nA.cu.prototype={\ncK(a){this.$ti.h(\"cv<1>\").a(a).bv(this.b)}}\nA.en.prototype={\ncK(a){a.bx(this.b,this.c)}}\nA.hV.prototype={\ncK(a){a.bw()},\ngbb(a){return null},\nsbb(a,b){throw A.b(A.K(\"No events after a done.\"))},\n$ibG:1}\nA.b4.prototype={\nbT(a){var s,r=this\nr.$ti.h(\"cv<1>\").a(a)\ns=r.a\nif(s===1)return\nif(s>=1){r.a=1\nreturn}A.qm(new A.ms(r,a))\nr.a=1},\nm(a,b){var s=this,r=s.c\nif(r==null)s.b=s.c=b\nelse{r.sbb(0,b)\ns.c=b}}}\nA.ms.prototype={\n$0(){var s,r,q,p=this.a,o=p.a\np.a=0\nif(o===3)return\ns=p.$ti.h(\"cv<1>\").a(this.b)\nr=p.b\nq=r.gbb(r)\np.b=q\nif(q==null)p.c=null\nr.cK(s)},\n$S:0}\nA.iD.prototype={}\nA.mO.prototype={\n$0(){return this.a.b_(this.b)},\n$S:0}\nA.iT.prototype={}\nA.eT.prototype={$ibF:1}\nA.n_.prototype={\n$0(){var s=this.a,r=this.b\nA.c7(s,\"error\",t.K)\nA.c7(r,\"stackTrace\",t.l)\nA.rh(s,r)},\n$S:0}\nA.iu.prototype={\ngfi(){return B.an},\ngaI(){return this},\nea(a){var s,r,q\nt.M.a(a)\ntry{if(B.d===$.D){a.$0()\nreturn}A.pV(null,null,this,a,t.H)}catch(q){s=A.M(q)\nr=A.a_(q)\nA.mZ(t.K.a(s),t.l.a(r))}},\ncR(a,b,c){var s,r,q\nc.h(\"~(0)\").a(a)\nc.a(b)\ntry{if(B.d===$.D){a.$1(b)\nreturn}A.pX(null,null,this,a,b,t.H,c)}catch(q){s=A.M(q)\nr=A.a_(q)\nA.mZ(t.K.a(s),t.l.a(r))}},\nhm(a,b,c,d,e){var s,r,q\nd.h(\"@<0>\").q(e).h(\"~(1,2)\").a(a)\nd.a(b)\ne.a(c)\ntry{if(B.d===$.D){a.$2(b,c)\nreturn}A.pW(null,null,this,a,b,c,t.H,d,e)}catch(q){s=A.M(q)\nr=A.a_(q)\nA.mZ(t.K.a(s),t.l.a(r))}},\nfD(a,b){return new A.mw(this,b.h(\"0()\").a(a),b)},\ncr(a){return new A.mv(this,t.M.a(a))},\ndM(a,b){return new A.mx(this,b.h(\"~(0)\").a(a),b)},\ndU(a,b){A.mZ(a,t.l.a(b))},\ncP(a,b){b.h(\"0()\").a(a)\nif($.D===B.d)return a.$0()\nreturn A.pV(null,null,this,a,b)},\ncQ(a,b,c,d){c.h(\"@<0>\").q(d).h(\"1(2)\").a(a)\nd.a(b)\nif($.D===B.d)return a.$1(b)\nreturn A.pX(null,null,this,a,b,c,d)},\nhl(a,b,c,d,e,f){d.h(\"@<0>\").q(e).q(f).h(\"1(2,3)\").a(a)\ne.a(b)\nf.a(c)\nif($.D===B.d)return a.$2(b,c)\nreturn A.pW(null,null,this,a,b,c,d,e,f)},\ncO(a,b){return b.h(\"0()\").a(a)},\nbN(a,b,c){return b.h(\"@<0>\").q(c).h(\"1(2)\").a(a)},\ncN(a,b,c,d){return b.h(\"@<0>\").q(c).q(d).h(\"1(2,3)\").a(a)},\nb7(a,b){t.fw.a(b)\nreturn null},\naB(a){A.n0(null,null,this,t.M.a(a))},\ndQ(a,b){return A.p6(a,t.M.a(b))}}\nA.mw.prototype={\n$0(){return this.a.cP(this.b,this.c)},\n$S(){return this.c.h(\"0()\")}}\nA.mv.prototype={\n$0(){return this.a.ea(this.b)},\n$S:0}\nA.mx.prototype={\n$1(a){var s=this.c\nreturn this.a.cR(this.b,s.a(a),s)},\n$S(){return this.c.h(\"~(0)\")}}\nA.et.prototype={\naM(a){return A.j7(a)&1073741823},\naN(a,b){var s,r,q\nif(a==null)return-1\ns=a.length\nfor(r=0;r<s;++r){q=a[r].a\nif(q==null?b==null:q===b)return r}return-1}}\nA.er.prototype={\ni(a,b){if(!A.aK(this.y.$1(b)))return null\nreturn this.eq(b)},\nk(a,b,c){var s=this.$ti\nthis.es(s.c.a(b),s.z[1].a(c))},\nF(a,b){if(!A.aK(this.y.$1(b)))return!1\nreturn this.ep(b)},\nG(a,b){if(!A.aK(this.y.$1(b)))return null\nreturn this.er(b)},\naM(a){return this.x.$1(this.$ti.c.a(a))&1073741823},\naN(a,b){var s,r,q,p\nif(a==null)return-1\ns=a.length\nfor(r=this.$ti.c,q=this.w,p=0;p<s;++p)if(A.aK(q.$2(r.a(a[p].a),r.a(b))))return p\nreturn-1}}\nA.mq.prototype={\n$1(a){return this.a.b(a)},\n$S:40}\nA.es.prototype={\ngE(a){var s=this,r=new A.cw(s,s.r,s.$ti.h(\"cw<1>\"))\nr.c=s.e\nreturn r},\ngj(a){return this.a},\ngC(a){return this.a===0},\ngP(a){return this.a!==0},\nS(a,b){var s,r\nif(b!==\"__proto__\"){s=this.b\nif(s==null)return!1\nreturn t.R.a(s[b])!=null}else{r=this.eP(b)\nreturn r}},\neP(a){var s=this.d\nif(s==null)return!1\nreturn this.cb(s[B.a.gI(a)&1073741823],a)>=0},\ngA(a){var s=this.e\nif(s==null)throw A.b(A.K(\"No elements\"))\nreturn this.$ti.c.a(s.a)},\nm(a,b){var s,r,q=this\nq.$ti.c.a(b)\nif(typeof b==\"string\"&&b!==\"__proto__\"){s=q.b\nreturn q.d6(s==null?q.b=A.nY():s,b)}else if(typeof b==\"number\"&&(b&1073741823)===b){r=q.c\nreturn q.d6(r==null?q.c=A.nY():r,b)}else return q.eN(0,b)},\neN(a,b){var s,r,q,p=this\np.$ti.c.a(b)\ns=p.d\nif(s==null)s=p.d=A.nY()\nr=J.ax(b)&1073741823\nq=s[r]\nif(q==null)s[r]=[p.c4(b)]\nelse{if(p.cb(q,b)>=0)return!1\nq.push(p.c4(b))}return!0},\nG(a,b){var s=this\nif(typeof b==\"string\"&&b!==\"__proto__\")return s.d8(s.b,b)\nelse if(typeof b==\"number\"&&(b&1073741823)===b)return s.d8(s.c,b)\nelse return s.fe(0,b)},\nfe(a,b){var s,r,q,p,o=this.d\nif(o==null)return!1\ns=J.ax(b)&1073741823\nr=o[s]\nq=this.cb(r,b)\nif(q<0)return!1\np=r.splice(q,1)[0]\nif(0===r.length)delete o[s]\nthis.d9(p)\nreturn!0},\nd6(a,b){this.$ti.c.a(b)\nif(t.R.a(a[b])!=null)return!1\na[b]=this.c4(b)\nreturn!0},\nd8(a,b){var s\nif(a==null)return!1\ns=t.R.a(a[b])\nif(s==null)return!1\nthis.d9(s)\ndelete a[b]\nreturn!0},\nd7(){this.r=this.r+1&1073741823},\nc4(a){var s,r=this,q=new A.ib(r.$ti.c.a(a))\nif(r.e==null)r.e=r.f=q\nelse{s=r.f\ns.toString\nq.c=s\nr.f=s.b=q}++r.a\nr.d7()\nreturn q},\nd9(a){var s=this,r=a.c,q=a.b\nif(r==null)s.e=q\nelse r.b=q\nif(q==null)s.f=r\nelse q.c=r;--s.a\ns.d7()},\ncb(a,b){var s,r\nif(a==null)return-1\ns=a.length\nfor(r=0;r<s;++r)if(J.a7(a[r].a,b))return r\nreturn-1}}\nA.ib.prototype={}\nA.cw.prototype={\ngu(a){var s=this.d\nreturn s==null?this.$ti.c.a(s):s},\np(){var s=this,r=s.c,q=s.a\nif(s.b!==q.r)throw A.b(A.ap(q))\nelse if(r==null){s.sad(null)\nreturn!1}else{s.sad(s.$ti.h(\"1?\").a(r.a))\ns.c=r.b\nreturn!0}},\nsad(a){this.d=this.$ti.h(\"1?\").a(a)},\n$iL:1}\nA.dH.prototype={}\nA.jW.prototype={\n$2(a,b){this.a.k(0,this.b.a(a),this.c.a(b))},\n$S:7}\nA.cR.prototype={\nG(a,b){this.$ti.c.a(b)\nif(b.a!==this)return!1\nthis.cm(b)\nreturn!0},\nS(a,b){return!1},\ngE(a){var s=this\nreturn new A.eu(s,s.a,s.c,s.$ti.h(\"eu<1>\"))},\ngj(a){return this.b},\ngA(a){var s\nif(this.b===0)throw A.b(A.K(\"No such element\"))\ns=this.c\ns.toString\nreturn s},\ngai(a){var s\nif(this.b===0)throw A.b(A.K(\"No such element\"))\ns=this.c.c\ns.toString\nreturn s},\ngC(a){return this.b===0},\ncf(a,b,c){var s=this,r=s.$ti\nr.h(\"1?\").a(a)\nr.c.a(b)\nif(b.a!=null)throw A.b(A.K(\"LinkedListEntry is already in a LinkedList\"));++s.a\nb.sdj(s)\nif(s.b===0){b.sao(b)\nb.sb1(b)\ns.scc(b);++s.b\nreturn}r=a.c\nr.toString\nb.sb1(r)\nb.sao(a)\nr.sao(b)\na.sb1(b);++s.b},\ncm(a){var s,r,q=this,p=null\nq.$ti.c.a(a);++q.a\na.b.sb1(a.c)\ns=a.c\nr=a.b\ns.sao(r);--q.b\na.sb1(p)\na.sao(p)\na.sdj(p)\nif(q.b===0)q.scc(p)\nelse if(a===q.c)q.scc(r)},\nscc(a){this.c=this.$ti.h(\"1?\").a(a)}}\nA.eu.prototype={\ngu(a){var s=this.c\nreturn s==null?this.$ti.c.a(s):s},\np(){var s=this,r=s.a\nif(s.b!==r.a)throw A.b(A.ap(s))\nif(r.b!==0)r=s.e&&s.d===r.gA(r)\nelse r=!0\nif(r){s.sad(null)\nreturn!1}s.e=!0\ns.sad(s.d)\ns.sao(s.d.b)\nreturn!0},\nsad(a){this.c=this.$ti.h(\"1?\").a(a)},\nsao(a){this.d=this.$ti.h(\"1?\").a(a)},\n$iL:1}\nA.ae.prototype={\ngbc(){var s=this.a\nif(s==null||this===s.gA(s))return null\nreturn this.c},\nsdj(a){this.a=A.v(this).h(\"cR<ae.E>?\").a(a)},\nsao(a){this.b=A.v(this).h(\"ae.E?\").a(a)},\nsb1(a){this.c=A.v(this).h(\"ae.E?\").a(a)}}\nA.dN.prototype={$ik:1,$ie:1,$im:1}\nA.h.prototype={\ngE(a){return new A.aP(a,this.gj(a),A.a0(a).h(\"aP<h.E>\"))},\nv(a,b){return this.i(a,b)},\nD(a,b){var s,r\nA.a0(a).h(\"~(h.E)\").a(b)\ns=this.gj(a)\nfor(r=0;r<s;++r){b.$1(this.i(a,r))\nif(s!==this.gj(a))throw A.b(A.ap(a))}},\ngC(a){return this.gj(a)===0},\ngP(a){return!this.gC(a)},\ngA(a){if(this.gj(a)===0)throw A.b(A.bt())\nreturn this.i(a,0)},\nS(a,b){var s,r=this.gj(a)\nfor(s=0;s<r;++s){if(J.a7(this.i(a,s),b))return!0\nif(r!==this.gj(a))throw A.b(A.ap(a))}return!1},\naj(a,b,c){var s=A.a0(a)\nreturn new A.af(a,s.q(c).h(\"1(h.E)\").a(b),s.h(\"@<h.E>\").q(c).h(\"af<1,2>\"))},\na2(a,b){return A.eb(a,b,null,A.a0(a).h(\"h.E\"))},\nbz(a,b){return new A.ba(a,A.a0(a).h(\"@<h.E>\").q(b).h(\"ba<1,2>\"))},\ndT(a,b,c,d){var s\nA.a0(a).h(\"h.E?\").a(d)\nA.by(b,c,this.gj(a))\nfor(s=b;s<c;++s)this.k(a,s,d)},\nT(a,b,c,d,e){var s,r,q,p,o=A.a0(a)\no.h(\"e<h.E>\").a(d)\nA.by(b,c,this.gj(a))\ns=c-b\nif(s===0)return\nA.aT(e,\"skipCount\")\nif(o.h(\"m<h.E>\").b(d)){r=e\nq=d}else{q=J.ns(d,e).bQ(0,!1)\nr=0}o=J.T(q)\nif(r+s>o.gj(q))throw A.b(A.oJ())\nif(r<b)for(p=s-1;p>=0;--p)this.k(a,b+p,o.i(q,r+p))\nelse for(p=0;p<s;++p)this.k(a,b+p,o.i(q,r+p))},\na6(a,b,c,d){return this.T(a,b,c,d,0)},\nam(a,b,c){var s,r\nA.a0(a).h(\"e<h.E>\").a(c)\nif(t.j.b(c))this.a6(a,b,b+c.length,c)\nelse for(s=J.an(c);s.p();b=r){r=b+1\nthis.k(a,b,s.gu(s))}},\nl(a){return A.nw(a,\"[\",\"]\")}}\nA.dP.prototype={}\nA.k_.prototype={\n$2(a,b){var s,r=this.a\nif(!r.a)this.b.a+=\", \"\nr.a=!1\nr=this.b\ns=r.a+=A.q(a)\nr.a=s+\": \"\nr.a+=A.q(b)},\n$S:39}\nA.w.prototype={\nfE(a,b,c){var s=A.a0(a)\nreturn A.rA(a,s.h(\"w.K\"),s.h(\"w.V\"),b,c)},\nD(a,b){var s,r,q,p=A.a0(a)\np.h(\"~(w.K,w.V)\").a(b)\nfor(s=J.an(this.gK(a)),p=p.h(\"w.V\");s.p();){r=s.gu(s)\nq=this.i(a,r)\nb.$2(r,q==null?p.a(q):q)}},\ngaH(a){return J.ov(this.gK(a),new A.k0(a),A.a0(a).h(\"a4<w.K,w.V>\"))},\nh9(a,b,c,d){var s,r,q,p,o,n=A.a0(a)\nn.q(c).q(d).h(\"a4<1,2>(w.K,w.V)\").a(b)\ns=A.X(c,d)\nfor(r=J.an(this.gK(a)),n=n.h(\"w.V\");r.p();){q=r.gu(r)\np=this.i(a,q)\no=b.$2(q,p==null?n.a(p):p)\ns.k(0,o.a,o.b)}return s},\nF(a,b){return J.nr(this.gK(a),b)},\ngj(a){return J.Y(this.gK(a))},\ngC(a){return J.dr(this.gK(a))},\ngP(a){return J.f3(this.gK(a))},\ngU(a){var s=A.a0(a)\nreturn new A.ew(a,s.h(\"@<w.K>\").q(s.h(\"w.V\")).h(\"ew<1,2>\"))},\nl(a){return A.jZ(a)},\n$iI:1}\nA.k0.prototype={\n$1(a){var s=this.a,r=A.a0(s)\nr.h(\"w.K\").a(a)\ns=J.ab(s,a)\nif(s==null)s=r.h(\"w.V\").a(s)\nreturn new A.a4(a,s,r.h(\"@<w.K>\").q(r.h(\"w.V\")).h(\"a4<1,2>\"))},\n$S(){return A.a0(this.a).h(\"a4<w.K,w.V>(w.K)\")}}\nA.d3.prototype={}\nA.ew.prototype={\ngj(a){return J.Y(this.a)},\ngC(a){return J.dr(this.a)},\ngP(a){return J.f3(this.a)},\ngA(a){var s=this.a,r=J.a2(s)\ns=r.i(s,J.bP(r.gK(s)))\nreturn s==null?this.$ti.z[1].a(s):s},\ngE(a){var s=this.a,r=this.$ti\nreturn new A.ex(J.an(J.ou(s)),s,r.h(\"@<1>\").q(r.z[1]).h(\"ex<1,2>\"))}}\nA.ex.prototype={\np(){var s=this,r=s.a\nif(r.p()){s.sad(J.ab(s.b,r.gu(r)))\nreturn!0}s.sad(null)\nreturn!1},\ngu(a){var s=this.c\nreturn s==null?this.$ti.z[1].a(s):s},\nsad(a){this.c=this.$ti.h(\"2?\").a(a)},\n$iL:1}\nA.c5.prototype={\nG(a,b){throw A.b(A.x(\"Cannot modify unmodifiable map\"))}}\nA.cS.prototype={\ni(a,b){return this.a.i(0,b)},\nF(a,b){return this.a.F(0,b)},\nD(a,b){this.a.D(0,A.v(this).h(\"~(1,2)\").a(b))},\ngj(a){var s=this.a\nreturn s.gj(s)},\ngK(a){var s=this.a\nreturn s.gK(s)},\nl(a){var s=this.a\nreturn s.l(s)},\ngU(a){var s=this.a\nreturn s.gU(s)},\ngaH(a){var s=this.a\nreturn s.gaH(s)},\n$iI:1}\nA.ed.prototype={}\nA.e1.prototype={\ngC(a){return this.a===0},\ngP(a){return this.a!==0},\naj(a,b,c){var s=this.$ti\nreturn new A.ce(this,s.q(c).h(\"1(2)\").a(b),s.h(\"@<1>\").q(c).h(\"ce<1,2>\"))},\nl(a){return A.nw(this,\"{\",\"}\")},\na2(a,b){return A.p0(this,b,this.$ti.c)},\ngA(a){var s,r=A.pk(this,this.r,this.$ti.c)\nif(!r.p())throw A.b(A.bt())\ns=r.d\nreturn s==null?r.$ti.c.a(s):s},\nv(a,b){var s,r,q,p,o=this,n=\"index\"\nA.c7(b,n,t.S)\nA.aT(b,n)\nfor(s=A.pk(o,o.r,o.$ti.c),r=s.$ti.c,q=0;s.p();){p=s.d\nif(p==null)p=r.a(p)\nif(b===q)return p;++q}throw A.b(A.V(b,q,o,null,n))}}\nA.eE.prototype={$ik:1,$ie:1,$ip_:1}\nA.ev.prototype={}\nA.dh.prototype={}\nA.eV.prototype={}\nA.ln.prototype={\n$0(){var s,r\ntry{s=new TextDecoder(\"utf-8\",{fatal:true})\nreturn s}catch(r){}return null},\n$S:19}\nA.lm.prototype={\n$0(){var s,r\ntry{s=new TextDecoder(\"utf-8\",{fatal:false})\nreturn s}catch(r){}return null},\n$S:19}\nA.fc.prototype={\nhe(a1,a2,a3,a4){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a,a0=\"Invalid base64 encoding length \"\na4=A.by(a3,a4,a2.length)\ns=$.qE()\nfor(r=s.length,q=a3,p=q,o=null,n=-1,m=-1,l=0;q<a4;q=k){k=q+1\nj=B.a.t(a2,q)\nif(j===37){i=k+2\nif(i<=a4){h=A.n9(B.a.t(a2,k))\ng=A.n9(B.a.t(a2,k+1))\nf=h*16+g-(g&256)\nif(f===37)f=-1\nk=i}else f=-1}else f=j\nif(0<=f&&f<=127){if(!(f>=0&&f<r))return A.d(s,f)\ne=s[f]\nif(e>=0){f=B.a.B(\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\",e)\nif(f===j)continue\nj=f}else{if(e===-1){if(n<0){d=o==null?null:o.a.length\nif(d==null)d=0\nn=d+(q-p)\nm=q}++l\nif(j===61)continue}j=f}if(e!==-2){if(o==null){o=new A.ah(\"\")\nd=o}else d=o\nc=d.a+=B.a.n(a2,p,q)\nd.a=c+A.bx(j)\np=k\ncontinue}}throw A.b(A.ad(\"Invalid base64 data\",a2,q))}if(o!=null){r=o.a+=B.a.n(a2,p,a4)\nd=r.length\nif(n>=0)A.ow(a2,m,a4,n,l,d)\nelse{b=B.c.ab(d-1,4)+1\nif(b===1)throw A.b(A.ad(a0,a2,a4))\nfor(;b<4;){r+=\"=\"\no.a=r;++b}}r=o.a\nreturn B.a.az(a2,a3,a4,r.charCodeAt(0)==0?r:r)}a=a4-a3\nif(n>=0)A.ow(a2,m,a4,n,l,a)\nelse{b=B.c.ab(a,4)\nif(b===1)throw A.b(A.ad(a0,a2,a4))\nif(b>1)a2=B.a.az(a2,a4,a4,b===2?\"==\":\"=\")}return a2}}\nA.jr.prototype={}\nA.ay.prototype={}\nA.fl.prototype={}\nA.fw.prototype={}\nA.ee.prototype={\nb5(a,b){t.L.a(b)\nreturn B.t.a9(b)},\ngaG(){return B.S}}\nA.lo.prototype={\na9(a){var s,r,q=A.by(0,null,a.length),p=q-0\nif(p===0)return new Uint8Array(0)\ns=new Uint8Array(p*3)\nr=new A.mI(s)\nif(r.f_(a,0,q)!==q){B.a.B(a,q-1)\nr.cn()}return B.e.el(s,0,r.b)}}\nA.mI.prototype={\ncn(){var s=this,r=s.c,q=s.b,p=s.b=q+1,o=r.length\nif(!(q<o))return A.d(r,q)\nr[q]=239\nq=s.b=p+1\nif(!(p<o))return A.d(r,p)\nr[p]=191\ns.b=q+1\nif(!(q<o))return A.d(r,q)\nr[q]=189},\nfv(a,b){var s,r,q,p,o,n=this\nif((b&64512)===56320){s=65536+((a&1023)<<10)|b&1023\nr=n.c\nq=n.b\np=n.b=q+1\no=r.length\nif(!(q<o))return A.d(r,q)\nr[q]=s>>>18|240\nq=n.b=p+1\nif(!(p<o))return A.d(r,p)\nr[p]=s>>>12&63|128\np=n.b=q+1\nif(!(q<o))return A.d(r,q)\nr[q]=s>>>6&63|128\nn.b=p+1\nif(!(p<o))return A.d(r,p)\nr[p]=s&63|128\nreturn!0}else{n.cn()\nreturn!1}},\nf_(a,b,c){var s,r,q,p,o,n,m,l=this\nif(b!==c&&(B.a.B(a,c-1)&64512)===55296)--c\nfor(s=l.c,r=s.length,q=b;q<c;++q){p=B.a.t(a,q)\nif(p<=127){o=l.b\nif(o>=r)break\nl.b=o+1\ns[o]=p}else{o=p&64512\nif(o===55296){if(l.b+4>r)break\nn=q+1\nif(l.fv(p,B.a.t(a,n)))q=n}else if(o===56320){if(l.b+3>r)break\nl.cn()}else if(p<=2047){o=l.b\nm=o+1\nif(m>=r)break\nl.b=m\nif(!(o<r))return A.d(s,o)\ns[o]=p>>>6|192\nl.b=m+1\ns[m]=p&63|128}else{o=l.b\nif(o+2>=r)break\nm=l.b=o+1\nif(!(o<r))return A.d(s,o)\ns[o]=p>>>12|224\no=l.b=m+1\nif(!(m<r))return A.d(s,m)\ns[m]=p>>>6&63|128\nl.b=o+1\nif(!(o<r))return A.d(s,o)\ns[o]=p&63|128}}}return q}}\nA.ll.prototype={\ndO(a,b,c){var s,r\nt.L.a(a)\ns=this.a\nr=A.tt(s,a,b,c)\nif(r!=null)return r\nreturn new A.mH(s).fJ(a,b,c,!0)},\na9(a){return this.dO(a,0,null)}}\nA.mH.prototype={\nfJ(a,b,c,d){var s,r,q,p,o,n,m=this\nt.L.a(a)\ns=A.by(b,c,J.Y(a))\nif(b===s)return\"\"\nif(t.p.b(a)){r=a\nq=0}else{r=A.ue(a,b,s)\ns-=b\nq=b\nb=0}p=m.c6(r,b,s,!0)\no=m.b\nif((o&1)!==0){n=A.uf(o)\nm.b=0\nthrow A.b(A.ad(n,a,q+m.c))}return p},\nc6(a,b,c,d){var s,r,q=this\nif(c-b>1000){s=B.c.R(b+c,2)\nr=q.c6(a,b,s,!1)\nif((q.b&1)!==0)return r\nreturn r+q.c6(a,s,c,d)}return q.fN(a,b,c,d)},\nfN(a,b,c,d){var s,r,q,p,o,n,m,l,k=this,j=65533,i=k.b,h=k.c,g=new A.ah(\"\"),f=b+1,e=a.length\nif(!(b>=0&&b<e))return A.d(a,b)\ns=a[b]\n$label0$0:for(r=k.a;!0;){for(;!0;f=o){q=B.a.t(\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFFFFFFFFFFFFFFFGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHHHHHHHHHHIHHHJEEBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBKCCCCCCCCCCCCDCLONNNMEEEEEEEEEEE\",s)&31\nh=i<=32?s&61694>>>q:(s&63|h<<6)>>>0\ni=B.a.t(\" \\x000:XECCCCCN:lDb \\x000:XECCCCCNvlDb \\x000:XECCCCCN:lDb AAAAA\\x00\\x00\\x00\\x00\\x00AAAAA00000AAAAA:::::AAAAAGG000AAAAA00KKKAAAAAG::::AAAAA:IIIIAAAAA000\\x800AAAAA\\x00\\x00\\x00\\x00 AAAAA\",i+q)\nif(i===0){g.a+=A.bx(h)\nif(f===c)break $label0$0\nbreak}else if((i&1)!==0){if(r)switch(i){case 69:case 67:g.a+=A.bx(j)\nbreak\ncase 65:g.a+=A.bx(j);--f\nbreak\ndefault:p=g.a+=A.bx(j)\ng.a=p+A.bx(j)\nbreak}else{k.b=i\nk.c=f-1\nreturn\"\"}i=0}if(f===c)break $label0$0\no=f+1\nif(!(f>=0&&f<e))return A.d(a,f)\ns=a[f]}o=f+1\nif(!(f>=0&&f<e))return A.d(a,f)\ns=a[f]\nif(s<128){while(!0){if(!(o<c)){n=c\nbreak}m=o+1\nif(!(o>=0&&o<e))return A.d(a,o)\ns=a[o]\nif(s>=128){n=m-1\no=m\nbreak}o=m}if(n-f<20)for(l=f;l<n;++l){if(!(l<e))return A.d(a,l)\ng.a+=A.bx(a[l])}else g.a+=A.p5(a,f,n)\nif(n===c)break $label0$0\nf=o}else f=o}if(d&&i>32)if(r)g.a+=A.bx(j)\nelse{k.b=77\nk.c=c\nreturn\"\"}k.b=i\nk.c=h\ne=g.a\nreturn e.charCodeAt(0)==0?e:e}}\nA.i3.prototype={}\nA.k6.prototype={\n$2(a,b){var s,r,q\nt.bR.a(a)\ns=this.b\nr=this.a\nq=s.a+=r.a\nq+=a.a\ns.a=q\ns.a=q+\": \"\ns.a+=A.cg(b)\nr.a=\", \"},\n$S:36}\nA.a8.prototype={\nac(a){var s,r,q=this,p=q.c\nif(p===0)return q\ns=!q.a\nr=q.b\np=A.b3(p,r)\nreturn new A.a8(p===0?!1:s,r,p)},\neV(a){var s,r,q,p,o,n,m,l,k=this,j=k.c\nif(j===0)return $.bN()\ns=j-a\nif(s<=0)return k.a?$.oo():$.bN()\nr=k.b\nq=new Uint16Array(s)\nfor(p=r.length,o=a;o<j;++o){n=o-a\nif(!(o>=0&&o<p))return A.d(r,o)\nm=r[o]\nif(!(n<s))return A.d(q,n)\nq[n]=m}n=k.a\nm=A.b3(s,q)\nl=new A.a8(m===0?!1:n,q,m)\nif(n)for(o=0;o<a;++o){if(!(o<p))return A.d(r,o)\nif(r[o]!==0)return l.aX(0,$.jb())}return l},\naV(a,b){var s,r,q,p,o,n,m,l,k,j=this\nif(b<0)throw A.b(A.ao(\"shift-amount must be posititve \"+b,null))\ns=j.c\nif(s===0)return j\nr=B.c.R(b,16)\nq=B.c.ab(b,16)\nif(q===0)return j.eV(r)\np=s-r\nif(p<=0)return j.a?$.oo():$.bN()\no=j.b\nn=new Uint16Array(p)\nA.tE(o,s,b,n)\ns=j.a\nm=A.b3(p,n)\nl=new A.a8(m===0?!1:s,n,m)\nif(s){s=o.length\nif(!(r>=0&&r<s))return A.d(o,r)\nif((o[r]&B.c.aU(1,q)-1)>>>0!==0)return l.aX(0,$.jb())\nfor(k=0;k<r;++k){if(!(k<s))return A.d(o,k)\nif(o[k]!==0)return l.aX(0,$.jb())}}return l},\na8(a,b){var s,r\nt.d.a(b)\ns=this.a\nif(s===b.a){r=A.lD(this.b,this.c,b.b,b.c)\nreturn s?0-r:r}return s?-1:1},\nbV(a,b){var s,r,q,p=this,o=p.c,n=a.c\nif(o<n)return a.bV(p,b)\nif(o===0)return $.bN()\nif(n===0)return p.a===b?p:p.ac(0)\ns=o+1\nr=new Uint16Array(s)\nA.tz(p.b,o,a.b,n,r)\nq=A.b3(s,r)\nreturn new A.a8(q===0?!1:b,r,q)},\nbi(a,b){var s,r,q,p=this,o=p.c\nif(o===0)return $.bN()\ns=a.c\nif(s===0)return p.a===b?p:p.ac(0)\nr=new Uint16Array(o)\nA.hR(p.b,o,a.b,s,r)\nq=A.b3(o,r)\nreturn new A.a8(q===0?!1:b,r,q)},\nbf(a,b){var s,r,q=this,p=q.c\nif(p===0)return b\ns=b.c\nif(s===0)return q\nr=q.a\nif(r===b.a)return q.bV(b,r)\nif(A.lD(q.b,p,b.b,s)>=0)return q.bi(b,r)\nreturn b.bi(q,!r)},\naX(a,b){var s,r,q,p=this\nt.d.a(b)\ns=p.c\nif(s===0)return b.ac(0)\nr=b.c\nif(r===0)return p\nq=p.a\nif(q!==b.a)return p.bV(b,q)\nif(A.lD(p.b,s,b.b,r)>=0)return p.bi(b,q)\nreturn b.bi(p,!q)},\nbg(a,b){var s,r,q,p,o,n,m,l,k\nt.d.a(b)\ns=this.c\nr=b.c\nif(s===0||r===0)return $.bN()\nq=s+r\np=this.b\no=b.b\nn=new Uint16Array(q)\nfor(m=o.length,l=0;l<r;){if(!(l<m))return A.d(o,l)\nA.ph(o[l],p,0,n,l,s);++l}m=this.a!==b.a\nk=A.b3(q,n)\nreturn new A.a8(k===0?!1:m,n,k)},\neU(a){var s,r,q,p\nif(this.c<a.c)return $.bN()\nthis.dd(a)\ns=$.nT.a_()-$.ei.a_()\nr=A.nV($.nS.a_(),$.ei.a_(),$.nT.a_(),s)\nq=A.b3(s,r)\np=new A.a8(!1,r,q)\nreturn this.a!==a.a&&q>0?p.ac(0):p},\nfd(a){var s,r,q,p=this\nif(p.c<a.c)return p\np.dd(a)\ns=A.nV($.nS.a_(),0,$.ei.a_(),$.ei.a_())\nr=A.b3($.ei.a_(),s)\nq=new A.a8(!1,s,r)\nif($.nU.a_()>0)q=q.aV(0,$.nU.a_())\nreturn p.a&&q.c>0?q.ac(0):q},\ndd(a0){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b=this,a=b.c\nif(a===$.pe&&a0.c===$.pg&&b.b===$.pd&&a0.b===$.pf)return\ns=a0.b\nr=a0.c\nq=r-1\nif(!(q>=0&&q<s.length))return A.d(s,q)\np=16-B.c.gdN(s[q])\nif(p>0){o=new Uint16Array(r+5)\nn=A.pc(s,r,p,o)\nm=new Uint16Array(a+5)\nl=A.pc(b.b,a,p,m)}else{m=A.nV(b.b,0,a,a+2)\nn=r\no=s\nl=a}q=n-1\nif(!(q>=0&&q<o.length))return A.d(o,q)\nk=o[q]\nj=l-n\ni=new Uint16Array(l)\nh=A.nW(o,n,j,i)\ng=l+1\nq=m.length\nif(A.lD(m,l,i,h)>=0){if(!(l>=0&&l<q))return A.d(m,l)\nm[l]=1\nA.hR(m,g,i,h,m)}else{if(!(l>=0&&l<q))return A.d(m,l)\nm[l]=0}f=n+2\ne=new Uint16Array(f)\nif(!(n>=0&&n<f))return A.d(e,n)\ne[n]=1\nA.hR(e,n+1,o,n,e)\nd=l-1\nfor(;j>0;){c=A.tA(k,m,d);--j\nA.ph(c,e,0,m,j,n)\nif(!(d>=0&&d<q))return A.d(m,d)\nif(m[d]<c){h=A.nW(e,n,j,i)\nA.hR(m,g,i,h,m)\nfor(;--c,m[d]<c;)A.hR(m,g,i,h,m)}--d}$.pd=b.b\n$.pe=a\n$.pf=s\n$.pg=r\n$.nS.b=m\n$.nT.b=g\n$.ei.b=n\n$.nU.b=p},\ngI(a){var s,r,q,p,o=new A.lE(),n=this.c\nif(n===0)return 6707\ns=this.a?83585:429689\nfor(r=this.b,q=r.length,p=0;p<n;++p){if(!(p<q))return A.d(r,p)\ns=o.$2(s,r[p])}return new A.lF().$1(s)},\nW(a,b){if(b==null)return!1\nreturn b instanceof A.a8&&this.a8(0,b)===0},\nl(a){var s,r,q,p,o,n,m=this,l=m.c\nif(l===0)return\"0\"\nif(l===1){if(m.a){l=m.b\nif(0>=l.length)return A.d(l,0)\nreturn B.c.l(-l[0])}l=m.b\nif(0>=l.length)return A.d(l,0)\nreturn B.c.l(l[0])}s=A.t([],t.s)\nl=m.a\nr=l?m.ac(0):m\nfor(q=t.d;r.c>1;){p=q.a($.on())\nif(p.c===0)A.J(B.K)\no=r.fd(p).l(0)\nB.b.m(s,o)\nn=o.length\nif(n===1)B.b.m(s,\"000\")\nif(n===2)B.b.m(s,\"00\")\nif(n===3)B.b.m(s,\"0\")\nr=r.eU(p)}q=r.b\nif(0>=q.length)return A.d(q,0)\nB.b.m(s,B.c.l(q[0]))\nif(l)B.b.m(s,\"-\")\nreturn new A.e0(s,t.hF).h7(0)},\n$icC:1,\n$iaj:1}\nA.lE.prototype={\n$2(a,b){a=a+b&536870911\na=a+((a&524287)<<10)&536870911\nreturn a^a>>>6},\n$S:8}\nA.lF.prototype={\n$1(a){a=a+((a&67108863)<<3)&536870911\na^=a>>>11\nreturn a+((a&16383)<<15)&536870911},\n$S:22}\nA.bU.prototype={\nW(a,b){if(b==null)return!1\nreturn b instanceof A.bU&&this.a===b.a&&this.b===b.b},\na8(a,b){return B.c.a8(this.a,t.cs.a(b).a)},\ngI(a){var s=this.a\nreturn(s^B.c.M(s,30))&1073741823},\nl(a){var s=this,r=A.re(A.rQ(s)),q=A.fs(A.rO(s)),p=A.fs(A.rK(s)),o=A.fs(A.rL(s)),n=A.fs(A.rN(s)),m=A.fs(A.rP(s)),l=A.rf(A.rM(s)),k=r+\"-\"+q\nif(s.b)return k+\"-\"+p+\" \"+o+\":\"+n+\":\"+m+\".\"+l+\"Z\"\nelse return k+\"-\"+p+\" \"+o+\":\"+n+\":\"+m+\".\"+l},\n$iaj:1}\nA.cd.prototype={\nW(a,b){if(b==null)return!1\nreturn b instanceof A.cd&&!0},\ngI(a){return B.c.gI(0)},\na8(a,b){t.jS.a(b)\nreturn 0},\nl(a){return\"\"+Math.abs(0)+\":00:00.\"+B.a.hg(B.c.l(0),6,\"0\")},\n$iaj:1}\nA.lL.prototype={\nl(a){return this.eX()}}\nA.Q.prototype={\ngaW(){return A.a_(this.$thrownJsError)}}\nA.ds.prototype={\nl(a){var s=this.a\nif(s!=null)return\"Assertion failed: \"+A.cg(s)\nreturn\"Assertion failed\"}}\nA.bn.prototype={}\nA.h1.prototype={\nl(a){return\"Throw of null.\"},\n$ibn:1}\nA.bh.prototype={\ngc9(){return\"Invalid argument\"+(!this.a?\"(s)\":\"\")},\ngc8(){return\"\"},\nl(a){var s=this,r=s.c,q=r==null?\"\":\" (\"+r+\")\",p=s.d,o=p==null?\"\":\": \"+A.q(p),n=s.gc9()+q+o\nif(!s.a)return n\nreturn n+s.gc8()+\": \"+A.cg(s.gcE())},\ngcE(){return this.b}}\nA.cX.prototype={\ngcE(){return A.ui(this.b)},\ngc9(){return\"RangeError\"},\ngc8(){var s,r=this.e,q=this.f\nif(r==null)s=q!=null?\": Not less than or equal to \"+A.q(q):\"\"\nelse if(q==null)s=\": Not greater than or equal to \"+A.q(r)\nelse if(q>r)s=\": Not in inclusive range \"+A.q(r)+\"..\"+A.q(q)\nelse s=q<r?\": Valid value range is empty\":\": Only valid value is \"+A.q(r)\nreturn s}}\nA.fE.prototype={\ngcE(){return A.j(this.b)},\ngc9(){return\"RangeError\"},\ngc8(){if(A.j(this.b)<0)return\": index must not be negative\"\nvar s=this.f\nif(s===0)return\": no indices are valid\"\nreturn\": index should be less than \"+s},\ngj(a){return this.f}}\nA.dU.prototype={\nl(a){var s,r,q,p,o,n,m,l,k=this,j={},i=new A.ah(\"\")\nj.a=\"\"\ns=k.c\nfor(r=s.length,q=0,p=\"\",o=\"\";q<r;++q,o=\", \"){n=s[q]\ni.a=p+o\np=i.a+=A.cg(n)\nj.a=\", \"}k.d.D(0,new A.k6(j,i))\nm=A.cg(k.a)\nl=i.l(0)\nreturn\"NoSuchMethodError: method not found: '\"+k.b.a+\"'\\nReceiver: \"+m+\"\\nArguments: [\"+l+\"]\"}}\nA.hz.prototype={\nl(a){return\"Unsupported operation: \"+this.a}}\nA.hv.prototype={\nl(a){return\"UnimplementedError: \"+this.a}}\nA.bB.prototype={\nl(a){return\"Bad state: \"+this.a}}\nA.fj.prototype={\nl(a){var s=this.a\nif(s==null)return\"Concurrent modification during iteration.\"\nreturn\"Concurrent modification during iteration: \"+A.cg(s)+\".\"}}\nA.h5.prototype={\nl(a){return\"Out of Memory\"},\ngaW(){return null},\n$iQ:1}\nA.e9.prototype={\nl(a){return\"Stack Overflow\"},\ngaW(){return null},\n$iQ:1}\nA.fp.prototype={\nl(a){return\"Reading static variable '\"+this.a+\"' during its initialization\"}}\nA.i0.prototype={\nl(a){return\"Exception: \"+this.a},\n$iac:1}\nA.fC.prototype={\nl(a){var s,r,q,p,o,n,m,l,k,j,i,h=this.a,g=\"\"!==h?\"FormatException: \"+h:\"FormatException\",f=this.c,e=this.b\nif(typeof e==\"string\"){if(f!=null)s=f<0||f>e.length\nelse s=!1\nif(s)f=null\nif(f==null){if(e.length>78)e=B.a.n(e,0,75)+\"...\"\nreturn g+\"\\n\"+e}for(r=1,q=0,p=!1,o=0;o<f;++o){n=B.a.t(e,o)\nif(n===10){if(q!==o||!p)++r\nq=o+1\np=!1}else if(n===13){++r\nq=o+1\np=!0}}g=r>1?g+(\" (at line \"+r+\", character \"+(f-q+1)+\")\\n\"):g+(\" (at character \"+(f+1)+\")\\n\")\nm=e.length\nfor(o=f;o<m;++o){n=B.a.B(e,o)\nif(n===10||n===13){m=o\nbreak}}if(m-q>78)if(f-q<75){l=q+75\nk=q\nj=\"\"\ni=\"...\"}else{if(m-f<75){k=m-75\nl=m\ni=\"\"}else{k=f-36\nl=f+36\ni=\"...\"}j=\"...\"}else{l=m\nk=q\nj=\"\"\ni=\"\"}return g+j+B.a.n(e,k,l)+i+\"\\n\"+B.a.bg(\" \",f-k+j.length)+\"^\\n\"}else return f!=null?g+(\" (at offset \"+A.q(f)+\")\"):g},\n$iac:1}\nA.fG.prototype={\ngaW(){return null},\nl(a){return\"IntegerDivisionByZeroException\"},\n$iQ:1,\n$iac:1}\nA.e.prototype={\nbz(a,b){return A.fe(this,A.v(this).h(\"e.E\"),b)},\naj(a,b,c){var s=A.v(this)\nreturn A.nD(this,s.q(c).h(\"1(e.E)\").a(b),s.h(\"e.E\"),c)},\nS(a,b){var s\nfor(s=this.gE(this);s.p();)if(J.a7(s.gu(s),b))return!0\nreturn!1},\nD(a,b){var s\nA.v(this).h(\"~(e.E)\").a(b)\nfor(s=this.gE(this);s.p();)b.$1(s.gu(s))},\nbQ(a,b){return A.fM(this,b,A.v(this).h(\"e.E\"))},\ngj(a){var s,r=this.gE(this)\nfor(s=0;r.p();)++s\nreturn s},\ngC(a){return!this.gE(this).p()},\ngP(a){return!this.gC(this)},\na2(a,b){return A.p0(this,b,A.v(this).h(\"e.E\"))},\ngA(a){var s=this.gE(this)\nif(!s.p())throw A.b(A.bt())\nreturn s.gu(s)},\nv(a,b){var s,r,q\nA.aT(b,\"index\")\nfor(s=this.gE(this),r=0;s.p();){q=s.gu(s)\nif(b===r)return q;++r}throw A.b(A.V(b,r,this,null,\"index\"))},\nl(a){return A.rp(this,\"(\",\")\")}}\nA.L.prototype={}\nA.a4.prototype={\nl(a){return\"MapEntry(\"+A.q(this.a)+\": \"+A.q(this.b)+\")\"}}\nA.R.prototype={\ngI(a){return A.r.prototype.gI.call(this,this)},\nl(a){return\"null\"}}\nA.r.prototype={$ir:1,\nW(a,b){return this===b},\ngI(a){return A.dZ(this)},\nl(a){return\"Instance of '\"+A.kb(this)+\"'\"},\ne2(a,b){t.bg.a(b)\nthrow A.b(A.rC(this,b.ge0(),b.ge5(),b.ge1(),null))},\ngN(a){return A.oh(this)},\ntoString(){return this.l(this)}}\nA.iI.prototype={\nl(a){return\"\"},\n$iaG:1}\nA.ah.prototype={\ngj(a){return this.a.length},\nl(a){var s=this.a\nreturn s.charCodeAt(0)==0?s:s},\n$itj:1}\nA.lh.prototype={\n$2(a,b){throw A.b(A.ad(\"Illegal IPv4 address, \"+a,this.a,b))},\n$S:28}\nA.lj.prototype={\n$2(a,b){throw A.b(A.ad(\"Illegal IPv6 address, \"+a,this.a,b))},\n$S:44}\nA.lk.prototype={\n$2(a,b){var s\nif(b-a>4)this.a.$2(\"an IPv6 part can only contain a maximum of 4 hex digits\",a)\ns=A.nd(B.a.n(this.b,a,b),16)\nif(s<0||s>65535)this.a.$2(\"each part must be in the range of `0x0..0xFFFF`\",a)\nreturn s},\n$S:8}\nA.eR.prototype={\ngdD(){var s,r,q,p,o=this,n=o.w\nif(n===$){s=o.a\nr=s.length!==0?\"\"+s+\":\":\"\"\nq=o.c\np=q==null\nif(!p||s===\"file\"){s=r+\"//\"\nr=o.b\nif(r.length!==0)s=s+r+\"@\"\nif(!p)s+=q\nr=o.d\nif(r!=null)s=s+\":\"+A.q(r)}else s=r\ns+=o.e\nr=o.f\nif(r!=null)s=s+\"?\"+r\nr=o.r\nif(r!=null)s=s+\"#\"+r\nn!==$&&A.nm(\"_text\")\nn=o.w=s.charCodeAt(0)==0?s:s}return n},\ngcJ(){var s,r,q=this,p=q.x\nif(p===$){s=q.e\nif(s.length!==0&&B.a.t(s,0)===47)s=B.a.O(s,1)\nr=s.length===0?B.q:A.fN(new A.af(A.t(s.split(\"/\"),t.s),t.ha.a(A.v6()),t.iZ),t.N)\nq.x!==$&&A.nm(\"pathSegments\")\nq.seE(r)\np=r}return p},\ngI(a){var s,r=this,q=r.y\nif(q===$){s=B.a.gI(r.gdD())\nr.y!==$&&A.nm(\"hashCode\")\nr.y=s\nq=s}return q},\ngbe(){return this.b},\ngah(a){var s=this.c\nif(s==null)return\"\"\nif(B.a.J(s,\"[\"))return B.a.n(s,1,s.length-1)\nreturn s},\ngaP(a){var s=this.d\nreturn s==null?A.pu(this.a):s},\ngaw(a){var s=this.f\nreturn s==null?\"\":s},\ngbE(){var s=this.r\nreturn s==null?\"\":s},\nh6(a){var s=this.a\nif(a.length!==s.length)return!1\nreturn A.uo(a,s,0)>=0},\ndk(a,b){var s,r,q,p,o,n\nfor(s=0,r=0;B.a.H(b,\"../\",r);){r+=3;++s}q=B.a.cG(a,\"/\")\nwhile(!0){if(!(q>0&&s>0))break\np=B.a.e_(a,\"/\",q-1)\nif(p<0)break\no=q-p\nn=o!==2\nif(!n||o===3)if(B.a.B(a,p+1)===46)n=!n||B.a.B(a,p+2)===46\nelse n=!1\nelse n=!1\nif(n)break;--s\nq=p}return B.a.az(a,q+1,null,B.a.O(b,r-3*s))},\ne9(a){return this.bd(A.li(a))},\nbd(a){var s,r,q,p,o,n,m,l,k,j,i=this,h=null\nif(a.gal().length!==0){s=a.gal()\nif(a.gb9()){r=a.gbe()\nq=a.gah(a)\np=a.gba()?a.gaP(a):h}else{p=h\nq=p\nr=\"\"}o=A.bJ(a.gX(a))\nn=a.gaK()?a.gaw(a):h}else{s=i.a\nif(a.gb9()){r=a.gbe()\nq=a.gah(a)\np=A.o3(a.gba()?a.gaP(a):h,s)\no=A.bJ(a.gX(a))\nn=a.gaK()?a.gaw(a):h}else{r=i.b\nq=i.c\np=i.d\no=i.e\nif(a.gX(a)===\"\")n=a.gaK()?a.gaw(a):i.f\nelse{m=A.uc(i,o)\nif(m>0){l=B.a.n(o,0,m)\no=a.gbG()?l+A.bJ(a.gX(a)):l+A.bJ(i.dk(B.a.O(o,l.length),a.gX(a)))}else if(a.gbG())o=A.bJ(a.gX(a))\nelse if(o.length===0)if(q==null)o=s.length===0?a.gX(a):A.bJ(a.gX(a))\nelse o=A.bJ(\"/\"+a.gX(a))\nelse{k=i.dk(o,a.gX(a))\nj=s.length===0\nif(!j||q!=null||B.a.J(o,\"/\"))o=A.bJ(k)\nelse o=A.o5(k,!j||q!=null)}n=a.gaK()?a.gaw(a):h}}}return A.mG(s,r,q,p,o,n,a.gcA()?a.gbE():h)},\ngb9(){return this.c!=null},\ngba(){return this.d!=null},\ngaK(){return this.f!=null},\ngcA(){return this.r!=null},\ngbG(){return B.a.J(this.e,\"/\")},\ncS(){var s,r=this,q=r.a\nif(q!==\"\"&&q!==\"file\")throw A.b(A.x(\"Cannot extract a file path from a \"+q+\" URI\"))\nq=r.f\nif((q==null?\"\":q)!==\"\")throw A.b(A.x(u.i))\nq=r.r\nif((q==null?\"\":q)!==\"\")throw A.b(A.x(u.l))\nq=$.op()\nif(A.aK(q))q=A.pF(r)\nelse{if(r.c!=null&&r.gah(r)!==\"\")A.J(A.x(u.j))\ns=r.gcJ()\nA.u5(s,!1)\nq=A.lb(B.a.J(r.e,\"/\")?\"\"+\"/\":\"\",s,\"/\")\nq=q.charCodeAt(0)==0?q:q}return q},\nl(a){return this.gdD()},\nW(a,b){var s,r,q=this\nif(b==null)return!1\nif(q===b)return!0\nif(t.jJ.b(b))if(q.a===b.gal())if(q.c!=null===b.gb9())if(q.b===b.gbe())if(q.gah(q)===b.gah(b))if(q.gaP(q)===b.gaP(b))if(q.e===b.gX(b)){s=q.f\nr=s==null\nif(!r===b.gaK()){if(r)s=\"\"\nif(s===b.gaw(b)){s=q.r\nr=s==null\nif(!r===b.gcA()){if(r)s=\"\"\ns=s===b.gbE()}else s=!1}else s=!1}else s=!1}else s=!1\nelse s=!1\nelse s=!1\nelse s=!1\nelse s=!1\nelse s=!1\nelse s=!1\nreturn s},\nseE(a){this.x=t.i.a(a)},\n$ihA:1,\ngal(){return this.a},\ngX(a){return this.e}}\nA.lg.prototype={\nged(){var s,r,q,p,o=this,n=null,m=o.c\nif(m==null){m=o.b\nif(0>=m.length)return A.d(m,0)\ns=o.a\nm=m[0]+1\nr=B.a.aq(s,\"?\",m)\nq=s.length\nif(r>=0){p=A.eS(s,r+1,q,B.k,!1,!1)\nq=r}else p=n\nm=o.c=new A.hU(\"data\",\"\",n,n,A.eS(s,m,q,B.B,!1,!1),p,n)}return m},\nl(a){var s,r=this.b\nif(0>=r.length)return A.d(r,0)\ns=this.a\nreturn r[0]===-1?\"data:\"+s:s}}\nA.mR.prototype={\n$2(a,b){var s=this.a\nif(!(a<s.length))return A.d(s,a)\ns=s[a]\nB.e.dT(s,0,96,b)\nreturn s},\n$S:60}\nA.mS.prototype={\n$3(a,b,c){var s,r,q\nfor(s=b.length,r=0;r<s;++r){q=B.a.t(b,r)^96\nif(!(q<96))return A.d(a,q)\na[q]=c}},\n$S:14}\nA.mT.prototype={\n$3(a,b,c){var s,r,q\nfor(s=B.a.t(b,0),r=B.a.t(b,1);s<=r;++s){q=(s^96)>>>0\nif(!(q<96))return A.d(a,q)\na[q]=c}},\n$S:14}\nA.b5.prototype={\ngb9(){return this.c>0},\ngba(){return this.c>0&&this.d+1<this.e},\ngaK(){return this.f<this.r},\ngcA(){return this.r<this.a.length},\ngbG(){return B.a.H(this.a,\"/\",this.e)},\ngal(){var s=this.w\nreturn s==null?this.w=this.eO():s},\neO(){var s,r=this,q=r.b\nif(q<=0)return\"\"\ns=q===4\nif(s&&B.a.J(r.a,\"http\"))return\"http\"\nif(q===5&&B.a.J(r.a,\"https\"))return\"https\"\nif(s&&B.a.J(r.a,\"file\"))return\"file\"\nif(q===7&&B.a.J(r.a,\"package\"))return\"package\"\nreturn B.a.n(r.a,0,q)},\ngbe(){var s=this.c,r=this.b+3\nreturn s>r?B.a.n(this.a,r,s-1):\"\"},\ngah(a){var s=this.c\nreturn s>0?B.a.n(this.a,s,this.d):\"\"},\ngaP(a){var s,r=this\nif(r.gba())return A.nd(B.a.n(r.a,r.d+1,r.e),null)\ns=r.b\nif(s===4&&B.a.J(r.a,\"http\"))return 80\nif(s===5&&B.a.J(r.a,\"https\"))return 443\nreturn 0},\ngX(a){return B.a.n(this.a,this.e,this.f)},\ngaw(a){var s=this.f,r=this.r\nreturn s<r?B.a.n(this.a,s+1,r):\"\"},\ngbE(){var s=this.r,r=this.a\nreturn s<r.length?B.a.O(r,s+1):\"\"},\ngcJ(){var s,r,q=this.e,p=this.f,o=this.a\nif(B.a.H(o,\"/\",q))++q\nif(q===p)return B.q\ns=A.t([],t.s)\nfor(r=q;r<p;++r)if(B.a.B(o,r)===47){B.b.m(s,B.a.n(o,q,r))\nq=r+1}B.b.m(s,B.a.n(o,q,p))\nreturn A.fN(s,t.N)},\ndi(a){var s=this.d+1\nreturn s+a.length===this.e&&B.a.H(this.a,a,s)},\nhj(){var s=this,r=s.r,q=s.a\nif(r>=q.length)return s\nreturn new A.b5(B.a.n(q,0,r),s.b,s.c,s.d,s.e,s.f,r,s.w)},\ne9(a){return this.bd(A.li(a))},\nbd(a){if(a instanceof A.b5)return this.fo(this,a)\nreturn this.dF().bd(a)},\nfo(a,b){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c=b.b\nif(c>0)return b\ns=b.c\nif(s>0){r=a.b\nif(r<=0)return b\nq=r===4\nif(q&&B.a.J(a.a,\"file\"))p=b.e!==b.f\nelse if(q&&B.a.J(a.a,\"http\"))p=!b.di(\"80\")\nelse p=!(r===5&&B.a.J(a.a,\"https\"))||!b.di(\"443\")\nif(p){o=r+1\nreturn new A.b5(B.a.n(a.a,0,o)+B.a.O(b.a,c+1),r,s+o,b.d+o,b.e+o,b.f+o,b.r+o,a.w)}else return this.dF().bd(b)}n=b.e\nc=b.f\nif(n===c){s=b.r\nif(c<s){r=a.f\no=r-c\nreturn new A.b5(B.a.n(a.a,0,r)+B.a.O(b.a,c),a.b,a.c,a.d,a.e,c+o,s+o,a.w)}c=b.a\nif(s<c.length){r=a.r\nreturn new A.b5(B.a.n(a.a,0,r)+B.a.O(c,s),a.b,a.c,a.d,a.e,a.f,s+(r-s),a.w)}return a.hj()}s=b.a\nif(B.a.H(s,\"/\",n)){m=a.e\nl=A.po(this)\nk=l>0?l:m\no=k-n\nreturn new A.b5(B.a.n(a.a,0,k)+B.a.O(s,n),a.b,a.c,a.d,m,c+o,b.r+o,a.w)}j=a.e\ni=a.f\nif(j===i&&a.c>0){for(;B.a.H(s,\"../\",n);)n+=3\no=j-n+1\nreturn new A.b5(B.a.n(a.a,0,j)+\"/\"+B.a.O(s,n),a.b,a.c,a.d,j,c+o,b.r+o,a.w)}h=a.a\nl=A.po(this)\nif(l>=0)g=l\nelse for(g=j;B.a.H(h,\"../\",g);)g+=3\nf=0\nwhile(!0){e=n+3\nif(!(e<=c&&B.a.H(s,\"../\",n)))break;++f\nn=e}for(d=\"\";i>g;){--i\nif(B.a.B(h,i)===47){if(f===0){d=\"/\"\nbreak}--f\nd=\"/\"}}if(i===g&&a.b<=0&&!B.a.H(h,\"/\",j)){n-=f*3\nd=\"\"}o=i-n+d.length\nreturn new A.b5(B.a.n(h,0,i)+d+B.a.O(s,n),a.b,a.c,a.d,j,c+o,b.r+o,a.w)},\ncS(){var s,r,q=this,p=q.b\nif(p>=0){s=!(p===4&&B.a.J(q.a,\"file\"))\np=s}else p=!1\nif(p)throw A.b(A.x(\"Cannot extract a file path from a \"+q.gal()+\" URI\"))\np=q.f\ns=q.a\nif(p<s.length){if(p<q.r)throw A.b(A.x(u.i))\nthrow A.b(A.x(u.l))}r=$.op()\nif(A.aK(r))p=A.pF(q)\nelse{if(q.c<q.d)A.J(A.x(u.j))\np=B.a.n(s,q.e,p)}return p},\ngI(a){var s=this.x\nreturn s==null?this.x=B.a.gI(this.a):s},\nW(a,b){if(b==null)return!1\nif(this===b)return!0\nreturn t.jJ.b(b)&&this.a===b.l(0)},\ndF(){var s=this,r=null,q=s.gal(),p=s.gbe(),o=s.c>0?s.gah(s):r,n=s.gba()?s.gaP(s):r,m=s.a,l=s.f,k=B.a.n(m,s.e,l),j=s.r\nl=l<j?s.gaw(s):r\nreturn A.mG(q,p,o,n,k,l,j<m.length?s.gbE():r)},\nl(a){return this.a},\n$ihA:1}\nA.hU.prototype={}\nA.o.prototype={}\nA.f4.prototype={\ngj(a){return a.length}}\nA.f5.prototype={\nl(a){var s=String(a)\ns.toString\nreturn s}}\nA.f6.prototype={\nl(a){var s=String(a)\ns.toString\nreturn s}}\nA.bR.prototype={$ibR:1}\nA.bi.prototype={\ngj(a){return a.length}}\nA.fm.prototype={\ngj(a){return a.length}}\nA.P.prototype={$iP:1}\nA.cE.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s}}\nA.jx.prototype={}\nA.aq.prototype={}\nA.bb.prototype={}\nA.fn.prototype={\ngj(a){return a.length}}\nA.fo.prototype={\ngj(a){return a.length}}\nA.fq.prototype={\ngj(a){return a.length}}\nA.ft.prototype={\nl(a){var s=String(a)\ns.toString\nreturn s}}\nA.dz.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length,r=b>>>0!==b||b>=s\nr.toString\nif(r)throw A.b(A.V(b,s,a,null,null))\ns=a[b]\ns.toString\nreturn s},\nk(a,b,c){t.q.a(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){var s\nif(a.length>0){s=a[0]\ns.toString\nreturn s}throw A.b(A.K(\"No elements\"))},\nv(a,b){if(!(b>=0&&b<a.length))return A.d(a,b)\nreturn a[b]},\n$ik:1,\n$iF:1,\n$ie:1,\n$im:1}\nA.dA.prototype={\nl(a){var s,r=a.left\nr.toString\ns=a.top\ns.toString\nreturn\"Rectangle (\"+A.q(r)+\", \"+A.q(s)+\") \"+A.q(this.gaT(a))+\" x \"+A.q(this.gaL(a))},\nW(a,b){var s,r\nif(b==null)return!1\nif(t.q.b(b)){s=a.left\ns.toString\nr=b.left\nr.toString\nif(s===r){s=a.top\ns.toString\nr=b.top\nr.toString\nif(s===r){s=J.a2(b)\ns=this.gaT(a)===s.gaT(b)&&this.gaL(a)===s.gaL(b)}else s=!1}else s=!1}else s=!1\nreturn s},\ngI(a){var s,r=a.left\nr.toString\ns=a.top\ns.toString\nreturn A.oP(r,s,this.gaT(a),this.gaL(a))},\ngdh(a){return a.height},\ngaL(a){var s=this.gdh(a)\ns.toString\nreturn s},\ngdJ(a){return a.width},\ngaT(a){var s=this.gdJ(a)\ns.toString\nreturn s},\n$ibk:1}\nA.fu.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length,r=b>>>0!==b||b>=s\nr.toString\nif(r)throw A.b(A.V(b,s,a,null,null))\ns=a[b]\ns.toString\nreturn s},\nk(a,b,c){A.S(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){var s\nif(a.length>0){s=a[0]\ns.toString\nreturn s}throw A.b(A.K(\"No elements\"))},\nv(a,b){if(!(b>=0&&b<a.length))return A.d(a,b)\nreturn a[b]},\n$ik:1,\n$iF:1,\n$ie:1,\n$im:1}\nA.fv.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s}}\nA.n.prototype={\nl(a){var s=a.localName\ns.toString\nreturn s}}\nA.l.prototype={$il:1}\nA.f.prototype={\nco(a,b,c,d){t.o.a(c)\nif(c!=null)this.eH(a,b,c,d)},\nfB(a,b,c){return this.co(a,b,c,null)},\neH(a,b,c,d){return a.addEventListener(b,A.c8(t.o.a(c),1),d)},\nff(a,b,c,d){return a.removeEventListener(b,A.c8(t.o.a(c),1),!1)},\n$if:1}\nA.az.prototype={$iaz:1}\nA.cI.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length,r=b>>>0!==b||b>=s\nr.toString\nif(r)throw A.b(A.V(b,s,a,null,null))\ns=a[b]\ns.toString\nreturn s},\nk(a,b,c){t.dY.a(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){var s\nif(a.length>0){s=a[0]\ns.toString\nreturn s}throw A.b(A.K(\"No elements\"))},\nv(a,b){if(!(b>=0&&b<a.length))return A.d(a,b)\nreturn a[b]},\n$ik:1,\n$iF:1,\n$ie:1,\n$im:1,\n$icI:1}\nA.fz.prototype={\ngj(a){return a.length}}\nA.fB.prototype={\ngj(a){return a.length}}\nA.aA.prototype={$iaA:1}\nA.fD.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s}}\nA.ci.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length,r=b>>>0!==b||b>=s\nr.toString\nif(r)throw A.b(A.V(b,s,a,null,null))\ns=a[b]\ns.toString\nreturn s},\nk(a,b,c){t.G.a(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){var s\nif(a.length>0){s=a[0]\ns.toString\nreturn s}throw A.b(A.K(\"No elements\"))},\nv(a,b){if(!(b>=0&&b<a.length))return A.d(a,b)\nreturn a[b]},\n$ik:1,\n$iF:1,\n$ie:1,\n$im:1}\nA.cL.prototype={$icL:1}\nA.fO.prototype={\nl(a){var s=String(a)\ns.toString\nreturn s}}\nA.fP.prototype={\ngj(a){return a.length}}\nA.cV.prototype={$icV:1}\nA.ck.prototype={\ne6(a,b){a.postMessage(new A.cx([],[]).Z(b))\nreturn},\nfp(a){return a.start()},\n$ick:1}\nA.fQ.prototype={\nF(a,b){return A.b7(a.get(b))!=null},\ni(a,b){return A.b7(a.get(A.S(b)))},\nD(a,b){var s,r,q\nt.u.a(b)\ns=a.entries()\nfor(;!0;){r=s.next()\nq=r.done\nq.toString\nif(q)return\nq=r.value[0]\nq.toString\nb.$2(q,A.b7(r.value[1]))}},\ngK(a){var s=A.t([],t.s)\nthis.D(a,new A.k2(s))\nreturn s},\ngU(a){var s=A.t([],t.C)\nthis.D(a,new A.k3(s))\nreturn s},\ngj(a){var s=a.size\ns.toString\nreturn s},\ngC(a){var s=a.size\ns.toString\nreturn s===0},\ngP(a){var s=a.size\ns.toString\nreturn s!==0},\nG(a,b){throw A.b(A.x(\"Not supported\"))},\n$iI:1}\nA.k2.prototype={\n$2(a,b){return B.b.m(this.a,a)},\n$S:1}\nA.k3.prototype={\n$2(a,b){return B.b.m(this.a,t.f.a(b))},\n$S:1}\nA.fR.prototype={\nF(a,b){return A.b7(a.get(b))!=null},\ni(a,b){return A.b7(a.get(A.S(b)))},\nD(a,b){var s,r,q\nt.u.a(b)\ns=a.entries()\nfor(;!0;){r=s.next()\nq=r.done\nq.toString\nif(q)return\nq=r.value[0]\nq.toString\nb.$2(q,A.b7(r.value[1]))}},\ngK(a){var s=A.t([],t.s)\nthis.D(a,new A.k4(s))\nreturn s},\ngU(a){var s=A.t([],t.C)\nthis.D(a,new A.k5(s))\nreturn s},\ngj(a){var s=a.size\ns.toString\nreturn s},\ngC(a){var s=a.size\ns.toString\nreturn s===0},\ngP(a){var s=a.size\ns.toString\nreturn s!==0},\nG(a,b){throw A.b(A.x(\"Not supported\"))},\n$iI:1}\nA.k4.prototype={\n$2(a,b){return B.b.m(this.a,a)},\n$S:1}\nA.k5.prototype={\n$2(a,b){return B.b.m(this.a,t.f.a(b))},\n$S:1}\nA.aB.prototype={$iaB:1}\nA.fS.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length,r=b>>>0!==b||b>=s\nr.toString\nif(r)throw A.b(A.V(b,s,a,null,null))\ns=a[b]\ns.toString\nreturn s},\nk(a,b,c){t.ib.a(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){var s\nif(a.length>0){s=a[0]\ns.toString\nreturn s}throw A.b(A.K(\"No elements\"))},\nv(a,b){if(!(b>=0&&b<a.length))return A.d(a,b)\nreturn a[b]},\n$ik:1,\n$iF:1,\n$ie:1,\n$im:1}\nA.G.prototype={\nl(a){var s=a.nodeValue\nreturn s==null?this.eo(a):s},\n$iG:1}\nA.dV.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length,r=b>>>0!==b||b>=s\nr.toString\nif(r)throw A.b(A.V(b,s,a,null,null))\ns=a[b]\ns.toString\nreturn s},\nk(a,b,c){t.G.a(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){var s\nif(a.length>0){s=a[0]\ns.toString\nreturn s}throw A.b(A.K(\"No elements\"))},\nv(a,b){if(!(b>=0&&b<a.length))return A.d(a,b)\nreturn a[b]},\n$ik:1,\n$iF:1,\n$ie:1,\n$im:1}\nA.aC.prototype={\ngj(a){return a.length},\n$iaC:1}\nA.h7.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length,r=b>>>0!==b||b>=s\nr.toString\nif(r)throw A.b(A.V(b,s,a,null,null))\ns=a[b]\ns.toString\nreturn s},\nk(a,b,c){t.d8.a(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){var s\nif(a.length>0){s=a[0]\ns.toString\nreturn s}throw A.b(A.K(\"No elements\"))},\nv(a,b){if(!(b>=0&&b<a.length))return A.d(a,b)\nreturn a[b]},\n$ik:1,\n$iF:1,\n$ie:1,\n$im:1}\nA.hb.prototype={\nF(a,b){return A.b7(a.get(b))!=null},\ni(a,b){return A.b7(a.get(A.S(b)))},\nD(a,b){var s,r,q\nt.u.a(b)\ns=a.entries()\nfor(;!0;){r=s.next()\nq=r.done\nq.toString\nif(q)return\nq=r.value[0]\nq.toString\nb.$2(q,A.b7(r.value[1]))}},\ngK(a){var s=A.t([],t.s)\nthis.D(a,new A.kl(s))\nreturn s},\ngU(a){var s=A.t([],t.C)\nthis.D(a,new A.km(s))\nreturn s},\ngj(a){var s=a.size\ns.toString\nreturn s},\ngC(a){var s=a.size\ns.toString\nreturn s===0},\ngP(a){var s=a.size\ns.toString\nreturn s!==0},\nG(a,b){throw A.b(A.x(\"Not supported\"))},\n$iI:1}\nA.kl.prototype={\n$2(a,b){return B.b.m(this.a,a)},\n$S:1}\nA.km.prototype={\n$2(a,b){return B.b.m(this.a,t.f.a(b))},\n$S:1}\nA.hd.prototype={\ngj(a){return a.length}}\nA.cY.prototype={$icY:1}\nA.cZ.prototype={$icZ:1}\nA.aD.prototype={$iaD:1}\nA.hf.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length,r=b>>>0!==b||b>=s\nr.toString\nif(r)throw A.b(A.V(b,s,a,null,null))\ns=a[b]\ns.toString\nreturn s},\nk(a,b,c){t.ls.a(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){var s\nif(a.length>0){s=a[0]\ns.toString\nreturn s}throw A.b(A.K(\"No elements\"))},\nv(a,b){if(!(b>=0&&b<a.length))return A.d(a,b)\nreturn a[b]},\n$ik:1,\n$iF:1,\n$ie:1,\n$im:1}\nA.aE.prototype={$iaE:1}\nA.hg.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length,r=b>>>0!==b||b>=s\nr.toString\nif(r)throw A.b(A.V(b,s,a,null,null))\ns=a[b]\ns.toString\nreturn s},\nk(a,b,c){t.cA.a(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){var s\nif(a.length>0){s=a[0]\ns.toString\nreturn s}throw A.b(A.K(\"No elements\"))},\nv(a,b){if(!(b>=0&&b<a.length))return A.d(a,b)\nreturn a[b]},\n$ik:1,\n$iF:1,\n$ie:1,\n$im:1}\nA.aF.prototype={\ngj(a){return a.length},\n$iaF:1}\nA.hl.prototype={\nF(a,b){return a.getItem(b)!=null},\ni(a,b){return a.getItem(A.S(b))},\nG(a,b){var s=a.getItem(b)\na.removeItem(b)\nreturn s},\nD(a,b){var s,r,q\nt.bm.a(b)\nfor(s=0;!0;++s){r=a.key(s)\nif(r==null)return\nq=a.getItem(r)\nq.toString\nb.$2(r,q)}},\ngK(a){var s=A.t([],t.s)\nthis.D(a,new A.l5(s))\nreturn s},\ngU(a){var s=A.t([],t.s)\nthis.D(a,new A.l6(s))\nreturn s},\ngj(a){var s=a.length\ns.toString\nreturn s},\ngC(a){return a.key(0)==null},\ngP(a){return a.key(0)!=null},\n$iI:1}\nA.l5.prototype={\n$2(a,b){return B.b.m(this.a,a)},\n$S:24}\nA.l6.prototype={\n$2(a,b){return B.b.m(this.a,b)},\n$S:24}\nA.al.prototype={$ial:1}\nA.aH.prototype={$iaH:1}\nA.am.prototype={$iam:1}\nA.hp.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length,r=b>>>0!==b||b>=s\nr.toString\nif(r)throw A.b(A.V(b,s,a,null,null))\ns=a[b]\ns.toString\nreturn s},\nk(a,b,c){t.gJ.a(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){var s\nif(a.length>0){s=a[0]\ns.toString\nreturn s}throw A.b(A.K(\"No elements\"))},\nv(a,b){if(!(b>=0&&b<a.length))return A.d(a,b)\nreturn a[b]},\n$ik:1,\n$iF:1,\n$ie:1,\n$im:1}\nA.hq.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length,r=b>>>0!==b||b>=s\nr.toString\nif(r)throw A.b(A.V(b,s,a,null,null))\ns=a[b]\ns.toString\nreturn s},\nk(a,b,c){t.dR.a(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){var s\nif(a.length>0){s=a[0]\ns.toString\nreturn s}throw A.b(A.K(\"No elements\"))},\nv(a,b){if(!(b>=0&&b<a.length))return A.d(a,b)\nreturn a[b]},\n$ik:1,\n$iF:1,\n$ie:1,\n$im:1}\nA.hr.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s}}\nA.aI.prototype={$iaI:1}\nA.hs.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length,r=b>>>0!==b||b>=s\nr.toString\nif(r)throw A.b(A.V(b,s,a,null,null))\ns=a[b]\ns.toString\nreturn s},\nk(a,b,c){t.ki.a(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){var s\nif(a.length>0){s=a[0]\ns.toString\nreturn s}throw A.b(A.K(\"No elements\"))},\nv(a,b){if(!(b>=0&&b<a.length))return A.d(a,b)\nreturn a[b]},\n$ik:1,\n$iF:1,\n$ie:1,\n$im:1}\nA.ht.prototype={\ngj(a){return a.length}}\nA.hB.prototype={\nl(a){var s=String(a)\ns.toString\nreturn s}}\nA.hD.prototype={\ngj(a){return a.length}}\nA.c1.prototype={}\nA.hS.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length,r=b>>>0!==b||b>=s\nr.toString\nif(r)throw A.b(A.V(b,s,a,null,null))\ns=a[b]\ns.toString\nreturn s},\nk(a,b,c){t.d5.a(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){var s\nif(a.length>0){s=a[0]\ns.toString\nreturn s}throw A.b(A.K(\"No elements\"))},\nv(a,b){if(!(b>=0&&b<a.length))return A.d(a,b)\nreturn a[b]},\n$ik:1,\n$iF:1,\n$ie:1,\n$im:1}\nA.eo.prototype={\nl(a){var s,r,q,p=a.left\np.toString\ns=a.top\ns.toString\nr=a.width\nr.toString\nq=a.height\nq.toString\nreturn\"Rectangle (\"+A.q(p)+\", \"+A.q(s)+\") \"+A.q(r)+\" x \"+A.q(q)},\nW(a,b){var s,r\nif(b==null)return!1\nif(t.q.b(b)){s=a.left\ns.toString\nr=b.left\nr.toString\nif(s===r){s=a.top\ns.toString\nr=b.top\nr.toString\nif(s===r){s=a.width\ns.toString\nr=J.a2(b)\nif(s===r.gaT(b)){s=a.height\ns.toString\nr=s===r.gaL(b)\ns=r}else s=!1}else s=!1}else s=!1}else s=!1\nreturn s},\ngI(a){var s,r,q,p=a.left\np.toString\ns=a.top\ns.toString\nr=a.width\nr.toString\nq=a.height\nq.toString\nreturn A.oP(p,s,r,q)},\ngdh(a){return a.height},\ngaL(a){var s=a.height\ns.toString\nreturn s},\ngdJ(a){return a.width},\ngaT(a){var s=a.width\ns.toString\nreturn s}}\nA.i5.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length,r=b>>>0!==b||b>=s\nr.toString\nif(r)throw A.b(A.V(b,s,a,null,null))\nreturn a[b]},\nk(a,b,c){t.ef.a(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){if(a.length>0)return a[0]\nthrow A.b(A.K(\"No elements\"))},\nv(a,b){if(!(b>=0&&b<a.length))return A.d(a,b)\nreturn a[b]},\n$ik:1,\n$iF:1,\n$ie:1,\n$im:1}\nA.ez.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length,r=b>>>0!==b||b>=s\nr.toString\nif(r)throw A.b(A.V(b,s,a,null,null))\ns=a[b]\ns.toString\nreturn s},\nk(a,b,c){t.G.a(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){var s\nif(a.length>0){s=a[0]\ns.toString\nreturn s}throw A.b(A.K(\"No elements\"))},\nv(a,b){if(!(b>=0&&b<a.length))return A.d(a,b)\nreturn a[b]},\n$ik:1,\n$iF:1,\n$ie:1,\n$im:1}\nA.iA.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length,r=b>>>0!==b||b>=s\nr.toString\nif(r)throw A.b(A.V(b,s,a,null,null))\ns=a[b]\ns.toString\nreturn s},\nk(a,b,c){t.hI.a(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){var s\nif(a.length>0){s=a[0]\ns.toString\nreturn s}throw A.b(A.K(\"No elements\"))},\nv(a,b){if(!(b>=0&&b<a.length))return A.d(a,b)\nreturn a[b]},\n$ik:1,\n$iF:1,\n$ie:1,\n$im:1}\nA.iJ.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length,r=b>>>0!==b||b>=s\nr.toString\nif(r)throw A.b(A.V(b,s,a,null,null))\ns=a[b]\ns.toString\nreturn s},\nk(a,b,c){t.lv.a(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){var s\nif(a.length>0){s=a[0]\ns.toString\nreturn s}throw A.b(A.K(\"No elements\"))},\nv(a,b){if(!(b>=0&&b<a.length))return A.d(a,b)\nreturn a[b]},\n$ik:1,\n$iF:1,\n$ie:1,\n$im:1}\nA.nu.prototype={}\nA.lM.prototype={\ncH(a,b,c,d){var s=this.$ti\ns.h(\"~(1)?\").a(a)\nt.Z.a(c)\nreturn A.bf(this.a,this.b,a,!1,s.c)}}\nA.eq.prototype={\nY(a){var s=this\nif(s.b==null)return $.np()\ns.dI()\ns.b=null\ns.sdq(null)\nreturn $.np()},\ne3(a){var s,r=this\nr.$ti.h(\"~(1)?\").a(a)\nif(r.b==null)throw A.b(A.K(\"Subscription has been canceled.\"))\nr.dI()\ns=A.q4(new A.lO(a),t.A)\nr.sdq(s)\nr.dG()},\ndG(){var s,r=this,q=r.d\nif(q!=null&&r.a<=0){s=r.b\ns.toString\nJ.qQ(s,r.c,q,!1)}},\ndI(){var s,r=this.d\nif(r!=null){s=this.b\ns.toString\nJ.qN(s,this.c,t.o.a(r),!1)}},\nsdq(a){this.d=t.o.a(a)}}\nA.lN.prototype={\n$1(a){return this.a.$1(t.A.a(a))},\n$S:2}\nA.lO.prototype={\n$1(a){return this.a.$1(t.A.a(a))},\n$S:2}\nA.u.prototype={\ngE(a){return new A.dD(a,this.gj(a),A.a0(a).h(\"dD<u.E>\"))},\nT(a,b,c,d,e){A.a0(a).h(\"e<u.E>\").a(d)\nthrow A.b(A.x(\"Cannot setRange on immutable List.\"))},\na6(a,b,c,d){return this.T(a,b,c,d,0)}}\nA.dD.prototype={\np(){var s=this,r=s.c+1,q=s.b\nif(r<q){s.sdc(J.ab(s.a,r))\ns.c=r\nreturn!0}s.sdc(null)\ns.c=q\nreturn!1},\ngu(a){var s=this.d\nreturn s==null?this.$ti.c.a(s):s},\nsdc(a){this.d=this.$ti.h(\"1?\").a(a)},\n$iL:1}\nA.hT.prototype={}\nA.hW.prototype={}\nA.hX.prototype={}\nA.hY.prototype={}\nA.hZ.prototype={}\nA.i1.prototype={}\nA.i2.prototype={}\nA.i6.prototype={}\nA.i7.prototype={}\nA.id.prototype={}\nA.ie.prototype={}\nA.ig.prototype={}\nA.ih.prototype={}\nA.ii.prototype={}\nA.ij.prototype={}\nA.io.prototype={}\nA.ip.prototype={}\nA.ix.prototype={}\nA.eF.prototype={}\nA.eG.prototype={}\nA.iy.prototype={}\nA.iz.prototype={}\nA.iC.prototype={}\nA.iL.prototype={}\nA.iM.prototype={}\nA.eL.prototype={}\nA.eM.prototype={}\nA.iN.prototype={}\nA.iO.prototype={}\nA.iU.prototype={}\nA.iV.prototype={}\nA.iW.prototype={}\nA.iX.prototype={}\nA.iY.prototype={}\nA.iZ.prototype={}\nA.j_.prototype={}\nA.j0.prototype={}\nA.j1.prototype={}\nA.j2.prototype={}\nA.mA.prototype={\naJ(a){var s,r=this.a,q=r.length\nfor(s=0;s<q;++s)if(r[s]===a)return s\nB.b.m(r,a)\nB.b.m(this.b,null)\nreturn q},\nZ(a){var s,r,q,p,o=this,n={}\nif(a==null)return a\nif(A.cz(a))return a\nif(typeof a==\"number\")return a\nif(typeof a==\"string\")return a\nif(a instanceof A.bU)return new Date(a.a)\nif(t.kl.b(a))throw A.b(A.hw(\"structured clone of RegExp\"))\nif(t.dY.b(a))return a\nif(t.w.b(a))return a\nif(t.kL.b(a))return a\nif(t.ad.b(a))return a\nif(t.hH.b(a)||t.hK.b(a)||t.oA.b(a)||t.kI.b(a))return a\nif(t.f.b(a)){s=o.aJ(a)\nr=o.b\nif(!(s<r.length))return A.d(r,s)\nq=n.a=r[s]\nif(q!=null)return q\nq={}\nn.a=q\nB.b.k(r,s,q)\nJ.bo(a,new A.mB(n,o))\nreturn n.a}if(t.j.b(a)){s=o.aJ(a)\nn=o.b\nif(!(s<n.length))return A.d(n,s)\nq=n[s]\nif(q!=null)return q\nreturn o.fK(a,s)}if(t.bp.b(a)){s=o.aJ(a)\nr=o.b\nif(!(s<r.length))return A.d(r,s)\nq=n.b=r[s]\nif(q!=null)return q\np={}\np.toString\nn.b=p\nB.b.k(r,s,p)\no.fV(a,new A.mC(n,o))\nreturn n.b}throw A.b(A.hw(\"structured clone of other type\"))},\nfK(a,b){var s,r=J.T(a),q=r.gj(a),p=new Array(q)\np.toString\nB.b.k(this.b,b,p)\nfor(s=0;s<q;++s)B.b.k(p,s,this.Z(r.i(a,s)))\nreturn p}}\nA.mB.prototype={\n$2(a,b){this.a.a[a]=this.b.Z(b)},\n$S:7}\nA.mC.prototype={\n$2(a,b){this.a.b[a]=this.b.Z(b)},\n$S:29}\nA.lw.prototype={\naJ(a){var s,r=this.a,q=r.length\nfor(s=0;s<q;++s)if(r[s]===a)return s\nB.b.m(r,a)\nB.b.m(this.b,null)\nreturn q},\nZ(a){var s,r,q,p,o,n,m,l,k,j=this\nif(a==null)return a\nif(A.cz(a))return a\nif(typeof a==\"number\")return a\nif(typeof a==\"string\")return a\ns=a instanceof Date\ns.toString\nif(s){s=a.getTime()\ns.toString\nif(Math.abs(s)<=864e13)r=!1\nelse r=!0\nif(r)A.J(A.ao(\"DateTime is outside valid range: \"+s,null))\nA.c7(!0,\"isUtc\",t.y)\nreturn new A.bU(s,!0)}s=a instanceof RegExp\ns.toString\nif(s)throw A.b(A.hw(\"structured clone of RegExp\"))\ns=typeof Promise!=\"undefined\"&&a instanceof Promise\ns.toString\nif(s)return A.j8(a,t.z)\nif(A.qh(a)){q=j.aJ(a)\ns=j.b\nif(!(q<s.length))return A.d(s,q)\np=s[q]\nif(p!=null)return p\nr=t.z\no=A.X(r,r)\nB.b.k(s,q,o)\nj.fU(a,new A.lx(j,o))\nreturn o}s=a instanceof Array\ns.toString\nif(s){s=a\ns.toString\nq=j.aJ(s)\nr=j.b\nif(!(q<r.length))return A.d(r,q)\np=r[q]\nif(p!=null)return p\nn=J.T(s)\nm=n.gj(s)\nif(j.c){l=new Array(m)\nl.toString\np=l}else p=s\nB.b.k(r,q,p)\nfor(r=J.b8(p),k=0;k<m;++k)r.k(p,k,j.Z(n.i(s,k)))\nreturn p}return a},\naF(a,b){this.c=b\nreturn this.Z(a)}}\nA.lx.prototype={\n$2(a,b){var s=this.a.Z(b)\nthis.b.k(0,a,s)\nreturn s},\n$S:30}\nA.mQ.prototype={\n$1(a){this.a.push(A.pL(a))},\n$S:4}\nA.n5.prototype={\n$2(a,b){this.a[a]=A.pL(b)},\n$S:7}\nA.cx.prototype={\nfV(a,b){var s,r,q,p\nt.p1.a(b)\nfor(s=Object.keys(a),r=s.length,q=0;q<s.length;s.length===r||(0,A.aM)(s),++q){p=s[q]\nb.$2(p,a[p])}}}\nA.c2.prototype={\nfU(a,b){var s,r,q,p\nt.p1.a(b)\nfor(s=Object.keys(a),r=s.length,q=0;q<s.length;s.length===r||(0,A.aM)(s),++q){p=s[q]\nb.$2(p,a[p])}}}\nA.bT.prototype={\ncV(a,b){var s,r,q,p\ntry{q=a.update(new A.cx([],[]).Z(b))\nq.toString\nq=A.j3(q,t.z)\nreturn q}catch(p){s=A.M(p)\nr=A.a_(p)\nq=A.dE(s,r,t.z)\nreturn q}},\nhc(a){a.continue()},\n$ibT:1}\nA.br.prototype={$ibr:1}\nA.bj.prototype={\ndP(a,b,c){var s=t.z,r=A.X(s,s)\nif(c!=null)r.k(0,\"autoIncrement\",c)\nreturn this.eR(a,b,r)},\nfM(a,b){return this.dP(a,b,null)},\ncT(a,b,c){var s\nif(c!==\"readonly\"&&c!==\"readwrite\")throw A.b(A.ao(c,null))\ns=a.transaction(b,c)\ns.toString\nreturn s},\nbR(a,b,c){var s\nt.i.a(b)\nif(c!==\"readonly\"&&c!==\"readwrite\")throw A.b(A.ao(c,null))\ns=a.transaction(b,c)\ns.toString\nreturn s},\neR(a,b,c){var s=a.createObjectStore(b,A.oe(c))\ns.toString\nreturn s},\n$ibj:1}\nA.cj.prototype={\nhf(a,b,c,d,e){var s,r,q,p,o,n\nt.jM.a(d)\nt.a.a(c)\ntry{s=null\ns=this.f7(a,b,e)\np=t.iB\no=p.a(s)\nt.Z.a(null)\nA.bf(o,\"upgradeneeded\",d,!1,t.bo)\nA.bf(p.a(s),\"blocked\",c,!1,t.A)\np=A.j3(s,t.E)\nreturn p}catch(n){r=A.M(n)\nq=A.a_(n)\np=A.dE(r,q,t.E)\nreturn p}},\nf7(a,b,c){var s=a.open(b,c)\ns.toString\nreturn s},\n$icj:1}\nA.mP.prototype={\n$1(a){this.b.a0(0,this.c.a(new A.c2([],[]).aF(this.a.result,!1)))},\n$S:2}\nA.dG.prototype={\nef(a,b){var s,r,q,p,o\ntry{p=a.getKey(b)\np.toString\ns=p\np=A.j3(s,t.z)\nreturn p}catch(o){r=A.M(o)\nq=A.a_(o)\np=A.dE(r,q,t.z)\nreturn p}}}\nA.dX.prototype={\ncv(a,b){var s,r,q,p\ntry{q=a.delete(b==null?t.K.a(b):b)\nq.toString\nq=A.j3(q,t.z)\nreturn q}catch(p){s=A.M(p)\nr=A.a_(p)\nq=A.dE(s,r,t.z)\nreturn q}},\nhh(a,b,c){var s,r,q,p,o\ntry{s=null\ns=this.fb(a,b,c)\np=A.j3(t.B.a(s),t.z)\nreturn p}catch(o){r=A.M(o)\nq=A.a_(o)\np=A.dE(r,q,t.z)\nreturn p}},\ne4(a,b){var s=this.f8(a,b)\nreturn A.rE(s,null,t.nT)},\neQ(a,b,c,d){var s=a.createIndex(b,c,A.oe(d))\ns.toString\nreturn s},\nhr(a,b,c){var s=a.openCursor(b,c)\ns.toString\nreturn s},\nf8(a,b){return a.openCursor(b)},\nfb(a,b,c){var s\nif(c!=null){s=a.put(new A.cx([],[]).Z(b),new A.cx([],[]).Z(c))\ns.toString\nreturn s}s=a.put(new A.cx([],[]).Z(b))\ns.toString\nreturn s}}\nA.k7.prototype={\n$1(a){var s=this.d.h(\"0?\").a(new A.c2([],[]).aF(this.a.result,!1)),r=this.b\nif(s==null)r.af(0)\nelse{A.v(r).c.a(s)\nif(r.b>=4)A.J(r.bY())\nr.bW(0,s)}},\n$S:2}\nA.bz.prototype={$ibz:1}\nA.ec.prototype={$iec:1}\nA.bD.prototype={$ibD:1}\nA.nj.prototype={\n$1(a){return this.a.a0(0,this.b.h(\"0/?\").a(a))},\n$S:4}\nA.nk.prototype={\n$1(a){if(a==null)return this.a.ag(new A.h0(a===undefined))\nreturn this.a.ag(a)},\n$S:4}\nA.h0.prototype={\nl(a){return\"Promise was rejected with a value of `\"+(this.a?\"undefined\":\"null\")+\"`.\"},\n$iac:1}\nA.i8.prototype={\neB(){var s=self.crypto\nif(s!=null)if(s.getRandomValues!=null)return\nthrow A.b(A.x(\"No source of cryptographically secure random numbers available.\"))},\nhd(a){var s,r,q,p,o,n,m,l,k\nif(a<=0||a>4294967296)throw A.b(A.rV(\"max must be in range 0 < max \\u2264 2^32, was \"+a))\nif(a>255)if(a>65535)s=a>16777215?4:3\nelse s=2\nelse s=1\nr=this.a\nB.E.fm(r,0,0,!1)\nq=4-s\np=A.j(Math.pow(256,s))\nfor(o=a-1,n=(a&o)===0;!0;){m=r.buffer\nm=new Uint8Array(m,q,s)\ncrypto.getRandomValues(m)\nl=B.E.f0(r,0,!1)\nif(n)return(l&o)>>>0\nk=l%a\nif(l-k+a<p)return k}},\n$irU:1}\nA.aN.prototype={$iaN:1}\nA.fL.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length\ns.toString\ns=b>>>0!==b||b>=s\ns.toString\nif(s)throw A.b(A.V(b,this.gj(a),a,null,null))\ns=a.getItem(b)\ns.toString\nreturn s},\nk(a,b,c){t.kT.a(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){var s=a.length\ns.toString\nif(s>0){s=a[0]\ns.toString\nreturn s}throw A.b(A.K(\"No elements\"))},\nv(a,b){return this.i(a,b)},\n$ik:1,\n$ie:1,\n$im:1}\nA.aR.prototype={$iaR:1}\nA.h3.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length\ns.toString\ns=b>>>0!==b||b>=s\ns.toString\nif(s)throw A.b(A.V(b,this.gj(a),a,null,null))\ns=a.getItem(b)\ns.toString\nreturn s},\nk(a,b,c){t.ai.a(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){var s=a.length\ns.toString\nif(s>0){s=a[0]\ns.toString\nreturn s}throw A.b(A.K(\"No elements\"))},\nv(a,b){return this.i(a,b)},\n$ik:1,\n$ie:1,\n$im:1}\nA.h8.prototype={\ngj(a){return a.length}}\nA.hn.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length\ns.toString\ns=b>>>0!==b||b>=s\ns.toString\nif(s)throw A.b(A.V(b,this.gj(a),a,null,null))\ns=a.getItem(b)\ns.toString\nreturn s},\nk(a,b,c){A.S(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){var s=a.length\ns.toString\nif(s>0){s=a[0]\ns.toString\nreturn s}throw A.b(A.K(\"No elements\"))},\nv(a,b){return this.i(a,b)},\n$ik:1,\n$ie:1,\n$im:1}\nA.aW.prototype={$iaW:1}\nA.hu.prototype={\ngj(a){var s=a.length\ns.toString\nreturn s},\ni(a,b){var s=a.length\ns.toString\ns=b>>>0!==b||b>=s\ns.toString\nif(s)throw A.b(A.V(b,this.gj(a),a,null,null))\ns=a.getItem(b)\ns.toString\nreturn s},\nk(a,b,c){t.hk.a(c)\nthrow A.b(A.x(\"Cannot assign element of immutable List.\"))},\ngA(a){var s=a.length\ns.toString\nif(s>0){s=a[0]\ns.toString\nreturn s}throw A.b(A.K(\"No elements\"))},\nv(a,b){return this.i(a,b)},\n$ik:1,\n$ie:1,\n$im:1}\nA.i9.prototype={}\nA.ia.prototype={}\nA.ik.prototype={}\nA.il.prototype={}\nA.iG.prototype={}\nA.iH.prototype={}\nA.iP.prototype={}\nA.iQ.prototype={}\nA.f9.prototype={\ngj(a){return a.length}}\nA.fa.prototype={\nF(a,b){return A.b7(a.get(b))!=null},\ni(a,b){return A.b7(a.get(A.S(b)))},\nD(a,b){var s,r,q\nt.u.a(b)\ns=a.entries()\nfor(;!0;){r=s.next()\nq=r.done\nq.toString\nif(q)return\nq=r.value[0]\nq.toString\nb.$2(q,A.b7(r.value[1]))}},\ngK(a){var s=A.t([],t.s)\nthis.D(a,new A.jp(s))\nreturn s},\ngU(a){var s=A.t([],t.C)\nthis.D(a,new A.jq(s))\nreturn s},\ngj(a){var s=a.size\ns.toString\nreturn s},\ngC(a){var s=a.size\ns.toString\nreturn s===0},\ngP(a){var s=a.size\ns.toString\nreturn s!==0},\nG(a,b){throw A.b(A.x(\"Not supported\"))},\n$iI:1}\nA.jp.prototype={\n$2(a,b){return B.b.m(this.a,a)},\n$S:1}\nA.jq.prototype={\n$2(a,b){return B.b.m(this.a,t.f.a(b))},\n$S:1}\nA.fb.prototype={\ngj(a){return a.length}}\nA.bQ.prototype={}\nA.h4.prototype={\ngj(a){return a.length}}\nA.hQ.prototype={}\nA.h_.prototype={}\nA.hy.prototype={\nG(a,b){return A.tq()}}\nA.fk.prototype={\nfw(a,b){var s,r=null\nA.q3(\"absolute\",A.t([b,null,null,null,null,null,null,null,null,null,null,null,null,null,null],t.mf))\ns=this.a\ns=s.ak(b)>0&&!s.ar(b)\nif(s)return b\ns=this.b\nreturn this.dZ(0,s==null?A.v9():s,b,r,r,r,r,r,r,r,r,r,r,r,r,r,r)},\ndZ(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q){var s=A.t([b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q],t.mf)\nA.q3(\"join\",s)\nreturn this.h8(new A.ef(s,t.lS))},\nh8(a){var s,r,q,p,o,n,m,l,k,j\nt.bq.a(a)\nfor(s=a.$ti,r=s.h(\"aw(e.E)\").a(new A.jw()),q=a.gE(a),s=new A.cq(q,r,s.h(\"cq<e.E>\")),r=this.a,p=!1,o=!1,n=\"\";s.p();){m=q.gu(q)\nif(r.ar(m)&&o){l=A.rF(m,r)\nk=n.charCodeAt(0)==0?n:n\nn=B.a.n(k,0,r.aR(k,!0))\nl.b=n\nif(r.bK(n))B.b.k(l.e,0,r.gbh())\nn=\"\"+l.l(0)}else if(r.ak(m)>0){o=!r.ar(m)\nn=\"\"+m}else{j=m.length\nif(j!==0){if(0>=j)return A.d(m,0)\nj=r.cs(m[0])}else j=!1\nif(!j)if(p)n+=r.gbh()\nn+=m}p=r.bK(m)}return n.charCodeAt(0)==0?n:n}}\nA.jw.prototype={\n$1(a){return A.S(a)!==\"\"},\n$S:31}\nA.n1.prototype={\n$1(a){A.o6(a)\nreturn a==null?\"null\":'\"'+a+'\"'},\n$S:32}\nA.bV.prototype={\neg(a){var s,r=this.ak(a)\nif(r>0)return B.a.n(a,0,r)\nif(this.ar(a)){if(0>=a.length)return A.d(a,0)\ns=a[0]}else s=null\nreturn s}}\nA.k8.prototype={\nl(a){var s,r,q,p=this,o=p.b\no=o!=null?\"\"+o:\"\"\nfor(s=0;s<p.d.length;++s,o=q){r=p.e\nif(!(s<r.length))return A.d(r,s)\nr=A.q(r[s])\nq=p.d\nif(!(s<q.length))return A.d(q,s)\nq=o+r+A.q(q[s])}o+=A.q(B.b.gai(p.e))\nreturn o.charCodeAt(0)==0?o:o}}\nA.lc.prototype={\nl(a){return this.gaO(this)}}\nA.h9.prototype={\ncs(a){return B.a.S(a,\"/\")},\nbI(a){return a===47},\nbK(a){var s=a.length\nreturn s!==0&&B.a.B(a,s-1)!==47},\naR(a,b){if(a.length!==0&&B.a.t(a,0)===47)return 1\nreturn 0},\nak(a){return this.aR(a,!1)},\nar(a){return!1},\ngaO(){return\"posix\"},\ngbh(){return\"/\"}}\nA.hC.prototype={\ncs(a){return B.a.S(a,\"/\")},\nbI(a){return a===47},\nbK(a){var s=a.length\nif(s===0)return!1\nif(B.a.B(a,s-1)!==47)return!0\nreturn B.a.dR(a,\"://\")&&this.ak(a)===s},\naR(a,b){var s,r,q,p,o=a.length\nif(o===0)return 0\nif(B.a.t(a,0)===47)return 1\nfor(s=0;s<o;++s){r=B.a.t(a,s)\nif(r===47)return 0\nif(r===58){if(s===0)return 0\nq=B.a.aq(a,\"/\",B.a.H(a,\"//\",s+1)?s+3:s)\nif(q<=0)return o\nif(!b||o<q+3)return q\nif(!B.a.J(a,\"file://\"))return q\nif(!A.vm(a,q+1))return q\np=q+3\nreturn o===p?p:q+4}}return 0},\nak(a){return this.aR(a,!1)},\nar(a){return a.length!==0&&B.a.t(a,0)===47},\ngaO(){return\"url\"},\ngbh(){return\"/\"}}\nA.hK.prototype={\ncs(a){return B.a.S(a,\"/\")},\nbI(a){return a===47||a===92},\nbK(a){var s=a.length\nif(s===0)return!1\ns=B.a.B(a,s-1)\nreturn!(s===47||s===92)},\naR(a,b){var s,r,q=a.length\nif(q===0)return 0\ns=B.a.t(a,0)\nif(s===47)return 1\nif(s===92){if(q<2||B.a.t(a,1)!==92)return 1\nr=B.a.aq(a,\"\\\\\",2)\nif(r>0){r=B.a.aq(a,\"\\\\\",r+1)\nif(r>0)return r}return q}if(q<3)return 0\nif(!A.qg(s))return 0\nif(B.a.t(a,1)!==58)return 0\nq=B.a.t(a,2)\nif(!(q===47||q===92))return 0\nreturn 3},\nak(a){return this.aR(a,!1)},\nar(a){return this.ak(a)===1},\ngaO(){return\"windows\"},\ngbh(){return\"\\\\\"}}\nA.n3.prototype={\n$1(a){return A.uZ(a)},\n$S:33}\nA.dy.prototype={\nl(a){return\"DatabaseException(\"+this.a+\")\"},\n$iac:1}\nA.e3.prototype={\nl(a){return this.em(0)},\nbS(){var s=this.b\nif(s==null){s=new A.ko(this).$0()\nthis.sfg(s)}return s},\nsfg(a){this.b=A.dj(a)}}\nA.ko.prototype={\n$0(){var s=new A.kp(this.a.a.toLowerCase()),r=s.$1(\"(sqlite code \")\nif(r!=null)return r\nr=s.$1(\"(code \")\nif(r!=null)return r\nr=s.$1(\"code=\")\nif(r!=null)return r\nreturn null},\n$S:34}\nA.kp.prototype={\n$1(a){var s,r,q,p,o,n=this.a,m=B.a.cB(n,a)\nif(!J.a7(m,-1))try{p=m\nif(typeof p!==\"number\")return p.bf()\np=B.a.hn(B.a.O(n,p+a.length)).split(\" \")\nif(0>=p.length)return A.d(p,0)\ns=p[0]\nr=J.qY(s,\")\")\nif(!J.a7(r,-1))s=J.r2(s,0,r)\nq=A.nE(s,null)\nif(q!=null)return q}catch(o){}return null},\n$S:35}\nA.jB.prototype={}\nA.fx.prototype={\nl(a){return A.oh(this).l(0)+\"(\"+this.a+\", \"+A.q(this.b)+\")\"}}\nA.cH.prototype={}\nA.bl.prototype={\nl(a){var s,r=this,q=t.N,p=t.X,o=A.X(q,p),n=r.x\nif(n!=null){n=A.nB(n,q,p)\ns=n.fE(n,q,p)\np=s.a\nq=J.b8(p)\nn=s.$ti.h(\"4?\")\nn.a(q.G(p,\"arguments\"))\nn.a(q.G(p,\"sql\"))\nif(q.gP(p))o.k(0,\"details\",s)}q=r.bS()==null?\"\":\": \"+A.q(r.bS())+\", \"\nq=\"\"+(\"SqfliteFfiException(\"+r.w+q+\", \"+r.a+\"})\")\np=r.f\nif(p!=null){q+=\" sql \"+p\np=r.r\np=p==null?null:!p.gC(p)\nif(p===!0){p=r.r\np.toString\np=q+(\" args \"+A.q6(p))\nq=p}}else q+=\" \"+r.ev(0)\nif(o.a!==0)q+=\" \"+o.l(0)\nreturn q.charCodeAt(0)==0?q:q},\nsfP(a,b){this.x=t.h9.a(b)}}\nA.kC.prototype={}\nA.e6.prototype={\nl(a){var s=this.a,r=this.b,q=this.c,p=q==null?null:!q.gC(q)\nif(p===!0){q.toString\nq=\" \"+A.q6(q)}else q=\"\"\nreturn A.q(s)+\" \"+(A.q(r)+q)},\nsej(a){this.c=t.kR.a(a)}}\nA.iB.prototype={}\nA.iq.prototype={\nL(){var s=0,r=A.B(t.H),q=1,p,o=this,n,m,l,k\nvar $async$L=A.C(function(a,b){if(a===1){p=b\ns=q}while(true)switch(s){case 0:q=3\ns=6\nreturn A.p(o.a.$0(),$async$L)\ncase 6:n=b\no.b.a0(0,n)\nq=1\ns=5\nbreak\ncase 3:q=2\nk=p\nm=A.M(k)\no.b.ag(m)\ns=5\nbreak\ncase 2:s=1\nbreak\ncase 5:return A.z(null,r)\ncase 1:return A.y(p,r)}})\nreturn A.A($async$L,r)}}\nA.aU.prototype={\nec(){var s=this\nreturn A.aO([\"path\",s.r,\"id\",s.e,\"readOnly\",s.w,\"singleInstance\",s.f],t.N,t.X)},\ndf(){var s,r=this\nif(r.dg()===0)return null\ns=r.x.b\ns=s.a.rx.$1(s.b)\ns=self.Number(s==null?t.K.a(s):s)\nif(r.y>=1)A.b9(\"[sqflite-\"+r.e+\"] Inserted \"+A.q(s))\nreturn s},\nl(a){return A.jZ(this.ec())},\naf(a){var s=this\ns.bl()\ns.av(\"Closing database \"+s.l(0))\ns.x.a1()},\nca(a){var s=a==null?null:new A.ba(a.a,a.$ti.h(\"ba<1,r?>\"))\nreturn s==null?B.m:s},\nfZ(a,b){return this.d.a7(new A.kx(this,a,b),t.H)},\nae(a,b){return this.f3(a,b)},\nf3(a,b){var s=0,r=A.B(t.H),q,p=[],o=this,n,m,l\nvar $async$ae=A.C(function(c,d){if(c===1)return A.y(d,r)\nwhile(true)switch(s){case 0:o.cI(a,b)\nm=b==null?null:!b.gC(b)\nl=o.x\nif(m===!0){n=l.cL(a)\ntry{n.bC(o.ca(b))\ns=1\nbreak}finally{n.a1()}}else l.bC(a)\ncase 1:return A.z(q,r)}})\nreturn A.A($async$ae,r)},\nav(a){if(a!=null&&this.y>=1)A.b9(\"[sqflite-\"+this.e+\"] \"+A.q(a))},\ncI(a,b){var s\nif(this.y>=1){s=b==null?null:!b.gC(b)\ns=s===!0?\" \"+A.q(b):\"\"\nA.b9(\"[sqflite-\"+this.e+\"] \"+a+s)\nthis.av(null)}},\nbu(){var s=0,r=A.B(t.H),q=this\nvar $async$bu=A.C(function(a,b){if(a===1)return A.y(b,r)\nwhile(true)switch(s){case 0:s=q.c.length!==0?2:3\nbreak\ncase 2:s=4\nreturn A.p(q.as.a7(new A.kv(q),t.P),$async$bu)\ncase 4:case 3:return A.z(null,r)}})\nreturn A.A($async$bu,r)},\nbl(){var s=0,r=A.B(t.H),q=this\nvar $async$bl=A.C(function(a,b){if(a===1)return A.y(b,r)\nwhile(true)switch(s){case 0:s=q.c.length!==0?2:3\nbreak\ncase 2:s=4\nreturn A.p(q.as.a7(new A.kq(q),t.P),$async$bl)\ncase 4:case 3:return A.z(null,r)}})\nreturn A.A($async$bl,r)},\nb8(a,b){return this.h2(a,t.gq.a(b))},\nh2(a,b){var s=0,r=A.B(t.z),q,p=2,o,n=[],m=this,l\nvar $async$b8=A.C(function(c,d){if(c===1){o=d\ns=p}while(true)switch(s){case 0:l=m.b\ns=l==null?3:5\nbreak\ncase 3:s=6\nreturn A.p(b.$0(),$async$b8)\ncase 6:q=d\ns=1\nbreak\ns=4\nbreak\ncase 5:s=a===l||a===-1?7:9\nbreak\ncase 7:p=10\ns=13\nreturn A.p(b.$0(),$async$b8)\ncase 13:l=d\nq=l\nn=[1]\ns=11\nbreak\nn.push(12)\ns=11\nbreak\ncase 10:n=[2]\ncase 11:p=2\nif(m.b==null)m.bu()\ns=n.pop()\nbreak\ncase 12:s=8\nbreak\ncase 9:l=new A.E($.D,t.D)\nB.b.m(m.c,new A.iq(b,new A.cr(l,t.ou)))\nq=l\ns=1\nbreak\ncase 8:case 4:case 1:return A.z(q,r)\ncase 2:return A.y(o,r)}})\nreturn A.A($async$b8,r)},\nh_(a,b){return this.d.a7(new A.ky(this,a,b),t.I)},\nbm(a,b){var s=0,r=A.B(t.I),q,p=this,o\nvar $async$bm=A.C(function(c,d){if(c===1)return A.y(d,r)\nwhile(true)switch(s){case 0:if(p.w)A.J(A.hh(\"sqlite_error\",null,\"Database readonly\",null))\ns=3\nreturn A.p(p.ae(a,b),$async$bm)\ncase 3:o=p.df()\nif(p.y>=1)A.b9(\"[sqflite-\"+p.e+\"] Inserted id \"+A.q(o))\nq=o\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$bm,r)},\nh3(a,b){return this.d.a7(new A.kB(this,a,b),t.S)},\nbo(a,b){var s=0,r=A.B(t.S),q,p=this\nvar $async$bo=A.C(function(c,d){if(c===1)return A.y(d,r)\nwhile(true)switch(s){case 0:if(p.w)A.J(A.hh(\"sqlite_error\",null,\"Database readonly\",null))\ns=3\nreturn A.p(p.ae(a,b),$async$bo)\ncase 3:q=p.dg()\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$bo,r)},\nh0(a,b,c){return this.d.a7(new A.kA(this,a,c,b),t.z)},\nbn(a,b){return this.f4(a,b)},\nf4(a,b){var s=0,r=A.B(t.z),q,p=[],o=this,n,m,l,k,j\nvar $async$bn=A.C(function(c,d){if(c===1)return A.y(d,r)\nwhile(true)switch(s){case 0:j=o.x.cL(a)\ntry{o.cI(a,b)\nm=j\nl=o.ca(b)\nk=m.c\nif(k.d)A.J(A.K(u.n))\nk.bs()\nm.f=null\nm.bZ(l)\nn=m.fj()\no.av(\"Found \"+n.d.length+\" rows\")\nm=n\nm=A.aO([\"columns\",m.a,\"rows\",m.d],t.N,t.X)\nq=m\ns=1\nbreak}finally{j.a1()}case 1:return A.z(q,r)}})\nreturn A.A($async$bn,r)},\ndw(a){var s,r,q,p,o,n,m,l,k=a.a,j=k\ntry{s=a.d\nr=s.a\nq=A.t([],t.dO)\nfor(n=a.c;!0;){if(s.p()){m=s.x\nm===$&&A.aZ(\"current\")\np=m\nJ.qO(q,p.b)}else{a.e=!0\nbreak}if(J.Y(q)>=n)break}o=A.aO([\"columns\",r,\"rows\",q],t.N,t.X)\nif(!a.e)J.nq(o,\"cursorId\",k)\nreturn o}catch(l){this.c3(j)\nthrow l}finally{if(a.e)this.c3(j)}},\ncd(a,b,c){var s=0,r=A.B(t.X),q,p=this,o,n,m,l,k\nvar $async$cd=A.C(function(d,e){if(d===1)return A.y(e,r)\nwhile(true)switch(s){case 0:k=p.x.cL(b)\np.cI(b,c)\no=p.ca(c)\nn=k.c\nif(n.d)A.J(A.K(u.n))\nn.bs()\nk.f=null\nk.bZ(o)\no=k.gc5()\nk.gdB()\nm=new A.hL(k,o,B.C)\nm.c_()\nn.c=!1\nk.f=m\nn=++p.Q\nl=new A.iB(n,k,a,m)\np.z.k(0,n,l)\nq=p.dw(l)\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$cd,r)},\nh1(a,b){return this.d.a7(new A.kz(this,b,a),t.z)},\nce(a,b){var s=0,r=A.B(t.X),q,p=this,o,n\nvar $async$ce=A.C(function(c,d){if(c===1)return A.y(d,r)\nwhile(true)switch(s){case 0:if(p.y>=2){o=a===!0?\" (cancel)\":\"\"\np.av(\"queryCursorNext \"+b+o)}n=p.z.i(0,b)\nif(a===!0){p.c3(b)\nq=null\ns=1\nbreak}if(n==null)throw A.b(A.K(\"Cursor \"+b+\" not found\"))\nq=p.dw(n)\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$ce,r)},\nc3(a){var s=this.z.G(0,a)\nif(s!=null){if(this.y>=2)this.av(\"Closing cursor \"+a)\ns.b.a1()}},\ndg(){var s=this.x.b,r=A.j(s.a.RG.$1(s.b))\nif(this.y>=1)A.b9(\"[sqflite-\"+this.e+\"] Modified \"+r+\" rows\")\nreturn r},\nfW(a,b,c){return this.d.a7(new A.kw(this,t.fr.a(c),b,a),t.z)},\nan(a,b,c){return this.f2(a,b,t.fr.a(c))},\nf2(b3,b4,b5){var s=0,r=A.B(t.z),q,p=2,o,n=this,m,l,k,j,i,h,g,f,e,d,c,b,a,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,b0,b1,b2\nvar $async$an=A.C(function(b6,b7){if(b6===1){o=b7\ns=p}while(true)switch(s){case 0:a8={}\na8.a=null\nd=!b4\nif(d)a8.a=A.t([],t.ke)\nc=b5.length,b=n.y>=1,a=n.x.b,a0=a.b,a=a.a.RG,a1=\"[sqflite-\"+n.e+\"] Modified \",a2=0\ncase 3:if(!(a2<b5.length)){s=5\nbreak}m=b5[a2]\nl=new A.kt(a8,b4)\nk=new A.kr(a8,n,m,b3,b4,new A.ku())\ncase 6:switch(m.a){case\"insert\":s=8\nbreak\ncase\"execute\":s=9\nbreak\ncase\"query\":s=10\nbreak\ncase\"update\":s=11\nbreak\ndefault:s=12\nbreak}break\ncase 8:p=14\na3=m.b\na3.toString\ns=17\nreturn A.p(n.ae(a3,m.c),$async$an)\ncase 17:if(d)l.$1(n.df())\np=2\ns=16\nbreak\ncase 14:p=13\na9=o\nj=A.M(a9)\ni=A.a_(a9)\nk.$2(j,i)\ns=16\nbreak\ncase 13:s=2\nbreak\ncase 16:s=7\nbreak\ncase 9:p=19\na3=m.b\na3.toString\ns=22\nreturn A.p(n.ae(a3,m.c),$async$an)\ncase 22:l.$1(null)\np=2\ns=21\nbreak\ncase 19:p=18\nb0=o\nh=A.M(b0)\nk.$1(h)\ns=21\nbreak\ncase 18:s=2\nbreak\ncase 21:s=7\nbreak\ncase 10:p=24\na3=m.b\na3.toString\ns=27\nreturn A.p(n.bn(a3,m.c),$async$an)\ncase 27:g=b7\nl.$1(g)\np=2\ns=26\nbreak\ncase 24:p=23\nb1=o\nf=A.M(b1)\nk.$1(f)\ns=26\nbreak\ncase 23:s=2\nbreak\ncase 26:s=7\nbreak\ncase 11:p=29\na3=m.b\na3.toString\ns=32\nreturn A.p(n.ae(a3,m.c),$async$an)\ncase 32:if(d){a5=A.j(a.$1(a0))\nif(b){a6=a1+a5+\" rows\"\na7=$.qk\nif(a7==null)A.qj(a6)\nelse a7.$1(a6)}l.$1(a5)}p=2\ns=31\nbreak\ncase 29:p=28\nb2=o\ne=A.M(b2)\nk.$1(e)\ns=31\nbreak\ncase 28:s=2\nbreak\ncase 31:s=7\nbreak\ncase 12:throw A.b(\"batch operation \"+A.q(m.a)+\" not supported\")\ncase 7:case 4:b5.length===c||(0,A.aM)(b5),++a2\ns=3\nbreak\ncase 5:q=a8.a\ns=1\nbreak\ncase 1:return A.z(q,r)\ncase 2:return A.y(o,r)}})\nreturn A.A($async$an,r)}}\nA.kx.prototype={\n$0(){return this.a.ae(this.b,this.c)},\n$S:3}\nA.kv.prototype={\n$0(){var s=0,r=A.B(t.P),q=this,p,o,n\nvar $async$$0=A.C(function(a,b){if(a===1)return A.y(b,r)\nwhile(true)switch(s){case 0:p=q.a,o=p.c\ncase 2:if(!!0){s=3\nbreak}s=o.length!==0?4:6\nbreak\ncase 4:n=B.b.gA(o)\nif(p.b!=null){s=3\nbreak}s=7\nreturn A.p(n.L(),$async$$0)\ncase 7:B.b.hi(o,0)\ns=5\nbreak\ncase 6:s=3\nbreak\ncase 5:s=2\nbreak\ncase 3:return A.z(null,r)}})\nreturn A.A($async$$0,r)},\n$S:10}\nA.kq.prototype={\n$0(){var s=0,r=A.B(t.P),q=this,p,o,n\nvar $async$$0=A.C(function(a,b){if(a===1)return A.y(b,r)\nwhile(true)switch(s){case 0:for(p=q.a.c,o=p.length,n=0;n<p.length;p.length===o||(0,A.aM)(p),++n)p[n].b.ag(new A.bB(\"Database has been closed\"))\nreturn A.z(null,r)}})\nreturn A.A($async$$0,r)},\n$S:10}\nA.ky.prototype={\n$0(){return this.a.bm(this.b,this.c)},\n$S:37}\nA.kB.prototype={\n$0(){return this.a.bo(this.b,this.c)},\n$S:38}\nA.kA.prototype={\n$0(){var s=this,r=s.b,q=s.a,p=s.c,o=s.d\nif(r==null)return q.bn(o,p)\nelse return q.cd(r,o,p)},\n$S:18}\nA.kz.prototype={\n$0(){return this.a.ce(this.c,this.b)},\n$S:18}\nA.kw.prototype={\n$0(){var s=this\nreturn s.a.an(s.d,s.c,s.b)},\n$S:5}\nA.ku.prototype={\n$1(a){var s,r,q=t.N,p=t.X,o=A.X(q,p)\no.k(0,\"message\",a.l(0))\ns=a.f\nif(s!=null||a.r!=null){r=A.X(q,p)\nr.k(0,\"sql\",s)\ns=a.r\nif(s!=null)r.k(0,\"arguments\",s)\no.k(0,\"data\",r)}return A.aO([\"error\",o],q,p)},\n$S:41}\nA.kt.prototype={\n$1(a){var s\nif(!this.b){s=this.a.a\ns.toString\nB.b.m(s,A.aO([\"result\",a],t.N,t.X))}},\n$S:4}\nA.kr.prototype={\n$2(a,b){var s,r=this,q=new A.ks(r.b,r.c)\nif(r.d){if(!r.e){s=r.a.a\ns.toString\nB.b.m(s,r.f.$1(q.$1(a)))}}else throw A.b(q.$1(a))},\n$1(a){return this.$2(a,null)},\n$S:42}\nA.ks.prototype={\n$1(a){var s=this.b\nreturn A.mX(a,this.a,s.b,s.c)},\n$S:43}\nA.kG.prototype={\n$0(){return this.a.$1(this.b)},\n$S:5}\nA.kF.prototype={\n$0(){return this.a.$0()},\n$S:5}\nA.kQ.prototype={\n$0(){return A.kY(this.a)},\n$S:27}\nA.kZ.prototype={\n$1(a){return A.aO([\"id\",a],t.N,t.X)},\n$S:45}\nA.kK.prototype={\n$0(){return A.nG(this.a)},\n$S:5}\nA.kI.prototype={\n$1(a){var s,r,q\nt.f.a(a)\ns=new A.e6()\nr=J.T(a)\ns.b=A.o6(r.i(a,\"sql\"))\nq=t.lH.a(r.i(a,\"arguments\"))\ns.sej(q==null?null:J.jd(q,t.X))\ns.a=A.S(r.i(a,\"method\"))\nB.b.m(this.a,s)},\n$S:46}\nA.kT.prototype={\n$1(a){return A.nL(this.a,a)},\n$S:13}\nA.kS.prototype={\n$1(a){return A.nM(this.a,a)},\n$S:13}\nA.kN.prototype={\n$1(a){return A.kW(this.a,a)},\n$S:48}\nA.kR.prototype={\n$0(){return A.l_(this.a)},\n$S:5}\nA.kP.prototype={\n$1(a){return A.nK(this.a,a)},\n$S:49}\nA.kU.prototype={\n$1(a){return A.nN(this.a,a)},\n$S:50}\nA.kJ.prototype={\n$1(a){var s,r,q,p=this.a,o=A.t1(p)\np=t.f.a(p.b)\ns=J.T(p)\nr=A.eW(s.i(p,\"noResult\"))\nq=A.eW(s.i(p,\"continueOnError\"))\nreturn a.fW(q===!0,r===!0,o)},\n$S:13}\nA.kO.prototype={\n$0(){return A.nJ(this.a)},\n$S:5}\nA.kM.prototype={\n$0(){return A.kV(this.a)},\n$S:3}\nA.kL.prototype={\n$0(){return A.nH(this.a)},\n$S:51}\nA.kD.prototype={\nbH(){var s=0,r=A.B(t.i_),q,p=this,o\nvar $async$bH=A.C(function(a,b){if(a===1)return A.y(b,r)\nwhile(true)switch(s){case 0:o=p.c\nq=o==null?p.c=p.a.b:o\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$bH,r)},\ncC(){var s=0,r=A.B(t.H),q=this\nvar $async$cC=A.C(function(a,b){if(a===1)return A.y(b,r)\nwhile(true)switch(s){case 0:if(q.b==null)q.b=q.a.c\nreturn A.z(null,r)}})\nreturn A.A($async$cC,r)},\nbM(a){var s=0,r=A.B(t.bT),q,p=this,o,n,m,l,k,j,i,h,g,f,e,d\nvar $async$bM=A.C(function(b,c){if(b===1)return A.y(c,r)\nwhile(true)switch(s){case 0:s=3\nreturn A.p(p.cC(),$async$bM)\ncase 3:o=J.T(a)\nn=A.S(o.i(a,\"path\"))\no=A.eW(o.i(a,\"readOnly\"))\nm=o===!0?B.F:B.G\no=p.b\no.toString\nswitch(m){case B.F:l=1\nbreak\ncase B.a2:l=2\nbreak\ncase B.G:l=6\nbreak\ndefault:l=null}o=o.a\nA.j(l)\nk=o.a\nt.O.h(\"ay.S\").a(n)\nj=k.by(B.f.gaG().a9(n),1)\ni=A.j(k.d.$1(4))\nh=A.j(k.Q.$4(j,i,l,0))\ng=A.dS(J.bO(k.b),0,null)\nf=B.c.M(i,2)\nif(!(f<g.length)){q=A.d(g,f)\ns=1\nbreak}e=g[f]\nf=k.e\nf.$1(j)\nf.$1(0)\ng=new A.hF(k,e)\nif(h!==0){A.j(k.as.$1(e))\nA.J(A.of(o,g,h,\"opening the database\",null,null))}A.j(k.ch.$2(e,1))\nk=A.t([],t.jP)\nf=new A.fA(o,g,A.t([],t.eY))\nd=new A.fr(o,g,f,k)\no=$.jc()\nA.v(o).c.a(f)\no.a.register(d,f,d)\nq=d\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$bM,r)},\nb6(a){return this.fO(a)},\nfO(a){var s=0,r=A.B(t.H),q=1,p,o=[],n=this,m\nvar $async$b6=A.C(function(b,c){if(b===1){p=c\ns=q}while(true)switch(s){case 0:s=2\nreturn A.p(n.bH(),$async$b6)\ncase 2:m=c\nq=3\nm.aa(a)\ns=m instanceof A.cM?6:7\nbreak\ncase 6:s=8\nreturn A.p(J.qV(m),$async$b6)\ncase 8:case 7:o.push(5)\ns=4\nbreak\ncase 3:o=[1]\ncase 4:q=1\ns=o.pop()\nbreak\ncase 5:return A.z(null,r)\ncase 1:return A.y(p,r)}})\nreturn A.A($async$b6,r)},\nbF(a){return this.fX(a)},\nfX(a){var s=0,r=A.B(t.y),q,p=2,o,n=this,m,l,k,j\nvar $async$bF=A.C(function(b,c){if(b===1){o=c\ns=p}while(true)switch(s){case 0:p=4\ns=7\nreturn A.p(n.bH(),$async$bF)\ncase 7:m=c\nl=m.cw(a)\nq=l\ns=1\nbreak\np=2\ns=6\nbreak\ncase 4:p=3\nj=o\nq=!1\ns=1\nbreak\ns=6\nbreak\ncase 3:s=2\nbreak\ncase 6:case 1:return A.z(q,r)\ncase 2:return A.y(o,r)}})\nreturn A.A($async$bF,r)},\ncz(a){var s=0,r=A.B(t.H)\nvar $async$cz=A.C(function(b,c){if(b===1)return A.y(c,r)\nwhile(true)switch(s){case 0:return A.z(null,r)}})\nreturn A.A($async$cz,r)}}\nA.mY.prototype={\n$1(a){var s=A.X(t.N,t.X),r=a.a\nr===$&&A.aZ(\"result\")\nif(r!=null)s.k(0,\"result\",r)\nelse{r=a.b\nr===$&&A.aZ(\"error\")\nif(r!=null)s.k(0,\"error\",r)}B.a1.e6(this.a,s)},\n$S:65}\nA.ng.prototype={\n$1(a){return this.ee(a)},\nee(a){var s=0,r=A.B(t.H),q,p,o\nvar $async$$1=A.C(function(b,c){if(b===1)return A.y(c,r)\nwhile(true)switch(s){case 0:o=t.hy.a(a).ports\no.toString\nq=J.bP(o)\no=q\nt.o.a(A.ok())\np=J.a2(o)\np.fp(o)\np.en(o,\"message\",A.ok(),null)\nreturn A.z(null,r)}})\nreturn A.A($async$$1,r)},\n$S:25}\nA.dg.prototype={}\nA.be.prototype={\nb5(a,b){if(typeof b==\"string\")return A.nX(b,null)\nthrow A.b(A.x(\"invalid encoding for bigInt \"+A.q(b)))}}\nA.mL.prototype={\n$2(a,b){A.j(a)\nt.ap.a(b)\nreturn new A.a4(b.a,b,t.ag)},\n$S:54}\nA.mW.prototype={\n$2(a,b){var s,r,q\nif(typeof a!=\"string\")throw A.b(A.bq(a,null,null))\ns=A.o8(b)\nif(s==null?b!=null:s!==b){r=this.a\nq=r.a;(q==null?r.a=A.nB(this.b,t.N,t.X):q).k(0,a,s)}},\n$S:7}\nA.mV.prototype={\n$2(a,b){var s,r,q=A.o7(b)\nif(q==null?b!=null:q!==b){s=this.a\nr=s.a\ns=r==null?s.a=A.nB(this.b,t.N,t.X):r\ns.k(0,J.bp(a),q)}},\n$S:7}\nA.l0.prototype={}\nA.e7.prototype={}\nA.e8.prototype={}\nA.d_.prototype={\nl(a){var s=this,r=\"SqliteException(\"+s.c+\"): \"+(\"while \"+s.d+\", \")+s.a+\", \"+s.b,q=s.e\nif(q!=null){r=r+\"\\n  Causing statement: \"+q\nq=s.f\nif(q!=null)r+=\", parameters: \"+J.ov(q,new A.l3(),t.N).au(0,\", \")}return r.charCodeAt(0)==0?r:r},\n$iac:1}\nA.l3.prototype={\n$1(a){if(t.p.b(a))return\"blob (\"+a.length+\" bytes)\"\nelse return J.bp(a)},\n$S:55}\nA.hj.prototype={}\nA.kc.prototype={}\nA.kd.prototype={}\nA.fA.prototype={\na1(){var s,r,q,p,o,n,m\nfor(s=this.d,r=s.length,q=0;q<s.length;s.length===r||(0,A.aM)(s),++q){p=s[q]\nif(!p.d){p.d=!0\nif(!p.c){o=p.b\nA.j(o.c.fr.$1(o.b))\np.c=!0}o=p.b\no.cu()\nA.j(o.c.R8.$1(o.b))}}s=this.c\nn=A.j(s.a.as.$1(s.b))\nm=n!==0?A.of(this.b,s,n,\"closing database\",null,null):null\nif(m!=null)throw A.b(m)}}\nA.fr.prototype={\na1(){var s,r,q,p=this\nif(p.e)return\n$.jc().a.unregister(p)\np.e=!0\nfor(s=p.d,r=0;!1;++r)s[r].af(0)\ns=p.b\nq=s.a\nq.c.sh4(t.hC.a(null))\nq.w.$2(s.b,-1)\np.c.a1()},\nbC(a){var s,r,q,p,o=this,n=B.m\nif(J.Y(n)===0){if(o.e)A.J(A.K(\"This database has already been closed\"))\nr=o.b\nq=r.a\nt.O.h(\"ay.S\").a(a)\ns=q.by(B.f.gaG().a9(a),1)\np=A.j(q.CW.$5(r.b,s,0,0,0))\nq.e.$1(s)\nif(p!==0)A.j9(o,p,\"executing\",a,n)}else{s=o.e7(a,!0)\ntry{s.bC(n)}finally{s.a1()}}},\nfa(a0,a1,a2,a3,a4){var s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a=this\nif(a.e)A.J(A.K(\"This database has already been closed\"))\nt.O.h(\"ay.S\").a(a0)\ns=B.f.gaG().a9(a0)\nr=a.b\nq=r.a\np=q.cq(t.L.a(s))\no=q.d\nn=A.j(o.$1(4))\no=A.j(o.$1(4))\nm=new A.lu(r,p,n,o)\nl=A.t([],t.lE)\nk=new A.jz(m,l)\nfor(r=s.length,q=q.b,n=J.a2(q),j=0;j<r;j=e){i=m.cY(j,r-j,0)\nh=i.a\nif(h!==0){k.$0()\nA.j9(a,h,\"preparing statement\",a0,null)}h=n.gaE(q)\ng=B.c.R(h.byteLength-0,4)\nh=new Uint32Array(h,0,g)\nf=B.c.M(o,2)\nif(!(f<h.length))return A.d(h,f)\ne=h[f]-p\nd=i.b\nif(d!=null){c=B.t.dO(s,j,e)\nB.b.m(l,new A.d0(d,a,new A.cJ(d),c))}if(l.length===a2){j=e\nbreak}}if(a1)for(;j<r;){i=m.cY(j,r-j,0)\nh=n.gaE(q)\ng=B.c.R(h.byteLength-0,4)\nh=new Uint32Array(h,0,g)\nf=B.c.M(o,2)\nif(!(f<h.length))return A.d(h,f)\nj=h[f]-p\nd=i.b\nif(d!=null){B.b.m(l,new A.d0(d,a,new A.cJ(d),\"\"))\nk.$0()\nthrow A.b(A.bq(a0,\"sql\",\"Had an unexpected trailing statement.\"))}else if(i.a!==0){k.$0()\nthrow A.b(A.bq(a0,\"sql\",\"Has trailing data after the first sql statement:\"))}}m.af(0)\nfor(r=l.length,q=a.c.d,b=0;b<l.length;l.length===r||(0,A.aM)(l),++b)B.b.m(q,l[b].c)\nreturn l},\ne7(a,b){var s=this.fa(a,b,1,!1,!0)\nif(s.length===0)throw A.b(A.bq(a,\"sql\",\"Must contain an SQL statement.\"))\nreturn B.b.gA(s)},\ncL(a){return this.e7(a,!1)},\n$ioE:1}\nA.jz.prototype={\n$0(){var s,r,q,p,o,n\nthis.a.af(0)\nfor(s=this.b,r=s.length,q=0;q<s.length;s.length===r||(0,A.aM)(s),++q){p=s[q]\no=p.c\nif(!o.d){$.jc().a.unregister(p)\nif(!o.d){o.d=!0\nif(!o.c){n=o.b\nA.j(n.c.fr.$1(n.b))\no.c=!0}n=o.b\nn.cu()\nA.j(n.c.R8.$1(n.b))}n=p.b\nif(!n.e)B.b.G(n.c.d,o)}}},\n$S:0}\nA.bs.prototype={}\nA.n7.prototype={\n$1(a){t.m.a(a)\na.a1()},\n$S:56}\nA.l1.prototype={}\nA.cJ.prototype={\na1(){var s,r=this\nif(!r.d){r.d=!0\nr.bs()\ns=r.b\nA.j(s.c.R8.$1(s.b))}},\nbs(){var s,r=this\nif(!r.c){s=r.b\nA.j(s.c.fr.$1(s.b))\nr.c=!0}r.b.cu()}}\nA.d0.prototype={\ngc5(){var s,r,q,p,o,n,m,l,k,j=this.a,i=j.c\nj=j.b\ns=A.j(i.dx.$1(j))\nr=A.t([],t.s)\nfor(q=t.L,p=i.dy,i=i.b,o=J.a2(i),n=0;n<s;++n){m=A.j(p.$2(j,n))\nl=o.gaE(i)\nk=A.oV(i,m)\nl=q.a(new Uint8Array(l,m,k))\nr.push(B.t.a9(l))}return r},\ngdB(){return null},\neZ(){var s,r=this,q=r.c.c=!1,p=r.a,o=p.b\np=p.c.fx\ndo s=A.j(p.$1(o))\nwhile(s===100)\nif(s!==0?s!==101:q)A.j9(r.b,s,\"executing statement\",r.d,r.e)},\nfj(){var s,r,q,p,o,n,m,l,k=this,j=A.t([],t.dO),i=k.c.c=!1\nfor(s=k.a,r=s.c,s=s.b,q=r.fx,r=r.dx,p=-1;o=A.j(q.$1(s)),o===100;){if(p===-1)p=A.j(r.$1(s))\nn=[]\nfor(m=0;m<p;++m)n.push(k.du(m))\nB.b.m(j,n)}if(o!==0?o!==101:i)A.j9(k.b,o,\"selecting from statement\",k.d,k.e)\nl=k.gc5()\nk.gdB()\ni=new A.ha(j,l,B.C)\ni.c_()\nreturn i},\ndu(a){var s,r,q,p,o=this.a,n=o.c\no=o.b\nswitch(A.j(n.fy.$2(o,a))){case 1:s=n.ek(o,a)\no=s.a\nreturn-9007199254740992<=o&&o<=9007199254740992?self.Number(o):A.tF(A.S(o.toString()),null)\ncase 2:return A.pI(n.id.$2(o,a))\ncase 3:return A.b0(n.b,A.j(n.k2.$2(o,a)))\ncase 4:r=A.j(n.k1.$2(o,a))\nq=A.j(n.k3.$2(o,a))\np=new Uint8Array(r)\nB.e.am(p,0,A.b_(J.bO(n.b),q,r))\nreturn p\ncase 5:default:return null}},\nbZ(a0){var s,r,q,p,o,n,m,l,k,j,i,h,g,f=J.T(a0),e=f.gj(a0),d=this.a,c=d.c,b=d.b,a=A.j(c.db.$1(b))\nif(e!==a)A.J(A.bq(a0,\"parameters\",\"Expected \"+a+\" parameters, got \"+e))\ns=f.gC(a0)\nif(s)return\nfor(s=t.L,r=t.k,d=d.d,q=c.p3,p=t.O.h(\"ay.S\"),o=c.p2,n=c.p1,m=t.d,l=c.ok,k=c.k4,j=1;j<=f.gj(a0);++j){i=f.i(a0,j-1)\nif(i==null)A.j(k.$2(b,j))\nelse if(A.dl(i))A.j(l.$3(b,j,self.BigInt(i)))\nelse if(r.b(i)){if(i.a8(0,m.a($.qM()))<0||i.a8(0,m.a($.qL()))>0)A.J(A.oF(\"BigInt value exceeds the range of 64 bits\"))\nA.j(l.$3(b,j,self.BigInt(i.l(0))))}else if(A.cz(i))A.j(l.$3(b,j,self.BigInt(i?1:0)))\nelse if(typeof i==\"number\")A.j(n.$3(b,j,i))\nelse if(typeof i==\"string\"){p.a(i)\nh=B.f.gaG().a9(i)\ng=c.cq(h)\nB.b.m(d,g)\nA.j(o.$5(b,j,g,h.length,0))}else if(s.b(i)){s.a(i)\ng=c.cq(i)\nB.b.m(d,g)\nA.j(q.$5(b,j,g,self.BigInt(J.Y(i)),0))}else A.J(A.bq(i,\"params[\"+j+\"]\",\"Allowed parameters must either be null or bool, int, num, String or List<int>.\"))}this.e=a0},\na1(){var s,r=this.c\nif(!r.d){$.jc().a.unregister(this)\nr.a1()\ns=this.b\nif(!s.e)B.b.G(s.c.d,r)}},\nbC(a){var s=this,r=s.c\nif(r.d)A.J(A.K(u.n))\nr.bs()\ns.f=null\ns.bZ(a)\ns.eZ()},\n$irc:1}\nA.hL.prototype={\ngu(a){var s=this.x\ns===$&&A.aZ(\"current\")\nreturn s},\np(){var s,r,q,p,o=this,n=o.r\nif(n.c.d||n.f!==o)return!1\ns=n.a\nr=s.c\ns=s.b\nq=A.j(r.fx.$1(s))\nif(q===100){if(!o.y){o.w=A.j(r.dx.$1(s))\no.sfh(t.i.a(n.gc5()))\no.c_()\no.y=!0}s=[]\nfor(p=0;p<o.w;++p)s.push(n.du(p))\no.x=new A.ak(o,A.fN(s,t.X))\nreturn!0}n.f=null\nif(q!==0&&q!==101)A.j9(n.b,q,\"iterating through statement\",n.d,n.e)\nreturn!1}}\nA.cF.prototype={\nc_(){var s,r,q,p,o=A.X(t.N,t.S)\nfor(s=this.a,r=s.length,q=0;q<s.length;s.length===r||(0,A.aM)(s),++q){p=s[q]\no.k(0,p,B.b.cG(this.a,p))}this.seK(o)},\nsfh(a){this.a=t.i.a(a)},\nseK(a){this.c=t.dV.a(a)}}\nA.dI.prototype={$iL:1}\nA.ha.prototype={\ngE(a){return new A.ir(this)},\ni(a,b){var s=this.d\nif(!(b>=0&&b<s.length))return A.d(s,b)\nreturn new A.ak(this,A.fN(s[b],t.X))},\nk(a,b,c){t.oy.a(c)\nthrow A.b(A.x(\"Can't change rows from a result set\"))},\ngj(a){return this.d.length},\n$ik:1,\n$ie:1,\n$im:1}\nA.ak.prototype={\ni(a,b){var s,r\nif(typeof b!=\"string\"){if(A.dl(b)){s=this.b\nif(b>>>0!==b||b>=s.length)return A.d(s,b)\nreturn s[b]}return null}r=this.a.c.i(0,b)\nif(r==null)return null\ns=this.b\nif(r>>>0!==r||r>=s.length)return A.d(s,r)\nreturn s[r]},\ngK(a){return this.a.a},\ngU(a){return this.b},\n$iI:1}\nA.ir.prototype={\ngu(a){var s=this.a,r=s.d,q=this.b\nif(!(q>=0&&q<r.length))return A.d(r,q)\nreturn new A.ak(s,A.fN(r[q],t.X))},\np(){return++this.b<this.a.d.length}}\nA.is.prototype={}\nA.it.prototype={}\nA.iv.prototype={}\nA.iw.prototype={}\nA.dY.prototype={\neX(){return\"OpenMode.\"+this.b}}\nA.hI.prototype={$irW:1}\nA.hF.prototype={$irX:1}\nA.lu.prototype={\naf(a){var s=this,r=s.a.a.e\nr.$1(s.b)\nr.$1(s.c)\nr.$1(s.d)},\ncY(a,b,c){var s,r,q=this,p=q.a,o=p.a,n=q.c,m=A.j(o.cy.$6(p.b,q.b+a,b,c,n,q.d))\np=A.dS(J.bO(o.b),0,null)\nn=B.c.M(n,2)\nif(!(n<p.length))return A.d(p,n)\ns=p[n]\nr=s===0?null:new A.hJ(s,o,A.t([],t.t))\nreturn new A.hj(m,r,t.kY)}}\nA.hJ.prototype={\ncu(){var s,r,q,p\nfor(s=this.d,r=s.length,q=this.c.e,p=0;p<s.length;s.length===r||(0,A.aM)(s),++p)q.$1(s[p])\nB.b.fG(s)},\n$ioT:1}\nA.cp.prototype={}\nA.bE.prototype={}\nA.d4.prototype={\ni(a,b){var s=A.dS(J.bO(this.a.b),0,null),r=B.c.M(this.c+b*4,2)\nif(!(r<s.length))return A.d(s,r)\nreturn new A.bE()},\nk(a,b,c){t.cI.a(c)\nthrow A.b(A.x(\"Setting element in WasmValueList\"))},\ngj(a){return this.b}}\nA.l2.prototype={}\nA.bc.prototype={\nl(a){return\"FileSystemException: (\"+this.a+\") \"+this.b},\n$iac:1}\nA.ji.prototype={\nbL(a){var s=0,r=A.B(t.H),q=this,p,o,n\nvar $async$bL=A.C(function(b,c){if(b===1)return A.y(c,r)\nwhile(true)switch(s){case 0:p=new A.E($.D,t.go)\no=new A.aa(p,t.my)\nn=t.kq.a(self.self.indexedDB)\nn.toString\no.a0(0,B.U.hf(n,q.b,new A.jm(o),new A.jn(),1))\ns=2\nreturn A.p(p,$async$bL)\ncase 2:q.seT(c)\nreturn A.z(null,r)}})\nreturn A.A($async$bL,r)},\nbJ(){var s=0,r=A.B(t.dV),q,p=this,o,n,m,l,k\nvar $async$bJ=A.C(function(a,b){if(a===1)return A.y(b,r)\nwhile(true)switch(s){case 0:l=p.a\nl.toString\no=A.X(t.N,t.S)\nn=new A.d7(t.B.a(B.h.cT(l,\"files\",\"readonly\").objectStore(\"files\").index(\"fileName\").openKeyCursor()),t.oz)\ncase 3:k=A\ns=5\nreturn A.p(n.p(),$async$bJ)\ncase 5:if(!k.aK(b)){s=4\nbreak}m=n.a\nif(m==null)m=A.J(A.K(\"Await moveNext() first\"))\no.k(0,A.S(m.key),A.j(m.primaryKey))\ns=3\nbreak\ncase 4:q=o\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$bJ,r)},\nbD(a){var s=0,r=A.B(t.I),q,p=this,o,n\nvar $async$bD=A.C(function(b,c){if(b===1)return A.y(c,r)\nwhile(true)switch(s){case 0:o=p.a\no.toString\no=B.h.cT(o,\"files\",\"readonly\").objectStore(\"files\").index(\"fileName\")\no.toString\nn=A\ns=3\nreturn A.p(B.V.ef(o,a),$async$bD)\ncase 3:q=n.dj(c)\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$bD,r)},\ncj(a,b){var s=a.objectStore(\"files\")\ns.toString\nreturn A.nF(A.n4(s,\"get\",[b],t.B),!1,t.jV).eb(new A.jj(b),t.bc)},\naQ(a){var s=0,r=A.B(t.p),q,p=this,o,n,m,l,k,j,i,h,g,f,e,d\nvar $async$aQ=A.C(function(b,c){if(b===1)return A.y(c,r)\nwhile(true)switch(s){case 0:e=p.a\ne.toString\no=B.h.bR(e,B.n,\"readonly\")\ne=o.objectStore(\"blocks\")\ne.toString\ns=3\nreturn A.p(p.cj(o,a),$async$aQ)\ncase 3:n=c\nm=J.T(n)\nl=m.gj(n)\nk=new Uint8Array(l)\nj=A.t([],t.iw)\nl=t.t\ni=new A.d7(A.n4(e,\"openCursor\",[self.IDBKeyRange.bound(A.t([a,0],l),A.t([a,9007199254740992],l))],t.B),t.c6)\ne=t.j,l=t.H\ncase 4:d=A\ns=6\nreturn A.p(i.p(),$async$aQ)\ncase 6:if(!d.aK(c)){s=5\nbreak}h=i.a\nif(h==null)h=A.J(A.K(\"Await moveNext() first\"))\ng=A.j(J.ab(e.a(h.key),1))\nf=m.gj(n)\nif(typeof f!==\"number\"){q=f.aX()\ns=1\nbreak}B.b.m(j,A.oG(new A.jo(h,k,g,Math.min(4096,f-g)),l))\ns=4\nbreak\ncase 5:s=7\nreturn A.p(A.nv(j,l),$async$aQ)\ncase 7:q=k\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$aQ,r)},\nap(a,b){return this.fu(A.j(a),b)},\nfu(a,b){var s=0,r=A.B(t.H),q=this,p,o,n,m,l,k,j\nvar $async$ap=A.C(function(c,d){if(c===1)return A.y(d,r)\nwhile(true)switch(s){case 0:k=q.a\nk.toString\np=B.h.bR(k,B.n,\"readwrite\")\nk=p.objectStore(\"blocks\")\nk.toString\ns=2\nreturn A.p(q.cj(p,a),$async$ap)\ncase 2:o=d\nn=b.b\nm=A.v(n).h(\"bv<1>\")\nl=A.fM(new A.bv(n,m),!0,m.h(\"e.E\"))\nB.b.eh(l)\nm=A.av(l)\ns=3\nreturn A.p(A.nv(new A.af(l,m.h(\"H<~>(1)\").a(new A.jk(new A.jl(k,a),b)),m.h(\"af<1,H<~>>\")),t.H),$async$ap)\ncase 3:k=J.T(o)\ns=b.c!==k.gj(o)?4:5\nbreak\ncase 4:n=p.objectStore(\"files\")\nn.toString\nn=B.i.e4(n,a)\nj=B.p\ns=7\nreturn A.p(n.gA(n),$async$ap)\ncase 7:s=6\nreturn A.p(j.cV(d,{name:k.gaO(o),length:b.c}),$async$ap)\ncase 6:case 5:return A.z(null,r)}})\nreturn A.A($async$ap,r)},\naA(a,b,c){return this.ho(0,A.j(b),c)},\nho(a,b,c){var s=0,r=A.B(t.H),q=this,p,o,n,m,l,k,j\nvar $async$aA=A.C(function(d,e){if(d===1)return A.y(e,r)\nwhile(true)switch(s){case 0:k=q.a\nk.toString\np=B.h.bR(k,B.n,\"readwrite\")\nk=p.objectStore(\"files\")\nk.toString\no=p.objectStore(\"blocks\")\no.toString\ns=2\nreturn A.p(q.cj(p,b),$async$aA)\ncase 2:n=e\nm=J.T(n)\ns=m.gj(n)>c?3:4\nbreak\ncase 3:l=t.t\ns=5\nreturn A.p(B.i.cv(o,self.IDBKeyRange.bound(A.t([b,B.c.R(c,4096)*4096+1],l),A.t([b,9007199254740992],l))),$async$aA)\ncase 5:case 4:k=B.i.e4(k,b)\nj=B.p\ns=7\nreturn A.p(k.gA(k),$async$aA)\ncase 7:s=6\nreturn A.p(j.cV(e,{name:m.gaO(n),length:c}),$async$aA)\ncase 6:return A.z(null,r)}})\nreturn A.A($async$aA,r)},\naa(a){var s=0,r=A.B(t.H),q=this,p,o,n,m\nvar $async$aa=A.C(function(b,c){if(b===1)return A.y(c,r)\nwhile(true)switch(s){case 0:m=q.a\nm.toString\np=B.h.bR(m,B.n,\"readwrite\")\nm=t.t\no=self.IDBKeyRange.bound(A.t([a,0],m),A.t([a,9007199254740992],m))\nm=p.objectStore(\"blocks\")\nm.toString\nm=B.i.cv(m,o)\nn=p.objectStore(\"files\")\nn.toString\ns=2\nreturn A.p(A.nv(A.t([m,B.i.cv(n,a)],t.iw),t.H),$async$aa)\ncase 2:return A.z(null,r)}})\nreturn A.A($async$aa,r)},\nseT(a){this.a=t.k5.a(a)}}\nA.jn.prototype={\n$1(a){var s,r,q,p\nt.bo.a(a)\ns=t.E.a(new A.c2([],[]).aF(a.target.result,!1))\nr=a.oldVersion\nif(r==null||r===0){q=B.h.dP(s,\"files\",!0)\nr=t.z\np=A.X(r,r)\np.k(0,\"unique\",!0)\nB.i.eQ(q,\"fileName\",\"name\",p)\nB.h.fM(s,\"blocks\")}},\n$S:57}\nA.jm.prototype={\n$1(a){return this.a.ag(\"Opening database blocked: \"+A.q(a))},\n$S:2}\nA.jj.prototype={\n$1(a){t.jV.a(a)\nif(a==null)throw A.b(A.bq(this.a,\"fileId\",\"File not found in database\"))\nelse return a},\n$S:58}\nA.jo.prototype={\n$0(){var s=0,r=A.B(t.H),q=this,p,o,n,m\nvar $async$$0=A.C(function(a,b){if(a===1)return A.y(b,r)\nwhile(true)switch(s){case 0:p=B.e\no=q.b\nn=q.c\nm=A\ns=2\nreturn A.p(A.ke(t.w.a(new A.c2([],[]).aF(q.a.value,!1))),$async$$0)\ncase 2:p.am(o,n,m.b_(b.buffer,0,q.d))\nreturn A.z(null,r)}})\nreturn A.A($async$$0,r)},\n$S:3}\nA.jl.prototype={\n$2(a,b){var s=0,r=A.B(t.H),q=this,p,o,n,m,l\nvar $async$$2=A.C(function(c,d){if(c===1)return A.y(d,r)\nwhile(true)switch(s){case 0:p=q.a\no=q.b\nn=t.t\ns=2\nreturn A.p(A.nF(A.n4(p,\"openCursor\",[self.IDBKeyRange.only(A.t([o,a],n))],t.B),!0,t.g9),$async$$2)\ncase 2:m=d\nl=A.r4(A.t([b],t.bs))\ns=m==null?3:5\nbreak\ncase 3:s=6\nreturn A.p(B.i.hh(p,l,A.t([o,a],n)),$async$$2)\ncase 6:s=4\nbreak\ncase 5:s=7\nreturn A.p(B.p.cV(m,l),$async$$2)\ncase 7:case 4:return A.z(null,r)}})\nreturn A.A($async$$2,r)},\n$S:59}\nA.jk.prototype={\n$1(a){var s\nA.j(a)\ns=this.b.b.i(0,a)\ns.toString\nreturn this.a.$2(a,s)},\n$S:78}\nA.bg.prototype={}\nA.lP.prototype={\nft(a,b,c){B.e.am(this.b.e8(0,a,new A.lQ(this,a)),b,c)},\nfC(a,b){var s,r,q,p,o,n,m,l,k\nfor(s=b.length,r=0;r<s;){q=a+r\np=B.c.R(q,4096)\no=B.c.ab(q,4096)\nn=s-r\nif(o!==0)m=Math.min(4096-o,n)\nelse{m=Math.min(4096,n)\no=0}n=b.buffer\nl=b.byteOffset\nk=new Uint8Array(n,l+r,m)\nr+=m\nthis.ft(p*4096,o,k)}this.shb(Math.max(this.c,a+s))},\nshb(a){this.c=A.j(a)}}\nA.lQ.prototype={\n$0(){var s=new Uint8Array(4096),r=this.a.a,q=r.length,p=this.b\nif(q>p)B.e.am(s,0,A.b_(r.buffer,r.byteOffset+p,A.dj(Math.min(4096,q-p))))\nreturn s},\n$S:61}\nA.im.prototype={}\nA.cM.prototype={\nb3(a){var s=this.a.a\nif(s==null)A.J(A.bd(10,\"FileSystem closed\"))\nif(a.cD(this.e)){this.dA()\nreturn a.d.a}else return A.oH(null,t.H)},\ndA(){var s,r,q=this\nif(q.c==null){s=q.e\ns=!s.gC(s)}else s=!1\nif(s){s=q.e\nr=q.c=s.gA(s)\ns.G(0,r)\nr.d.a0(0,A.rl(r.gbO(),t.H).aS(new A.jM(q)))}},\naD(a){var s=0,r=A.B(t.S),q,p=this,o,n\nvar $async$aD=A.C(function(b,c){if(b===1)return A.y(c,r)\nwhile(true)switch(s){case 0:n=p.r\ns=n.F(0,a)?3:5\nbreak\ncase 3:n=n.i(0,a)\nn.toString\nq=n\ns=1\nbreak\ns=4\nbreak\ncase 5:s=6\nreturn A.p(p.a.bD(a),$async$aD)\ncase 6:o=c\no.toString\nn.k(0,a,o)\nq=o\ns=1\nbreak\ncase 4:case 1:return A.z(q,r)}})\nreturn A.A($async$aD,r)},\nb2(){var s=0,r=A.B(t.H),q=this,p,o,n,m,l,k,j\nvar $async$b2=A.C(function(a,b){if(a===1)return A.y(b,r)\nwhile(true)switch(s){case 0:m=q.a\ns=2\nreturn A.p(m.bJ(),$async$b2)\ncase 2:l=b\nq.r.b4(0,l)\np=J.ot(l),p=p.gE(p),o=q.d.a\ncase 3:if(!p.p()){s=4\nbreak}n=p.gu(p)\nk=o\nj=n.a\ns=5\nreturn A.p(m.aQ(n.b),$async$b2)\ncase 5:k.k(0,j,b)\ns=3\nbreak\ncase 4:return A.z(null,r)}})\nreturn A.A($async$b2,r)},\nfT(a){return this.b3(new A.d9(t.M.a(new A.jN()),new A.aa(new A.E($.D,t.D),t.F)))},\nbB(a,b,c,d){var s,r=this,q=r.a.a\nif(q==null)A.J(A.bd(10,\"FileSystem closed\"))\nq=r.d\ns=q.a.F(0,b)\nq.bB(0,b,c,d)\nif(!s)r.b3(new A.ct(r,b,new A.aa(new A.E($.D,t.D),t.F)))},\nct(){var s,r=this.a.a\nif(r==null)A.J(A.bd(10,\"FileSystem closed\"))\ns=this.d.ct()\nthis.f.m(0,s)\nreturn s},\naa(a){var s=this\ns.d.aa(a)\nif(!s.f.G(0,a))s.b3(new A.d8(s,a,new A.aa(new A.E($.D,t.D),t.F)))},\ncw(a){var s=this.a.a\nif(s==null)A.J(A.bd(10,\"FileSystem closed\"))\nreturn this.d.a.F(0,a)},\ncM(a,b,c,d){var s\nA.j(d)\ns=this.a.a\nif(s==null)A.J(A.bd(10,\"FileSystem closed\"))\nreturn this.d.cM(0,b,c,d)},\nbU(a){var s=this.a.a\nif(s==null)A.J(A.bd(10,\"FileSystem closed\"))\nreturn this.d.bU(a)},\ncU(a,b){var s=this,r=s.a.a\nif(r==null)A.J(A.bd(10,\"FileSystem closed\"))\ns.d.cU(a,b)\nif(!s.f.S(0,a))s.b3(new A.d9(t.M.a(new A.jO(s,a,b)),new A.aa(new A.E($.D,t.D),t.F)))},\ncX(a,b,c,d){var s,r,q,p=this\nA.j(d)\ns=p.a.a\nif(s==null)A.J(A.bd(10,\"FileSystem closed\"))\ns=p.d\nr=s.a.i(0,b)\nif(r==null)r=new Uint8Array(0)\ns.cX(0,b,c,d)\nif(!p.f.S(0,b)){s=A.t([],t.o6)\nq=$.D\nB.b.m(s,new A.im(d,c))\np.b3(new A.cy(p,b,r,s,new A.aa(new A.E(q,t.D),t.F)))}},\n$ijD:1}\nA.jM.prototype={\n$0(){var s=this.a\ns.c=null\ns.dA()},\n$S:6}\nA.jN.prototype={\n$0(){},\n$S:6}\nA.jO.prototype={\n$0(){var s=0,r=A.B(t.H),q,p=this,o,n\nvar $async$$0=A.C(function(a,b){if(a===1)return A.y(b,r)\nwhile(true)switch(s){case 0:o=p.a\nn=o.a\ns=3\nreturn A.p(o.aD(p.b),$async$$0)\ncase 3:q=n.aA(0,b,p.c)\ns=1\nbreak\ncase 1:return A.z(q,r)}})\nreturn A.A($async$$0,r)},\n$S:3}\nA.a9.prototype={\ncD(a){t.h.a(a)\na.$ti.c.a(this)\na.cf(a.c,this,!1)\nreturn!0}}\nA.d9.prototype={\nL(){return this.w.$0()}}\nA.d8.prototype={\ncD(a){var s,r,q,p\nt.h.a(a)\nif(!a.gC(a)){s=a.gai(a)\nfor(r=this.x;s!=null;)if(s instanceof A.d8)if(s.x===r)return!1\nelse s=s.gbc()\nelse if(s instanceof A.cy){q=s.gbc()\nif(s.x===r){p=s.a\np.toString\np.cm(A.v(s).h(\"ae.E\").a(s))}s=q}else if(s instanceof A.ct){if(s.x===r){r=s.a\nr.toString\nr.cm(A.v(s).h(\"ae.E\").a(s))\nreturn!1}s=s.gbc()}else break}a.$ti.c.a(this)\na.cf(a.c,this,!1)\nreturn!0},\nL(){var s=0,r=A.B(t.H),q=this,p,o,n\nvar $async$L=A.C(function(a,b){if(a===1)return A.y(b,r)\nwhile(true)switch(s){case 0:p=q.w\no=q.x\ns=2\nreturn A.p(p.aD(o),$async$L)\ncase 2:n=b\np.r.G(0,o)\ns=3\nreturn A.p(p.a.aa(n),$async$L)\ncase 3:return A.z(null,r)}})\nreturn A.A($async$L,r)}}\nA.ct.prototype={\nL(){var s=0,r=A.B(t.H),q=this,p,o,n,m,l\nvar $async$L=A.C(function(a,b){if(a===1)return A.y(b,r)\nwhile(true)switch(s){case 0:p=q.w\no=q.x\nn=p.a.a\nn.toString\nn=B.h.cT(n,\"files\",\"readwrite\").objectStore(\"files\")\nn.toString\nm=p.r\nl=o\ns=2\nreturn A.p(A.nF(A.rD(n,{name:o,length:0}),!0,t.S),$async$L)\ncase 2:m.k(0,l,b)\nreturn A.z(null,r)}})\nreturn A.A($async$L,r)}}\nA.cy.prototype={\ncD(a){var s,r\nt.h.a(a)\ns=a.b===0?null:a.gai(a)\nfor(r=this.x;s!=null;)if(s instanceof A.cy)if(s.x===r){B.b.b4(s.z,this.z)\nreturn!1}else s=s.gbc()\nelse if(s instanceof A.ct){if(s.x===r)break\ns=s.gbc()}else break\na.$ti.c.a(this)\na.cf(a.c,this,!1)\nreturn!0},\nL(){var s=0,r=A.B(t.H),q=this,p,o,n,m,l,k\nvar $async$L=A.C(function(a,b){if(a===1)return A.y(b,r)\nwhile(true)switch(s){case 0:m=q.y\nl=new A.lP(m,A.X(t.S,t.p),m.length)\nfor(m=q.z,p=m.length,o=0;o<m.length;m.length===p||(0,A.aM)(m),++o){n=m[o]\nl.fC(n.a,n.b)}m=q.w\nk=m.a\ns=3\nreturn A.p(m.aD(q.x),$async$L)\ncase 3:s=2\nreturn A.p(k.ap(b,l),$async$L)\ncase 2:return A.z(null,r)}})\nreturn A.A($async$L,r)}}\nA.dF.prototype={\ncw(a){return this.a.F(0,a)},\nbB(a,b,c,d){var s=this.a,r=s.F(0,b)\nif(c&&r)throw A.b(A.bd(10,\"File already exists\"))\nif(d&&!r)throw A.b(A.bd(10,\"File not exists\"))\ns.e8(0,b,new A.jL())\n!r},\nfL(a,b){return this.bB(a,b,!1,!1)},\nct(){var s,r,q\nfor(s=this.a,r=0;q=\"/tmp/\"+r,s.F(0,q);)++r\nthis.fL(0,q)\nreturn q},\naa(a){var s=this.a\nif(!s.F(0,a))throw A.b(A.bd(5898,\"SQLITE_ERROR\"))\ns.G(0,a)},\ncM(a,b,c,d){var s,r\nA.j(d)\ns=this.a.i(0,b)\nif(s==null||s.length<=d)return 0\nr=Math.min(c.length,s.length-d)\nB.e.T(c,0,r,s,d)\nreturn r},\nbU(a){var s=this.a\nif(!s.F(0,a))throw A.b(A.bd(1,\"SQLITE_ERROR\"))\ns=s.i(0,a)\ns=s==null?null:J.Y(s)\nreturn s==null?0:s},\ncU(a,b){var s=this.a,r=s.i(0,a),q=new Uint8Array(b)\nif(r!=null)B.e.a6(q,0,Math.min(b,r.length),r)\ns.k(0,a,q)},\ncX(a,b,c,d){var s,r,q,p,o,n\nA.j(d)\ns=this.a\nr=s.i(0,b)\nif(r==null)r=new Uint8Array(0)\nq=d+c.length\np=r.length\no=q-p\nif(o<=0)B.e.a6(r,d,q,c)\nelse{n=new Uint8Array(p+o)\nB.e.am(n,0,r)\nB.e.am(n,d,c)\ns.k(0,b,n)}},\n$ijD:1}\nA.jL.prototype={\n$0(){return null},\n$S:6}\nA.jR.prototype={}\nA.cP.prototype={\nl(a){return A.S(this.a.toString())}}\nA.lf.prototype={}\nA.jC.prototype={}\nA.kk.prototype={}\nA.kj.prototype={}\nA.mr.prototype={}\nA.l4.prototype={}\nA.fy.prototype={}\nA.jE.prototype={}\nA.jF.prototype={}\nA.jH.prototype={}\nA.m3.prototype={}\nA.mt.prototype={}\nA.jG.prototype={}\nA.kg.prototype={\n$0(){var s=this.a,r=s.b\nif(r!=null)r.Y(0)\ns=s.a\nif(s!=null)s.Y(0)},\n$S:0}\nA.kh.prototype={\n$1(a){var s,r=this\nr.a.$0()\ns=r.e\nr.b.a0(0,A.oG(new A.kf(r.c,r.d,s),s))},\n$S:2}\nA.kf.prototype={\n$0(){var s=this.b\ns=this.a?new A.c2([],[]).aF(s.result,!1):s.result\nreturn this.c.a(s)},\n$S(){return this.c.h(\"0()\")}}\nA.ki.prototype={\n$1(a){var s\nthis.b.$0()\ns=this.a.a\nif(s==null)s=a\nthis.c.ag(s)},\n$S:2}\nA.d7.prototype={\nY(a){var s=0,r=A.B(t.H),q=this,p\nvar $async$Y=A.C(function(b,c){if(b===1)return A.y(c,r)\nwhile(true)switch(s){case 0:p=q.b\nif(p!=null)p.Y(0)\np=q.c\nif(p!=null)p.Y(0)\nq.c=q.b=null\nreturn A.z(null,r)}})\nreturn A.A($async$Y,r)},\np(){var s,r,q,p,o,n=this,m=n.a\nif(m!=null)J.qZ(m)\nm=new A.E($.D,t.g5)\ns=new A.aa(m,t.ex)\nr=n.d\nq=t.a\np=q.a(new A.lJ(n,s))\nt.Z.a(null)\no=t.A\nn.b=A.bf(r,\"success\",p,!1,o)\nn.c=A.bf(r,\"success\",q.a(new A.lK(n,s)),!1,o)\nreturn m},\nseS(a,b){this.a=this.$ti.h(\"1?\").a(b)}}\nA.lJ.prototype={\n$1(a){var s=this.a\ns.Y(0)\ns.seS(0,s.$ti.h(\"1?\").a(s.d.result))\nthis.b.a0(0,s.a!=null)},\n$S:2}\nA.lK.prototype={\n$1(a){var s=this.a\ns.Y(0)\ns=s.d.error\nif(s==null)s=a\nthis.b.ag(s)},\n$S:2}\nA.jA.prototype={}\nA.mK.prototype={}\nA.dc.prototype={}\nA.hG.prototype={\nez(a){var s,r,q,p,o,n,m,l,k,j\nfor(s=J.a2(a),r=J.jd(Object.keys(s.gdS(a)),t.N),q=A.v(r),r=new A.aP(r,r.gj(r),q.h(\"aP<h.E>\")),p=t.ng,o=t.Y,n=t.K,q=q.h(\"h.E\"),m=this.b,l=this.a;r.p();){k=r.d\nif(k==null)k=q.a(k)\nj=n.a(s.gdS(a)[k])\nif(o.b(j))l.k(0,k,j)\nelse if(p.b(j))m.k(0,k,j)}}}\nA.ls.prototype={\n$2(a,b){var s\nA.S(a)\nt.lK.a(b)\ns={}\nthis.a[a]=s\nJ.bo(b,new A.lr(s))},\n$S:62}\nA.lr.prototype={\n$2(a,b){this.a[A.S(a)]=t.K.a(b)},\n$S:63}\nA.k1.prototype={}\nA.cU.prototype={}\nA.cK.prototype={}\nA.hH.prototype={}\nA.hE.prototype={\nby(a,b){var s,r,q\nt.L.a(a)\ns=J.T(a)\nr=A.j(this.d.$1(s.gj(a)+b))\nq=A.b_(J.bO(this.b),0,null)\nB.e.a6(q,r,r+s.gj(a),a)\nB.e.dT(q,r+s.gj(a),r+s.gj(a)+b,0)\nreturn r},\ncq(a){return this.by(a,0)},\nek(a,b){var s=this.go.$2(a,b)\nreturn new A.cP(s==null?t.K.a(s):s)}}\nA.m4.prototype={\neA(a){var s,r,q,p=this,o=t.gt.a(new self.WebAssembly.Memory({initial:16}))\np.c=o\ns=t.N\nr=t.K\nq=t.Y\np.seD(t.n2.a(A.aO([\"env\",A.aO([\"memory\",o],s,r),\"dart\",A.aO([\"random\",A.a6(new A.m5(o,a),q),\"error_log\",A.a6(new A.m6(o),q),\"now\",A.a6(new A.m7(),q),\"path_normalize\",A.a6(new A.mh(o),q),\"function_xFunc\",A.a6(new A.mi(p),q),\"function_xStep\",A.a6(new A.mj(p),q),\"function_xInverse\",A.a6(new A.mk(p),q),\"function_xFinal\",A.a6(new A.ml(p),q),\"function_xValue\",A.a6(new A.mm(p),q),\"function_forget\",A.a6(new A.mn(p),q),\"function_compare\",A.a6(new A.mo(p,o),q),\"function_hook\",A.a6(new A.m8(p,o),q),\"fs_create\",A.a6(new A.m9(o,a),q),\"fs_temp_create\",A.a6(new A.ma(p,a),q),\"fs_size\",A.a6(new A.mb(p,a,o),q),\"fs_truncate\",A.a6(new A.mc(a,o),q),\"fs_read\",A.a6(new A.md(a,o),q),\"fs_write\",A.a6(new A.me(a,o),q),\"fs_delete\",A.a6(new A.mf(a,o),q),\"fs_access\",A.a6(new A.mg(p,a,o),q)],s,r)],s,t.lK)))},\nseD(a){this.b=t.n2.a(a)}}\nA.m5.prototype={\n$2(a,b){var s,r,q,p\nA.j(a)\nA.j(b)\ns=A.b_(this.a.buffer,a,b)\nr=this.b.a\nfor(q=s.length,p=0;p<q;++p)B.e.k(s,p,r.hd(256))},\n$S:64}\nA.m6.prototype={\n$1(a){A.b9(\"Error reported by native handler: \"+A.b0(this.a,A.j(a)))},\n$S:9}\nA.m7.prototype={\n$0(){return new A.cP(self.BigInt(Date.now()))},\n$S:66}\nA.mh.prototype={\n$3(a,b,c){var s,r,q\nA.j(a)\nA.j(b)\nA.j(c)\ns=this.a\nr=t.O.h(\"ay.S\").a($.qH().fw(0,A.b0(s,a)))\nq=B.f.gaG().a9(r)\nif(q.length>=c)return 1\nelse{B.e.am(A.b_(s.buffer,b,c),0,q)\nreturn 0}},\n$C:\"$3\",\n$R:3,\n$S:20}\nA.mi.prototype={\n$3(a,b,c){var s,r\nA.j(a)\nA.j(b)\nA.j(c)\ns=this.a\nr=s.a\nr===$&&A.aZ(\"bindings\")\ns.d.b.i(0,A.j(r.ry.$1(a))).ghv().$2(new A.cp(),new A.d4(s.a,b,c))},\n$C:\"$3\",\n$R:3,\n$S:12}\nA.mj.prototype={\n$3(a,b,c){var s,r\nA.j(a)\nA.j(b)\nA.j(c)\ns=this.a\nr=s.a\nr===$&&A.aZ(\"bindings\")\ns.d.b.i(0,A.j(r.ry.$1(a))).ghx().$2(new A.cp(),new A.d4(s.a,b,c))},\n$C:\"$3\",\n$R:3,\n$S:12}\nA.mk.prototype={\n$3(a,b,c){var s,r\nA.j(a)\nA.j(b)\nA.j(c)\ns=this.a\nr=s.a\nr===$&&A.aZ(\"bindings\")\ns.d.b.i(0,A.j(r.ry.$1(a))).ghw().$2(new A.cp(),new A.d4(s.a,b,c))},\n$C:\"$3\",\n$R:3,\n$S:12}\nA.ml.prototype={\n$1(a){var s,r\nA.j(a)\ns=this.a\nr=s.a\nr===$&&A.aZ(\"bindings\")\ns.d.b.i(0,A.j(r.ry.$1(a))).ghu().$1(new A.cp())},\n$S:9}\nA.mm.prototype={\n$1(a){var s,r\nA.j(a)\ns=this.a\nr=s.a\nr===$&&A.aZ(\"bindings\")\ns.d.b.i(0,A.j(r.ry.$1(a))).ghy().$1(new A.cp())},\n$S:9}\nA.mn.prototype={\n$1(a){this.a.d.b.G(0,A.j(a))},\n$S:9}\nA.mo.prototype={\n$5(a,b,c,d,e){var s,r,q\nA.j(a)\nA.j(b)\nA.j(c)\nA.j(d)\nA.j(e)\ns=this.b\nr=A.oU(s,c,b)\nq=A.oU(s,e,d)\nreturn this.a.d.b.i(0,a).ghs().$2(r,q)},\n$C:\"$5\",\n$R:5,\n$S:69}\nA.m8.prototype={\n$5(a,b,c,d,e){A.j(a)\nA.j(b)\nA.j(c)\nA.j(d)\nt.K.a(e)\nA.b0(this.b,d)},\n$C:\"$5\",\n$R:5,\n$S:70}\nA.m9.prototype={\n$2(a,b){var s,r,q,p,o,n\nA.j(a)\nA.j(b)\ns=A.b0(this.a,a)\nr=(b&4)!==0\nq=(b&16)!==0\ntry{this.b.b.bB(0,s,q,!A.aK(r))\nreturn 0}catch(o){n=A.M(o)\nif(n instanceof A.bc){p=n\nreturn p.a}else throw o}},\n$S:8}\nA.ma.prototype={\n$0(){var s=this.b.b.ct(),r=this.a.a\nr===$&&A.aZ(\"bindings\")\nt.O.h(\"ay.S\").a(s)\nreturn r.by(B.f.gaG().a9(s),1)},\n$S:71}\nA.mb.prototype={\n$2(a,b){var s,r,q,p,o,n,m\nA.j(a)\nA.j(b)\ntry{s=this.b.b.bU(A.b0(this.c,a))\nq=this.a.a\nq===$&&A.aZ(\"bindings\")\nq=q.b\np=J.a2(q)\no=A.dS(p.gaE(q),0,null)\nn=B.c.M(b,2)\nif(!(n<o.length))return A.d(o,n)\no[n]=0\nn=A.j(s)\nq=A.dS(p.gaE(q),0,null)\np=B.c.M(b+1,2)\nif(!(p<q.length))return A.d(q,p)\nq[p]=n\nreturn 0}catch(m){q=A.M(m)\nif(q instanceof A.bc){r=q\nreturn r.a}else throw m}},\n$S:8}\nA.mc.prototype={\n$2(a,b){var s,r,q\nA.j(a)\nA.j(b)\ntry{this.a.b.cU(A.b0(this.b,a),b)\nreturn 0}catch(r){q=A.M(r)\nif(q instanceof A.bc){s=q\nreturn s.a}else throw r}},\n$S:8}\nA.md.prototype={\n$4(a,b,c,d){var s,r,q\nA.j(a)\nA.j(b)\nA.j(c)\nt.K.a(d)\ntry{r=this.b\nr=this.a.b.cM(0,A.b0(r,a),A.b_(r.buffer,b,c),self.Number(d))\nreturn r}catch(q){r=A.M(q)\nif(r instanceof A.bc){s=r\nreturn-s.a}else throw q}},\n$C:\"$4\",\n$R:4,\n$S:17}\nA.me.prototype={\n$4(a,b,c,d){var s,r,q\nA.j(a)\nA.j(b)\nA.j(c)\nt.K.a(d)\ntry{r=this.b\nthis.a.b.cX(0,A.b0(r,a),A.b_(r.buffer,b,c),self.Number(d))\nreturn 0}catch(q){r=A.M(q)\nif(r instanceof A.bc){s=r\nreturn s.a}else throw q}},\n$C:\"$4\",\n$R:4,\n$S:17}\nA.mf.prototype={\n$1(a){var s,r,q\nA.j(a)\ntry{this.a.b.aa(A.b0(this.b,a))\nreturn 0}catch(r){q=A.M(r)\nif(q instanceof A.bc){s=q\nreturn s.a}else throw r}},\n$S:22}\nA.mg.prototype={\n$3(a,b,c){var s,r,q,p,o,n\nA.j(a)\nA.j(b)\nA.j(c)\ntry{s=this.b.b.cw(A.b0(this.c,a))\nq=this.a.a\nq===$&&A.aZ(\"bindings\")\np=A.aK(s)?1:0\nq=A.dS(J.bO(q.b),0,null)\no=B.c.M(c,2)\nif(!(o<q.length))return A.d(q,o)\nq[o]=p\nreturn 0}catch(n){q=A.M(n)\nif(q instanceof A.bc){r=q\nreturn r.a}else throw n}},\n$C:\"$3\",\n$R:3,\n$S:20}\nA.jy.prototype={\nsh4(a){this.e=t.hC.a(a)}}\nA.fd.prototype={\naY(a,b,c){return this.ew(c.h(\"0/()\").a(a),b,c,c)},\na7(a,b){return this.aY(a,null,b)},\new(a,b,c,d){var s=0,r=A.B(d),q,p=2,o,n=[],m=this,l,k,j,i,h\nvar $async$aY=A.C(function(e,f){if(e===1){o=f\ns=p}while(true)switch(s){case 0:i=m.a\nh=new A.aa(new A.E($.D,t.D),t.F)\nm.a=h.a\np=3\ns=i!=null?6:7\nbreak\ncase 6:s=8\nreturn A.p(i,$async$aY)\ncase 8:case 7:l=a.$0()\ns=t.c.b(l)?9:11\nbreak\ncase 9:s=12\nreturn A.p(l,$async$aY)\ncase 12:j=f\nq=j\nn=[1]\ns=4\nbreak\ns=10\nbreak\ncase 11:q=l\nn=[1]\ns=4\nbreak\ncase 10:n.push(5)\ns=4\nbreak\ncase 3:n=[2]\ncase 4:p=2\nk=new A.js(m,h)\nk.$0()\ns=n.pop()\nbreak\ncase 5:case 1:return A.z(q,r)\ncase 2:return A.y(o,r)}})\nreturn A.A($async$aY,r)},\nl(a){return\"Lock[\"+A.j7(this)+\"]\"},\n$irz:1}\nA.js.prototype={\n$0(){var s=this.a,r=this.b\nif(s.a===r.a)s.a=null\nr.fH(0)},\n$S:0};(function aliases(){var s=J.cN.prototype\ns.eo=s.l\ns=J.U.prototype\ns.eu=s.l\ns=A.as.prototype\ns.ep=s.dV\ns.eq=s.dW\ns.es=s.dY\ns.er=s.dX\ns=A.h.prototype\ns.cZ=s.T\ns=A.f.prototype\ns.en=s.co\ns=A.dy.prototype\ns.em=s.l\ns=A.e3.prototype\ns.ev=s.l})();(function installTearOffs(){var s=hunkHelpers._static_2,r=hunkHelpers._static_1,q=hunkHelpers._static_0,p=hunkHelpers.installStaticTearOff,o=hunkHelpers.installInstanceTearOff,n=hunkHelpers._instance_2u,m=hunkHelpers._instance_0u\ns(J,\"uE\",\"rs\",73)\nr(A,\"v0\",\"tw\",11)\nr(A,\"v1\",\"tx\",11)\nr(A,\"v2\",\"ty\",11)\nq(A,\"q7\",\"uU\",0)\nr(A,\"v3\",\"uQ\",4)\np(A,\"v4\",4,null,[\"$4\"],[\"n0\"],75,0)\no(A.cs.prototype,\"gfI\",0,1,function(){return[null]},[\"$2\",\"$1\"],[\"bA\",\"ag\"],26,0,0)\nn(A.E.prototype,\"gda\",\"V\",21)\no(A.dd.prototype,\"gfz\",0,1,null,[\"$2\",\"$1\"],[\"dK\",\"fA\"],26,0,0)\ns(A,\"q9\",\"ut\",23)\nr(A,\"qa\",\"uu\",15)\nr(A,\"v8\",\"vh\",15)\ns(A,\"v7\",\"vg\",23)\nr(A,\"v6\",\"ts\",52)\nr(A,\"ok\",\"j4\",25)\nm(A.d9.prototype,\"gbO\",\"L\",0)\nm(A.d8.prototype,\"gbO\",\"L\",3)\nm(A.ct.prototype,\"gbO\",\"L\",3)\nm(A.cy.prototype,\"gbO\",\"L\",3)})();(function inheritance(){var s=hunkHelpers.mixin,r=hunkHelpers.inherit,q=hunkHelpers.inheritMany\nr(A.r,null)\nq(A.r,[A.nz,J.cN,J.ca,A.e,A.du,A.w,A.bS,A.Q,A.ev,A.kn,A.aP,A.L,A.dB,A.eg,A.ar,A.c0,A.d1,A.cS,A.dw,A.fI,A.ld,A.h2,A.dC,A.eH,A.mu,A.jV,A.dM,A.dL,A.ey,A.hN,A.ea,A.iF,A.lI,A.b2,A.i4,A.iR,A.mD,A.eh,A.db,A.de,A.dt,A.cs,A.bH,A.E,A.hP,A.aV,A.bm,A.hm,A.dd,A.iK,A.ej,A.bG,A.hV,A.b4,A.iD,A.iT,A.eT,A.eV,A.ib,A.cw,A.eu,A.ae,A.h,A.ex,A.c5,A.e1,A.ay,A.mI,A.mH,A.i3,A.a8,A.bU,A.cd,A.lL,A.h5,A.e9,A.i0,A.fC,A.fG,A.a4,A.R,A.iI,A.ah,A.eR,A.lg,A.b5,A.jx,A.nu,A.u,A.dD,A.mA,A.lw,A.h0,A.i8,A.h_,A.hy,A.fk,A.lc,A.k8,A.dy,A.jB,A.fx,A.cH,A.kC,A.e6,A.iB,A.iq,A.aU,A.dg,A.l0,A.e7,A.d_,A.hj,A.kc,A.kd,A.bs,A.fr,A.l1,A.d0,A.cF,A.iv,A.hI,A.hF,A.lu,A.hJ,A.l2,A.bc,A.ji,A.lP,A.im,A.cM,A.dF,A.cP,A.d7,A.hG,A.hE,A.m4,A.jy,A.fd])\nq(J.cN,[J.fH,J.dK,J.a,J.O,J.cO,J.bW,A.cW,A.a5])\nq(J.a,[J.U,A.f,A.f4,A.bR,A.bb,A.P,A.hT,A.aq,A.fq,A.ft,A.hW,A.dA,A.hY,A.fv,A.l,A.i1,A.aA,A.fD,A.i6,A.cL,A.fO,A.fP,A.id,A.ie,A.aB,A.ig,A.ii,A.aC,A.io,A.ix,A.cY,A.aE,A.iy,A.aF,A.iC,A.al,A.iL,A.hr,A.aI,A.iN,A.ht,A.hB,A.iU,A.iW,A.iY,A.j_,A.j1,A.bT,A.cj,A.dG,A.dX,A.aN,A.i9,A.aR,A.ik,A.h8,A.iG,A.aW,A.iP,A.f9,A.hQ])\nq(J.U,[J.h6,J.c_,J.bu,A.bg,A.jR,A.lf,A.jC,A.kk,A.kj,A.mr,A.l4,A.fy,A.jH,A.m3,A.mt,A.jG,A.jA,A.mK,A.dc,A.k1,A.cU,A.cK])\nr(J.jQ,J.O)\nq(J.cO,[J.dJ,J.fJ])\nq(A.e,[A.c3,A.k,A.bw,A.lv,A.bA,A.ef,A.em,A.dH,A.iE,A.cR])\nq(A.c3,[A.cb,A.eU])\nr(A.ep,A.cb)\nr(A.ek,A.eU)\nr(A.ba,A.ek)\nr(A.dP,A.w)\nq(A.dP,[A.dv,A.d3,A.as])\nq(A.bS,[A.fg,A.jt,A.ff,A.jv,A.ho,A.jT,A.na,A.nc,A.lz,A.ly,A.mM,A.jJ,A.lV,A.m2,A.l9,A.l8,A.mx,A.mq,A.k0,A.lF,A.mS,A.mT,A.lN,A.lO,A.mQ,A.mP,A.k7,A.nj,A.nk,A.jw,A.n1,A.n3,A.kp,A.ku,A.kt,A.kr,A.ks,A.kZ,A.kI,A.kT,A.kS,A.kN,A.kP,A.kU,A.kJ,A.mY,A.ng,A.l3,A.n7,A.jn,A.jm,A.jj,A.jk,A.kh,A.ki,A.lJ,A.lK,A.m6,A.mh,A.mi,A.mj,A.mk,A.ml,A.mm,A.mn,A.mo,A.m8,A.md,A.me,A.mf,A.mg])\nq(A.fg,[A.ju,A.ka,A.jS,A.nb,A.mN,A.n2,A.jK,A.lW,A.jW,A.k_,A.k6,A.lE,A.lh,A.lj,A.lk,A.mR,A.k2,A.k3,A.k4,A.k5,A.kl,A.km,A.l5,A.l6,A.mB,A.mC,A.lx,A.n5,A.jp,A.jq,A.mL,A.mW,A.mV,A.jl,A.ls,A.lr,A.m5,A.m9,A.mb,A.mc])\nq(A.Q,[A.cQ,A.bn,A.fK,A.hx,A.hc,A.ds,A.i_,A.h1,A.bh,A.dU,A.hz,A.hv,A.bB,A.fj,A.fp])\nr(A.dN,A.ev)\nq(A.dN,[A.d2,A.d4])\nr(A.fh,A.d2)\nq(A.ff,[A.ni,A.lA,A.lB,A.mE,A.jI,A.lR,A.lZ,A.lX,A.lT,A.lY,A.lS,A.m1,A.m0,A.m_,A.la,A.l7,A.mz,A.my,A.lH,A.lG,A.ms,A.mO,A.n_,A.mw,A.mv,A.ln,A.lm,A.ko,A.kx,A.kv,A.kq,A.ky,A.kB,A.kA,A.kz,A.kw,A.kG,A.kF,A.kQ,A.kK,A.kR,A.kO,A.kM,A.kL,A.jz,A.jo,A.lQ,A.jM,A.jN,A.jO,A.jL,A.kg,A.kf,A.m7,A.ma,A.js])\nq(A.k,[A.a3,A.cf,A.bv,A.ew])\nq(A.a3,[A.cn,A.af,A.ic,A.e0])\nr(A.ce,A.bw)\nq(A.L,[A.dQ,A.cq,A.e2,A.ir])\nr(A.cG,A.bA)\nr(A.dO,A.d3)\nr(A.dh,A.cS)\nr(A.ed,A.dh)\nr(A.dx,A.ed)\nr(A.cc,A.dw)\nr(A.dW,A.bn)\nq(A.ho,[A.hk,A.cD])\nr(A.hO,A.ds)\nq(A.dH,[A.hM,A.eK])\nq(A.a5,[A.dR,A.ag])\nq(A.ag,[A.eA,A.eC])\nr(A.eB,A.eA)\nr(A.bX,A.eB)\nr(A.eD,A.eC)\nr(A.aQ,A.eD)\nq(A.bX,[A.fT,A.fU])\nq(A.aQ,[A.fV,A.fW,A.fX,A.fY,A.fZ,A.dT,A.cl])\nr(A.eN,A.i_)\nq(A.cs,[A.cr,A.aa])\nr(A.df,A.dd)\nq(A.aV,[A.eJ,A.lM])\nr(A.d5,A.eJ)\nr(A.d6,A.ej)\nq(A.bG,[A.cu,A.en])\nr(A.iu,A.eT)\nq(A.as,[A.et,A.er])\nr(A.eE,A.eV)\nr(A.es,A.eE)\nq(A.ay,[A.fc,A.fw])\nr(A.fl,A.hm)\nq(A.fl,[A.jr,A.lo,A.ll])\nr(A.ee,A.fw)\nq(A.bh,[A.cX,A.fE])\nr(A.hU,A.eR)\nq(A.f,[A.G,A.fz,A.ck,A.c1,A.aD,A.eF,A.aH,A.am,A.eL,A.hD,A.bj,A.bz,A.ec,A.fb,A.bQ])\nq(A.G,[A.n,A.bi])\nr(A.o,A.n)\nq(A.o,[A.f5,A.f6,A.fB,A.hd])\nr(A.fm,A.bb)\nr(A.cE,A.hT)\nq(A.aq,[A.fn,A.fo])\nr(A.hX,A.hW)\nr(A.dz,A.hX)\nr(A.hZ,A.hY)\nr(A.fu,A.hZ)\nr(A.az,A.bR)\nr(A.i2,A.i1)\nr(A.cI,A.i2)\nr(A.i7,A.i6)\nr(A.ci,A.i7)\nq(A.l,[A.cV,A.bD])\nr(A.fQ,A.id)\nr(A.fR,A.ie)\nr(A.ih,A.ig)\nr(A.fS,A.ih)\nr(A.ij,A.ii)\nr(A.dV,A.ij)\nr(A.ip,A.io)\nr(A.h7,A.ip)\nr(A.hb,A.ix)\nr(A.cZ,A.c1)\nr(A.eG,A.eF)\nr(A.hf,A.eG)\nr(A.iz,A.iy)\nr(A.hg,A.iz)\nr(A.hl,A.iC)\nr(A.iM,A.iL)\nr(A.hp,A.iM)\nr(A.eM,A.eL)\nr(A.hq,A.eM)\nr(A.iO,A.iN)\nr(A.hs,A.iO)\nr(A.iV,A.iU)\nr(A.hS,A.iV)\nr(A.eo,A.dA)\nr(A.iX,A.iW)\nr(A.i5,A.iX)\nr(A.iZ,A.iY)\nr(A.ez,A.iZ)\nr(A.j0,A.j_)\nr(A.iA,A.j0)\nr(A.j2,A.j1)\nr(A.iJ,A.j2)\nr(A.eq,A.bm)\nr(A.cx,A.mA)\nr(A.c2,A.lw)\nr(A.br,A.bT)\nr(A.ia,A.i9)\nr(A.fL,A.ia)\nr(A.il,A.ik)\nr(A.h3,A.il)\nr(A.iH,A.iG)\nr(A.hn,A.iH)\nr(A.iQ,A.iP)\nr(A.hu,A.iQ)\nr(A.fa,A.hQ)\nr(A.h4,A.bQ)\nr(A.bV,A.lc)\nq(A.bV,[A.h9,A.hC,A.hK])\nr(A.e3,A.dy)\nr(A.bl,A.e3)\nr(A.kD,A.kC)\nr(A.be,A.dg)\nr(A.e8,A.e7)\nq(A.bs,[A.fA,A.cJ])\nq(A.cF,[A.dI,A.is])\nr(A.hL,A.dI)\nr(A.it,A.is)\nr(A.ha,A.it)\nr(A.iw,A.iv)\nr(A.ak,A.iw)\nr(A.dY,A.lL)\nr(A.cp,A.kc)\nr(A.bE,A.kd)\nr(A.a9,A.ae)\nq(A.a9,[A.d9,A.d8,A.ct,A.cy])\nq(A.fy,[A.jE,A.jF])\nr(A.hH,A.l1)\ns(A.d2,A.c0)\ns(A.eU,A.h)\ns(A.eA,A.h)\ns(A.eB,A.ar)\ns(A.eC,A.h)\ns(A.eD,A.ar)\ns(A.df,A.iK)\ns(A.d3,A.c5)\ns(A.ev,A.h)\ns(A.dh,A.c5)\ns(A.eV,A.e1)\ns(A.hT,A.jx)\ns(A.hW,A.h)\ns(A.hX,A.u)\ns(A.hY,A.h)\ns(A.hZ,A.u)\ns(A.i1,A.h)\ns(A.i2,A.u)\ns(A.i6,A.h)\ns(A.i7,A.u)\ns(A.id,A.w)\ns(A.ie,A.w)\ns(A.ig,A.h)\ns(A.ih,A.u)\ns(A.ii,A.h)\ns(A.ij,A.u)\ns(A.io,A.h)\ns(A.ip,A.u)\ns(A.ix,A.w)\ns(A.eF,A.h)\ns(A.eG,A.u)\ns(A.iy,A.h)\ns(A.iz,A.u)\ns(A.iC,A.w)\ns(A.iL,A.h)\ns(A.iM,A.u)\ns(A.eL,A.h)\ns(A.eM,A.u)\ns(A.iN,A.h)\ns(A.iO,A.u)\ns(A.iU,A.h)\ns(A.iV,A.u)\ns(A.iW,A.h)\ns(A.iX,A.u)\ns(A.iY,A.h)\ns(A.iZ,A.u)\ns(A.j_,A.h)\ns(A.j0,A.u)\ns(A.j1,A.h)\ns(A.j2,A.u)\ns(A.i9,A.h)\ns(A.ia,A.u)\ns(A.ik,A.h)\ns(A.il,A.u)\ns(A.iG,A.h)\ns(A.iH,A.u)\ns(A.iP,A.h)\ns(A.iQ,A.u)\ns(A.hQ,A.w)\ns(A.is,A.h)\ns(A.it,A.h_)\ns(A.iv,A.hy)\ns(A.iw,A.w)})()\nvar v={typeUniverse:{eC:new Map(),tR:{},eT:{},tPV:{},sEA:[]},mangledGlobalNames:{c:\"int\",N:\"double\",W:\"num\",i:\"String\",aw:\"bool\",R:\"Null\",m:\"List\"},mangledNames:{},types:[\"~()\",\"~(i,@)\",\"~(l)\",\"H<~>()\",\"~(@)\",\"H<@>()\",\"R()\",\"~(@,@)\",\"c(c,c)\",\"R(c)\",\"H<R>()\",\"~(~())\",\"R(c,c,c)\",\"H<@>(aU)\",\"~(aX,i,c)\",\"c(r?)\",\"R(@)\",\"c(c,c,c,r)\",\"H<r?>()\",\"@()\",\"c(c,c,c)\",\"~(r,aG)\",\"c(c)\",\"aw(r?,r?)\",\"~(i,i)\",\"H<~>(l)\",\"~(r[aG?])\",\"H<I<@,@>>()\",\"~(i,c)\",\"R(@,@)\",\"@(@,@)\",\"aw(i)\",\"i(i?)\",\"i?(r?)\",\"c?()\",\"c?(i)\",\"~(co,@)\",\"H<c?>()\",\"H<c>()\",\"~(r?,r?)\",\"aw(@)\",\"I<i,r?>(bl)\",\"~(@[@])\",\"bl(@)\",\"~(i,c?)\",\"I<@,@>(c)\",\"~(I<@,@>)\",\"E<@>(@)\",\"H<r?>(aU)\",\"H<c?>(aU)\",\"H<c>(aU)\",\"H<aw>()\",\"i(i)\",\"R(r,aG)\",\"a4<i,be>(c,be)\",\"i(r?)\",\"~(bs)\",\"~(bD)\",\"bg(bg?)\",\"H<~>(c,aX)\",\"aX(@,@)\",\"aX()\",\"~(i,I<i,r>)\",\"~(i,r)\",\"R(c,c)\",\"~(cH)\",\"cP()\",\"~(c,@)\",\"R(@,aG)\",\"c(c,c,c,c,c)\",\"R(c,c,c,c,r)\",\"c()\",\"R(~())\",\"c(@,@)\",\"@(i)\",\"~(bF?,nR?,bF,~())\",\"@(@,i)\",\"@(@)\",\"H<~>(c)\"],interceptorsByTag:null,leafTags:null,arrayRti:Symbol(\"$ti\")}\nA.u0(v.typeUniverse,JSON.parse('{\"h6\":\"U\",\"c_\":\"U\",\"bu\":\"U\",\"bg\":\"U\",\"jR\":\"U\",\"lf\":\"U\",\"jC\":\"U\",\"kk\":\"U\",\"kj\":\"U\",\"mr\":\"U\",\"l4\":\"U\",\"fy\":\"U\",\"jE\":\"U\",\"jF\":\"U\",\"jH\":\"U\",\"m3\":\"U\",\"mt\":\"U\",\"jG\":\"U\",\"jA\":\"U\",\"dc\":\"U\",\"cK\":\"U\",\"mK\":\"U\",\"k1\":\"U\",\"cU\":\"U\",\"vS\":\"a\",\"vT\":\"a\",\"vB\":\"a\",\"vz\":\"l\",\"vO\":\"l\",\"vC\":\"bQ\",\"vA\":\"f\",\"vY\":\"f\",\"w1\":\"f\",\"vU\":\"n\",\"vX\":\"bz\",\"vD\":\"o\",\"vV\":\"o\",\"vQ\":\"G\",\"vN\":\"G\",\"wl\":\"am\",\"vM\":\"c1\",\"vE\":\"bi\",\"w8\":\"bi\",\"vR\":\"ci\",\"vF\":\"P\",\"vH\":\"bb\",\"vJ\":\"al\",\"vK\":\"aq\",\"vG\":\"aq\",\"vI\":\"aq\",\"fH\":{\"aw\":[]},\"dK\":{\"R\":[]},\"U\":{\"a\":[],\"ny\":[],\"bg\":[],\"dc\":[],\"cU\":[],\"cK\":[]},\"O\":{\"m\":[\"1\"],\"k\":[\"1\"],\"e\":[\"1\"]},\"jQ\":{\"O\":[\"1\"],\"m\":[\"1\"],\"k\":[\"1\"],\"e\":[\"1\"]},\"ca\":{\"L\":[\"1\"]},\"cO\":{\"N\":[],\"W\":[],\"aj\":[\"W\"]},\"dJ\":{\"N\":[],\"c\":[],\"W\":[],\"aj\":[\"W\"]},\"fJ\":{\"N\":[],\"W\":[],\"aj\":[\"W\"]},\"bW\":{\"i\":[],\"aj\":[\"i\"],\"k9\":[]},\"c3\":{\"e\":[\"2\"]},\"du\":{\"L\":[\"2\"]},\"cb\":{\"c3\":[\"1\",\"2\"],\"e\":[\"2\"],\"e.E\":\"2\"},\"ep\":{\"cb\":[\"1\",\"2\"],\"c3\":[\"1\",\"2\"],\"k\":[\"2\"],\"e\":[\"2\"],\"e.E\":\"2\"},\"ek\":{\"h\":[\"2\"],\"m\":[\"2\"],\"c3\":[\"1\",\"2\"],\"k\":[\"2\"],\"e\":[\"2\"]},\"ba\":{\"ek\":[\"1\",\"2\"],\"h\":[\"2\"],\"m\":[\"2\"],\"c3\":[\"1\",\"2\"],\"k\":[\"2\"],\"e\":[\"2\"],\"h.E\":\"2\",\"e.E\":\"2\"},\"dv\":{\"w\":[\"3\",\"4\"],\"I\":[\"3\",\"4\"],\"w.K\":\"3\",\"w.V\":\"4\"},\"cQ\":{\"Q\":[]},\"fh\":{\"h\":[\"c\"],\"c0\":[\"c\"],\"m\":[\"c\"],\"k\":[\"c\"],\"e\":[\"c\"],\"h.E\":\"c\",\"c0.E\":\"c\"},\"k\":{\"e\":[\"1\"]},\"a3\":{\"k\":[\"1\"],\"e\":[\"1\"]},\"cn\":{\"a3\":[\"1\"],\"k\":[\"1\"],\"e\":[\"1\"],\"a3.E\":\"1\",\"e.E\":\"1\"},\"aP\":{\"L\":[\"1\"]},\"bw\":{\"e\":[\"2\"],\"e.E\":\"2\"},\"ce\":{\"bw\":[\"1\",\"2\"],\"k\":[\"2\"],\"e\":[\"2\"],\"e.E\":\"2\"},\"dQ\":{\"L\":[\"2\"]},\"af\":{\"a3\":[\"2\"],\"k\":[\"2\"],\"e\":[\"2\"],\"a3.E\":\"2\",\"e.E\":\"2\"},\"lv\":{\"e\":[\"1\"],\"e.E\":\"1\"},\"cq\":{\"L\":[\"1\"]},\"bA\":{\"e\":[\"1\"],\"e.E\":\"1\"},\"cG\":{\"bA\":[\"1\"],\"k\":[\"1\"],\"e\":[\"1\"],\"e.E\":\"1\"},\"e2\":{\"L\":[\"1\"]},\"cf\":{\"k\":[\"1\"],\"e\":[\"1\"],\"e.E\":\"1\"},\"dB\":{\"L\":[\"1\"]},\"ef\":{\"e\":[\"1\"],\"e.E\":\"1\"},\"eg\":{\"L\":[\"1\"]},\"d2\":{\"h\":[\"1\"],\"c0\":[\"1\"],\"m\":[\"1\"],\"k\":[\"1\"],\"e\":[\"1\"]},\"ic\":{\"a3\":[\"c\"],\"k\":[\"c\"],\"e\":[\"c\"],\"a3.E\":\"c\",\"e.E\":\"c\"},\"dO\":{\"w\":[\"c\",\"1\"],\"c5\":[\"c\",\"1\"],\"I\":[\"c\",\"1\"],\"w.K\":\"c\",\"w.V\":\"1\"},\"e0\":{\"a3\":[\"1\"],\"k\":[\"1\"],\"e\":[\"1\"],\"a3.E\":\"1\",\"e.E\":\"1\"},\"d1\":{\"co\":[]},\"dx\":{\"ed\":[\"1\",\"2\"],\"dh\":[\"1\",\"2\"],\"cS\":[\"1\",\"2\"],\"c5\":[\"1\",\"2\"],\"I\":[\"1\",\"2\"]},\"dw\":{\"I\":[\"1\",\"2\"]},\"cc\":{\"dw\":[\"1\",\"2\"],\"I\":[\"1\",\"2\"]},\"em\":{\"e\":[\"1\"],\"e.E\":\"1\"},\"fI\":{\"oI\":[]},\"dW\":{\"bn\":[],\"Q\":[]},\"fK\":{\"Q\":[]},\"hx\":{\"Q\":[]},\"h2\":{\"ac\":[]},\"eH\":{\"aG\":[]},\"bS\":{\"ch\":[]},\"ff\":{\"ch\":[]},\"fg\":{\"ch\":[]},\"ho\":{\"ch\":[]},\"hk\":{\"ch\":[]},\"cD\":{\"ch\":[]},\"hc\":{\"Q\":[]},\"hO\":{\"Q\":[]},\"as\":{\"w\":[\"1\",\"2\"],\"jU\":[\"1\",\"2\"],\"I\":[\"1\",\"2\"],\"w.K\":\"1\",\"w.V\":\"2\"},\"bv\":{\"k\":[\"1\"],\"e\":[\"1\"],\"e.E\":\"1\"},\"dM\":{\"L\":[\"1\"]},\"dL\":{\"oW\":[],\"k9\":[]},\"ey\":{\"e_\":[],\"cT\":[]},\"hM\":{\"e\":[\"e_\"],\"e.E\":\"e_\"},\"hN\":{\"L\":[\"e_\"]},\"ea\":{\"cT\":[]},\"iE\":{\"e\":[\"cT\"],\"e.E\":\"cT\"},\"iF\":{\"L\":[\"cT\"]},\"cW\":{\"nt\":[]},\"dR\":{\"a5\":[],\"oC\":[]},\"ag\":{\"F\":[\"1\"],\"a5\":[]},\"bX\":{\"ag\":[\"N\"],\"h\":[\"N\"],\"F\":[\"N\"],\"m\":[\"N\"],\"a5\":[],\"k\":[\"N\"],\"e\":[\"N\"],\"ar\":[\"N\"]},\"aQ\":{\"ag\":[\"c\"],\"h\":[\"c\"],\"F\":[\"c\"],\"m\":[\"c\"],\"a5\":[],\"k\":[\"c\"],\"e\":[\"c\"],\"ar\":[\"c\"]},\"fT\":{\"bX\":[],\"ag\":[\"N\"],\"h\":[\"N\"],\"F\":[\"N\"],\"m\":[\"N\"],\"a5\":[],\"k\":[\"N\"],\"e\":[\"N\"],\"ar\":[\"N\"],\"h.E\":\"N\"},\"fU\":{\"bX\":[],\"ag\":[\"N\"],\"h\":[\"N\"],\"F\":[\"N\"],\"m\":[\"N\"],\"a5\":[],\"k\":[\"N\"],\"e\":[\"N\"],\"ar\":[\"N\"],\"h.E\":\"N\"},\"fV\":{\"aQ\":[],\"ag\":[\"c\"],\"h\":[\"c\"],\"F\":[\"c\"],\"m\":[\"c\"],\"a5\":[],\"k\":[\"c\"],\"e\":[\"c\"],\"ar\":[\"c\"],\"h.E\":\"c\"},\"fW\":{\"aQ\":[],\"ag\":[\"c\"],\"h\":[\"c\"],\"F\":[\"c\"],\"m\":[\"c\"],\"a5\":[],\"k\":[\"c\"],\"e\":[\"c\"],\"ar\":[\"c\"],\"h.E\":\"c\"},\"fX\":{\"aQ\":[],\"ag\":[\"c\"],\"h\":[\"c\"],\"F\":[\"c\"],\"m\":[\"c\"],\"a5\":[],\"k\":[\"c\"],\"e\":[\"c\"],\"ar\":[\"c\"],\"h.E\":\"c\"},\"fY\":{\"aQ\":[],\"ag\":[\"c\"],\"h\":[\"c\"],\"nP\":[],\"F\":[\"c\"],\"m\":[\"c\"],\"a5\":[],\"k\":[\"c\"],\"e\":[\"c\"],\"ar\":[\"c\"],\"h.E\":\"c\"},\"fZ\":{\"aQ\":[],\"ag\":[\"c\"],\"h\":[\"c\"],\"F\":[\"c\"],\"m\":[\"c\"],\"a5\":[],\"k\":[\"c\"],\"e\":[\"c\"],\"ar\":[\"c\"],\"h.E\":\"c\"},\"dT\":{\"aQ\":[],\"ag\":[\"c\"],\"h\":[\"c\"],\"F\":[\"c\"],\"m\":[\"c\"],\"a5\":[],\"k\":[\"c\"],\"e\":[\"c\"],\"ar\":[\"c\"],\"h.E\":\"c\"},\"cl\":{\"aQ\":[],\"ag\":[\"c\"],\"h\":[\"c\"],\"aX\":[],\"F\":[\"c\"],\"m\":[\"c\"],\"a5\":[],\"k\":[\"c\"],\"e\":[\"c\"],\"ar\":[\"c\"],\"h.E\":\"c\"},\"i_\":{\"Q\":[]},\"eN\":{\"bn\":[],\"Q\":[]},\"E\":{\"H\":[\"1\"]},\"eh\":{\"fi\":[\"1\"]},\"de\":{\"L\":[\"1\"]},\"eK\":{\"e\":[\"1\"],\"e.E\":\"1\"},\"dt\":{\"Q\":[]},\"cs\":{\"fi\":[\"1\"]},\"cr\":{\"cs\":[\"1\"],\"fi\":[\"1\"]},\"aa\":{\"cs\":[\"1\"],\"fi\":[\"1\"]},\"dd\":{\"pp\":[\"1\"],\"cv\":[\"1\"]},\"df\":{\"iK\":[\"1\"],\"dd\":[\"1\"],\"pp\":[\"1\"],\"cv\":[\"1\"]},\"d5\":{\"eJ\":[\"1\"],\"aV\":[\"1\"],\"aV.T\":\"1\"},\"d6\":{\"ej\":[\"1\"],\"bm\":[\"1\"],\"cv\":[\"1\"]},\"ej\":{\"bm\":[\"1\"],\"cv\":[\"1\"]},\"eJ\":{\"aV\":[\"1\"]},\"cu\":{\"bG\":[\"1\"]},\"en\":{\"bG\":[\"@\"]},\"hV\":{\"bG\":[\"@\"]},\"eT\":{\"bF\":[]},\"iu\":{\"eT\":[],\"bF\":[]},\"et\":{\"as\":[\"1\",\"2\"],\"w\":[\"1\",\"2\"],\"jU\":[\"1\",\"2\"],\"I\":[\"1\",\"2\"],\"w.K\":\"1\",\"w.V\":\"2\"},\"er\":{\"as\":[\"1\",\"2\"],\"w\":[\"1\",\"2\"],\"jU\":[\"1\",\"2\"],\"I\":[\"1\",\"2\"],\"w.K\":\"1\",\"w.V\":\"2\"},\"es\":{\"e1\":[\"1\"],\"p_\":[\"1\"],\"k\":[\"1\"],\"e\":[\"1\"]},\"cw\":{\"L\":[\"1\"]},\"dH\":{\"e\":[\"1\"]},\"cR\":{\"e\":[\"1\"],\"e.E\":\"1\"},\"eu\":{\"L\":[\"1\"]},\"dN\":{\"h\":[\"1\"],\"m\":[\"1\"],\"k\":[\"1\"],\"e\":[\"1\"]},\"dP\":{\"w\":[\"1\",\"2\"],\"I\":[\"1\",\"2\"]},\"w\":{\"I\":[\"1\",\"2\"]},\"d3\":{\"w\":[\"1\",\"2\"],\"c5\":[\"1\",\"2\"],\"I\":[\"1\",\"2\"]},\"ew\":{\"k\":[\"2\"],\"e\":[\"2\"],\"e.E\":\"2\"},\"ex\":{\"L\":[\"2\"]},\"cS\":{\"I\":[\"1\",\"2\"]},\"ed\":{\"dh\":[\"1\",\"2\"],\"cS\":[\"1\",\"2\"],\"c5\":[\"1\",\"2\"],\"I\":[\"1\",\"2\"]},\"eE\":{\"e1\":[\"1\"],\"p_\":[\"1\"],\"k\":[\"1\"],\"e\":[\"1\"]},\"fc\":{\"ay\":[\"m<c>\",\"i\"],\"ay.S\":\"m<c>\"},\"fw\":{\"ay\":[\"i\",\"m<c>\"]},\"ee\":{\"ay\":[\"i\",\"m<c>\"],\"ay.S\":\"i\"},\"cC\":{\"aj\":[\"cC\"]},\"bU\":{\"aj\":[\"bU\"]},\"N\":{\"W\":[],\"aj\":[\"W\"]},\"cd\":{\"aj\":[\"cd\"]},\"c\":{\"W\":[],\"aj\":[\"W\"]},\"m\":{\"k\":[\"1\"],\"e\":[\"1\"]},\"W\":{\"aj\":[\"W\"]},\"e_\":{\"cT\":[]},\"i\":{\"aj\":[\"i\"],\"k9\":[]},\"a8\":{\"cC\":[],\"aj\":[\"cC\"]},\"ds\":{\"Q\":[]},\"bn\":{\"Q\":[]},\"h1\":{\"bn\":[],\"Q\":[]},\"bh\":{\"Q\":[]},\"cX\":{\"Q\":[]},\"fE\":{\"Q\":[]},\"dU\":{\"Q\":[]},\"hz\":{\"Q\":[]},\"hv\":{\"Q\":[]},\"bB\":{\"Q\":[]},\"fj\":{\"Q\":[]},\"h5\":{\"Q\":[]},\"e9\":{\"Q\":[]},\"fp\":{\"Q\":[]},\"i0\":{\"ac\":[]},\"fC\":{\"ac\":[]},\"fG\":{\"ac\":[],\"Q\":[]},\"iI\":{\"aG\":[]},\"ah\":{\"tj\":[]},\"eR\":{\"hA\":[]},\"b5\":{\"hA\":[]},\"hU\":{\"hA\":[]},\"P\":{\"a\":[]},\"l\":{\"a\":[]},\"az\":{\"bR\":[],\"a\":[]},\"aA\":{\"a\":[]},\"aB\":{\"a\":[]},\"G\":{\"f\":[],\"a\":[]},\"aC\":{\"a\":[]},\"aD\":{\"f\":[],\"a\":[]},\"aE\":{\"a\":[]},\"aF\":{\"a\":[]},\"al\":{\"a\":[]},\"aH\":{\"f\":[],\"a\":[]},\"am\":{\"f\":[],\"a\":[]},\"aI\":{\"a\":[]},\"o\":{\"G\":[],\"f\":[],\"a\":[]},\"f4\":{\"a\":[]},\"f5\":{\"G\":[],\"f\":[],\"a\":[]},\"f6\":{\"G\":[],\"f\":[],\"a\":[]},\"bR\":{\"a\":[]},\"bi\":{\"G\":[],\"f\":[],\"a\":[]},\"fm\":{\"a\":[]},\"cE\":{\"a\":[]},\"aq\":{\"a\":[]},\"bb\":{\"a\":[]},\"fn\":{\"a\":[]},\"fo\":{\"a\":[]},\"fq\":{\"a\":[]},\"ft\":{\"a\":[]},\"dz\":{\"h\":[\"bk<W>\"],\"u\":[\"bk<W>\"],\"m\":[\"bk<W>\"],\"F\":[\"bk<W>\"],\"a\":[],\"k\":[\"bk<W>\"],\"e\":[\"bk<W>\"],\"u.E\":\"bk<W>\",\"h.E\":\"bk<W>\"},\"dA\":{\"a\":[],\"bk\":[\"W\"]},\"fu\":{\"h\":[\"i\"],\"u\":[\"i\"],\"m\":[\"i\"],\"F\":[\"i\"],\"a\":[],\"k\":[\"i\"],\"e\":[\"i\"],\"u.E\":\"i\",\"h.E\":\"i\"},\"fv\":{\"a\":[]},\"n\":{\"G\":[],\"f\":[],\"a\":[]},\"f\":{\"a\":[]},\"cI\":{\"h\":[\"az\"],\"u\":[\"az\"],\"m\":[\"az\"],\"F\":[\"az\"],\"a\":[],\"k\":[\"az\"],\"e\":[\"az\"],\"u.E\":\"az\",\"h.E\":\"az\"},\"fz\":{\"f\":[],\"a\":[]},\"fB\":{\"G\":[],\"f\":[],\"a\":[]},\"fD\":{\"a\":[]},\"ci\":{\"h\":[\"G\"],\"u\":[\"G\"],\"m\":[\"G\"],\"F\":[\"G\"],\"a\":[],\"k\":[\"G\"],\"e\":[\"G\"],\"u.E\":\"G\",\"h.E\":\"G\"},\"cL\":{\"a\":[]},\"fO\":{\"a\":[]},\"fP\":{\"a\":[]},\"cV\":{\"l\":[],\"a\":[]},\"ck\":{\"f\":[],\"a\":[]},\"fQ\":{\"a\":[],\"w\":[\"i\",\"@\"],\"I\":[\"i\",\"@\"],\"w.K\":\"i\",\"w.V\":\"@\"},\"fR\":{\"a\":[],\"w\":[\"i\",\"@\"],\"I\":[\"i\",\"@\"],\"w.K\":\"i\",\"w.V\":\"@\"},\"fS\":{\"h\":[\"aB\"],\"u\":[\"aB\"],\"m\":[\"aB\"],\"F\":[\"aB\"],\"a\":[],\"k\":[\"aB\"],\"e\":[\"aB\"],\"u.E\":\"aB\",\"h.E\":\"aB\"},\"dV\":{\"h\":[\"G\"],\"u\":[\"G\"],\"m\":[\"G\"],\"F\":[\"G\"],\"a\":[],\"k\":[\"G\"],\"e\":[\"G\"],\"u.E\":\"G\",\"h.E\":\"G\"},\"h7\":{\"h\":[\"aC\"],\"u\":[\"aC\"],\"m\":[\"aC\"],\"F\":[\"aC\"],\"a\":[],\"k\":[\"aC\"],\"e\":[\"aC\"],\"u.E\":\"aC\",\"h.E\":\"aC\"},\"hb\":{\"a\":[],\"w\":[\"i\",\"@\"],\"I\":[\"i\",\"@\"],\"w.K\":\"i\",\"w.V\":\"@\"},\"hd\":{\"G\":[],\"f\":[],\"a\":[]},\"cY\":{\"a\":[]},\"cZ\":{\"f\":[],\"a\":[]},\"hf\":{\"h\":[\"aD\"],\"u\":[\"aD\"],\"f\":[],\"m\":[\"aD\"],\"F\":[\"aD\"],\"a\":[],\"k\":[\"aD\"],\"e\":[\"aD\"],\"u.E\":\"aD\",\"h.E\":\"aD\"},\"hg\":{\"h\":[\"aE\"],\"u\":[\"aE\"],\"m\":[\"aE\"],\"F\":[\"aE\"],\"a\":[],\"k\":[\"aE\"],\"e\":[\"aE\"],\"u.E\":\"aE\",\"h.E\":\"aE\"},\"hl\":{\"a\":[],\"w\":[\"i\",\"i\"],\"I\":[\"i\",\"i\"],\"w.K\":\"i\",\"w.V\":\"i\"},\"hp\":{\"h\":[\"am\"],\"u\":[\"am\"],\"m\":[\"am\"],\"F\":[\"am\"],\"a\":[],\"k\":[\"am\"],\"e\":[\"am\"],\"u.E\":\"am\",\"h.E\":\"am\"},\"hq\":{\"h\":[\"aH\"],\"u\":[\"aH\"],\"f\":[],\"m\":[\"aH\"],\"F\":[\"aH\"],\"a\":[],\"k\":[\"aH\"],\"e\":[\"aH\"],\"u.E\":\"aH\",\"h.E\":\"aH\"},\"hr\":{\"a\":[]},\"hs\":{\"h\":[\"aI\"],\"u\":[\"aI\"],\"m\":[\"aI\"],\"F\":[\"aI\"],\"a\":[],\"k\":[\"aI\"],\"e\":[\"aI\"],\"u.E\":\"aI\",\"h.E\":\"aI\"},\"ht\":{\"a\":[]},\"hB\":{\"a\":[]},\"hD\":{\"f\":[],\"a\":[]},\"c1\":{\"f\":[],\"a\":[]},\"hS\":{\"h\":[\"P\"],\"u\":[\"P\"],\"m\":[\"P\"],\"F\":[\"P\"],\"a\":[],\"k\":[\"P\"],\"e\":[\"P\"],\"u.E\":\"P\",\"h.E\":\"P\"},\"eo\":{\"a\":[],\"bk\":[\"W\"]},\"i5\":{\"h\":[\"aA?\"],\"u\":[\"aA?\"],\"m\":[\"aA?\"],\"F\":[\"aA?\"],\"a\":[],\"k\":[\"aA?\"],\"e\":[\"aA?\"],\"u.E\":\"aA?\",\"h.E\":\"aA?\"},\"ez\":{\"h\":[\"G\"],\"u\":[\"G\"],\"m\":[\"G\"],\"F\":[\"G\"],\"a\":[],\"k\":[\"G\"],\"e\":[\"G\"],\"u.E\":\"G\",\"h.E\":\"G\"},\"iA\":{\"h\":[\"aF\"],\"u\":[\"aF\"],\"m\":[\"aF\"],\"F\":[\"aF\"],\"a\":[],\"k\":[\"aF\"],\"e\":[\"aF\"],\"u.E\":\"aF\",\"h.E\":\"aF\"},\"iJ\":{\"h\":[\"al\"],\"u\":[\"al\"],\"m\":[\"al\"],\"F\":[\"al\"],\"a\":[],\"k\":[\"al\"],\"e\":[\"al\"],\"u.E\":\"al\",\"h.E\":\"al\"},\"lM\":{\"aV\":[\"1\"],\"aV.T\":\"1\"},\"eq\":{\"bm\":[\"1\"]},\"dD\":{\"L\":[\"1\"]},\"bT\":{\"a\":[]},\"br\":{\"bT\":[],\"a\":[]},\"bj\":{\"f\":[],\"a\":[]},\"cj\":{\"a\":[]},\"bz\":{\"f\":[],\"a\":[]},\"bD\":{\"l\":[],\"a\":[]},\"dG\":{\"a\":[]},\"dX\":{\"a\":[]},\"ec\":{\"f\":[],\"a\":[]},\"h0\":{\"ac\":[]},\"i8\":{\"rU\":[]},\"aN\":{\"a\":[]},\"aR\":{\"a\":[]},\"aW\":{\"a\":[]},\"fL\":{\"h\":[\"aN\"],\"u\":[\"aN\"],\"m\":[\"aN\"],\"a\":[],\"k\":[\"aN\"],\"e\":[\"aN\"],\"u.E\":\"aN\",\"h.E\":\"aN\"},\"h3\":{\"h\":[\"aR\"],\"u\":[\"aR\"],\"m\":[\"aR\"],\"a\":[],\"k\":[\"aR\"],\"e\":[\"aR\"],\"u.E\":\"aR\",\"h.E\":\"aR\"},\"h8\":{\"a\":[]},\"hn\":{\"h\":[\"i\"],\"u\":[\"i\"],\"m\":[\"i\"],\"a\":[],\"k\":[\"i\"],\"e\":[\"i\"],\"u.E\":\"i\",\"h.E\":\"i\"},\"hu\":{\"h\":[\"aW\"],\"u\":[\"aW\"],\"m\":[\"aW\"],\"a\":[],\"k\":[\"aW\"],\"e\":[\"aW\"],\"u.E\":\"aW\",\"h.E\":\"aW\"},\"f9\":{\"a\":[]},\"fa\":{\"a\":[],\"w\":[\"i\",\"@\"],\"I\":[\"i\",\"@\"],\"w.K\":\"i\",\"w.V\":\"@\"},\"fb\":{\"f\":[],\"a\":[]},\"bQ\":{\"f\":[],\"a\":[]},\"h4\":{\"f\":[],\"a\":[]},\"h9\":{\"bV\":[]},\"hC\":{\"bV\":[]},\"hK\":{\"bV\":[]},\"dy\":{\"ac\":[]},\"e3\":{\"ac\":[]},\"bl\":{\"ac\":[]},\"be\":{\"dg\":[\"cC\"],\"dg.T\":\"cC\"},\"e8\":{\"e7\":[]},\"d_\":{\"ac\":[]},\"fA\":{\"bs\":[]},\"fr\":{\"oE\":[]},\"cJ\":{\"bs\":[]},\"d0\":{\"rc\":[]},\"hL\":{\"dI\":[],\"cF\":[],\"L\":[\"ak\"]},\"ak\":{\"hy\":[\"i\",\"@\"],\"w\":[\"i\",\"@\"],\"I\":[\"i\",\"@\"],\"w.K\":\"i\",\"w.V\":\"@\"},\"dI\":{\"cF\":[],\"L\":[\"ak\"]},\"ha\":{\"h\":[\"ak\"],\"h_\":[\"ak\"],\"m\":[\"ak\"],\"k\":[\"ak\"],\"cF\":[],\"e\":[\"ak\"],\"h.E\":\"ak\"},\"ir\":{\"L\":[\"ak\"]},\"hI\":{\"rW\":[]},\"hF\":{\"rX\":[]},\"hJ\":{\"oT\":[]},\"d4\":{\"h\":[\"bE\"],\"m\":[\"bE\"],\"k\":[\"bE\"],\"e\":[\"bE\"],\"h.E\":\"bE\"},\"bc\":{\"ac\":[]},\"cM\":{\"jD\":[]},\"a9\":{\"ae\":[\"a9\"]},\"d9\":{\"a9\":[],\"ae\":[\"a9\"],\"ae.E\":\"a9\"},\"d8\":{\"a9\":[],\"ae\":[\"a9\"],\"ae.E\":\"a9\"},\"ct\":{\"a9\":[],\"ae\":[\"a9\"],\"ae.E\":\"a9\"},\"cy\":{\"a9\":[],\"ae\":[\"a9\"],\"ae.E\":\"a9\"},\"dF\":{\"jD\":[]},\"fd\":{\"rz\":[]},\"ro\":{\"m\":[\"c\"],\"k\":[\"c\"],\"e\":[\"c\"]},\"aX\":{\"m\":[\"c\"],\"k\":[\"c\"],\"e\":[\"c\"]},\"tp\":{\"m\":[\"c\"],\"k\":[\"c\"],\"e\":[\"c\"]},\"rm\":{\"m\":[\"c\"],\"k\":[\"c\"],\"e\":[\"c\"]},\"nP\":{\"m\":[\"c\"],\"k\":[\"c\"],\"e\":[\"c\"]},\"rn\":{\"m\":[\"c\"],\"k\":[\"c\"],\"e\":[\"c\"]},\"to\":{\"m\":[\"c\"],\"k\":[\"c\"],\"e\":[\"c\"]},\"rj\":{\"m\":[\"N\"],\"k\":[\"N\"],\"e\":[\"N\"]},\"rk\":{\"m\":[\"N\"],\"k\":[\"N\"],\"e\":[\"N\"]}}'))\nA.u_(v.typeUniverse,JSON.parse('{\"d2\":1,\"eU\":2,\"ag\":1,\"hm\":2,\"bG\":1,\"dH\":1,\"dN\":1,\"dP\":2,\"d3\":2,\"eE\":1,\"ev\":1,\"eV\":1,\"fl\":2,\"r3\":1}'))\nvar u={l:\"Cannot extract a file path from a URI with a fragment component\",i:\"Cannot extract a file path from a URI with a query component\",j:\"Cannot extract a non-Windows file path from a file URI with an authority\",c:\"Error handler must accept one Object or one Object and a StackTrace as arguments, and return a value of the returned future's type\",n:\"Tried to operate on a released prepared statement\"}\nvar t=(function rtii(){var s=A.aL\nreturn{ie:s(\"r3<r?>\"),n:s(\"dt\"),k:s(\"cC\"),w:s(\"bR\"),U:s(\"nt\"),bT:s(\"oE\"),bP:s(\"aj<@>\"),i9:s(\"dx<co,@>\"),d5:s(\"P\"),nT:s(\"br\"),E:s(\"bj\"),cs:s(\"bU\"),jS:s(\"cd\"),V:s(\"k<@>\"),W:s(\"Q\"),A:s(\"l\"),mA:s(\"ac\"),dY:s(\"az\"),kL:s(\"cI\"),i_:s(\"jD\"),m:s(\"bs\"),Y:s(\"ch\"),c:s(\"H<@>\"),gq:s(\"H<@>()\"),p8:s(\"H<~>\"),ng:s(\"cK\"),ad:s(\"cL\"),cF:s(\"cM\"),bg:s(\"oI\"),bq:s(\"e<i>\"),id:s(\"e<N>\"),e7:s(\"e<@>\"),fm:s(\"e<c>\"),eY:s(\"O<cJ>\"),iw:s(\"O<H<~>>\"),dO:s(\"O<m<r?>>\"),C:s(\"O<I<@,@>>\"),ke:s(\"O<I<i,r?>>\"),jP:s(\"O<vW<w2>>\"),hf:s(\"O<r>\"),bw:s(\"O<e6>\"),lE:s(\"O<d0>\"),s:s(\"O<i>\"),bs:s(\"O<aX>\"),o6:s(\"O<im>\"),it:s(\"O<iq>\"),b:s(\"O<@>\"),t:s(\"O<c>\"),mf:s(\"O<i?>\"),T:s(\"dK\"),bp:s(\"ny\"),et:s(\"bu\"),dX:s(\"F<@>\"),d9:s(\"a\"),bX:s(\"as<co,@>\"),kT:s(\"aN\"),h:s(\"cR<a9>\"),fr:s(\"m<e6>\"),i:s(\"m<i>\"),j:s(\"m<@>\"),L:s(\"m<c>\"),ag:s(\"a4<i,be>\"),lK:s(\"I<i,r>\"),dV:s(\"I<i,c>\"),f:s(\"I<@,@>\"),n2:s(\"I<i,I<i,r>>\"),lb:s(\"I<i,r?>\"),iZ:s(\"af<i,@>\"),gt:s(\"cU\"),hy:s(\"cV\"),oA:s(\"ck\"),ib:s(\"aB\"),hH:s(\"cW\"),dQ:s(\"bX\"),aj:s(\"aQ\"),hK:s(\"a5\"),hD:s(\"cl\"),G:s(\"G\"),P:s(\"R\"),ai:s(\"aR\"),K:s(\"r\"),d8:s(\"aC\"),lZ:s(\"w_\"),q:s(\"bk<W>\"),kl:s(\"oW\"),lu:s(\"e_\"),lq:s(\"w0\"),B:s(\"bz\"),hF:s(\"e0<i>\"),oy:s(\"ak\"),kI:s(\"cY\"),aD:s(\"cZ\"),ls:s(\"aD\"),cA:s(\"aE\"),hI:s(\"aF\"),cE:s(\"e7\"),db:s(\"e8\"),kY:s(\"hj<oT?>\"),l:s(\"aG\"),N:s(\"i\"),lv:s(\"al\"),bR:s(\"co\"),dR:s(\"aH\"),gJ:s(\"am\"),ki:s(\"aI\"),hk:s(\"aW\"),do:s(\"bn\"),p:s(\"aX\"),cx:s(\"c_\"),jJ:s(\"hA\"),O:s(\"ee\"),bo:s(\"bD\"),n0:s(\"hE\"),ax:s(\"hG\"),es:s(\"hH\"),cI:s(\"bE\"),lS:s(\"ef<i>\"),x:s(\"bF\"),ou:s(\"cr<~>\"),ap:s(\"be\"),d:s(\"a8\"),oz:s(\"d7<bT>\"),c6:s(\"d7<br>\"),bc:s(\"bg\"),go:s(\"E<bj>\"),g5:s(\"E<aw>\"),g:s(\"E<@>\"),g_:s(\"E<c>\"),D:s(\"E<~>\"),ot:s(\"dc\"),lz:s(\"iB\"),gL:s(\"eI<r?>\"),my:s(\"aa<bj>\"),ex:s(\"aa<aw>\"),F:s(\"aa<~>\"),y:s(\"aw\"),iW:s(\"aw(r)\"),dx:s(\"N\"),z:s(\"@\"),mY:s(\"@()\"),v:s(\"@(r)\"),Q:s(\"@(r,aG)\"),ha:s(\"@(i)\"),p1:s(\"@(@,@)\"),S:s(\"c\"),eK:s(\"0&*\"),_:s(\"r*\"),g9:s(\"br?\"),k5:s(\"bj?\"),iB:s(\"f?\"),gK:s(\"H<R>?\"),ef:s(\"aA?\"),kq:s(\"cj?\"),lH:s(\"m<@>?\"),kR:s(\"m<r?>?\"),h9:s(\"I<i,r?>?\"),X:s(\"r?\"),fw:s(\"aG?\"),nh:s(\"aX?\"),J:s(\"bF?\"),r:s(\"nR?\"),lT:s(\"bG<@>?\"),jV:s(\"bg?\"),e:s(\"bH<@,@>?\"),R:s(\"ib?\"),o:s(\"@(l)?\"),I:s(\"c?\"),Z:s(\"~()?\"),a:s(\"~(l)?\"),jM:s(\"~(bD)?\"),hC:s(\"~(c,i,c)?\"),cZ:s(\"W\"),H:s(\"~\"),M:s(\"~()\"),i6:s(\"~(r)\"),b9:s(\"~(r,aG)\"),bm:s(\"~(i,i)\"),u:s(\"~(i,@)\")}})();(function constants(){var s=hunkHelpers.makeConstList\nB.p=A.br.prototype\nB.h=A.bj.prototype\nB.U=A.cj.prototype\nB.V=A.dG.prototype\nB.W=J.cN.prototype\nB.b=J.O.prototype\nB.c=J.dJ.prototype\nB.X=J.cO.prototype\nB.a=J.bW.prototype\nB.Y=J.bu.prototype\nB.Z=J.a.prototype\nB.a1=A.ck.prototype\nB.E=A.dR.prototype\nB.e=A.cl.prototype\nB.i=A.dX.prototype\nB.H=J.h6.prototype\nB.r=J.c_.prototype\nB.ao=new A.jr()\nB.I=new A.fc()\nB.u=new A.cd()\nB.J=new A.dB(A.aL(\"dB<0&>\"))\nB.K=new A.fG()\nB.v=function getTagFallback(o) {\n  var s = Object.prototype.toString.call(o);\n  return s.substring(8, s.length - 1);\n}\nB.L=function() {\n  var toStringFunction = Object.prototype.toString;\n  function getTag(o) {\n    var s = toStringFunction.call(o);\n    return s.substring(8, s.length - 1);\n  }\n  function getUnknownTag(object, tag) {\n    if (/^HTML[A-Z].*Element$/.test(tag)) {\n      var name = toStringFunction.call(object);\n      if (name == \"[object Object]\") return null;\n      return \"HTMLElement\";\n    }\n  }\n  function getUnknownTagGenericBrowser(object, tag) {\n    if (self.HTMLElement && object instanceof HTMLElement) return \"HTMLElement\";\n    return getUnknownTag(object, tag);\n  }\n  function prototypeForTag(tag) {\n    if (typeof window == \"undefined\") return null;\n    if (typeof window[tag] == \"undefined\") return null;\n    var constructor = window[tag];\n    if (typeof constructor != \"function\") return null;\n    return constructor.prototype;\n  }\n  function discriminator(tag) { return null; }\n  var isBrowser = typeof navigator == \"object\";\n  return {\n    getTag: getTag,\n    getUnknownTag: isBrowser ? getUnknownTagGenericBrowser : getUnknownTag,\n    prototypeForTag: prototypeForTag,\n    discriminator: discriminator };\n}\nB.Q=function(getTagFallback) {\n  return function(hooks) {\n    if (typeof navigator != \"object\") return hooks;\n    var ua = navigator.userAgent;\n    if (ua.indexOf(\"DumpRenderTree\") >= 0) return hooks;\n    if (ua.indexOf(\"Chrome\") >= 0) {\n      function confirm(p) {\n        return typeof window == \"object\" && window[p] && window[p].name == p;\n      }\n      if (confirm(\"Window\") && confirm(\"HTMLElement\")) return hooks;\n    }\n    hooks.getTag = getTagFallback;\n  };\n}\nB.M=function(hooks) {\n  if (typeof dartExperimentalFixupGetTag != \"function\") return hooks;\n  hooks.getTag = dartExperimentalFixupGetTag(hooks.getTag);\n}\nB.N=function(hooks) {\n  var getTag = hooks.getTag;\n  var prototypeForTag = hooks.prototypeForTag;\n  function getTagFixed(o) {\n    var tag = getTag(o);\n    if (tag == \"Document\") {\n      if (!!o.xmlVersion) return \"!Document\";\n      return \"!HTMLDocument\";\n    }\n    return tag;\n  }\n  function prototypeForTagFixed(tag) {\n    if (tag == \"Document\") return null;\n    return prototypeForTag(tag);\n  }\n  hooks.getTag = getTagFixed;\n  hooks.prototypeForTag = prototypeForTagFixed;\n}\nB.P=function(hooks) {\n  var userAgent = typeof navigator == \"object\" ? navigator.userAgent : \"\";\n  if (userAgent.indexOf(\"Firefox\") == -1) return hooks;\n  var getTag = hooks.getTag;\n  var quickMap = {\n    \"BeforeUnloadEvent\": \"Event\",\n    \"DataTransfer\": \"Clipboard\",\n    \"GeoGeolocation\": \"Geolocation\",\n    \"Location\": \"!Location\",\n    \"WorkerMessageEvent\": \"MessageEvent\",\n    \"XMLDocument\": \"!Document\"};\n  function getTagFirefox(o) {\n    var tag = getTag(o);\n    return quickMap[tag] || tag;\n  }\n  hooks.getTag = getTagFirefox;\n}\nB.O=function(hooks) {\n  var userAgent = typeof navigator == \"object\" ? navigator.userAgent : \"\";\n  if (userAgent.indexOf(\"Trident/\") == -1) return hooks;\n  var getTag = hooks.getTag;\n  var quickMap = {\n    \"BeforeUnloadEvent\": \"Event\",\n    \"DataTransfer\": \"Clipboard\",\n    \"HTMLDDElement\": \"HTMLElement\",\n    \"HTMLDTElement\": \"HTMLElement\",\n    \"HTMLPhraseElement\": \"HTMLElement\",\n    \"Position\": \"Geoposition\"\n  };\n  function getTagIE(o) {\n    var tag = getTag(o);\n    var newTag = quickMap[tag];\n    if (newTag) return newTag;\n    if (tag == \"Object\") {\n      if (window.DataView && (o instanceof window.DataView)) return \"DataView\";\n    }\n    return tag;\n  }\n  function prototypeForTagIE(tag) {\n    var constructor = window[tag];\n    if (constructor == null) return null;\n    return constructor.prototype;\n  }\n  hooks.getTag = getTagIE;\n  hooks.prototypeForTag = prototypeForTagIE;\n}\nB.w=function(hooks) { return hooks; }\n\nB.R=new A.h5()\nB.x=new A.kn()\nB.f=new A.ee()\nB.S=new A.lo()\nB.y=new A.hV()\nB.z=new A.mu()\nB.d=new A.iu()\nB.T=new A.iI()\nB.j=A.t(s([0,0,32776,33792,1,10240,0,0]),t.t)\nB.k=A.t(s([0,0,65490,45055,65535,34815,65534,18431]),t.t)\nB.l=A.t(s([0,0,26624,1023,65534,2047,65534,2047]),t.t)\nB.ap=A.t(s([]),t.hf)\nB.q=A.t(s([]),t.s)\nB.m=A.t(s([]),t.b)\nB.n=A.t(s([\"files\",\"blocks\"]),t.s)\nB.a0=A.t(s([0,0,32722,12287,65534,34815,65534,18431]),t.t)\nB.o=A.t(s([0,0,24576,1023,65534,34815,65534,18431]),t.t)\nB.A=A.t(s([0,0,32754,11263,65534,34815,65534,18431]),t.t)\nB.B=A.t(s([0,0,65490,12287,65535,34815,65534,18431]),t.t)\nB.C=new A.cc(0,{},B.q,A.aL(\"cc<i,c>\"))\nB.a_=A.t(s([]),A.aL(\"O<co>\"))\nB.D=new A.cc(0,{},B.a_,A.aL(\"cc<co,@>\"))\nB.F=new A.dY(\"readOnly\")\nB.a2=new A.dY(\"readWrite\")\nB.G=new A.dY(\"readWriteCreate\")\nB.a3=new A.d1(\"call\")\nB.a4=A.ai(\"nt\")\nB.a5=A.ai(\"oC\")\nB.a6=A.ai(\"rj\")\nB.a7=A.ai(\"rk\")\nB.a8=A.ai(\"rm\")\nB.a9=A.ai(\"rn\")\nB.aa=A.ai(\"ro\")\nB.ab=A.ai(\"ny\")\nB.ac=A.ai(\"r\")\nB.ad=A.ai(\"i\")\nB.ae=A.ai(\"nP\")\nB.af=A.ai(\"to\")\nB.ag=A.ai(\"tp\")\nB.ah=A.ai(\"aX\")\nB.ai=A.ai(\"aw\")\nB.aj=A.ai(\"N\")\nB.ak=A.ai(\"c\")\nB.al=A.ai(\"W\")\nB.t=new A.ll(!1)\nB.am=new A.db(null,2)\nB.an=new A.iT(B.d,A.v4(),A.aL(\"iT<~(bF,nR,bF,~())>\"))})();(function staticFields(){$.mp=null\n$.qk=null\n$.oR=null\n$.oA=null\n$.oz=null\n$.qe=null\n$.q5=null\n$.ql=null\n$.n6=null\n$.ne=null\n$.oi=null\n$.dm=null\n$.eX=null\n$.eY=null\n$.oa=!1\n$.D=B.d\n$.aY=A.t([],t.hf)\n$.pd=null\n$.pe=null\n$.pf=null\n$.pg=null\n$.nS=A.el(\"_lastQuoRemDigits\")\n$.nT=A.el(\"_lastQuoRemUsed\")\n$.ei=A.el(\"_lastRemUsed\")\n$.nU=A.el(\"_lastRem_nsh\")\n$.pN=null\n$.mU=null\n$.q2=null\n$.pS=null\n$.qc=A.X(t.S,A.aL(\"aU\"))\n$.j6=A.X(A.aL(\"i?\"),A.aL(\"aU\"))\n$.pT=0\n$.nf=0\n$.b6=null\n$.qn=A.X(t.N,t.X)\n$.q1=null\n$.eZ=\"/shw2\"})();(function lazyInitializers(){var s=hunkHelpers.lazyFinal,r=hunkHelpers.lazy\ns($,\"vL\",\"ol\",()=>A.vd(\"_$dart_dartClosure\"))\ns($,\"wO\",\"np\",()=>B.d.cP(new A.ni(),A.aL(\"H<R>\")))\ns($,\"w9\",\"qs\",()=>A.bC(A.le({\ntoString:function(){return\"$receiver$\"}})))\ns($,\"wa\",\"qt\",()=>A.bC(A.le({$method$:null,\ntoString:function(){return\"$receiver$\"}})))\ns($,\"wb\",\"qu\",()=>A.bC(A.le(null)))\ns($,\"wc\",\"qv\",()=>A.bC(function(){var $argumentsExpr$=\"$arguments$\"\ntry{null.$method$($argumentsExpr$)}catch(q){return q.message}}()))\ns($,\"wf\",\"qy\",()=>A.bC(A.le(void 0)))\ns($,\"wg\",\"qz\",()=>A.bC(function(){var $argumentsExpr$=\"$arguments$\"\ntry{(void 0).$method$($argumentsExpr$)}catch(q){return q.message}}()))\ns($,\"we\",\"qx\",()=>A.bC(A.p7(null)))\ns($,\"wd\",\"qw\",()=>A.bC(function(){try{null.$method$}catch(q){return q.message}}()))\ns($,\"wi\",\"qB\",()=>A.bC(A.p7(void 0)))\ns($,\"wh\",\"qA\",()=>A.bC(function(){try{(void 0).$method$}catch(q){return q.message}}()))\ns($,\"wm\",\"om\",()=>A.tv())\ns($,\"vP\",\"f2\",()=>A.aL(\"E<R>\").a($.np()))\ns($,\"wj\",\"qC\",()=>new A.ln().$0())\ns($,\"wk\",\"qD\",()=>new A.lm().$0())\ns($,\"wn\",\"qE\",()=>A.rB(A.uv(A.t([-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-1,-2,-2,-2,-2,-2,62,-2,62,-2,63,52,53,54,55,56,57,58,59,60,61,-2,-2,-2,-1,-2,-2,-2,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-2,-2,-2,-2,63,-2,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,-2,-2,-2,-2,-2],t.t))))\ns($,\"wu\",\"op\",()=>typeof process!=\"undefined\"&&Object.prototype.toString.call(process)==\"[object process]\"&&process.platform==\"win32\")\ns($,\"ws\",\"bN\",()=>A.lC(0))\ns($,\"wr\",\"jb\",()=>A.lC(1))\ns($,\"wp\",\"oo\",()=>$.jb().ac(0))\ns($,\"wo\",\"on\",()=>A.lC(1e4))\nr($,\"wq\",\"qF\",()=>A.b1(\"^\\\\s*([+-]?)((0x[a-f0-9]+)|(\\\\d+)|([a-z0-9]+))\\\\s*$\",!1))\ns($,\"wH\",\"no\",()=>A.j7(B.ac))\ns($,\"wI\",\"qK\",()=>A.us())\ns($,\"vZ\",\"qp\",()=>{var q=new A.i8(new DataView(new ArrayBuffer(A.up(8))))\nq.eB()\nreturn q})\ns($,\"wL\",\"or\",()=>new A.fk(A.aL(\"bV\").a($.nn()),null))\ns($,\"w5\",\"qq\",()=>new A.h9(A.b1(\"/\",!0),A.b1(\"[^/]$\",!0),A.b1(\"^/\",!0)))\ns($,\"w7\",\"qr\",()=>new A.hK(A.b1(\"[/\\\\\\\\]\",!0),A.b1(\"[^/\\\\\\\\]$\",!0),A.b1(\"^(\\\\\\\\\\\\\\\\[^\\\\\\\\]+\\\\\\\\[^\\\\\\\\/]+|[a-zA-Z]:[/\\\\\\\\])\",!0),A.b1(\"^[/\\\\\\\\](?![/\\\\\\\\])\",!0)))\ns($,\"w6\",\"ja\",()=>new A.hC(A.b1(\"/\",!0),A.b1(\"(^[a-zA-Z][-+.a-zA-Z\\\\d]*://|[^/])$\",!0),A.b1(\"[a-zA-Z][-+.a-zA-Z\\\\d]*://[^/]*\",!0),A.b1(\"^/\",!0)))\ns($,\"w4\",\"nn\",()=>A.tm())\ns($,\"wG\",\"qJ\",()=>A.nC())\nr($,\"wv\",\"oq\",()=>A.t([new A.be(\"BigInt\")],A.aL(\"O<be>\")))\nr($,\"ww\",\"qG\",()=>{var q=$.oq()\nq=A.ry(q,A.av(q).c)\nreturn q.h9(q,new A.mL(),t.N,t.ap)})\nr($,\"wF\",\"qI\",()=>A.li(\"sqlite3.wasm\"))\ns($,\"wK\",\"qM\",()=>A.ox(\"-9223372036854775808\"))\ns($,\"wJ\",\"qL\",()=>A.ox(\"9223372036854775807\"))\ns($,\"wN\",\"jc\",()=>new A.i3(new FinalizationRegistry(A.c8(A.vy(new A.n7(),t.m),1)),A.aL(\"i3<bs>\")))\ns($,\"wE\",\"qH\",()=>{var q=$.ja()\nif(q==null)q=$.nn()\nreturn new A.fk(A.aL(\"bV\").a(q),\"/\")})})();(function nativeSupport(){!function(){var s=function(a){var m={}\nm[a]=1\nreturn Object.keys(hunkHelpers.convertToFastObject(m))[0]}\nv.getIsolateTag=function(a){return s(\"___dart_\"+a+v.isolateTag)}\nvar r=\"___dart_isolate_tags_\"\nvar q=Object[r]||(Object[r]=Object.create(null))\nvar p=\"_ZxYxX\"\nfor(var o=0;;o++){var n=s(p+\"_\"+o+\"_\")\nif(!(n in q)){q[n]=1\nv.isolateTag=n\nbreak}}v.dispatchPropertyName=v.getIsolateTag(\"dispatch_record\")}()\nhunkHelpers.setOrUpdateInterceptorsByTag({WebGL:J.cN,AnimationEffectReadOnly:J.a,AnimationEffectTiming:J.a,AnimationEffectTimingReadOnly:J.a,AnimationTimeline:J.a,AnimationWorkletGlobalScope:J.a,AuthenticatorAssertionResponse:J.a,AuthenticatorAttestationResponse:J.a,AuthenticatorResponse:J.a,BackgroundFetchFetch:J.a,BackgroundFetchManager:J.a,BackgroundFetchSettledFetch:J.a,BarProp:J.a,BarcodeDetector:J.a,BluetoothRemoteGATTDescriptor:J.a,Body:J.a,BudgetState:J.a,CacheStorage:J.a,CanvasGradient:J.a,CanvasPattern:J.a,CanvasRenderingContext2D:J.a,Client:J.a,Clients:J.a,CookieStore:J.a,Coordinates:J.a,Credential:J.a,CredentialUserData:J.a,CredentialsContainer:J.a,Crypto:J.a,CryptoKey:J.a,CSS:J.a,CSSVariableReferenceValue:J.a,CustomElementRegistry:J.a,DataTransfer:J.a,DataTransferItem:J.a,DeprecatedStorageInfo:J.a,DeprecatedStorageQuota:J.a,DeprecationReport:J.a,DetectedBarcode:J.a,DetectedFace:J.a,DetectedText:J.a,DeviceAcceleration:J.a,DeviceRotationRate:J.a,DirectoryEntry:J.a,webkitFileSystemDirectoryEntry:J.a,FileSystemDirectoryEntry:J.a,DirectoryReader:J.a,WebKitDirectoryReader:J.a,webkitFileSystemDirectoryReader:J.a,FileSystemDirectoryReader:J.a,DocumentOrShadowRoot:J.a,DocumentTimeline:J.a,DOMError:J.a,DOMImplementation:J.a,Iterator:J.a,DOMMatrix:J.a,DOMMatrixReadOnly:J.a,DOMParser:J.a,DOMPoint:J.a,DOMPointReadOnly:J.a,DOMQuad:J.a,DOMStringMap:J.a,Entry:J.a,webkitFileSystemEntry:J.a,FileSystemEntry:J.a,External:J.a,FaceDetector:J.a,FederatedCredential:J.a,FileEntry:J.a,webkitFileSystemFileEntry:J.a,FileSystemFileEntry:J.a,DOMFileSystem:J.a,WebKitFileSystem:J.a,webkitFileSystem:J.a,FileSystem:J.a,FontFace:J.a,FontFaceSource:J.a,FormData:J.a,GamepadButton:J.a,GamepadPose:J.a,Geolocation:J.a,Position:J.a,GeolocationPosition:J.a,Headers:J.a,HTMLHyperlinkElementUtils:J.a,IdleDeadline:J.a,ImageBitmap:J.a,ImageBitmapRenderingContext:J.a,ImageCapture:J.a,InputDeviceCapabilities:J.a,IntersectionObserver:J.a,IntersectionObserverEntry:J.a,InterventionReport:J.a,KeyframeEffect:J.a,KeyframeEffectReadOnly:J.a,MediaCapabilities:J.a,MediaCapabilitiesInfo:J.a,MediaDeviceInfo:J.a,MediaError:J.a,MediaKeyStatusMap:J.a,MediaKeySystemAccess:J.a,MediaKeys:J.a,MediaKeysPolicy:J.a,MediaMetadata:J.a,MediaSession:J.a,MediaSettingsRange:J.a,MemoryInfo:J.a,MessageChannel:J.a,Metadata:J.a,MutationObserver:J.a,WebKitMutationObserver:J.a,MutationRecord:J.a,NavigationPreloadManager:J.a,Navigator:J.a,NavigatorAutomationInformation:J.a,NavigatorConcurrentHardware:J.a,NavigatorCookies:J.a,NavigatorUserMediaError:J.a,NodeFilter:J.a,NodeIterator:J.a,NonDocumentTypeChildNode:J.a,NonElementParentNode:J.a,NoncedElement:J.a,OffscreenCanvasRenderingContext2D:J.a,OverconstrainedError:J.a,PaintRenderingContext2D:J.a,PaintSize:J.a,PaintWorkletGlobalScope:J.a,PasswordCredential:J.a,Path2D:J.a,PaymentAddress:J.a,PaymentInstruments:J.a,PaymentManager:J.a,PaymentResponse:J.a,PerformanceEntry:J.a,PerformanceLongTaskTiming:J.a,PerformanceMark:J.a,PerformanceMeasure:J.a,PerformanceNavigation:J.a,PerformanceNavigationTiming:J.a,PerformanceObserver:J.a,PerformanceObserverEntryList:J.a,PerformancePaintTiming:J.a,PerformanceResourceTiming:J.a,PerformanceServerTiming:J.a,PerformanceTiming:J.a,Permissions:J.a,PhotoCapabilities:J.a,PositionError:J.a,GeolocationPositionError:J.a,Presentation:J.a,PresentationReceiver:J.a,PublicKeyCredential:J.a,PushManager:J.a,PushMessageData:J.a,PushSubscription:J.a,PushSubscriptionOptions:J.a,Range:J.a,RelatedApplication:J.a,ReportBody:J.a,ReportingObserver:J.a,ResizeObserver:J.a,ResizeObserverEntry:J.a,RTCCertificate:J.a,RTCIceCandidate:J.a,mozRTCIceCandidate:J.a,RTCLegacyStatsReport:J.a,RTCRtpContributingSource:J.a,RTCRtpReceiver:J.a,RTCRtpSender:J.a,RTCSessionDescription:J.a,mozRTCSessionDescription:J.a,RTCStatsResponse:J.a,Screen:J.a,ScrollState:J.a,ScrollTimeline:J.a,Selection:J.a,SpeechRecognitionAlternative:J.a,SpeechSynthesisVoice:J.a,StaticRange:J.a,StorageManager:J.a,StyleMedia:J.a,StylePropertyMap:J.a,StylePropertyMapReadonly:J.a,SyncManager:J.a,TaskAttributionTiming:J.a,TextDetector:J.a,TextMetrics:J.a,TrackDefault:J.a,TreeWalker:J.a,TrustedHTML:J.a,TrustedScriptURL:J.a,TrustedURL:J.a,UnderlyingSourceBase:J.a,URLSearchParams:J.a,VRCoordinateSystem:J.a,VRDisplayCapabilities:J.a,VREyeParameters:J.a,VRFrameData:J.a,VRFrameOfReference:J.a,VRPose:J.a,VRStageBounds:J.a,VRStageBoundsPoint:J.a,VRStageParameters:J.a,ValidityState:J.a,VideoPlaybackQuality:J.a,VideoTrack:J.a,VTTRegion:J.a,WindowClient:J.a,WorkletAnimation:J.a,WorkletGlobalScope:J.a,XPathEvaluator:J.a,XPathExpression:J.a,XPathNSResolver:J.a,XPathResult:J.a,XMLSerializer:J.a,XSLTProcessor:J.a,Bluetooth:J.a,BluetoothCharacteristicProperties:J.a,BluetoothRemoteGATTServer:J.a,BluetoothRemoteGATTService:J.a,BluetoothUUID:J.a,BudgetService:J.a,Cache:J.a,DOMFileSystemSync:J.a,DirectoryEntrySync:J.a,DirectoryReaderSync:J.a,EntrySync:J.a,FileEntrySync:J.a,FileReaderSync:J.a,FileWriterSync:J.a,HTMLAllCollection:J.a,Mojo:J.a,MojoHandle:J.a,MojoWatcher:J.a,NFC:J.a,PagePopupController:J.a,Report:J.a,Request:J.a,Response:J.a,SubtleCrypto:J.a,USBAlternateInterface:J.a,USBConfiguration:J.a,USBDevice:J.a,USBEndpoint:J.a,USBInTransferResult:J.a,USBInterface:J.a,USBIsochronousInTransferPacket:J.a,USBIsochronousInTransferResult:J.a,USBIsochronousOutTransferPacket:J.a,USBIsochronousOutTransferResult:J.a,USBOutTransferResult:J.a,WorkerLocation:J.a,WorkerNavigator:J.a,Worklet:J.a,IDBKeyRange:J.a,IDBObservation:J.a,IDBObserver:J.a,IDBObserverChanges:J.a,SVGAngle:J.a,SVGAnimatedAngle:J.a,SVGAnimatedBoolean:J.a,SVGAnimatedEnumeration:J.a,SVGAnimatedInteger:J.a,SVGAnimatedLength:J.a,SVGAnimatedLengthList:J.a,SVGAnimatedNumber:J.a,SVGAnimatedNumberList:J.a,SVGAnimatedPreserveAspectRatio:J.a,SVGAnimatedRect:J.a,SVGAnimatedString:J.a,SVGAnimatedTransformList:J.a,SVGMatrix:J.a,SVGPoint:J.a,SVGPreserveAspectRatio:J.a,SVGRect:J.a,SVGUnitTypes:J.a,AudioListener:J.a,AudioParam:J.a,AudioTrack:J.a,AudioWorkletGlobalScope:J.a,AudioWorkletProcessor:J.a,PeriodicWave:J.a,WebGLActiveInfo:J.a,ANGLEInstancedArrays:J.a,ANGLE_instanced_arrays:J.a,WebGLBuffer:J.a,WebGLCanvas:J.a,WebGLColorBufferFloat:J.a,WebGLCompressedTextureASTC:J.a,WebGLCompressedTextureATC:J.a,WEBGL_compressed_texture_atc:J.a,WebGLCompressedTextureETC1:J.a,WEBGL_compressed_texture_etc1:J.a,WebGLCompressedTextureETC:J.a,WebGLCompressedTexturePVRTC:J.a,WEBGL_compressed_texture_pvrtc:J.a,WebGLCompressedTextureS3TC:J.a,WEBGL_compressed_texture_s3tc:J.a,WebGLCompressedTextureS3TCsRGB:J.a,WebGLDebugRendererInfo:J.a,WEBGL_debug_renderer_info:J.a,WebGLDebugShaders:J.a,WEBGL_debug_shaders:J.a,WebGLDepthTexture:J.a,WEBGL_depth_texture:J.a,WebGLDrawBuffers:J.a,WEBGL_draw_buffers:J.a,EXTsRGB:J.a,EXT_sRGB:J.a,EXTBlendMinMax:J.a,EXT_blend_minmax:J.a,EXTColorBufferFloat:J.a,EXTColorBufferHalfFloat:J.a,EXTDisjointTimerQuery:J.a,EXTDisjointTimerQueryWebGL2:J.a,EXTFragDepth:J.a,EXT_frag_depth:J.a,EXTShaderTextureLOD:J.a,EXT_shader_texture_lod:J.a,EXTTextureFilterAnisotropic:J.a,EXT_texture_filter_anisotropic:J.a,WebGLFramebuffer:J.a,WebGLGetBufferSubDataAsync:J.a,WebGLLoseContext:J.a,WebGLExtensionLoseContext:J.a,WEBGL_lose_context:J.a,OESElementIndexUint:J.a,OES_element_index_uint:J.a,OESStandardDerivatives:J.a,OES_standard_derivatives:J.a,OESTextureFloat:J.a,OES_texture_float:J.a,OESTextureFloatLinear:J.a,OES_texture_float_linear:J.a,OESTextureHalfFloat:J.a,OES_texture_half_float:J.a,OESTextureHalfFloatLinear:J.a,OES_texture_half_float_linear:J.a,OESVertexArrayObject:J.a,OES_vertex_array_object:J.a,WebGLProgram:J.a,WebGLQuery:J.a,WebGLRenderbuffer:J.a,WebGLRenderingContext:J.a,WebGL2RenderingContext:J.a,WebGLSampler:J.a,WebGLShader:J.a,WebGLShaderPrecisionFormat:J.a,WebGLSync:J.a,WebGLTexture:J.a,WebGLTimerQueryEXT:J.a,WebGLTransformFeedback:J.a,WebGLUniformLocation:J.a,WebGLVertexArrayObject:J.a,WebGLVertexArrayObjectOES:J.a,WebGL2RenderingContextBase:J.a,ArrayBuffer:A.cW,ArrayBufferView:A.a5,DataView:A.dR,Float32Array:A.fT,Float64Array:A.fU,Int16Array:A.fV,Int32Array:A.fW,Int8Array:A.fX,Uint16Array:A.fY,Uint32Array:A.fZ,Uint8ClampedArray:A.dT,CanvasPixelArray:A.dT,Uint8Array:A.cl,HTMLAudioElement:A.o,HTMLBRElement:A.o,HTMLBaseElement:A.o,HTMLBodyElement:A.o,HTMLButtonElement:A.o,HTMLCanvasElement:A.o,HTMLContentElement:A.o,HTMLDListElement:A.o,HTMLDataElement:A.o,HTMLDataListElement:A.o,HTMLDetailsElement:A.o,HTMLDialogElement:A.o,HTMLDivElement:A.o,HTMLEmbedElement:A.o,HTMLFieldSetElement:A.o,HTMLHRElement:A.o,HTMLHeadElement:A.o,HTMLHeadingElement:A.o,HTMLHtmlElement:A.o,HTMLIFrameElement:A.o,HTMLImageElement:A.o,HTMLInputElement:A.o,HTMLLIElement:A.o,HTMLLabelElement:A.o,HTMLLegendElement:A.o,HTMLLinkElement:A.o,HTMLMapElement:A.o,HTMLMediaElement:A.o,HTMLMenuElement:A.o,HTMLMetaElement:A.o,HTMLMeterElement:A.o,HTMLModElement:A.o,HTMLOListElement:A.o,HTMLObjectElement:A.o,HTMLOptGroupElement:A.o,HTMLOptionElement:A.o,HTMLOutputElement:A.o,HTMLParagraphElement:A.o,HTMLParamElement:A.o,HTMLPictureElement:A.o,HTMLPreElement:A.o,HTMLProgressElement:A.o,HTMLQuoteElement:A.o,HTMLScriptElement:A.o,HTMLShadowElement:A.o,HTMLSlotElement:A.o,HTMLSourceElement:A.o,HTMLSpanElement:A.o,HTMLStyleElement:A.o,HTMLTableCaptionElement:A.o,HTMLTableCellElement:A.o,HTMLTableDataCellElement:A.o,HTMLTableHeaderCellElement:A.o,HTMLTableColElement:A.o,HTMLTableElement:A.o,HTMLTableRowElement:A.o,HTMLTableSectionElement:A.o,HTMLTemplateElement:A.o,HTMLTextAreaElement:A.o,HTMLTimeElement:A.o,HTMLTitleElement:A.o,HTMLTrackElement:A.o,HTMLUListElement:A.o,HTMLUnknownElement:A.o,HTMLVideoElement:A.o,HTMLDirectoryElement:A.o,HTMLFontElement:A.o,HTMLFrameElement:A.o,HTMLFrameSetElement:A.o,HTMLMarqueeElement:A.o,HTMLElement:A.o,AccessibleNodeList:A.f4,HTMLAnchorElement:A.f5,HTMLAreaElement:A.f6,Blob:A.bR,CDATASection:A.bi,CharacterData:A.bi,Comment:A.bi,ProcessingInstruction:A.bi,Text:A.bi,CSSPerspective:A.fm,CSSCharsetRule:A.P,CSSConditionRule:A.P,CSSFontFaceRule:A.P,CSSGroupingRule:A.P,CSSImportRule:A.P,CSSKeyframeRule:A.P,MozCSSKeyframeRule:A.P,WebKitCSSKeyframeRule:A.P,CSSKeyframesRule:A.P,MozCSSKeyframesRule:A.P,WebKitCSSKeyframesRule:A.P,CSSMediaRule:A.P,CSSNamespaceRule:A.P,CSSPageRule:A.P,CSSRule:A.P,CSSStyleRule:A.P,CSSSupportsRule:A.P,CSSViewportRule:A.P,CSSStyleDeclaration:A.cE,MSStyleCSSProperties:A.cE,CSS2Properties:A.cE,CSSImageValue:A.aq,CSSKeywordValue:A.aq,CSSNumericValue:A.aq,CSSPositionValue:A.aq,CSSResourceValue:A.aq,CSSUnitValue:A.aq,CSSURLImageValue:A.aq,CSSStyleValue:A.aq,CSSMatrixComponent:A.bb,CSSRotation:A.bb,CSSScale:A.bb,CSSSkew:A.bb,CSSTranslation:A.bb,CSSTransformComponent:A.bb,CSSTransformValue:A.fn,CSSUnparsedValue:A.fo,DataTransferItemList:A.fq,DOMException:A.ft,ClientRectList:A.dz,DOMRectList:A.dz,DOMRectReadOnly:A.dA,DOMStringList:A.fu,DOMTokenList:A.fv,MathMLElement:A.n,SVGAElement:A.n,SVGAnimateElement:A.n,SVGAnimateMotionElement:A.n,SVGAnimateTransformElement:A.n,SVGAnimationElement:A.n,SVGCircleElement:A.n,SVGClipPathElement:A.n,SVGDefsElement:A.n,SVGDescElement:A.n,SVGDiscardElement:A.n,SVGEllipseElement:A.n,SVGFEBlendElement:A.n,SVGFEColorMatrixElement:A.n,SVGFEComponentTransferElement:A.n,SVGFECompositeElement:A.n,SVGFEConvolveMatrixElement:A.n,SVGFEDiffuseLightingElement:A.n,SVGFEDisplacementMapElement:A.n,SVGFEDistantLightElement:A.n,SVGFEFloodElement:A.n,SVGFEFuncAElement:A.n,SVGFEFuncBElement:A.n,SVGFEFuncGElement:A.n,SVGFEFuncRElement:A.n,SVGFEGaussianBlurElement:A.n,SVGFEImageElement:A.n,SVGFEMergeElement:A.n,SVGFEMergeNodeElement:A.n,SVGFEMorphologyElement:A.n,SVGFEOffsetElement:A.n,SVGFEPointLightElement:A.n,SVGFESpecularLightingElement:A.n,SVGFESpotLightElement:A.n,SVGFETileElement:A.n,SVGFETurbulenceElement:A.n,SVGFilterElement:A.n,SVGForeignObjectElement:A.n,SVGGElement:A.n,SVGGeometryElement:A.n,SVGGraphicsElement:A.n,SVGImageElement:A.n,SVGLineElement:A.n,SVGLinearGradientElement:A.n,SVGMarkerElement:A.n,SVGMaskElement:A.n,SVGMetadataElement:A.n,SVGPathElement:A.n,SVGPatternElement:A.n,SVGPolygonElement:A.n,SVGPolylineElement:A.n,SVGRadialGradientElement:A.n,SVGRectElement:A.n,SVGScriptElement:A.n,SVGSetElement:A.n,SVGStopElement:A.n,SVGStyleElement:A.n,SVGElement:A.n,SVGSVGElement:A.n,SVGSwitchElement:A.n,SVGSymbolElement:A.n,SVGTSpanElement:A.n,SVGTextContentElement:A.n,SVGTextElement:A.n,SVGTextPathElement:A.n,SVGTextPositioningElement:A.n,SVGTitleElement:A.n,SVGUseElement:A.n,SVGViewElement:A.n,SVGGradientElement:A.n,SVGComponentTransferFunctionElement:A.n,SVGFEDropShadowElement:A.n,SVGMPathElement:A.n,Element:A.n,AbortPaymentEvent:A.l,AnimationEvent:A.l,AnimationPlaybackEvent:A.l,ApplicationCacheErrorEvent:A.l,BackgroundFetchClickEvent:A.l,BackgroundFetchEvent:A.l,BackgroundFetchFailEvent:A.l,BackgroundFetchedEvent:A.l,BeforeInstallPromptEvent:A.l,BeforeUnloadEvent:A.l,BlobEvent:A.l,CanMakePaymentEvent:A.l,ClipboardEvent:A.l,CloseEvent:A.l,CompositionEvent:A.l,CustomEvent:A.l,DeviceMotionEvent:A.l,DeviceOrientationEvent:A.l,ErrorEvent:A.l,ExtendableEvent:A.l,ExtendableMessageEvent:A.l,FetchEvent:A.l,FocusEvent:A.l,FontFaceSetLoadEvent:A.l,ForeignFetchEvent:A.l,GamepadEvent:A.l,HashChangeEvent:A.l,InstallEvent:A.l,KeyboardEvent:A.l,MediaEncryptedEvent:A.l,MediaKeyMessageEvent:A.l,MediaQueryListEvent:A.l,MediaStreamEvent:A.l,MediaStreamTrackEvent:A.l,MIDIConnectionEvent:A.l,MIDIMessageEvent:A.l,MouseEvent:A.l,DragEvent:A.l,MutationEvent:A.l,NotificationEvent:A.l,PageTransitionEvent:A.l,PaymentRequestEvent:A.l,PaymentRequestUpdateEvent:A.l,PointerEvent:A.l,PopStateEvent:A.l,PresentationConnectionAvailableEvent:A.l,PresentationConnectionCloseEvent:A.l,ProgressEvent:A.l,PromiseRejectionEvent:A.l,PushEvent:A.l,RTCDataChannelEvent:A.l,RTCDTMFToneChangeEvent:A.l,RTCPeerConnectionIceEvent:A.l,RTCTrackEvent:A.l,SecurityPolicyViolationEvent:A.l,SensorErrorEvent:A.l,SpeechRecognitionError:A.l,SpeechRecognitionEvent:A.l,SpeechSynthesisEvent:A.l,StorageEvent:A.l,SyncEvent:A.l,TextEvent:A.l,TouchEvent:A.l,TrackEvent:A.l,TransitionEvent:A.l,WebKitTransitionEvent:A.l,UIEvent:A.l,VRDeviceEvent:A.l,VRDisplayEvent:A.l,VRSessionEvent:A.l,WheelEvent:A.l,MojoInterfaceRequestEvent:A.l,ResourceProgressEvent:A.l,USBConnectionEvent:A.l,AudioProcessingEvent:A.l,OfflineAudioCompletionEvent:A.l,WebGLContextEvent:A.l,Event:A.l,InputEvent:A.l,SubmitEvent:A.l,AbsoluteOrientationSensor:A.f,Accelerometer:A.f,AccessibleNode:A.f,AmbientLightSensor:A.f,Animation:A.f,ApplicationCache:A.f,DOMApplicationCache:A.f,OfflineResourceList:A.f,BackgroundFetchRegistration:A.f,BatteryManager:A.f,BroadcastChannel:A.f,CanvasCaptureMediaStreamTrack:A.f,EventSource:A.f,FileReader:A.f,FontFaceSet:A.f,Gyroscope:A.f,XMLHttpRequest:A.f,XMLHttpRequestEventTarget:A.f,XMLHttpRequestUpload:A.f,LinearAccelerationSensor:A.f,Magnetometer:A.f,MediaDevices:A.f,MediaKeySession:A.f,MediaQueryList:A.f,MediaRecorder:A.f,MediaSource:A.f,MediaStream:A.f,MediaStreamTrack:A.f,MIDIAccess:A.f,MIDIInput:A.f,MIDIOutput:A.f,MIDIPort:A.f,NetworkInformation:A.f,Notification:A.f,OffscreenCanvas:A.f,OrientationSensor:A.f,PaymentRequest:A.f,Performance:A.f,PermissionStatus:A.f,PresentationAvailability:A.f,PresentationConnection:A.f,PresentationConnectionList:A.f,PresentationRequest:A.f,RelativeOrientationSensor:A.f,RemotePlayback:A.f,RTCDataChannel:A.f,DataChannel:A.f,RTCDTMFSender:A.f,RTCPeerConnection:A.f,webkitRTCPeerConnection:A.f,mozRTCPeerConnection:A.f,ScreenOrientation:A.f,Sensor:A.f,ServiceWorker:A.f,ServiceWorkerContainer:A.f,ServiceWorkerRegistration:A.f,SharedWorker:A.f,SpeechRecognition:A.f,SpeechSynthesis:A.f,SpeechSynthesisUtterance:A.f,VR:A.f,VRDevice:A.f,VRDisplay:A.f,VRSession:A.f,VisualViewport:A.f,WebSocket:A.f,Window:A.f,DOMWindow:A.f,Worker:A.f,WorkerPerformance:A.f,BluetoothDevice:A.f,BluetoothRemoteGATTCharacteristic:A.f,Clipboard:A.f,MojoInterfaceInterceptor:A.f,USB:A.f,AnalyserNode:A.f,RealtimeAnalyserNode:A.f,AudioBufferSourceNode:A.f,AudioDestinationNode:A.f,AudioNode:A.f,AudioScheduledSourceNode:A.f,AudioWorkletNode:A.f,BiquadFilterNode:A.f,ChannelMergerNode:A.f,AudioChannelMerger:A.f,ChannelSplitterNode:A.f,AudioChannelSplitter:A.f,ConstantSourceNode:A.f,ConvolverNode:A.f,DelayNode:A.f,DynamicsCompressorNode:A.f,GainNode:A.f,AudioGainNode:A.f,IIRFilterNode:A.f,MediaElementAudioSourceNode:A.f,MediaStreamAudioDestinationNode:A.f,MediaStreamAudioSourceNode:A.f,OscillatorNode:A.f,Oscillator:A.f,PannerNode:A.f,AudioPannerNode:A.f,webkitAudioPannerNode:A.f,ScriptProcessorNode:A.f,JavaScriptAudioNode:A.f,StereoPannerNode:A.f,WaveShaperNode:A.f,EventTarget:A.f,File:A.az,FileList:A.cI,FileWriter:A.fz,HTMLFormElement:A.fB,Gamepad:A.aA,History:A.fD,HTMLCollection:A.ci,HTMLFormControlsCollection:A.ci,HTMLOptionsCollection:A.ci,ImageData:A.cL,Location:A.fO,MediaList:A.fP,MessageEvent:A.cV,MessagePort:A.ck,MIDIInputMap:A.fQ,MIDIOutputMap:A.fR,MimeType:A.aB,MimeTypeArray:A.fS,Document:A.G,DocumentFragment:A.G,HTMLDocument:A.G,ShadowRoot:A.G,XMLDocument:A.G,Attr:A.G,DocumentType:A.G,Node:A.G,NodeList:A.dV,RadioNodeList:A.dV,Plugin:A.aC,PluginArray:A.h7,RTCStatsReport:A.hb,HTMLSelectElement:A.hd,SharedArrayBuffer:A.cY,SharedWorkerGlobalScope:A.cZ,SourceBuffer:A.aD,SourceBufferList:A.hf,SpeechGrammar:A.aE,SpeechGrammarList:A.hg,SpeechRecognitionResult:A.aF,Storage:A.hl,CSSStyleSheet:A.al,StyleSheet:A.al,TextTrack:A.aH,TextTrackCue:A.am,VTTCue:A.am,TextTrackCueList:A.hp,TextTrackList:A.hq,TimeRanges:A.hr,Touch:A.aI,TouchList:A.hs,TrackDefaultList:A.ht,URL:A.hB,VideoTrackList:A.hD,DedicatedWorkerGlobalScope:A.c1,ServiceWorkerGlobalScope:A.c1,WorkerGlobalScope:A.c1,CSSRuleList:A.hS,ClientRect:A.eo,DOMRect:A.eo,GamepadList:A.i5,NamedNodeMap:A.ez,MozNamedAttrMap:A.ez,SpeechRecognitionResultList:A.iA,StyleSheetList:A.iJ,IDBCursor:A.bT,IDBCursorWithValue:A.br,IDBDatabase:A.bj,IDBFactory:A.cj,IDBIndex:A.dG,IDBObjectStore:A.dX,IDBOpenDBRequest:A.bz,IDBVersionChangeRequest:A.bz,IDBRequest:A.bz,IDBTransaction:A.ec,IDBVersionChangeEvent:A.bD,SVGLength:A.aN,SVGLengthList:A.fL,SVGNumber:A.aR,SVGNumberList:A.h3,SVGPointList:A.h8,SVGStringList:A.hn,SVGTransform:A.aW,SVGTransformList:A.hu,AudioBuffer:A.f9,AudioParamMap:A.fa,AudioTrackList:A.fb,AudioContext:A.bQ,webkitAudioContext:A.bQ,BaseAudioContext:A.bQ,OfflineAudioContext:A.h4})\nhunkHelpers.setOrUpdateLeafTags({WebGL:true,AnimationEffectReadOnly:true,AnimationEffectTiming:true,AnimationEffectTimingReadOnly:true,AnimationTimeline:true,AnimationWorkletGlobalScope:true,AuthenticatorAssertionResponse:true,AuthenticatorAttestationResponse:true,AuthenticatorResponse:true,BackgroundFetchFetch:true,BackgroundFetchManager:true,BackgroundFetchSettledFetch:true,BarProp:true,BarcodeDetector:true,BluetoothRemoteGATTDescriptor:true,Body:true,BudgetState:true,CacheStorage:true,CanvasGradient:true,CanvasPattern:true,CanvasRenderingContext2D:true,Client:true,Clients:true,CookieStore:true,Coordinates:true,Credential:true,CredentialUserData:true,CredentialsContainer:true,Crypto:true,CryptoKey:true,CSS:true,CSSVariableReferenceValue:true,CustomElementRegistry:true,DataTransfer:true,DataTransferItem:true,DeprecatedStorageInfo:true,DeprecatedStorageQuota:true,DeprecationReport:true,DetectedBarcode:true,DetectedFace:true,DetectedText:true,DeviceAcceleration:true,DeviceRotationRate:true,DirectoryEntry:true,webkitFileSystemDirectoryEntry:true,FileSystemDirectoryEntry:true,DirectoryReader:true,WebKitDirectoryReader:true,webkitFileSystemDirectoryReader:true,FileSystemDirectoryReader:true,DocumentOrShadowRoot:true,DocumentTimeline:true,DOMError:true,DOMImplementation:true,Iterator:true,DOMMatrix:true,DOMMatrixReadOnly:true,DOMParser:true,DOMPoint:true,DOMPointReadOnly:true,DOMQuad:true,DOMStringMap:true,Entry:true,webkitFileSystemEntry:true,FileSystemEntry:true,External:true,FaceDetector:true,FederatedCredential:true,FileEntry:true,webkitFileSystemFileEntry:true,FileSystemFileEntry:true,DOMFileSystem:true,WebKitFileSystem:true,webkitFileSystem:true,FileSystem:true,FontFace:true,FontFaceSource:true,FormData:true,GamepadButton:true,GamepadPose:true,Geolocation:true,Position:true,GeolocationPosition:true,Headers:true,HTMLHyperlinkElementUtils:true,IdleDeadline:true,ImageBitmap:true,ImageBitmapRenderingContext:true,ImageCapture:true,InputDeviceCapabilities:true,IntersectionObserver:true,IntersectionObserverEntry:true,InterventionReport:true,KeyframeEffect:true,KeyframeEffectReadOnly:true,MediaCapabilities:true,MediaCapabilitiesInfo:true,MediaDeviceInfo:true,MediaError:true,MediaKeyStatusMap:true,MediaKeySystemAccess:true,MediaKeys:true,MediaKeysPolicy:true,MediaMetadata:true,MediaSession:true,MediaSettingsRange:true,MemoryInfo:true,MessageChannel:true,Metadata:true,MutationObserver:true,WebKitMutationObserver:true,MutationRecord:true,NavigationPreloadManager:true,Navigator:true,NavigatorAutomationInformation:true,NavigatorConcurrentHardware:true,NavigatorCookies:true,NavigatorUserMediaError:true,NodeFilter:true,NodeIterator:true,NonDocumentTypeChildNode:true,NonElementParentNode:true,NoncedElement:true,OffscreenCanvasRenderingContext2D:true,OverconstrainedError:true,PaintRenderingContext2D:true,PaintSize:true,PaintWorkletGlobalScope:true,PasswordCredential:true,Path2D:true,PaymentAddress:true,PaymentInstruments:true,PaymentManager:true,PaymentResponse:true,PerformanceEntry:true,PerformanceLongTaskTiming:true,PerformanceMark:true,PerformanceMeasure:true,PerformanceNavigation:true,PerformanceNavigationTiming:true,PerformanceObserver:true,PerformanceObserverEntryList:true,PerformancePaintTiming:true,PerformanceResourceTiming:true,PerformanceServerTiming:true,PerformanceTiming:true,Permissions:true,PhotoCapabilities:true,PositionError:true,GeolocationPositionError:true,Presentation:true,PresentationReceiver:true,PublicKeyCredential:true,PushManager:true,PushMessageData:true,PushSubscription:true,PushSubscriptionOptions:true,Range:true,RelatedApplication:true,ReportBody:true,ReportingObserver:true,ResizeObserver:true,ResizeObserverEntry:true,RTCCertificate:true,RTCIceCandidate:true,mozRTCIceCandidate:true,RTCLegacyStatsReport:true,RTCRtpContributingSource:true,RTCRtpReceiver:true,RTCRtpSender:true,RTCSessionDescription:true,mozRTCSessionDescription:true,RTCStatsResponse:true,Screen:true,ScrollState:true,ScrollTimeline:true,Selection:true,SpeechRecognitionAlternative:true,SpeechSynthesisVoice:true,StaticRange:true,StorageManager:true,StyleMedia:true,StylePropertyMap:true,StylePropertyMapReadonly:true,SyncManager:true,TaskAttributionTiming:true,TextDetector:true,TextMetrics:true,TrackDefault:true,TreeWalker:true,TrustedHTML:true,TrustedScriptURL:true,TrustedURL:true,UnderlyingSourceBase:true,URLSearchParams:true,VRCoordinateSystem:true,VRDisplayCapabilities:true,VREyeParameters:true,VRFrameData:true,VRFrameOfReference:true,VRPose:true,VRStageBounds:true,VRStageBoundsPoint:true,VRStageParameters:true,ValidityState:true,VideoPlaybackQuality:true,VideoTrack:true,VTTRegion:true,WindowClient:true,WorkletAnimation:true,WorkletGlobalScope:true,XPathEvaluator:true,XPathExpression:true,XPathNSResolver:true,XPathResult:true,XMLSerializer:true,XSLTProcessor:true,Bluetooth:true,BluetoothCharacteristicProperties:true,BluetoothRemoteGATTServer:true,BluetoothRemoteGATTService:true,BluetoothUUID:true,BudgetService:true,Cache:true,DOMFileSystemSync:true,DirectoryEntrySync:true,DirectoryReaderSync:true,EntrySync:true,FileEntrySync:true,FileReaderSync:true,FileWriterSync:true,HTMLAllCollection:true,Mojo:true,MojoHandle:true,MojoWatcher:true,NFC:true,PagePopupController:true,Report:true,Request:true,Response:true,SubtleCrypto:true,USBAlternateInterface:true,USBConfiguration:true,USBDevice:true,USBEndpoint:true,USBInTransferResult:true,USBInterface:true,USBIsochronousInTransferPacket:true,USBIsochronousInTransferResult:true,USBIsochronousOutTransferPacket:true,USBIsochronousOutTransferResult:true,USBOutTransferResult:true,WorkerLocation:true,WorkerNavigator:true,Worklet:true,IDBKeyRange:true,IDBObservation:true,IDBObserver:true,IDBObserverChanges:true,SVGAngle:true,SVGAnimatedAngle:true,SVGAnimatedBoolean:true,SVGAnimatedEnumeration:true,SVGAnimatedInteger:true,SVGAnimatedLength:true,SVGAnimatedLengthList:true,SVGAnimatedNumber:true,SVGAnimatedNumberList:true,SVGAnimatedPreserveAspectRatio:true,SVGAnimatedRect:true,SVGAnimatedString:true,SVGAnimatedTransformList:true,SVGMatrix:true,SVGPoint:true,SVGPreserveAspectRatio:true,SVGRect:true,SVGUnitTypes:true,AudioListener:true,AudioParam:true,AudioTrack:true,AudioWorkletGlobalScope:true,AudioWorkletProcessor:true,PeriodicWave:true,WebGLActiveInfo:true,ANGLEInstancedArrays:true,ANGLE_instanced_arrays:true,WebGLBuffer:true,WebGLCanvas:true,WebGLColorBufferFloat:true,WebGLCompressedTextureASTC:true,WebGLCompressedTextureATC:true,WEBGL_compressed_texture_atc:true,WebGLCompressedTextureETC1:true,WEBGL_compressed_texture_etc1:true,WebGLCompressedTextureETC:true,WebGLCompressedTexturePVRTC:true,WEBGL_compressed_texture_pvrtc:true,WebGLCompressedTextureS3TC:true,WEBGL_compressed_texture_s3tc:true,WebGLCompressedTextureS3TCsRGB:true,WebGLDebugRendererInfo:true,WEBGL_debug_renderer_info:true,WebGLDebugShaders:true,WEBGL_debug_shaders:true,WebGLDepthTexture:true,WEBGL_depth_texture:true,WebGLDrawBuffers:true,WEBGL_draw_buffers:true,EXTsRGB:true,EXT_sRGB:true,EXTBlendMinMax:true,EXT_blend_minmax:true,EXTColorBufferFloat:true,EXTColorBufferHalfFloat:true,EXTDisjointTimerQuery:true,EXTDisjointTimerQueryWebGL2:true,EXTFragDepth:true,EXT_frag_depth:true,EXTShaderTextureLOD:true,EXT_shader_texture_lod:true,EXTTextureFilterAnisotropic:true,EXT_texture_filter_anisotropic:true,WebGLFramebuffer:true,WebGLGetBufferSubDataAsync:true,WebGLLoseContext:true,WebGLExtensionLoseContext:true,WEBGL_lose_context:true,OESElementIndexUint:true,OES_element_index_uint:true,OESStandardDerivatives:true,OES_standard_derivatives:true,OESTextureFloat:true,OES_texture_float:true,OESTextureFloatLinear:true,OES_texture_float_linear:true,OESTextureHalfFloat:true,OES_texture_half_float:true,OESTextureHalfFloatLinear:true,OES_texture_half_float_linear:true,OESVertexArrayObject:true,OES_vertex_array_object:true,WebGLProgram:true,WebGLQuery:true,WebGLRenderbuffer:true,WebGLRenderingContext:true,WebGL2RenderingContext:true,WebGLSampler:true,WebGLShader:true,WebGLShaderPrecisionFormat:true,WebGLSync:true,WebGLTexture:true,WebGLTimerQueryEXT:true,WebGLTransformFeedback:true,WebGLUniformLocation:true,WebGLVertexArrayObject:true,WebGLVertexArrayObjectOES:true,WebGL2RenderingContextBase:true,ArrayBuffer:true,ArrayBufferView:false,DataView:true,Float32Array:true,Float64Array:true,Int16Array:true,Int32Array:true,Int8Array:true,Uint16Array:true,Uint32Array:true,Uint8ClampedArray:true,CanvasPixelArray:true,Uint8Array:false,HTMLAudioElement:true,HTMLBRElement:true,HTMLBaseElement:true,HTMLBodyElement:true,HTMLButtonElement:true,HTMLCanvasElement:true,HTMLContentElement:true,HTMLDListElement:true,HTMLDataElement:true,HTMLDataListElement:true,HTMLDetailsElement:true,HTMLDialogElement:true,HTMLDivElement:true,HTMLEmbedElement:true,HTMLFieldSetElement:true,HTMLHRElement:true,HTMLHeadElement:true,HTMLHeadingElement:true,HTMLHtmlElement:true,HTMLIFrameElement:true,HTMLImageElement:true,HTMLInputElement:true,HTMLLIElement:true,HTMLLabelElement:true,HTMLLegendElement:true,HTMLLinkElement:true,HTMLMapElement:true,HTMLMediaElement:true,HTMLMenuElement:true,HTMLMetaElement:true,HTMLMeterElement:true,HTMLModElement:true,HTMLOListElement:true,HTMLObjectElement:true,HTMLOptGroupElement:true,HTMLOptionElement:true,HTMLOutputElement:true,HTMLParagraphElement:true,HTMLParamElement:true,HTMLPictureElement:true,HTMLPreElement:true,HTMLProgressElement:true,HTMLQuoteElement:true,HTMLScriptElement:true,HTMLShadowElement:true,HTMLSlotElement:true,HTMLSourceElement:true,HTMLSpanElement:true,HTMLStyleElement:true,HTMLTableCaptionElement:true,HTMLTableCellElement:true,HTMLTableDataCellElement:true,HTMLTableHeaderCellElement:true,HTMLTableColElement:true,HTMLTableElement:true,HTMLTableRowElement:true,HTMLTableSectionElement:true,HTMLTemplateElement:true,HTMLTextAreaElement:true,HTMLTimeElement:true,HTMLTitleElement:true,HTMLTrackElement:true,HTMLUListElement:true,HTMLUnknownElement:true,HTMLVideoElement:true,HTMLDirectoryElement:true,HTMLFontElement:true,HTMLFrameElement:true,HTMLFrameSetElement:true,HTMLMarqueeElement:true,HTMLElement:false,AccessibleNodeList:true,HTMLAnchorElement:true,HTMLAreaElement:true,Blob:false,CDATASection:true,CharacterData:true,Comment:true,ProcessingInstruction:true,Text:true,CSSPerspective:true,CSSCharsetRule:true,CSSConditionRule:true,CSSFontFaceRule:true,CSSGroupingRule:true,CSSImportRule:true,CSSKeyframeRule:true,MozCSSKeyframeRule:true,WebKitCSSKeyframeRule:true,CSSKeyframesRule:true,MozCSSKeyframesRule:true,WebKitCSSKeyframesRule:true,CSSMediaRule:true,CSSNamespaceRule:true,CSSPageRule:true,CSSRule:true,CSSStyleRule:true,CSSSupportsRule:true,CSSViewportRule:true,CSSStyleDeclaration:true,MSStyleCSSProperties:true,CSS2Properties:true,CSSImageValue:true,CSSKeywordValue:true,CSSNumericValue:true,CSSPositionValue:true,CSSResourceValue:true,CSSUnitValue:true,CSSURLImageValue:true,CSSStyleValue:false,CSSMatrixComponent:true,CSSRotation:true,CSSScale:true,CSSSkew:true,CSSTranslation:true,CSSTransformComponent:false,CSSTransformValue:true,CSSUnparsedValue:true,DataTransferItemList:true,DOMException:true,ClientRectList:true,DOMRectList:true,DOMRectReadOnly:false,DOMStringList:true,DOMTokenList:true,MathMLElement:true,SVGAElement:true,SVGAnimateElement:true,SVGAnimateMotionElement:true,SVGAnimateTransformElement:true,SVGAnimationElement:true,SVGCircleElement:true,SVGClipPathElement:true,SVGDefsElement:true,SVGDescElement:true,SVGDiscardElement:true,SVGEllipseElement:true,SVGFEBlendElement:true,SVGFEColorMatrixElement:true,SVGFEComponentTransferElement:true,SVGFECompositeElement:true,SVGFEConvolveMatrixElement:true,SVGFEDiffuseLightingElement:true,SVGFEDisplacementMapElement:true,SVGFEDistantLightElement:true,SVGFEFloodElement:true,SVGFEFuncAElement:true,SVGFEFuncBElement:true,SVGFEFuncGElement:true,SVGFEFuncRElement:true,SVGFEGaussianBlurElement:true,SVGFEImageElement:true,SVGFEMergeElement:true,SVGFEMergeNodeElement:true,SVGFEMorphologyElement:true,SVGFEOffsetElement:true,SVGFEPointLightElement:true,SVGFESpecularLightingElement:true,SVGFESpotLightElement:true,SVGFETileElement:true,SVGFETurbulenceElement:true,SVGFilterElement:true,SVGForeignObjectElement:true,SVGGElement:true,SVGGeometryElement:true,SVGGraphicsElement:true,SVGImageElement:true,SVGLineElement:true,SVGLinearGradientElement:true,SVGMarkerElement:true,SVGMaskElement:true,SVGMetadataElement:true,SVGPathElement:true,SVGPatternElement:true,SVGPolygonElement:true,SVGPolylineElement:true,SVGRadialGradientElement:true,SVGRectElement:true,SVGScriptElement:true,SVGSetElement:true,SVGStopElement:true,SVGStyleElement:true,SVGElement:true,SVGSVGElement:true,SVGSwitchElement:true,SVGSymbolElement:true,SVGTSpanElement:true,SVGTextContentElement:true,SVGTextElement:true,SVGTextPathElement:true,SVGTextPositioningElement:true,SVGTitleElement:true,SVGUseElement:true,SVGViewElement:true,SVGGradientElement:true,SVGComponentTransferFunctionElement:true,SVGFEDropShadowElement:true,SVGMPathElement:true,Element:false,AbortPaymentEvent:true,AnimationEvent:true,AnimationPlaybackEvent:true,ApplicationCacheErrorEvent:true,BackgroundFetchClickEvent:true,BackgroundFetchEvent:true,BackgroundFetchFailEvent:true,BackgroundFetchedEvent:true,BeforeInstallPromptEvent:true,BeforeUnloadEvent:true,BlobEvent:true,CanMakePaymentEvent:true,ClipboardEvent:true,CloseEvent:true,CompositionEvent:true,CustomEvent:true,DeviceMotionEvent:true,DeviceOrientationEvent:true,ErrorEvent:true,ExtendableEvent:true,ExtendableMessageEvent:true,FetchEvent:true,FocusEvent:true,FontFaceSetLoadEvent:true,ForeignFetchEvent:true,GamepadEvent:true,HashChangeEvent:true,InstallEvent:true,KeyboardEvent:true,MediaEncryptedEvent:true,MediaKeyMessageEvent:true,MediaQueryListEvent:true,MediaStreamEvent:true,MediaStreamTrackEvent:true,MIDIConnectionEvent:true,MIDIMessageEvent:true,MouseEvent:true,DragEvent:true,MutationEvent:true,NotificationEvent:true,PageTransitionEvent:true,PaymentRequestEvent:true,PaymentRequestUpdateEvent:true,PointerEvent:true,PopStateEvent:true,PresentationConnectionAvailableEvent:true,PresentationConnectionCloseEvent:true,ProgressEvent:true,PromiseRejectionEvent:true,PushEvent:true,RTCDataChannelEvent:true,RTCDTMFToneChangeEvent:true,RTCPeerConnectionIceEvent:true,RTCTrackEvent:true,SecurityPolicyViolationEvent:true,SensorErrorEvent:true,SpeechRecognitionError:true,SpeechRecognitionEvent:true,SpeechSynthesisEvent:true,StorageEvent:true,SyncEvent:true,TextEvent:true,TouchEvent:true,TrackEvent:true,TransitionEvent:true,WebKitTransitionEvent:true,UIEvent:true,VRDeviceEvent:true,VRDisplayEvent:true,VRSessionEvent:true,WheelEvent:true,MojoInterfaceRequestEvent:true,ResourceProgressEvent:true,USBConnectionEvent:true,AudioProcessingEvent:true,OfflineAudioCompletionEvent:true,WebGLContextEvent:true,Event:false,InputEvent:false,SubmitEvent:false,AbsoluteOrientationSensor:true,Accelerometer:true,AccessibleNode:true,AmbientLightSensor:true,Animation:true,ApplicationCache:true,DOMApplicationCache:true,OfflineResourceList:true,BackgroundFetchRegistration:true,BatteryManager:true,BroadcastChannel:true,CanvasCaptureMediaStreamTrack:true,EventSource:true,FileReader:true,FontFaceSet:true,Gyroscope:true,XMLHttpRequest:true,XMLHttpRequestEventTarget:true,XMLHttpRequestUpload:true,LinearAccelerationSensor:true,Magnetometer:true,MediaDevices:true,MediaKeySession:true,MediaQueryList:true,MediaRecorder:true,MediaSource:true,MediaStream:true,MediaStreamTrack:true,MIDIAccess:true,MIDIInput:true,MIDIOutput:true,MIDIPort:true,NetworkInformation:true,Notification:true,OffscreenCanvas:true,OrientationSensor:true,PaymentRequest:true,Performance:true,PermissionStatus:true,PresentationAvailability:true,PresentationConnection:true,PresentationConnectionList:true,PresentationRequest:true,RelativeOrientationSensor:true,RemotePlayback:true,RTCDataChannel:true,DataChannel:true,RTCDTMFSender:true,RTCPeerConnection:true,webkitRTCPeerConnection:true,mozRTCPeerConnection:true,ScreenOrientation:true,Sensor:true,ServiceWorker:true,ServiceWorkerContainer:true,ServiceWorkerRegistration:true,SharedWorker:true,SpeechRecognition:true,SpeechSynthesis:true,SpeechSynthesisUtterance:true,VR:true,VRDevice:true,VRDisplay:true,VRSession:true,VisualViewport:true,WebSocket:true,Window:true,DOMWindow:true,Worker:true,WorkerPerformance:true,BluetoothDevice:true,BluetoothRemoteGATTCharacteristic:true,Clipboard:true,MojoInterfaceInterceptor:true,USB:true,AnalyserNode:true,RealtimeAnalyserNode:true,AudioBufferSourceNode:true,AudioDestinationNode:true,AudioNode:true,AudioScheduledSourceNode:true,AudioWorkletNode:true,BiquadFilterNode:true,ChannelMergerNode:true,AudioChannelMerger:true,ChannelSplitterNode:true,AudioChannelSplitter:true,ConstantSourceNode:true,ConvolverNode:true,DelayNode:true,DynamicsCompressorNode:true,GainNode:true,AudioGainNode:true,IIRFilterNode:true,MediaElementAudioSourceNode:true,MediaStreamAudioDestinationNode:true,MediaStreamAudioSourceNode:true,OscillatorNode:true,Oscillator:true,PannerNode:true,AudioPannerNode:true,webkitAudioPannerNode:true,ScriptProcessorNode:true,JavaScriptAudioNode:true,StereoPannerNode:true,WaveShaperNode:true,EventTarget:false,File:true,FileList:true,FileWriter:true,HTMLFormElement:true,Gamepad:true,History:true,HTMLCollection:true,HTMLFormControlsCollection:true,HTMLOptionsCollection:true,ImageData:true,Location:true,MediaList:true,MessageEvent:true,MessagePort:true,MIDIInputMap:true,MIDIOutputMap:true,MimeType:true,MimeTypeArray:true,Document:true,DocumentFragment:true,HTMLDocument:true,ShadowRoot:true,XMLDocument:true,Attr:true,DocumentType:true,Node:false,NodeList:true,RadioNodeList:true,Plugin:true,PluginArray:true,RTCStatsReport:true,HTMLSelectElement:true,SharedArrayBuffer:true,SharedWorkerGlobalScope:true,SourceBuffer:true,SourceBufferList:true,SpeechGrammar:true,SpeechGrammarList:true,SpeechRecognitionResult:true,Storage:true,CSSStyleSheet:true,StyleSheet:true,TextTrack:true,TextTrackCue:true,VTTCue:true,TextTrackCueList:true,TextTrackList:true,TimeRanges:true,Touch:true,TouchList:true,TrackDefaultList:true,URL:true,VideoTrackList:true,DedicatedWorkerGlobalScope:true,ServiceWorkerGlobalScope:true,WorkerGlobalScope:false,CSSRuleList:true,ClientRect:true,DOMRect:true,GamepadList:true,NamedNodeMap:true,MozNamedAttrMap:true,SpeechRecognitionResultList:true,StyleSheetList:true,IDBCursor:false,IDBCursorWithValue:true,IDBDatabase:true,IDBFactory:true,IDBIndex:true,IDBObjectStore:true,IDBOpenDBRequest:true,IDBVersionChangeRequest:true,IDBRequest:true,IDBTransaction:true,IDBVersionChangeEvent:true,SVGLength:true,SVGLengthList:true,SVGNumber:true,SVGNumberList:true,SVGPointList:true,SVGStringList:true,SVGTransform:true,SVGTransformList:true,AudioBuffer:true,AudioParamMap:true,AudioTrackList:true,AudioContext:true,webkitAudioContext:true,BaseAudioContext:false,OfflineAudioContext:true})\nA.ag.$nativeSuperclassTag=\"ArrayBufferView\"\nA.eA.$nativeSuperclassTag=\"ArrayBufferView\"\nA.eB.$nativeSuperclassTag=\"ArrayBufferView\"\nA.bX.$nativeSuperclassTag=\"ArrayBufferView\"\nA.eC.$nativeSuperclassTag=\"ArrayBufferView\"\nA.eD.$nativeSuperclassTag=\"ArrayBufferView\"\nA.aQ.$nativeSuperclassTag=\"ArrayBufferView\"\nA.eF.$nativeSuperclassTag=\"EventTarget\"\nA.eG.$nativeSuperclassTag=\"EventTarget\"\nA.eL.$nativeSuperclassTag=\"EventTarget\"\nA.eM.$nativeSuperclassTag=\"EventTarget\"})()\nFunction.prototype.$2=function(a,b){return this(a,b)}\nFunction.prototype.$1=function(a){return this(a)}\nFunction.prototype.$0=function(){return this()}\nFunction.prototype.$3$3=function(a,b,c){return this(a,b,c)}\nFunction.prototype.$2$2=function(a,b){return this(a,b)}\nFunction.prototype.$1$1=function(a){return this(a)}\nFunction.prototype.$2$1=function(a){return this(a)}\nFunction.prototype.$3=function(a,b,c){return this(a,b,c)}\nFunction.prototype.$4=function(a,b,c,d){return this(a,b,c,d)}\nFunction.prototype.$3$1=function(a){return this(a)}\nFunction.prototype.$1$0=function(){return this()}\nFunction.prototype.$5=function(a,b,c,d,e){return this(a,b,c,d,e)}\nFunction.prototype.$6=function(a,b,c,d,e,f){return this(a,b,c,d,e,f)}\nFunction.prototype.$2$3=function(a,b,c){return this(a,b,c)}\nFunction.prototype.$1$2=function(a,b){return this(a,b)}\nconvertAllToFastObject(w)\nconvertToFastObject($);(function(a){if(typeof document===\"undefined\"){a(null)\nreturn}if(typeof document.currentScript!=\"undefined\"){a(document.currentScript)\nreturn}var s=document.scripts\nfunction onLoad(b){for(var q=0;q<s.length;++q)s[q].removeEventListener(\"load\",onLoad,false)\na(b.target)}for(var r=0;r<s.length;++r)s[r].addEventListener(\"load\",onLoad,false)})(function(a){v.currentScript=a\nvar s=function(b){return A.vq(A.v5(b))}\nif(typeof dartMainRunner===\"function\")dartMainRunner(s,[])\nelse s([])})})()\n//# sourceMappingURL=sqflite_sw.dart.js.map\n"
  },
  {
    "path": "windows/.gitignore",
    "content": "flutter/ephemeral/\n\n# Visual Studio user-specific files.\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# Visual Studio build-related files.\nx64/\nx86/\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n\n.vs/\nout/"
  },
  {
    "path": "windows/CMakeLists.txt",
    "content": "# Project-level configuration.\ncmake_minimum_required(VERSION 3.14)\nproject(askaide LANGUAGES CXX)\n\n# The name of the executable created for the application. Change this to change\n# the on-disk name of your application.\nset(BINARY_NAME \"AIdea\")\n\n# Explicitly opt in to modern CMake behaviors to avoid warnings with recent\n# versions of CMake.\ncmake_policy(SET CMP0063 NEW)\n\n# Define build configuration option.\nget_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)\nif(IS_MULTICONFIG)\n  set(CMAKE_CONFIGURATION_TYPES \"Debug;Profile;Release\"\n    CACHE STRING \"\" FORCE)\nelse()\n  if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)\n    set(CMAKE_BUILD_TYPE \"Debug\" CACHE\n      STRING \"Flutter build mode\" FORCE)\n    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS\n      \"Debug\" \"Profile\" \"Release\")\n  endif()\nendif()\n# Define settings for the Profile build mode.\nset(CMAKE_EXE_LINKER_FLAGS_PROFILE \"${CMAKE_EXE_LINKER_FLAGS_RELEASE}\")\nset(CMAKE_SHARED_LINKER_FLAGS_PROFILE \"${CMAKE_SHARED_LINKER_FLAGS_RELEASE}\")\nset(CMAKE_C_FLAGS_PROFILE \"${CMAKE_C_FLAGS_RELEASE}\")\nset(CMAKE_CXX_FLAGS_PROFILE \"${CMAKE_CXX_FLAGS_RELEASE}\")\n\n# Use Unicode for all projects.\nadd_definitions(-DUNICODE -D_UNICODE)\n\n# Compilation settings that should be applied to most targets.\n#\n# Be cautious about adding new options here, as plugins use this function by\n# default. In most cases, you should add new options to specific targets instead\n# of modifying this function.\nfunction(APPLY_STANDARD_SETTINGS TARGET)\n  target_compile_features(${TARGET} PUBLIC cxx_std_17)\n  target_compile_options(${TARGET} PRIVATE /W4 /WX /wd\"4100\")\n  target_compile_options(${TARGET} PRIVATE /EHsc)\n  target_compile_definitions(${TARGET} PRIVATE \"_HAS_EXCEPTIONS=0\")\n  target_compile_definitions(${TARGET} PRIVATE \"$<$<CONFIG:Debug>:_DEBUG>\")\nendfunction()\n\n# Flutter library and tool build rules.\nset(FLUTTER_MANAGED_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/flutter\")\nadd_subdirectory(${FLUTTER_MANAGED_DIR})\n\n# Application build; see runner/CMakeLists.txt.\nadd_subdirectory(\"runner\")\n\n# Generated plugin build rules, which manage building the plugins and adding\n# them to the application.\ninclude(flutter/generated_plugins.cmake)\n\n\n# === Installation ===\n# Support files are copied into place next to the executable, so that it can\n# run in place. This is done instead of making a separate bundle (as on Linux)\n# so that building and running from within Visual Studio will work.\nset(BUILD_BUNDLE_DIR \"$<TARGET_FILE_DIR:${BINARY_NAME}>\")\n# Make the \"install\" step default, as it's required to run.\nset(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)\nif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)\n  set(CMAKE_INSTALL_PREFIX \"${BUILD_BUNDLE_DIR}\" CACHE PATH \"...\" FORCE)\nendif()\n\nset(INSTALL_BUNDLE_DATA_DIR \"${CMAKE_INSTALL_PREFIX}/data\")\nset(INSTALL_BUNDLE_LIB_DIR \"${CMAKE_INSTALL_PREFIX}\")\n\ninstall(TARGETS ${BINARY_NAME} RUNTIME DESTINATION \"${CMAKE_INSTALL_PREFIX}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_ICU_DATA_FILE}\" DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n  COMPONENT Runtime)\n\nif(PLUGIN_BUNDLED_LIBRARIES)\n  install(FILES \"${PLUGIN_BUNDLED_LIBRARIES}\"\n    DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n    COMPONENT Runtime)\nendif()\n\n# Fully re-copy the assets directory on each build to avoid having stale files\n# from a previous install.\nset(FLUTTER_ASSET_DIR_NAME \"flutter_assets\")\ninstall(CODE \"\n  file(REMOVE_RECURSE \\\"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\\\")\n  \" COMPONENT Runtime)\ninstall(DIRECTORY \"${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}\"\n  DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\" COMPONENT Runtime)\n\n# Install the AOT library on non-Debug builds only.\ninstall(FILES \"${AOT_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\"\n  CONFIGURATIONS Profile;Release\n  COMPONENT Runtime)\n\n# Install sqlite3.dll\ninstall(FILES \"sqlite3.dll\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n  CONFIGURATIONS Profile;Release\n  COMPONENT Runtime)\n\ninstall(FILES \"msvcp140.dll\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n  CONFIGURATIONS Profile;Release\n  COMPONENT Runtime)\n\ninstall(FILES \"vcruntime140.dll\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n  CONFIGURATIONS Profile;Release\n  COMPONENT Runtime)\n\ninstall(FILES \"vcruntime140_1.dll\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n  CONFIGURATIONS Profile;Release\n  COMPONENT Runtime)\n"
  },
  {
    "path": "windows/flutter/CMakeLists.txt",
    "content": "# This file controls Flutter-level build steps. It should not be edited.\ncmake_minimum_required(VERSION 3.14)\n\nset(EPHEMERAL_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/ephemeral\")\n\n# Configuration provided via flutter tool.\ninclude(${EPHEMERAL_DIR}/generated_config.cmake)\n\n# TODO: Move the rest of this into files in ephemeral. See\n# https://github.com/flutter/flutter/issues/57146.\nset(WRAPPER_ROOT \"${EPHEMERAL_DIR}/cpp_client_wrapper\")\n\n# Set fallback configurations for older versions of the flutter tool.\nif (NOT DEFINED FLUTTER_TARGET_PLATFORM)\n  set(FLUTTER_TARGET_PLATFORM \"windows-x64\")\nendif()\n\n# === Flutter Library ===\nset(FLUTTER_LIBRARY \"${EPHEMERAL_DIR}/flutter_windows.dll\")\n\n# Published to parent scope for install step.\nset(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)\nset(FLUTTER_ICU_DATA_FILE \"${EPHEMERAL_DIR}/icudtl.dat\" PARENT_SCOPE)\nset(PROJECT_BUILD_DIR \"${PROJECT_DIR}/build/\" PARENT_SCOPE)\nset(AOT_LIBRARY \"${PROJECT_DIR}/build/windows/app.so\" PARENT_SCOPE)\n\nlist(APPEND FLUTTER_LIBRARY_HEADERS\n  \"flutter_export.h\"\n  \"flutter_windows.h\"\n  \"flutter_messenger.h\"\n  \"flutter_plugin_registrar.h\"\n  \"flutter_texture_registrar.h\"\n)\nlist(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND \"${EPHEMERAL_DIR}/\")\nadd_library(flutter INTERFACE)\ntarget_include_directories(flutter INTERFACE\n  \"${EPHEMERAL_DIR}\"\n)\ntarget_link_libraries(flutter INTERFACE \"${FLUTTER_LIBRARY}.lib\")\nadd_dependencies(flutter flutter_assemble)\n\n# === Wrapper ===\nlist(APPEND CPP_WRAPPER_SOURCES_CORE\n  \"core_implementations.cc\"\n  \"standard_codec.cc\"\n)\nlist(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND \"${WRAPPER_ROOT}/\")\nlist(APPEND CPP_WRAPPER_SOURCES_PLUGIN\n  \"plugin_registrar.cc\"\n)\nlist(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND \"${WRAPPER_ROOT}/\")\nlist(APPEND CPP_WRAPPER_SOURCES_APP\n  \"flutter_engine.cc\"\n  \"flutter_view_controller.cc\"\n)\nlist(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND \"${WRAPPER_ROOT}/\")\n\n# Wrapper sources needed for a plugin.\nadd_library(flutter_wrapper_plugin STATIC\n  ${CPP_WRAPPER_SOURCES_CORE}\n  ${CPP_WRAPPER_SOURCES_PLUGIN}\n)\napply_standard_settings(flutter_wrapper_plugin)\nset_target_properties(flutter_wrapper_plugin PROPERTIES\n  POSITION_INDEPENDENT_CODE ON)\nset_target_properties(flutter_wrapper_plugin PROPERTIES\n  CXX_VISIBILITY_PRESET hidden)\ntarget_link_libraries(flutter_wrapper_plugin PUBLIC flutter)\ntarget_include_directories(flutter_wrapper_plugin PUBLIC\n  \"${WRAPPER_ROOT}/include\"\n)\nadd_dependencies(flutter_wrapper_plugin flutter_assemble)\n\n# Wrapper sources needed for the runner.\nadd_library(flutter_wrapper_app STATIC\n  ${CPP_WRAPPER_SOURCES_CORE}\n  ${CPP_WRAPPER_SOURCES_APP}\n)\napply_standard_settings(flutter_wrapper_app)\ntarget_link_libraries(flutter_wrapper_app PUBLIC flutter)\ntarget_include_directories(flutter_wrapper_app PUBLIC\n  \"${WRAPPER_ROOT}/include\"\n)\nadd_dependencies(flutter_wrapper_app flutter_assemble)\n\n# === Flutter tool backend ===\n# _phony_ is a non-existent file to force this command to run every time,\n# since currently there's no way to get a full input/output list from the\n# flutter tool.\nset(PHONY_OUTPUT \"${CMAKE_CURRENT_BINARY_DIR}/_phony_\")\nset_source_files_properties(\"${PHONY_OUTPUT}\" PROPERTIES SYMBOLIC TRUE)\nadd_custom_command(\n  OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}\n    ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}\n    ${CPP_WRAPPER_SOURCES_APP}\n    ${PHONY_OUTPUT}\n  COMMAND ${CMAKE_COMMAND} -E env\n    ${FLUTTER_TOOL_ENVIRONMENT}\n    \"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat\"\n      ${FLUTTER_TARGET_PLATFORM} $<CONFIG>\n  VERBATIM\n)\nadd_custom_target(flutter_assemble DEPENDS\n  \"${FLUTTER_LIBRARY}\"\n  ${FLUTTER_LIBRARY_HEADERS}\n  ${CPP_WRAPPER_SOURCES_CORE}\n  ${CPP_WRAPPER_SOURCES_PLUGIN}\n  ${CPP_WRAPPER_SOURCES_APP}\n)\n"
  },
  {
    "path": "windows/flutter/generated_plugin_registrant.cc",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#include \"generated_plugin_registrant.h\"\n\n#include <audioplayers_windows/audioplayers_windows_plugin.h>\n#include <bitsdojo_window_windows/bitsdojo_window_plugin.h>\n#include <file_saver/file_saver_plugin.h>\n#include <flutter_localization/flutter_localization_plugin_c_api.h>\n#include <flutter_tts/flutter_tts_plugin.h>\n#include <media_kit_libs_windows_video/media_kit_libs_windows_video_plugin_c_api.h>\n#include <media_kit_video/media_kit_video_plugin_c_api.h>\n#include <record_windows/record_windows_plugin_c_api.h>\n#include <screen_brightness_windows/screen_brightness_windows_plugin.h>\n#include <share_plus/share_plus_windows_plugin_c_api.h>\n#include <url_launcher_windows/url_launcher_windows.h>\n\nvoid RegisterPlugins(flutter::PluginRegistry* registry) {\n  AudioplayersWindowsPluginRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"AudioplayersWindowsPlugin\"));\n  BitsdojoWindowPluginRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"BitsdojoWindowPlugin\"));\n  FileSaverPluginRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"FileSaverPlugin\"));\n  FlutterLocalizationPluginCApiRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"FlutterLocalizationPluginCApi\"));\n  FlutterTtsPluginRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"FlutterTtsPlugin\"));\n  MediaKitLibsWindowsVideoPluginCApiRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"MediaKitLibsWindowsVideoPluginCApi\"));\n  MediaKitVideoPluginCApiRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"MediaKitVideoPluginCApi\"));\n  RecordWindowsPluginCApiRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"RecordWindowsPluginCApi\"));\n  ScreenBrightnessWindowsPluginRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"ScreenBrightnessWindowsPlugin\"));\n  SharePlusWindowsPluginCApiRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"SharePlusWindowsPluginCApi\"));\n  UrlLauncherWindowsRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"UrlLauncherWindows\"));\n}\n"
  },
  {
    "path": "windows/flutter/generated_plugin_registrant.h",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#ifndef GENERATED_PLUGIN_REGISTRANT_\n#define GENERATED_PLUGIN_REGISTRANT_\n\n#include <flutter/plugin_registry.h>\n\n// Registers Flutter plugins.\nvoid RegisterPlugins(flutter::PluginRegistry* registry);\n\n#endif  // GENERATED_PLUGIN_REGISTRANT_\n"
  },
  {
    "path": "windows/flutter/generated_plugins.cmake",
    "content": "#\n# Generated file, do not edit.\n#\n\nlist(APPEND FLUTTER_PLUGIN_LIST\n  audioplayers_windows\n  bitsdojo_window_windows\n  file_saver\n  flutter_localization\n  flutter_tts\n  media_kit_libs_windows_video\n  media_kit_video\n  record_windows\n  screen_brightness_windows\n  share_plus\n  url_launcher_windows\n)\n\nlist(APPEND FLUTTER_FFI_PLUGIN_LIST\n  media_kit_native_event_loop\n)\n\nset(PLUGIN_BUNDLED_LIBRARIES)\n\nforeach(plugin ${FLUTTER_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})\n  target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})\nendforeach(plugin)\n\nforeach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})\nendforeach(ffi_plugin)\n"
  },
  {
    "path": "windows/runner/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14)\nproject(runner LANGUAGES CXX)\n\n# Define the application target. To change its name, change BINARY_NAME in the\n# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer\n# work.\n#\n# Any new source files that you add to the application should be added here.\nadd_executable(${BINARY_NAME} WIN32\n  \"flutter_window.cpp\"\n  \"main.cpp\"\n  \"utils.cpp\"\n  \"win32_window.cpp\"\n  \"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc\"\n  \"Runner.rc\"\n  \"runner.exe.manifest\"\n)\n\n# Apply the standard set of build settings. This can be removed for applications\n# that need different build settings.\napply_standard_settings(${BINARY_NAME})\n\n# Add preprocessor definitions for the build version.\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"FLUTTER_VERSION=\\\"${FLUTTER_VERSION}\\\"\")\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}\")\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}\")\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}\")\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}\")\n\n# Disable Windows macros that collide with C++ standard library functions.\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"NOMINMAX\")\n\n# Add dependency libraries and include directories. Add any application-specific\n# dependencies here.\ntarget_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)\ntarget_link_libraries(${BINARY_NAME} PRIVATE \"dwmapi.lib\")\ntarget_include_directories(${BINARY_NAME} PRIVATE \"${CMAKE_SOURCE_DIR}\")\n\n# Run the Flutter tool portions of the build. This must not be removed.\nadd_dependencies(${BINARY_NAME} flutter_assemble)\n"
  },
  {
    "path": "windows/runner/Runner.rc",
    "content": "// Microsoft Visual C++ generated resource script.\n//\n#pragma code_page(65001)\n#include \"resource.h\"\n\n#define APSTUDIO_READONLY_SYMBOLS\n/////////////////////////////////////////////////////////////////////////////\n//\n// Generated from the TEXTINCLUDE 2 resource.\n//\n#include \"winres.h\"\n\n/////////////////////////////////////////////////////////////////////////////\n#undef APSTUDIO_READONLY_SYMBOLS\n\n/////////////////////////////////////////////////////////////////////////////\n// English (United States) resources\n\n#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\nLANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US\n\n#ifdef APSTUDIO_INVOKED\n/////////////////////////////////////////////////////////////////////////////\n//\n// TEXTINCLUDE\n//\n\n1 TEXTINCLUDE\nBEGIN\n    \"resource.h\\0\"\nEND\n\n2 TEXTINCLUDE\nBEGIN\n    \"#include \"\"winres.h\"\"\\r\\n\"\n    \"\\0\"\nEND\n\n3 TEXTINCLUDE\nBEGIN\n    \"\\r\\n\"\n    \"\\0\"\nEND\n\n#endif    // APSTUDIO_INVOKED\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// Icon\n//\n\n// Icon with lowest ID value placed first to ensure application icon\n// remains consistent on all systems.\nIDI_APP_ICON            ICON                    \"resources\\\\app_icon.ico\"\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// Version\n//\n\n#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)\n#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD\n#else\n#define VERSION_AS_NUMBER 1,0,0,0\n#endif\n\n#if defined(FLUTTER_VERSION)\n#define VERSION_AS_STRING FLUTTER_VERSION\n#else\n#define VERSION_AS_STRING \"1.0.0\"\n#endif\n\nVS_VERSION_INFO VERSIONINFO\n FILEVERSION VERSION_AS_NUMBER\n PRODUCTVERSION VERSION_AS_NUMBER\n FILEFLAGSMASK VS_FFI_FILEFLAGSMASK\n#ifdef _DEBUG\n FILEFLAGS VS_FF_DEBUG\n#else\n FILEFLAGS 0x0L\n#endif\n FILEOS VOS__WINDOWS32\n FILETYPE VFT_APP\n FILESUBTYPE 0x0L\nBEGIN\n    BLOCK \"StringFileInfo\"\n    BEGIN\n        BLOCK \"040904e4\"\n        BEGIN\n            VALUE \"CompanyName\", \"cc.aicode.flutter.askaide\" \"\\0\"\n            VALUE \"FileDescription\", \"askaide\" \"\\0\"\n            VALUE \"FileVersion\", VERSION_AS_STRING \"\\0\"\n            VALUE \"InternalName\", \"askaide\" \"\\0\"\n            VALUE \"LegalCopyright\", \"Copyright (C) 2023 cc.aicode.flutter.askaide. All rights reserved.\" \"\\0\"\n            VALUE \"OriginalFilename\", \"askaide.exe\" \"\\0\"\n            VALUE \"ProductName\", \"askaide\" \"\\0\"\n            VALUE \"ProductVersion\", VERSION_AS_STRING \"\\0\"\n        END\n    END\n    BLOCK \"VarFileInfo\"\n    BEGIN\n        VALUE \"Translation\", 0x409, 1252\n    END\nEND\n\n#endif    // English (United States) resources\n/////////////////////////////////////////////////////////////////////////////\n\n\n\n#ifndef APSTUDIO_INVOKED\n/////////////////////////////////////////////////////////////////////////////\n//\n// Generated from the TEXTINCLUDE 3 resource.\n//\n\n\n/////////////////////////////////////////////////////////////////////////////\n#endif    // not APSTUDIO_INVOKED\n"
  },
  {
    "path": "windows/runner/flutter_window.cpp",
    "content": "#include \"flutter_window.h\"\n\n#include <optional>\n\n#include \"flutter/generated_plugin_registrant.h\"\n\nFlutterWindow::FlutterWindow(const flutter::DartProject& project)\n    : project_(project) {}\n\nFlutterWindow::~FlutterWindow() {}\n\nbool FlutterWindow::OnCreate() {\n  if (!Win32Window::OnCreate()) {\n    return false;\n  }\n\n  RECT frame = GetClientArea();\n\n  // The size here must match the window dimensions to avoid unnecessary surface\n  // creation / destruction in the startup path.\n  flutter_controller_ = std::make_unique<flutter::FlutterViewController>(\n      frame.right - frame.left, frame.bottom - frame.top, project_);\n  // Ensure that basic setup of the controller was successful.\n  if (!flutter_controller_->engine() || !flutter_controller_->view()) {\n    return false;\n  }\n  RegisterPlugins(flutter_controller_->engine());\n  SetChildContent(flutter_controller_->view()->GetNativeWindow());\n\n  flutter_controller_->engine()->SetNextFrameCallback([&]() {\n    this->Show();\n  });\n  flutter_controller_->ForceRedraw();\n\n  return true;\n}\n\nvoid FlutterWindow::OnDestroy() {\n  if (flutter_controller_) {\n    flutter_controller_ = nullptr;\n  }\n\n  Win32Window::OnDestroy();\n}\n\nLRESULT\nFlutterWindow::MessageHandler(HWND hwnd, UINT const message,\n                              WPARAM const wparam,\n                              LPARAM const lparam) noexcept {\n  // Give Flutter, including plugins, an opportunity to handle window messages.\n  if (flutter_controller_) {\n    std::optional<LRESULT> result =\n        flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,\n                                                      lparam);\n    if (result) {\n      return *result;\n    }\n  }\n\n  switch (message) {\n    case WM_FONTCHANGE:\n      flutter_controller_->engine()->ReloadSystemFonts();\n      break;\n  }\n\n  return Win32Window::MessageHandler(hwnd, message, wparam, lparam);\n}\n"
  },
  {
    "path": "windows/runner/flutter_window.h",
    "content": "#ifndef RUNNER_FLUTTER_WINDOW_H_\n#define RUNNER_FLUTTER_WINDOW_H_\n\n#include <flutter/dart_project.h>\n#include <flutter/flutter_view_controller.h>\n\n#include <memory>\n\n#include \"win32_window.h\"\n\n// A window that does nothing but host a Flutter view.\nclass FlutterWindow : public Win32Window {\n public:\n  // Creates a new FlutterWindow hosting a Flutter view running |project|.\n  explicit FlutterWindow(const flutter::DartProject& project);\n  virtual ~FlutterWindow();\n\n protected:\n  // Win32Window:\n  bool OnCreate() override;\n  void OnDestroy() override;\n  LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,\n                         LPARAM const lparam) noexcept override;\n\n private:\n  // The project to run.\n  flutter::DartProject project_;\n\n  // The Flutter instance hosted by this window.\n  std::unique_ptr<flutter::FlutterViewController> flutter_controller_;\n};\n\n#endif  // RUNNER_FLUTTER_WINDOW_H_\n"
  },
  {
    "path": "windows/runner/main.cpp",
    "content": "#include <flutter/dart_project.h>\n#include <flutter/flutter_view_controller.h>\n#include <windows.h>\n\n#include \"flutter_window.h\"\n#include \"utils.h\"\n\n#include <bitsdojo_window_windows/bitsdojo_window_plugin.h>\n\nauto bdw = bitsdojo_window_configure(BDW_CUSTOM_FRAME | BDW_HIDE_ON_STARTUP);\n\nint APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,\n                      _In_ wchar_t *command_line, _In_ int show_command) {\n  // Attach to console when present (e.g., 'flutter run') or create a\n  // new console when running with a debugger.\n  if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {\n    CreateAndAttachConsole();\n  }\n\n  // Initialize COM, so that it is available for use in the library and/or\n  // plugins.\n  ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);\n\n  flutter::DartProject project(L\"data\");\n\n  std::vector<std::string> command_line_arguments =\n      GetCommandLineArguments();\n\n  project.set_dart_entrypoint_arguments(std::move(command_line_arguments));\n\n  FlutterWindow window(project);\n  Win32Window::Point origin(10, 10);\n  Win32Window::Size size(850, 750);\n  if (!window.Create(L\"AIdea\", origin, size)) {\n    return EXIT_FAILURE;\n  }\n  window.SetQuitOnClose(true);\n\n  ::MSG msg;\n  while (::GetMessage(&msg, nullptr, 0, 0)) {\n    ::TranslateMessage(&msg);\n    ::DispatchMessage(&msg);\n  }\n\n  ::CoUninitialize();\n  return EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "windows/runner/resource.h",
    "content": "//{{NO_DEPENDENCIES}}\n// Microsoft Visual C++ generated include file.\n// Used by Runner.rc\n//\n#define IDI_APP_ICON                    101\n\n// Next default values for new objects\n//\n#ifdef APSTUDIO_INVOKED\n#ifndef APSTUDIO_READONLY_SYMBOLS\n#define _APS_NEXT_RESOURCE_VALUE        102\n#define _APS_NEXT_COMMAND_VALUE         40001\n#define _APS_NEXT_CONTROL_VALUE         1001\n#define _APS_NEXT_SYMED_VALUE           101\n#endif\n#endif\n"
  },
  {
    "path": "windows/runner/runner.exe.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n  <application xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n    <windowsSettings>\n      <dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">PerMonitorV2</dpiAwareness>\n    </windowsSettings>\n  </application>\n  <compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\">\n    <application>\n      <!-- Windows 10 and Windows 11 -->\n      <supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\"/>\n      <!-- Windows 8.1 -->\n      <supportedOS Id=\"{1f676c76-80e1-4239-95bb-83d0f6d0da78}\"/>\n      <!-- Windows 8 -->\n      <supportedOS Id=\"{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}\"/>\n      <!-- Windows 7 -->\n      <supportedOS Id=\"{35138b9a-5d96-4fbd-8e2d-a2440225f93a}\"/>\n    </application>\n  </compatibility>\n</assembly>\n"
  },
  {
    "path": "windows/runner/utils.cpp",
    "content": "#include \"utils.h\"\n\n#include <flutter_windows.h>\n#include <io.h>\n#include <stdio.h>\n#include <windows.h>\n\n#include <iostream>\n\nvoid CreateAndAttachConsole() {\n  if (::AllocConsole()) {\n    FILE *unused;\n    if (freopen_s(&unused, \"CONOUT$\", \"w\", stdout)) {\n      _dup2(_fileno(stdout), 1);\n    }\n    if (freopen_s(&unused, \"CONOUT$\", \"w\", stderr)) {\n      _dup2(_fileno(stdout), 2);\n    }\n    std::ios::sync_with_stdio();\n    FlutterDesktopResyncOutputStreams();\n  }\n}\n\nstd::vector<std::string> GetCommandLineArguments() {\n  // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.\n  int argc;\n  wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);\n  if (argv == nullptr) {\n    return std::vector<std::string>();\n  }\n\n  std::vector<std::string> command_line_arguments;\n\n  // Skip the first argument as it's the binary name.\n  for (int i = 1; i < argc; i++) {\n    command_line_arguments.push_back(Utf8FromUtf16(argv[i]));\n  }\n\n  ::LocalFree(argv);\n\n  return command_line_arguments;\n}\n\nstd::string Utf8FromUtf16(const wchar_t* utf16_string) {\n  if (utf16_string == nullptr) {\n    return std::string();\n  }\n  int target_length = ::WideCharToMultiByte(\n      CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,\n      -1, nullptr, 0, nullptr, nullptr);\n  std::string utf8_string;\n  if (target_length == 0 || target_length > utf8_string.max_size()) {\n    return utf8_string;\n  }\n  utf8_string.resize(target_length);\n  int converted_length = ::WideCharToMultiByte(\n      CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,\n      -1, utf8_string.data(),\n      target_length, nullptr, nullptr);\n  if (converted_length == 0) {\n    return std::string();\n  }\n  return utf8_string;\n}\n"
  },
  {
    "path": "windows/runner/utils.h",
    "content": "#ifndef RUNNER_UTILS_H_\n#define RUNNER_UTILS_H_\n\n#include <string>\n#include <vector>\n\n// Creates a console for the process, and redirects stdout and stderr to\n// it for both the runner and the Flutter library.\nvoid CreateAndAttachConsole();\n\n// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string\n// encoded in UTF-8. Returns an empty std::string on failure.\nstd::string Utf8FromUtf16(const wchar_t* utf16_string);\n\n// Gets the command line arguments passed in as a std::vector<std::string>,\n// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.\nstd::vector<std::string> GetCommandLineArguments();\n\n#endif  // RUNNER_UTILS_H_\n"
  },
  {
    "path": "windows/runner/win32_window.cpp",
    "content": "#include \"win32_window.h\"\n\n#include <dwmapi.h>\n#include <flutter_windows.h>\n\n#include \"resource.h\"\n\nnamespace {\n\n/// Window attribute that enables dark mode window decorations.\n///\n/// Redefined in case the developer's machine has a Windows SDK older than\n/// version 10.0.22000.0.\n/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute\n#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE\n#define DWMWA_USE_IMMERSIVE_DARK_MODE 20\n#endif\n\nconstexpr const wchar_t kWindowClassName[] = L\"FLUTTER_RUNNER_WIN32_WINDOW\";\n\n/// Registry key for app theme preference.\n///\n/// A value of 0 indicates apps should use dark mode. A non-zero or missing\n/// value indicates apps should use light mode.\nconstexpr const wchar_t kGetPreferredBrightnessRegKey[] =\n  L\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Themes\\\\Personalize\";\nconstexpr const wchar_t kGetPreferredBrightnessRegValue[] = L\"AppsUseLightTheme\";\n\n// The number of Win32Window objects that currently exist.\nstatic int g_active_window_count = 0;\n\nusing EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);\n\n// Scale helper to convert logical scaler values to physical using passed in\n// scale factor\nint Scale(int source, double scale_factor) {\n  return static_cast<int>(source * scale_factor);\n}\n\n// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.\n// This API is only needed for PerMonitor V1 awareness mode.\nvoid EnableFullDpiSupportIfAvailable(HWND hwnd) {\n  HMODULE user32_module = LoadLibraryA(\"User32.dll\");\n  if (!user32_module) {\n    return;\n  }\n  auto enable_non_client_dpi_scaling =\n      reinterpret_cast<EnableNonClientDpiScaling*>(\n          GetProcAddress(user32_module, \"EnableNonClientDpiScaling\"));\n  if (enable_non_client_dpi_scaling != nullptr) {\n    enable_non_client_dpi_scaling(hwnd);\n  }\n  FreeLibrary(user32_module);\n}\n\n}  // namespace\n\n// Manages the Win32Window's window class registration.\nclass WindowClassRegistrar {\n public:\n  ~WindowClassRegistrar() = default;\n\n  // Returns the singleton registar instance.\n  static WindowClassRegistrar* GetInstance() {\n    if (!instance_) {\n      instance_ = new WindowClassRegistrar();\n    }\n    return instance_;\n  }\n\n  // Returns the name of the window class, registering the class if it hasn't\n  // previously been registered.\n  const wchar_t* GetWindowClass();\n\n  // Unregisters the window class. Should only be called if there are no\n  // instances of the window.\n  void UnregisterWindowClass();\n\n private:\n  WindowClassRegistrar() = default;\n\n  static WindowClassRegistrar* instance_;\n\n  bool class_registered_ = false;\n};\n\nWindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;\n\nconst wchar_t* WindowClassRegistrar::GetWindowClass() {\n  if (!class_registered_) {\n    WNDCLASS window_class{};\n    window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);\n    window_class.lpszClassName = kWindowClassName;\n    window_class.style = CS_HREDRAW | CS_VREDRAW;\n    window_class.cbClsExtra = 0;\n    window_class.cbWndExtra = 0;\n    window_class.hInstance = GetModuleHandle(nullptr);\n    window_class.hIcon =\n        LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));\n    window_class.hbrBackground = 0;\n    window_class.lpszMenuName = nullptr;\n    window_class.lpfnWndProc = Win32Window::WndProc;\n    RegisterClass(&window_class);\n    class_registered_ = true;\n  }\n  return kWindowClassName;\n}\n\nvoid WindowClassRegistrar::UnregisterWindowClass() {\n  UnregisterClass(kWindowClassName, nullptr);\n  class_registered_ = false;\n}\n\nWin32Window::Win32Window() {\n  ++g_active_window_count;\n}\n\nWin32Window::~Win32Window() {\n  --g_active_window_count;\n  Destroy();\n}\n\nbool Win32Window::Create(const std::wstring& title,\n                         const Point& origin,\n                         const Size& size) {\n  Destroy();\n\n  const wchar_t* window_class =\n      WindowClassRegistrar::GetInstance()->GetWindowClass();\n\n  const POINT target_point = {static_cast<LONG>(origin.x),\n                              static_cast<LONG>(origin.y)};\n  HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);\n  UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);\n  double scale_factor = dpi / 96.0;\n\n  HWND window = CreateWindow(\n      window_class, title.c_str(), WS_OVERLAPPEDWINDOW,\n      Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),\n      Scale(size.width, scale_factor), Scale(size.height, scale_factor),\n      nullptr, nullptr, GetModuleHandle(nullptr), this);\n\n  if (!window) {\n    return false;\n  }\n\n  UpdateTheme(window);\n\n  return OnCreate();\n}\n\nbool Win32Window::Show() {\n  return ShowWindow(window_handle_, SW_SHOWNORMAL);\n}\n\n// static\nLRESULT CALLBACK Win32Window::WndProc(HWND const window,\n                                      UINT const message,\n                                      WPARAM const wparam,\n                                      LPARAM const lparam) noexcept {\n  if (message == WM_NCCREATE) {\n    auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);\n    SetWindowLongPtr(window, GWLP_USERDATA,\n                     reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));\n\n    auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);\n    EnableFullDpiSupportIfAvailable(window);\n    that->window_handle_ = window;\n  } else if (Win32Window* that = GetThisFromHandle(window)) {\n    return that->MessageHandler(window, message, wparam, lparam);\n  }\n\n  return DefWindowProc(window, message, wparam, lparam);\n}\n\nLRESULT\nWin32Window::MessageHandler(HWND hwnd,\n                            UINT const message,\n                            WPARAM const wparam,\n                            LPARAM const lparam) noexcept {\n  switch (message) {\n    case WM_DESTROY:\n      window_handle_ = nullptr;\n      Destroy();\n      if (quit_on_close_) {\n        PostQuitMessage(0);\n      }\n      return 0;\n\n    case WM_DPICHANGED: {\n      auto newRectSize = reinterpret_cast<RECT*>(lparam);\n      LONG newWidth = newRectSize->right - newRectSize->left;\n      LONG newHeight = newRectSize->bottom - newRectSize->top;\n\n      SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,\n                   newHeight, SWP_NOZORDER | SWP_NOACTIVATE);\n\n      return 0;\n    }\n    case WM_SIZE: {\n      RECT rect = GetClientArea();\n      if (child_content_ != nullptr) {\n        // Size and position the child window.\n        MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,\n                   rect.bottom - rect.top, TRUE);\n      }\n      return 0;\n    }\n\n    case WM_ACTIVATE:\n      if (child_content_ != nullptr) {\n        SetFocus(child_content_);\n      }\n      return 0;\n\n    case WM_DWMCOLORIZATIONCOLORCHANGED:\n      UpdateTheme(hwnd);\n      return 0;\n  }\n\n  return DefWindowProc(window_handle_, message, wparam, lparam);\n}\n\nvoid Win32Window::Destroy() {\n  OnDestroy();\n\n  if (window_handle_) {\n    DestroyWindow(window_handle_);\n    window_handle_ = nullptr;\n  }\n  if (g_active_window_count == 0) {\n    WindowClassRegistrar::GetInstance()->UnregisterWindowClass();\n  }\n}\n\nWin32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {\n  return reinterpret_cast<Win32Window*>(\n      GetWindowLongPtr(window, GWLP_USERDATA));\n}\n\nvoid Win32Window::SetChildContent(HWND content) {\n  child_content_ = content;\n  SetParent(content, window_handle_);\n  RECT frame = GetClientArea();\n\n  MoveWindow(content, frame.left, frame.top, frame.right - frame.left,\n             frame.bottom - frame.top, true);\n\n  SetFocus(child_content_);\n}\n\nRECT Win32Window::GetClientArea() {\n  RECT frame;\n  GetClientRect(window_handle_, &frame);\n  return frame;\n}\n\nHWND Win32Window::GetHandle() {\n  return window_handle_;\n}\n\nvoid Win32Window::SetQuitOnClose(bool quit_on_close) {\n  quit_on_close_ = quit_on_close;\n}\n\nbool Win32Window::OnCreate() {\n  // No-op; provided for subclasses.\n  return true;\n}\n\nvoid Win32Window::OnDestroy() {\n  // No-op; provided for subclasses.\n}\n\nvoid Win32Window::UpdateTheme(HWND const window) {\n  DWORD light_mode;\n  DWORD light_mode_size = sizeof(light_mode);\n  LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,\n                               kGetPreferredBrightnessRegValue,\n                               RRF_RT_REG_DWORD, nullptr, &light_mode,\n                               &light_mode_size);\n\n  if (result == ERROR_SUCCESS) {\n    BOOL enable_dark_mode = light_mode == 0;\n    DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE,\n                          &enable_dark_mode, sizeof(enable_dark_mode));\n  }\n}\n"
  },
  {
    "path": "windows/runner/win32_window.h",
    "content": "#ifndef RUNNER_WIN32_WINDOW_H_\n#define RUNNER_WIN32_WINDOW_H_\n\n#include <windows.h>\n\n#include <functional>\n#include <memory>\n#include <string>\n\n// A class abstraction for a high DPI-aware Win32 Window. Intended to be\n// inherited from by classes that wish to specialize with custom\n// rendering and input handling\nclass Win32Window {\n public:\n  struct Point {\n    unsigned int x;\n    unsigned int y;\n    Point(unsigned int x, unsigned int y) : x(x), y(y) {}\n  };\n\n  struct Size {\n    unsigned int width;\n    unsigned int height;\n    Size(unsigned int width, unsigned int height)\n        : width(width), height(height) {}\n  };\n\n  Win32Window();\n  virtual ~Win32Window();\n\n  // Creates a win32 window with |title| that is positioned and sized using\n  // |origin| and |size|. New windows are created on the default monitor. Window\n  // sizes are specified to the OS in physical pixels, hence to ensure a\n  // consistent size this function will scale the inputted width and height as\n  // as appropriate for the default monitor. The window is invisible until\n  // |Show| is called. Returns true if the window was created successfully.\n  bool Create(const std::wstring& title, const Point& origin, const Size& size);\n\n  // Show the current window. Returns true if the window was successfully shown.\n  bool Show();\n\n  // Release OS resources associated with window.\n  void Destroy();\n\n  // Inserts |content| into the window tree.\n  void SetChildContent(HWND content);\n\n  // Returns the backing Window handle to enable clients to set icon and other\n  // window properties. Returns nullptr if the window has been destroyed.\n  HWND GetHandle();\n\n  // If true, closing this window will quit the application.\n  void SetQuitOnClose(bool quit_on_close);\n\n  // Return a RECT representing the bounds of the current client area.\n  RECT GetClientArea();\n\n protected:\n  // Processes and route salient window messages for mouse handling,\n  // size change and DPI. Delegates handling of these to member overloads that\n  // inheriting classes can handle.\n  virtual LRESULT MessageHandler(HWND window,\n                                 UINT const message,\n                                 WPARAM const wparam,\n                                 LPARAM const lparam) noexcept;\n\n  // Called when CreateAndShow is called, allowing subclass window-related\n  // setup. Subclasses should return false if setup fails.\n  virtual bool OnCreate();\n\n  // Called when Destroy is called.\n  virtual void OnDestroy();\n\n private:\n  friend class WindowClassRegistrar;\n\n  // OS callback called by message pump. Handles the WM_NCCREATE message which\n  // is passed when the non-client area is being created and enables automatic\n  // non-client DPI scaling so that the non-client area automatically\n  // responsponds to changes in DPI. All other messages are handled by\n  // MessageHandler.\n  static LRESULT CALLBACK WndProc(HWND const window,\n                                  UINT const message,\n                                  WPARAM const wparam,\n                                  LPARAM const lparam) noexcept;\n\n  // Retrieves a class instance pointer for |window|\n  static Win32Window* GetThisFromHandle(HWND const window) noexcept;\n\n  // Update the window frame's theme to match the system theme.\n  static void UpdateTheme(HWND const window);\n\n  bool quit_on_close_ = false;\n\n  // window handle for top level window.\n  HWND window_handle_ = nullptr;\n\n  // window handle for hosted content.\n  HWND child_content_ = nullptr;\n};\n\n#endif  // RUNNER_WIN32_WINDOW_H_\n"
  }
]