[
  {
    "path": ".github/scripts/process_commits.sh",
    "content": "#!/bin/bash\n\n# 主标题的 emoji 映射\nprocess_commit() {\n  local title=\"$1\"\n  case \"$title\" in\n    feat:*|feature:*) echo \"✨ ${title#*: }\" ;; # 新功能\n    fix:*) echo \"🐛 ${title#*: }\" ;; # 修复\n    docs:*) echo \"📝 ${title#*: }\" ;; # 文档\n    style:*) echo \"💄 ${title#*: }\" ;; # 样式\n    refactor:*) echo \"♻️ ${title#*: }\" ;; # 重构\n    perf:*) echo \"⚡️ ${title#*: }\" ;; # 性能\n    test:*) echo \"🧪 ${title#*: }\" ;; # 测试\n    build:*) echo \"📦 ${title#*: }\" ;; # 构建\n    ci:*) echo \"🎡 ${title#*: }\" ;; # CI\n    chore:*) echo \"🔧 ${title#*: }\" ;; # 杂项\n    revert:*) echo \"⏮️ ${title#*: }\" ;; # 回退\n    *) \n      case \"$title\" in\n        Add*|Implement*) echo \"✨ $title\" ;; \n        Enhance*|Improve*) echo \"🚀 $title\" ;; \n        Update*) echo \"⚡️ $title\" ;; \n        Integrate*|Configure*) echo \"🔌 $title\" ;; \n        Fix*|Resolve*) echo \"🐛 $title\" ;; \n        Refactor*) echo \"♻️ $title\" ;; \n        Remove*|Delete*) echo \"🔥 $title\" ;; \n        Revert*) echo \"⏮️ $title\" ;; \n        *) echo \"🔧 $title\" ;; \n      esac\n      ;;\n  esac\n}\n\n# 处理详细信息\nprocess_details() {\n  local details=\"\"\n  local in_commit_body=false\n  \n  while IFS= read -r line; do\n    if [[ $line == -* ]]; then\n      # 处理列表项\n      details+=\"$(process_detail \"$line\")\\n\"\n    elif [[ $line =~ ^These[[:space:]]changes ]]; then\n      # 处理总结行，保持原样并添加换行\n      details+=\"     $line\\n\"\n    elif [[ -n $line ]]; then\n      # 处理其他非空行（比如列表项的延续行），保持缩进\n      details+=\"     $line\\n\"\n    else\n      # 空行处理\n      details+=\"\\n\"\n    fi\n  done\n  \n  echo -e \"$details\"\n}\n\n# 详细信息的 emoji 映射\nprocess_detail() {\n  local content=\"${1:2}\" # 删除开头的 \"- \"\n  local prefix=\"     - \" # 修改缩进格式，保持列表形式\n  \n  # 1. 首先检查动词开头\n  case \"$content\" in\n    Replace*|Swap*|Change*) echo \"${prefix}🔄 $content\" && return ;;\n    Increase*|Add*|Extend*) echo \"${prefix}⬆️ $content\" && return ;;\n    Decrease*|Reduce*|Remove*) echo \"${prefix}⬇️ $content\" && return ;;\n    Update*|Refresh*) echo \"${prefix}🔁 $content\" && return ;;\n    Enhance*|Improve*) echo \"${prefix}⚡️ $content\" && return ;;\n    Create*|Generate*) echo \"${prefix}✨ $content\" && return ;;\n    Modify*|Adjust*) echo \"${prefix}🔧 $content\" && return ;;\n    Fix*|Resolve*) echo \"${prefix}🐛 $content\" && return ;;\n    Refactor*) echo \"${prefix}♻️ $content\" && return ;;\n    Implement*) echo \"${prefix}🎯 $content\" && return ;;\n    Integrate*) echo \"${prefix}🔌 $content\" && return ;;\n    Ensure*|Verify*) echo \"${prefix}✅ $content\" && return ;;\n    Develop*) echo \"${prefix}🏗️ $content\" && return ;;\n  esac\n  \n  # 2. 检查特定功能/组件组合\n  if [[ \"$content\" =~ (audio.*player|player.*audio) ]]; then\n    echo \"${prefix}🎵 $content\" # 音频播放器特定\n    return\n  fi\n  \n  if [[ \"$content\" =~ (lyric.*overlay|overlay.*lyric) ]]; then\n    echo \"${prefix}📺 $content\" # 歌词覆盖层特定\n    return\n  fi\n  \n  if [[ \"$content\" =~ (cache.*response|response.*cache) ]]; then\n    echo \"${prefix}💾 $content\" # 响应缓存特定\n    return\n  fi\n  \n  if [[ \"$content\" =~ (error.*handling|handling.*error) ]]; then\n    echo \"${prefix}🛡️ $content\" # 错误处理特定\n    return\n  fi\n  \n  # 3. 检查特定技术术语\n  if [[ \"$content\" =~ dependency.injection ]]; then\n    echo \"${prefix}💉 $content\" # 依赖注入\n    return\n  fi\n  \n  if [[ \"$content\" =~ state.management ]]; then\n    echo \"${prefix}📊 $content\" # 状态管理\n    return\n  fi\n  \n  # 4. 检查具体内容类型\n  case \"$content\" in\n    *cache*|*Cache*|*storage*) \n      echo \"${prefix}💾 $content\" ;; # 缓存/存储相关\n    *API*|*service*|*Service*|*request*) \n      echo \"${prefix}🌐 $content\" ;; # API/服务相关\n    *UI*|*Screen*|*interface*|*layout*|*visual*|*theme*) \n      echo \"${prefix}💫 $content\" ;; # UI/布局/主题相关\n    *audio*|*playback*|*media*) \n      echo \"${prefix}🎵 $content\" ;; # 音频相关\n    *test*|*Test*) \n      echo \"${prefix}🧪 $content\" ;; # 测试相关\n    *security*|*permission*|*auth*) \n      echo \"${prefix}🔒 $content\" ;; # 安全/权限相关\n    *document*|*template*|*readability*) \n      echo \"${prefix}📝 $content\" ;; # 文档相关\n    *component*|*widget*|*display*) \n      echo \"${prefix}🎨 $content\" ;; # 组件/显示相关\n    *logic*|*handling*|*management*|*dependency*) \n      echo \"${prefix}🧮 $content\" ;; # 逻辑/处理/依赖相关\n    *performance*|*efficiency*|*optimization*) \n      echo \"${prefix}⚡️ $content\" ;; # 性能相关\n    *error*|*exception*|*handling*) \n      echo \"${prefix}🛡️ $content\" ;; # 错误处理相关\n    *animation*|*transition*) \n      echo \"${prefix}✨ $content\" ;; # 动画相关\n    *network*|*connectivity*) \n      echo \"${prefix}📡 $content\" ;; # 网络相关\n    *data*|*model*|*entity*) \n      echo \"${prefix}💽 $content\" ;; # 数据模型相关\n    *button*|*control*|*interaction*) \n      echo \"${prefix}🎮 $content\" ;; # 控件/交互相关\n    *style*|*color*|*font*) \n      echo \"${prefix}🎨 $content\" ;; # 样式相关\n    *) \n      echo \"${prefix}📌 $content\" ;; # 其他细节\n  esac\n}\n\n# 主处理逻辑\ncurrent_commit=\"\"\ncommit_details=\"\"\n\nwhile IFS= read -r line; do\n  if [[ $line =~ ^[A-Za-z] ]] && [[ ! $line =~ ^These[[:space:]]changes ]]; then\n    # 如果有之前的 commit，先输出它\n    if [ -n \"$current_commit\" ]; then\n      if [ -n \"$commit_details\" ]; then\n        echo \"▶ $current_commit\"\n        echo -e \"$(process_details \"$commit_details\")\\n\"\n      else\n        echo \"  $current_commit\"\n      fi\n    fi\n    current_commit=$(process_commit \"$line\")\n    commit_details=\"\"\n  elif [[ $line == -* ]]; then\n    commit_details+=\"$line\\n\"\n  fi\ndone\n\n# 输出最后一个 commit\nif [ -n \"$current_commit\" ]; then\n  if [ -n \"$commit_details\" ]; then\n    echo \"▶ $current_commit\"\n    echo -e \"$(process_details \"$commit_details\")\"\n  else\n    echo \"  $current_commit\"\n  fi\nfi "
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build and Release\n\npermissions:\n  contents: write\n\non:\n  push:\n    tags:\n      - 'v*'  # 当推送 v 开头的tag时触发，如 v1.0.0\n  workflow_dispatch:\n\nenv:\n  FLUTTER_VERSION: '3.27.0'\n\njobs:\n  build-android:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 0  # 获取完整的 git 历史\n\n      - name: Setup Java\n        uses: actions/setup-java@v3\n        with:\n          distribution: 'zulu'\n          java-version: '17'\n\n      - name: Setup Flutter\n        uses: subosito/flutter-action@v2\n        with:\n          flutter-version: ${{ env.FLUTTER_VERSION }}\n          channel: 'stable'\n\n      - name: Get dependencies\n        run: flutter pub get\n\n      - name: Create key.properties\n        run: |\n          echo \"storePassword=${{ secrets.KEY_STORE_PASSWORD }}\" >> android/key.properties\n          echo \"keyPassword=${{ secrets.KEY_PASSWORD }}\" >> android/key.properties\n          echo \"keyAlias=${{ secrets.KEY_ALIAS }}\" >> android/key.properties\n          echo \"storeFile=upload-keystore.jks\" >> android/key.properties\n\n      - name: Create keystore file\n        run: echo \"${{ secrets.KEYSTORE_BASE64 }}\" | base64 --decode > android/app/upload-keystore.jks\n\n      - name: Build APK\n        run: flutter build apk --release\n\n      - name: Build App Bundle\n        run: flutter build appbundle --release\n\n      - name: Upload Android artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: android-build\n          path: |\n            build/app/outputs/flutter-apk/app-release.apk\n            build/app/outputs/bundle/release/app-release.aab\n\n  build-ios:\n    runs-on: macos-latest\n\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 0  # 获取完整的 git 历史\n\n      - name: Setup Flutter\n        uses: subosito/flutter-action@v2\n        with:\n          flutter-version: '3.27.0'\n          channel: 'stable'\n\n      - name: Get dependencies\n        run: flutter pub get\n\n      - name: Build iOS\n        run: flutter build ios --release --no-codesign\n\n      - name: Package IPA\n        run: |\n          cd build/ios/iphoneos/\n          mkdir Payload\n          cp -r Runner.app Payload/\n          zip -qq -r -9 app-release.ipa Payload\n\n      - name: Upload iOS artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: ios-build\n          path: build/ios/iphoneos/app-release.ipa\n\n  upload:\n    runs-on: ubuntu-latest\n    needs: [ build-android, build-ios ]\n\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 0  # 获取完整的 git 历史\n\n      - name: Download artifacts\n        uses: actions/download-artifact@v4\n        with:\n          path: ./dist/\n          merge-multiple: true\n\n      - name: List files\n        run: tree dist\n\n      - name: Prepare scripts\n        run: |\n          mkdir -p .github/scripts\n          chmod +x .github/scripts/process_commits.sh\n\n      - name: Get Previous tag\n        id: previoustag\n        run: |\n          CURRENT_TAG=${GITHUB_REF#refs/tags/}\n          PREVIOUS_TAG=$(git describe --tags --abbrev=0 ${CURRENT_TAG}^ 2>/dev/null || echo \"v0.0.0\")\n          echo \"tag=${PREVIOUS_TAG}\" >> $GITHUB_OUTPUT\n\n      - name: Generate commit messages\n        id: commits\n        run: |\n          CURRENT_TAG=${GITHUB_REF#refs/tags/}\n          PREV_TAG=${{ steps.previoustag.outputs.tag }}\n          COMMITS=$(git log ${PREV_TAG}..${CURRENT_TAG} --pretty=format:\"%s%n%b\" | .github/scripts/process_commits.sh)\n          echo \"commits<<EOF\" >> $GITHUB_OUTPUT\n          echo \"$COMMITS\" >> $GITHUB_OUTPUT\n          echo \"EOF\" >> $GITHUB_OUTPUT\n\n      - name: Create Release\n        if: startsWith(github.ref, 'refs/tags/')\n        uses: softprops/action-gh-release@v1\n        with:\n          prerelease: true\n          draft: false\n          body: |\n            ## 🚧 Pre-release Version\n            \n            ### 📋 Release Information\n            **Version:** ${{ github.ref_name }}\n            **Previous Version:** ${{ steps.previoustag.outputs.tag }}\n            **Build Environment:** Flutter ${{ env.FLUTTER_VERSION }}\n            \n            ### 📝 Changelog\n            ${{ steps.commits.outputs.commits }}\n            \n            ### 📦 Distribution\n            | File | Description | Purpose |\n            |------|-------------|----------|\n            | `.apk` | Android Package | Direct installation for testing |\n            | `.aab` | Android App Bundle | Google Play Store deployment |\n            \n            ### 🔍 Additional Notes\n            - This is a pre-release build intended for testing purposes\n            - Features and functionality may not be fully stable\n            - Not recommended for production use\n            \n            ### 📱 Compatibility\n            - Minimum Android SDK: 21 (Android 5.0)\n            - Target Android SDK: 33 (Android 13)\n            \n            > **Note:** Please report any issues or bugs through the GitHub issue tracker.\n          files: |\n            dist/flutter-apk/app-release.apk\n            dist/bundle/release/app-release.aab\n            dist/app-release.ipa\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Upload artifacts if not release\n        if: startsWith(github.ref, 'refs/tags/') == false\n        uses: actions/upload-artifact@v4\n        with:\n          name: everything\n          path: dist/\n"
  },
  {
    "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.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\n# 添加以下内容\n**/android/key.properties\n**/android/app/upload-keystore.jks\n"
  },
  {
    "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 and should not be manually edited.\n\nversion:\n  revision: \"2663184aa79047d0a33a14a3b607954f8fdd8730\"\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: 2663184aa79047d0a33a14a3b607954f8fdd8730\n      base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730\n    - platform: android\n      create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730\n      base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730\n    - platform: ios\n      create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730\n      base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730\n    - platform: linux\n      create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730\n      base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730\n    - platform: macos\n      create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730\n      base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730\n    - platform: web\n      create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730\n      base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730\n    - platform: windows\n      create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730\n      base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730\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": "LICENSE",
    "content": "# Creative Commons Attribution-NonCommercial-ShareAlike License (CC BY-NC-SA)\n\n## License Summary\n\nThis license lets others remix, tweak, and build upon your work non-commercially, as long as they credit you and license their new creations under the identical terms.\n\n## Full License\n\n### 1. You are free to:\n\n- Share — copy and redistribute the material in any medium or format.\n- Adapt — remix, transform, and build upon the material.\n\n### 2. Under the following terms:\n\n- Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.\n- NonCommercial — You may not use the material for commercial purposes.\n- ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.\n\n### 3. No additional restrictions:\n\nYou may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.\n\n### 4. Notices:\n\nYou do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation.\n\n### 5. Other rights:\n\nIn no way are any of the following rights affected by the license:\n\n- Your fair dealing or fair use rights;\n- The rights of others to use the material for their own purposes;\n- The rights of the licensor to use the material for their own purposes.\n\n### 6. Disclaimer:\n\nThis license does not grant you any rights to use the material in a way that would infringe on the rights of others.\n\nFor more information, visit [Creative Commons](https://creativecommons.org/licenses/by-nc-sa/4.0/)."
  },
  {
    "path": "README.md",
    "content": "# Yuro\n\n[English](README_en.md)\n\n一个使用 Flutter 构建的 ASMR.ONE 客户端。\n\n## 项目概述\n\nYuro 旨在通过精美的动画和现代化的用户界面，提供流畅愉悦的 ASMR 聆听体验。\n\n## 特性\n\n- 稳定的后台播放，再也不用担心杀后台了\n- 精美的动画效果\n- 流畅的播放体验\n- 简洁的UI设计\n- 全方位的智能缓存机制\n  - 图片智能缓存：优化封面加载速度，告别重复加载\n  - 字幕本地缓存：实现快速字幕匹配与加载\n  - 音频文件缓存：减少重复下载，节省流量开销\n- 为服务器减轻压力\n  - 智能的缓存策略确保资源高效利用\n  - 懒加载机制避免无效请求\n  - 合理的缓存清理机制平衡本地存储\n\n## 开发准则\n\n我们维护了一套完整的开发准则以确保代码质量和一致性：\n- [开发准则](docs/guidelines_zh.md)\n\n## 项目结构\n\n<pre>\nlib/\n├── core/                 # 核心功能\n├── data/                # 数据层\n├── domain/              # 领域层\n├── presentation/        # 表现层\n└── common/             # 通用功能\n</pre>\n\n## 开始使用\n\n1. 克隆仓库\n```bash\ngit clone [repository-url]\n```\n\n2. 安装依赖\n```bash\nflutter pub get\n```\n\n3. 运行应用\n```bash\nflutter run\n```\n\n## 功能特性\n\n- 现代化UI设计\n- 流畅的动画效果\n- ASMR 播放控制\n- 播放列表管理\n- 搜索功能\n- 收藏功能\n\n## 贡献指南\n\n在提交贡献之前，请阅读我们的[开发准则](docs/guidelines_zh.md)。\n\n## 许可证\n\n本项目采用 Creative Commons 非商业性使用-相同方式共享许可证 (CC BY-NC-SA) - 查看 [LICENSE](LICENSE) 文件了解详细信息。该许可证允许他人修改和分享您的作品，但禁止商业用途，要求保留署名，并要求对修改后的作品以相同的许可证发布。\n"
  },
  {
    "path": "README_en.md",
    "content": "# ASMR One App\n\n[中文说明](README.md)\n\nA beautiful and modern ASMR player application built with Flutter.\n\n## Project Overview\n\nASMR One App is designed to provide a smooth and enjoyable ASMR listening experience with beautiful animations and a modern user interface.\n\n## Development Guidelines\n\nWe maintain a comprehensive set of development guidelines to ensure code quality and consistency:\n- [Development Guidelines](docs/guidelines_en.md)\n\n## Project Structure\n\n<pre>\nlib/\n├── core/                 # Core functionality\n├── data/                # Data layer\n├── domain/              # Domain layer\n├── presentation/        # Presentation layer\n└── common/             # Common functionality\n</pre>\n\n## Getting Started\n\n1. Clone the repository\n```bash\ngit clone [repository-url]\n```\n\n2. Install dependencies\n```bash\nflutter pub get\n```\n\n3. Run the app\n```bash\nflutter run\n```\n\n## Features\n\n- Modern UI design\n- Smooth animations\n- ASMR playback control\n- Playlist management\n- Search functionality\n- Favorites collection\n\n## Contributing\n\nPlease read our [Development Guidelines](docs/guidelines_en.md) before making a contribution.\n\n## License\n\nThis project is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike License (CC BY-NC-SA) - see the [LICENSE](LICENSE) file for details. This license allows others to remix, tweak, and build upon your work non-commercially, as long as they credit you and license their new creations under the identical terms. "
  },
  {
    "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 https://dart.dev/lints.\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\nanalyzer:\n  exclude:\n    - \"**/*.g.dart\"\n    - \"**/*.freezed.dart\"\n  errors:\n    invalid_annotation_target: ignore"
  },
  {
    "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/to/reference-keystore\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 keystoreProperties = new Properties()\ndef keystorePropertiesFile = rootProject.file('key.properties')\nif (keystorePropertiesFile.exists()) {\n    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))\n}\n\nandroid {\n    namespace = \"one.asmr.yuro\"\n    compileSdk = flutter.compileSdkVersion\n    ndkVersion = flutter.ndkVersion\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 = \"one.asmr.yuro\"\n        // You can update the following values to match your application needs.\n        // For more information, see: https://flutter.dev/to/review-gradle-config.\n        minSdk = flutter.minSdkVersion\n        targetSdk = flutter.targetSdkVersion\n        versionCode = flutter.versionCode\n        versionName = flutter.versionName\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\n    buildTypes {\n        release {\n            signingConfig signingConfigs.release\n            // 如果还有问题，可以临时禁用混淆\n            minifyEnabled true  // 改为 false 可以禁用混淆\n            shrinkResources true  // 改为 false 可以禁用资源压缩\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\nflutter {\n    source = \"../..\"\n}\n"
  },
  {
    "path": "android/app/proguard-rules.pro",
    "content": "## Flutter wrapper\n-keep class io.flutter.app.** { *; }\n-keep class io.flutter.plugin.**  { *; }\n-keep class io.flutter.util.**  { *; }\n-keep class io.flutter.view.**  { *; }\n-keep class io.flutter.**  { *; }\n-keep class io.flutter.plugins.**  { *; }\n-keep class io.flutter.plugin.editing.** { *; }\n-dontwarn io.flutter.embedding.**\n-keepattributes Signature\n-keepattributes *Annotation*\n\n## Gson rules\n-keepattributes Signature\n-keepattributes *Annotation*\n-dontwarn sun.misc.**\n\n## audio_service plugin\n-keep class com.ryanheise.audioservice.** { *; }\n\n## Fix Play Store Split\n-keep class com.google.android.play.core.splitcompat.** { *; }\n-dontwarn com.google.android.play.core.splitcompat.SplitCompatApplication\n\n## Fix for all Android classes that might be accessed via reflection\n-keep class androidx.lifecycle.DefaultLifecycleObserver\n-keep class androidx.lifecycle.LifecycleOwner\n-keepnames class androidx.lifecycle.LifecycleOwner\n\n## Just Audio\n-keep class com.google.android.exoplayer2.** { *; }\n-dontwarn com.google.android.exoplayer2.**\n\n## Cached network image\n-keep class com.bumptech.glide.** { *; }"
  },
  {
    "path": "android/app/src/debug/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\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    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <uses-permission android:name=\"android.permission.FOREGROUND_SERVICE\"/>\n    <uses-permission android:name=\"android.permission.WAKE_LOCK\"/>\n    <uses-permission android:name=\"android.permission.SYSTEM_ALERT_WINDOW\"/>\n    <application\n        android:label=\"Yuro\"\n        android:name=\"${applicationName}\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:enableOnBackInvokedCallback=\"true\"\n        android:networkSecurityConfig=\"@xml/network_security_config\">\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        </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        <service\n          android:name=\"com.ryanheise.audioservice.AudioService\"\n          android:foregroundServiceType=\"mediaPlayback\"\n          android:exported=\"true\">\n          <intent-filter>\n            <action android:name=\"android.media.browse.MediaBrowserService\" />\n          </intent-filter>\n        </service>\n\n        <receiver\n          android:name=\"com.ryanheise.audioservice.MediaButtonReceiver\"\n          android:exported=\"true\">\n          <intent-filter>\n            <action android:name=\"android.intent.action.MEDIA_BUTTON\" />\n          </intent-filter>\n        </receiver>\n        <service\n            android:name=\".lyric.LyricOverlayService\"\n            android:enabled=\"true\"\n            android:exported=\"false\" />\n    </application>\n    <!-- Required to query activities that can process text, see:\n         https://developer.android.com/training/package-visibility and\n         https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.\n\n         In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->\n    <queries>\n        <intent>\n            <action android:name=\"android.intent.action.PROCESS_TEXT\"/>\n            <data android:mimeType=\"text/plain\"/>\n        </intent>\n    </queries>\n</manifest>\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/example/asmrapp/MainActivity.kt",
    "content": "package com.example.asmrapp\n\nimport io.flutter.embedding.android.FlutterActivity\nimport com.ryanheise.audioservice.AudioServiceActivity\n\nclass MainActivity: AudioServiceActivity()\n"
  },
  {
    "path": "android/app/src/main/kotlin/one/asmr/yuro/MainActivity.kt",
    "content": "package one.asmr.yuro\n\nimport io.flutter.embedding.android.FlutterActivity\nimport com.ryanheise.audioservice.AudioServiceActivity\nimport io.flutter.embedding.engine.FlutterEngine\nimport io.flutter.plugin.common.MethodChannel\nimport one.asmr.yuro.lyric.LyricOverlayPlugin\n\nclass MainActivity: AudioServiceActivity() {\n    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {\n        super.configureFlutterEngine(flutterEngine)\n        \n        MethodChannel(\n            flutterEngine.dartExecutor.binaryMessenger,\n            \"one.asmr.yuro/lyric_overlay\"\n        ).setMethodCallHandler(LyricOverlayPlugin(this))\n    }\n} "
  },
  {
    "path": "android/app/src/main/kotlin/one/asmr/yuro/lyric/LyricOverlayPlugin.kt",
    "content": "package one.asmr.yuro.lyric\n\nimport android.content.ComponentName\nimport android.content.Context\nimport android.content.Intent\nimport android.content.ServiceConnection\nimport android.os.IBinder\nimport io.flutter.plugin.common.MethodCall\nimport io.flutter.plugin.common.MethodChannel.MethodCallHandler\nimport io.flutter.plugin.common.MethodChannel.Result\n\nclass LyricOverlayPlugin(private val context: Context) : MethodCallHandler {\n    private var service: LyricOverlayService? = null\n    private val serviceIntent by lazy { Intent(context, LyricOverlayService::class.java) }\n    private val serviceConnection = object : ServiceConnection {\n        override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {\n            service = (binder as? LyricOverlayService.LocalBinder)?.service\n        }\n\n        override fun onServiceDisconnected(name: ComponentName?) {\n            service = null\n        }\n    }\n\n    override fun onMethodCall(call: MethodCall, result: Result) {\n        when (call.method) {\n            \"initialize\" -> {\n                try {\n                    context.startService(serviceIntent)\n                    context.bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE)\n                    result.success(null)\n                } catch (e: Exception) {\n                    result.error(\n                        \"SERVICE_START_ERROR\",\n                        e.message,\n                        e.toString()\n                    )\n                }\n            }\n            \"show\" -> {\n                service?.showLyric(\"\")\n                result.success(null)\n            }\n            \"hide\" -> {\n                service?.hideLyric()\n                result.success(null)\n            }\n            \"updateLyric\" -> {\n                val arguments = call.arguments as? Map<*, *>\n                val text = arguments?.get(\"text\") as? String ?: \"无字幕\"\n                service?.showLyric(text)\n                result.success(null)\n            }\n            \"dispose\" -> {\n                context.unbindService(serviceConnection)\n                context.stopService(serviceIntent)\n                service = null\n                result.success(null)\n            }\n            \"isShowing\" -> {\n                result.success(service?.isShowing() ?: false)\n            }\n            else -> result.notImplemented()\n        }\n    }\n} "
  },
  {
    "path": "android/app/src/main/kotlin/one/asmr/yuro/lyric/LyricOverlayService.kt",
    "content": "package one.asmr.yuro.lyric\n\nimport android.app.Service\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.PixelFormat\nimport android.os.Binder\nimport android.os.IBinder\nimport android.view.LayoutInflater\nimport android.view.MotionEvent\nimport android.view.View\nimport android.view.WindowManager\nimport android.widget.TextView\nimport one.asmr.yuro.R\nimport android.view.Gravity\n\nclass LyricOverlayService : Service() {\n    private var windowManager: WindowManager? = null\n    private var lyricView: View? = null\n    private var params: WindowManager.LayoutParams? = null\n    private var initialX = 0\n    private var initialY = 0\n    private var initialTouchX = 0f\n    private var initialTouchY = 0f\n    private val binder = LocalBinder()\n    \n    companion object {\n        private const val PREFS_NAME = \"LyricOverlayPrefs\"\n        private const val KEY_X = \"window_x\"\n        private const val KEY_Y = \"window_y\"\n        private const val KEY_SHOWING = \"is_showing\"\n    }\n    \n    inner class LocalBinder : Binder() {\n        val service: LyricOverlayService\n            get() = this@LyricOverlayService\n    }\n    \n    override fun onBind(intent: Intent?): IBinder = binder\n    \n    override fun onCreate() {\n        super.onCreate()\n        windowManager = getSystemService(WINDOW_SERVICE) as WindowManager\n    }\n    \n    fun showLyric(text: String) {\n        if (lyricView == null) {\n            createLyricView()\n        }\n        (lyricView as? TextView)?.text = text\n        getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)\n            .edit()\n            .putBoolean(KEY_SHOWING, true)\n            .apply()\n    }\n    \n    private fun createLyricView() {\n        lyricView = LayoutInflater.from(this).inflate(R.layout.lyric_overlay, null)\n        \n        // 获取屏幕高度\n        val displayMetrics = resources.displayMetrics\n        val screenHeight = displayMetrics.heightPixels\n        \n        // 读取保存的位置，默认位置设在屏幕2/3处\n        val prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)\n        val savedX = prefs.getInt(KEY_X, 50)  // 距离右边50dp\n        val savedY = prefs.getInt(KEY_Y, (screenHeight * 2 / 3))  // 屏幕高度的2/3处\n        \n        params = WindowManager.LayoutParams(\n            360.dpToPx(),\n            WindowManager.LayoutParams.WRAP_CONTENT,\n            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,\n            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or\n            WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or\n            WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,\n            PixelFormat.TRANSLUCENT\n        ).apply {\n            gravity = Gravity.TOP or Gravity.END\n            x = savedX\n            y = savedY\n            windowAnimations = 0\n        }\n        \n        lyricView?.setOnTouchListener { _, event ->\n            when (event.action) {\n                MotionEvent.ACTION_DOWN -> {\n                    initialX = params?.x ?: 0\n                    initialY = params?.y ?: 0\n                    initialTouchX = event.rawX\n                    initialTouchY = event.rawY\n                }\n                MotionEvent.ACTION_MOVE -> {\n                    val dx = (event.rawX - initialTouchX).toInt()\n                    val dy = (event.rawY - initialTouchY).toInt()\n                    \n                    params?.x = initialX - dx\n                    params?.y = initialY + dy\n                    params?.let { windowManager?.updateViewLayout(lyricView, it) }\n                }\n                MotionEvent.ACTION_UP -> {\n                    // 保存新位置\n                    params?.let { params ->\n                        getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)\n                            .edit()\n                            .putInt(KEY_X, params.x)\n                            .putInt(KEY_Y, params.y)\n                            .apply()\n                    }\n                }\n            }\n            true\n        }\n        \n        windowManager?.addView(lyricView, params)\n    }\n    \n    private fun Int.dpToPx(): Int {\n        val scale = resources.displayMetrics.density\n        return (this * scale + 0.5f).toInt()\n    }\n    \n    fun hideLyric() {\n        try {\n            if (lyricView != null) {\n                windowManager?.removeView(lyricView)\n                lyricView = null\n                getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)\n                    .edit()\n                    .putBoolean(KEY_SHOWING, false)\n                    .apply()\n            }\n        } catch (e: Exception) {\n            e.printStackTrace()\n        }\n    }\n    \n    override fun onDestroy() {\n        super.onDestroy()\n        hideLyric()\n    }\n    \n    fun isShowing(): Boolean {\n        if (lyricView == null) {\n            return getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)\n                .getBoolean(KEY_SHOWING, false)\n        }\n        return true\n    }\n} "
  },
  {
    "path": "android/app/src/main/res/drawable/launch_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@android:color/white\" />\n\n    <!-- You can insert your own image assets here -->\n    <!-- <item>\n        <bitmap\n            android:gravity=\"center\"\n            android:src=\"@mipmap/launch_image\" />\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<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"?android:colorBackground\" />\n\n    <!-- You can insert your own image assets here -->\n    <!-- <item>\n        <bitmap\n            android:gravity=\"center\"\n            android:src=\"@mipmap/launch_image\" />\n    </item> -->\n</layer-list>\n"
  },
  {
    "path": "android/app/src/main/res/layout/lyric_overlay.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<TextView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/lyric_text\"\n    android:layout_width=\"360dp\"\n    android:layout_height=\"wrap_content\"\n    android:padding=\"12dp\"\n    android:gravity=\"center_horizontal\"\n    android:background=\"#33000000\"\n    android:textColor=\"#FFFFFF\"\n    android:textSize=\"20sp\"\n    android:fontFamily=\"sans-serif-medium\"\n    android:letterSpacing=\"0.05\"\n    android:elevation=\"4dp\"/> "
  },
  {
    "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=\"@android:style/Theme.Light.NoTitleBar\">\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    </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/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=\"@android:style/Theme.Black.NoTitleBar\">\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    </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/xml/network_security_config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<network-security-config>\n    <domain-config cleartextTrafficPermitted=\"true\">\n        <domain includeSubdomains=\"false\">127.0.0.1</domain>\n    </domain-config>\n</network-security-config> "
  },
  {
    "path": "android/app/src/profile/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\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\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"
  },
  {
    "path": "android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.3-all.zip\n"
  },
  {
    "path": "android/gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError\nandroid.useAndroidX=true\nandroid.enableJetifier=true\n"
  },
  {
    "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.1.0\" apply false\n    id \"org.jetbrains.kotlin.android\" version \"1.8.22\" apply false\n}\n\ninclude \":app\"\n"
  },
  {
    "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": "docs/architecture.md",
    "content": "# ASMR Music App 架构设计\n\n## 目录结构\n\n<pre>\nlib/\n├── main.dart              # 应用程序入口\n├── screens/              # 页面\n│   ├── home_screen.dart   # 主页(音乐列表)\n│   ├── player_screen.dart # 播放页面\n│   └── detail_screen.dart # 详情页面\n├── widgets/              # 可重用组件\n│   └── drawer_menu.dart   # 侧滑菜单\n└── models/              # 数据模型(待添加)\n    └── music.dart        # 音乐模型(待添加)\n</pre>\n\n## 主要功能模块\n\n1. 主页 (HomeScreen)\n   - 显示音乐列表\n   - 搜索功能\n   - 侧滑菜单访问\n\n2. 播放页 (PlayerScreen)\n   - 音乐播放控制\n   - 进度条\n   - 音量控制\n\n3. 详情页 (DetailScreen)\n   - 显示音乐详细信息\n   - 评论功能(待实现)\n   - 收藏功能(待实现)\n\n4. 侧滑菜单 (DrawerMenu)\n   - 主页导航\n   - 收藏列表\n   - 设置页面\n\n## 技术栈\n\n- Flutter SDK\n- Material Design 3\n- 路由管理: Flutter 内置导航\n- 状态管理: 待定\n\n## 开发计划\n\n1. 第一阶段：基础框架搭建\n   - [x] 创建基本页面结构\n   - [x] 实现页面导航\n   - [x] 设计侧滑菜单\n\n2. 第二阶段：UI 实现\n   - [ ] 设计并实现音乐列表\n   - [ ] 设计并实现播放器界面\n   - [ ] 设计并实现详情页面\n\n3. 第三阶段：功能实现\n   - [ ] 音乐播放功能\n   - [ ] 搜索功能\n   - [ ] 收藏功能\n\n4. 第四阶段：优化\n   - [ ] 性能优化\n   - [ ] UI/UX 改进\n   - [ ] 代码重构\n\n## 注意事项\n\n1. 代码规范\n   - 使用 const 构造函数\n   - 遵循 Flutter 官方代码风格\n   - 添加必要的代码注释\n\n2. 性能考虑\n   - 合理使用 StatelessWidget 和 StatefulWidget\n   - 避免不必要的重建\n   - 图片资源优化\n\n3. 用户体验\n   - 添加加载状态提示\n   - 错误处理和提示\n   - 合理的动画过渡\n</pre>"
  },
  {
    "path": "docs/audio_architecture.md",
    "content": "# ASMR One App 音频播放架构设计\n\n## 1. 架构概述\n\n本文档描述了 ASMR One App 音频播放功能的架构设计。遵循 Clean Architecture 原则，采用事件驱动架构，将音频播放功能分为核心层、数据层和表现层。\n\n## 2. 目录结构\n\n<pre>\nlib/\n├── core/\n│   └── audio/                      # 音频核心功能\n│       ├── audio_player_service.dart    # 音频服务实现\n│       ├── i_audio_player_service.dart  # 音频服务接口\n│       ├── controllers/                 # 控制器\n│       │   └── playback_controller.dart # 播放控制器\n│       ├── events/                      # 事件系统\n│       │   ├── playback_event.dart     # 事件定义\n│       │   └── playback_event_hub.dart # 事件中心\n│       ├── models/                      # 数据模型\n│       │   ├── audio_track_info.dart   # 音轨信息\n│       │   └── playback_context.dart   # 播放上下文\n│       ├── notification/                # 通知栏\n│       │   └── audio_notification_service.dart\n│       ├── state/                       # 状态管理\n│       │   └── playback_state_manager.dart\n│       ├── storage/                     # 状态持久化\n│       │   ├── i_playback_state_repository.dart\n│       │   └── playback_state_repository.dart\n│       └── utils/                       # 工具类\n│           ├── audio_error_handler.dart\n│           ├── playlist_builder.dart\n│           └── track_info_creator.dart\n└── presentation/\n    └── viewmodels/\n        └── player_viewmodel.dart   # 播放器视图模型\n</pre>\n\n## 3. 核心组件设计\n\n### 3.1 音频服务接口 (IAudioPlayerService)\n\n<pre>\nabstract class IAudioPlayerService {\n  // 基础播放控制\n  Future<void> pause();\n  Future<void> resume();\n  Future<void> stop();\n  Future<void> seek(Duration position);\n  Future<void> previous();\n  Future<void> next();\n  \n  // 上下文管理\n  Future<void> playWithContext(PlaybackContext context);\n  \n  // 状态访问\n  AudioTrackInfo? get currentTrack;\n  PlaybackContext? get currentContext;\n  \n  // 状态持久化\n  Future<void> savePlaybackState();\n  Future<void> restorePlaybackState();\n}\n</pre>\n\n### 3.2 事件系统 (PlaybackEventHub)\n\n<pre>\nclass PlaybackEventHub {\n  // 主事件流\n  final _eventSubject = PublishSubject<PlaybackEvent>();\n  \n  // 分类事件流\n  late final Stream<PlaybackStateEvent> playbackState;\n  late final Stream<TrackChangeEvent> trackChange;\n  late final Stream<PlaybackContextEvent> contextChange;\n  late final Stream<PlaybackProgressEvent> playbackProgress;\n  late final Stream<PlaybackErrorEvent> errors;\n  \n  void emit(PlaybackEvent event);\n}\n</pre>\n\n## 4. 事件模型\n\n### 4.1 播放事件 (PlaybackEvent)\n\n<pre>\nabstract class PlaybackEvent {}\n\nclass PlaybackStateEvent extends PlaybackEvent {\n  final PlayerState state;\n  final Duration position;\n  final Duration? duration;\n}\n\nclass TrackChangeEvent extends PlaybackEvent {\n  final AudioTrackInfo track;\n  final Child file;\n  final Work work;\n}\n\n// ... 其他事件定义\n</pre>\n\n## 5. 状态管理\n\n### 5.1 播放状态管理器 (PlaybackStateManager)\n\n<pre>\nclass PlaybackStateManager {\n  final AudioPlayer _player;\n  final PlaybackEventHub _eventHub;\n  final IPlaybackStateRepository _stateRepository;\n  \n  void initStateListeners();\n  void updateContext(PlaybackContext? context);\n  void updateTrackInfo(AudioTrackInfo track);\n  Future<void> saveState();\n  Future<PlaybackState?> loadState();\n}\n</pre>\n\n## 6. 通知栏集成\n\n### 6.1 通知栏服务 (AudioNotificationService)\n\n<pre>\nclass AudioNotificationService {\n  final AudioPlayer _player;\n  final PlaybackEventHub _eventHub;\n  AudioHandler? _audioHandler;\n  \n  Future<void> init();\n  void updateMetadata(AudioTrackInfo trackInfo);\n}\n</pre>\n\n## 7. 技术实现细节\n\n### 7.1 依赖注入\n\n使用 GetIt 进行依赖管理：\n- PlaybackEventHub 注册为单例\n- AudioPlayerService 注册为懒加载单例\n- 所有依赖通过构造函数注入\n\n### 7.2 事件驱动\n\n- 使用 RxDart 实现事件流\n- 统一的事件中心管理所有播放相关事件\n- 各组件通过事件通信，降低耦合\n\n### 7.3 错误处理\n\n- 统一的错误处理机制\n- 错误事件通过 EventHub 传递\n- 支持错误追踪和日志记录\n\n## 8. 开发计划\n\n1. 优化播放体验\n   - 优化事件处理性能\n   - 完善错误处理机制\n   - 改进状态同步逻辑\n\n2. 增强功能\n   - 添加播放列表功能\n   - 支持更多播放模式\n   - 优化缓存策略\n\n3. 改进架构\n   - 进一步解耦组件\n   - 优化依赖注入\n   - 完善单元测试\n</pre>"
  },
  {
    "path": "docs/guidelines.md",
    "content": "# ASMR Music App Development Guidelines\n# ASMR Music App 开发准则\n\n## Important Notice | 重要说明\n\nThese guidelines are living documents that will evolve with the project. Any changes in practice must be reflected in these guidelines, especially the architecture section which must stay consistent with the actual project structure.\n\n本准则是一个动态文档，会随项目发展而演进。实践中的任何变更都必须更新到本准则中，特别是架构部分必须与实际项目结构保持一致。\n\n## 1. Architecture Design Guidelines | 架构设计准则\n\n### 1.1 Decoupling Principles | 解耦原则\n- Follow SOLID principles strictly | 严格遵循 SOLID 原则\n- Use dependency injection | 使用依赖注入管理组件依赖\n- Implement BLoC pattern | 采用 BLoC 模式分离业务逻辑和UI\n- Define interfaces for inter-module communication | 使用接口定义模块间通信契约\n\n### 1.2 Modularization Principles | 模块化原则\n- Divide modules by functionality | 按功能划分模块\n- Follow single responsibility principle | 遵循单一职责原则\n- Clear interface communication | 模块间通过清晰的接口通信\n- Share common components | 共享组件放置在 common/shared 目录下\n\n### 1.3 Code Organization | 代码组织\n<pre>\nlib/\n├── core/                 # Core functionality | 核心功能\n│   ├── di/              # Dependency injection | 依赖注入\n│   ├── theme/           # Theme configuration | 主题配置\n│   └── utils/           # Utilities | 工具类\n├── data/                # Data layer | 数据层\n│   ├── models/          # Data models | 数据模型\n│   ├── repositories/    # Data repositories | 数据仓库\n│   └── services/        # Service implementations | 服务实现\n├── domain/              # Domain layer | 领域层\n│   ├── entities/        # Business entities | 业务实体\n│   └── repositories/    # Repository interfaces | 仓库接口\n├── presentation/        # Presentation layer | 表现层\n│   ├── blocs/          # State management | 状态管理\n│   ├── screens/        # Pages | 页面\n│   └── widgets/        # Components | 组件\n└── common/             # Common functionality | 通用功能\n    ├── constants/      # Constants | 常量\n    └── extensions/     # Extensions | 扩展方法\n</pre>\n\n## 2. UI/UX Design Guidelines | UI/UX 设计准则\n\n### 2.1 Interface Design | 界面设计\n- Follow Material Design 3 | 遵循 Material Design 3 设计规范\n- Consistent color theme and typography | 使用一致的颜色主题和字体\n- Maintain visual hierarchy | 保持视觉层次感和空间布局的平衡\n- Pixel-perfect alignment | 注重细节，保持像素级别的对齐\n\n### 2.2 Animation Effects | 动画效果\n- Use Flutter's built-in animation system | 使用 Flutter 内置动画系统\n- Animation duration: 200-300ms | 所有动画持续时间保持在 200-300ms 之间\n- Use curve animations (Curves.easeInOut) | 使用曲线动画\n- Smooth page transitions | 实现平滑的页面转场效果\n- Meaningful micro-interactions | 添加有意义的微交互动画\n\n### 2.3 Performance Optimization | 性能优化\n- Use const constructors | 使用 const 构造器\n- Proper use of StatelessWidget | 合理使用 StatelessWidget\n- Avoid complex calculations in build | 避免在 build 方法中进行复杂计算\n- Use ListView.builder for long lists | 使用 ListView.builder 处理长列表\n- Image compression and caching | 图片资源进行适当压缩和缓存\n\n## 3. Code Quality Guidelines | 代码质量准则\n\n### 3.1 Code Style | 代码风格\n- Follow Dart style guide | 遵循 Dart 官方代码风格指南\n- Use dartfmt | 使用 dartfmt 格式化代码\n- Type safety | 类型安全，避��使用 dynamic\n- Proper documentation | 添加必要的注释，特别是复杂业务逻辑\n\n### 3.2 Testing Standards | 测试规范\n- Unit test coverage > 80% | 单元测试覆盖率要求 > 80%\n- Widget testing | 编写 Widget 测试验证UI行为\n- Integration testing | 集成测试覆盖关键业务流程\n- Dependency isolation | 使用 mock 进行依赖隔离\n\n## 4. Version Control Guidelines | 版本控制准则\n\n### 4.1 Git Standards | Git 规范\n- Feature Branch workflow | 使用 Feature Branch 工作流\n- Angular commit convention | commit 信息遵循 Angular 规范\n- Regular code reviews | 定期进行代码审查\n- Stable main branch | 保持 main 分支稳定可用\n\n### 4.2 Release Standards | 发布规范\n- Semantic versioning | 遵循语义化版本控制\n- Clear changelog | 每次发布都要有清晰的更新日志\n- Complete testing before release | 重要版本发布前进行完整测试\n- Documentation updates | 保留每个版本的文档更新\n\n## 5. Project Management Guidelines | 项目管理准则\n\n### 5.1 Documentation Management | 文档管理\n- API documentation | 及时更新 API 文档\n- Clear README | 维护清晰的 README\n- Design decisions | 记录重要的设计决策\n- User and developer guides | 编写用户指南和开发指南\n\n### 5.2 Issue Tracking | 问题追踪\n- Track bugs and features | 使用 Issue 跟踪 bug 和新功能\n- Proper labeling | 为每个 Issue 添加适当的标签\n- Task traceability | 保持任务的可追踪性\n- Regular status updates | 定期回顾和更新任务状态 "
  },
  {
    "path": "docs/guidelines_en.md",
    "content": "# ASMR One App Development Guidelines\n\n[中文版本](guidelines_zh.md)\n\n## Important Notice\n\nThese guidelines are living documents that will evolve with the project. Any changes in practice must be reflected in these guidelines, especially the architecture section which must stay consistent with the actual project structure.\n\n## 1. Architecture Design Guidelines\n\n### 1.1 Decoupling Principles\n- Follow SOLID principles strictly\n- Use dependency injection\n- Implement BLoC pattern\n- Define interfaces for inter-module communication\n\n### 1.2 Modularization Principles\n- Divide modules by functionality\n- Follow single responsibility principle\n- Clear interface communication\n- Share common components\n\n### 1.3 Code Organization\n<pre>\nlib/\n├── core/                 # Core functionality\n│   ├── di/              # Dependency injection\n│   ├── theme/           # Theme configuration\n│   └── utils/           # Utilities\n├── data/                # Data layer\n│   ├── models/          # Data models\n│   ├── repositories/    # Data repositories\n│   └── services/        # Service implementations\n├── domain/              # Domain layer\n│   ├── entities/        # Business entities\n│   └── repositories/    # Repository interfaces\n├── presentation/        # Presentation layer\n│   ├── blocs/          # State management\n│   ├── screens/        # Pages\n│   └── widgets/        # Components\n└── common/             # Common functionality\n    ├── constants/      # Constants definitions\n    └── extensions/     # Extensions\n</pre>\n\n### 1.4 String Management\n- All text strings must be centrally defined in `lib/common/constants/strings.dart`\n- No hardcoded strings allowed in the code\n- String constants should be grouped by feature modules\n- Prepared for future internationalization\n- String names should clearly express their purpose\n\nExample:\n```dart\nclass Strings {\n  // App\n  static const String appName = 'asmr.one';\n  \n  // Common\n  static const String loading = 'Loading...';\n  \n  // Feature specific\n  static const String search = 'Search';\n}\n```\n\n## 2. UI/UX Design Guidelines\n\n### 2.1 Interface Design\n- Follow Material Design 3\n- Consistent color theme and typography\n- Maintain visual hierarchy\n- Pixel-perfect alignment\n\n### 2.2 Animation Effects\n- Use Flutter's built-in animation system\n- Animation duration: 200-300ms\n- Use curve animations (Curves.easeInOut)\n- Smooth page transitions\n- Meaningful micro-interactions\n\n### 2.3 Performance Optimization\n- Use const constructors\n- Proper use of StatelessWidget\n- Avoid complex calculations in build\n- Use ListView.builder for long lists\n- Image compression and caching\n\n## 3. Code Quality Guidelines\n\n### 3.1 Code Style\n- Follow Dart style guide\n- Use dartfmt\n- Type safety\n- Proper documentation\n\n### 3.2 Testing Standards\n- Unit test coverage > 80%\n- Widget testing\n- Integration testing\n- Dependency isolation\n\n## 4. Version Control Guidelines\n\n### 4.1 Git Standards\n- Feature Branch workflow\n- Angular commit convention\n- Regular code reviews\n- Stable main branch\n\n### 4.2 Release Standards\n- Semantic versioning\n- Clear changelog\n- Complete testing before release\n- Documentation updates\n\n## 5. Project Management Guidelines\n\n### 5.1 Documentation Management\n- API documentation\n- Clear README\n- Design decisions\n- User and developer guides\n\n### 5.2 Issue Tracking\n- Track bugs and features\n- Proper labeling\n- Task traceability\n- Regular status updates "
  },
  {
    "path": "docs/guidelines_zh.md",
    "content": "# ASMR One App 开发准则\n\n[English Version](guidelines_en.md)\n\n## 重要说明\n\n本准则是一个动态文档，会随项目发展而演进。实践中的任何变更都必须更新到本准则中，特别是架构部分必须与实际项目结构保持一致。\n\n## 1. 架构设计准则\n\n### 1.1 解耦原则\n- 严格遵循 SOLID 原则\n- 使用依赖注入管理组件依赖\n- 采用 BLoC 模式分离业务逻辑和UI\n- 使用接口定义模块间通信契约\n\n### 1.2 模块化原则\n- 按功能划分模块\n- 遵循单一职责原则\n- 模块间通过清晰的接口通信\n- 共享组件放置在 common/shared 目录下\n\n### 1.3 代码组织\n<pre>\nlib/\n├── core/                 # 核心功能\n│   ├── di/              # 依赖注入\n│   ├── theme/           # 主题配置\n│   └── utils/           # 工具类\n├── data/                # 数据层\n│   ├── models/          # 数据模型\n│   ├── repositories/    # 数据仓库\n│   └── services/        # 服务实现\n├── domain/              # 领域层\n│   ├── entities/        # 业务实体\n│   └── repositories/    # 仓库接口\n├── presentation/        # 表现层\n│   ├── blocs/          # 状态管理\n│   ├── screens/        # 页面\n│   └── widgets/        # 组件\n└── common/             # 通用功能\n    ├── constants/      # 常量定义\n    └── extensions/     # 扩展方法\n</pre>\n\n### 1.4 字符串管理\n- 所有文本字符串必须在 `lib/common/constants/strings.dart` 中集中定义\n- 禁止在代码中使用硬编码的字符串\n- 字符串常量按功能模块分组管理\n- 为后续国际化做好准备\n- 字符串命名应清晰表达其用途\n\n示例：\n```dart\nclass Strings {\n  // App\n  static const String appName = 'asmr.one';\n  \n  // Common\n  static const String loading = '加载中...';\n  \n  // Feature specific\n  static const String search = '搜索';\n}\n```\n\n## 2. UI/UX 设计准则\n\n### 2.1 界面设计\n- 遵循 Material Design 3 设计规范\n- 使用一致的颜色主题和字体\n- 保持视觉层次感和空间布局的平衡\n- 注重细节，保持像素级别的对齐\n\n### 2.2 动画效果\n- 使用 Flutter 内置动画系统\n- 所有动画持续时间保持在 200-300ms 之间\n- 使用曲线动画（推荐 Curves.easeInOut）\n- 实现平滑的页面转场效果\n- 添加有意义的微交互动画\n\n### 2.3 性能优化\n- 使用 const 构造器\n- 合理使用 StatelessWidget\n- 避免在 build 方法中进行复杂计算\n- 使用 ListView.builder 处理长列表\n- 图片资源进行适当压缩和缓存\n\n## 3. 代码质量准则\n\n### 3.1 代码风格\n- 遵循 Dart 官方代码风格指南\n- 使用 dartfmt 格式化代码\n- 类型安全，避免使用 dynamic\n- 添加必要的注释，特别是复杂业务逻辑\n\n### 3.2 测试规范\n- 单元测试覆盖率要求 > 80%\n- 编写 Widget 测试验证UI行为\n- 集成测试覆盖关键业务流程\n- 使用 mock 进行依赖隔离\n\n## 4. 版本控制准则\n\n### 4.1 Git 规范\n- 使用 Feature Branch 工作流\n- commit 信息遵循 Angular 规范\n- 定期进行代码审查\n- 保持 main 分支稳定可用\n\n### 4.2 发布规范\n- 遵循语义化版本控制\n- 每次发布都要有清晰的更新日志\n- 重要版本发布前进行完整测试\n- 保留���个版本的文档更新\n\n## 5. 项目管理准则\n\n### 5.1 文档管理\n- 及时更新 API 文档\n- 维护清晰的 README\n- 记录重要的设计决策\n- 编写用户指南和开发指南\n\n### 5.2 问题追踪\n- 使用 Issue 跟踪 bug 和新功能\n- 为每个 Issue 添加适当的标签\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\n# platform :ios, '12.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__))\n  target 'RunnerTests' do\n    inherit! :search_paths\n  end\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 Flutter\nimport UIKit\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": "{\n  \"images\" : [\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-40x40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-40x40@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-60x60@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-60x60@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-20x20@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-29x29@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-29x29@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-40x40@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-40x40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-76x76@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-76x76@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"83.5x83.5\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-83.5x83.5@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"1024x1024\",\n      \"idiom\" : \"ios-marketing\",\n      \"filename\" : \"Icon-App-1024x1024@1x.png\",\n      \"scale\" : \"1x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\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 opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" image=\"LaunchImage\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"YRO-k0-Ey4\">\n                            </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=\"centerX\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerX\" id=\"1a2-6s-vTC\"/>\n                            <constraint firstItem=\"YRO-k0-Ey4\" firstAttribute=\"centerY\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerY\" id=\"4X2-HB-R7a\"/>\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=\"168\" height=\"185\"/>\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<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>Asmrapp</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</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>asmrapp</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>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(FLUTTER_BUILD_NUMBER)</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>CADisableMinimumFrameDurationOnPhone</key>\n\t<true/>\n\t<key>UIApplicationSupportsIndirectInputEvents</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Runner/Runner-Bridging-Header.h",
    "content": "#import \"GeneratedPluginRegistrant.h\"\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\t331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };\n\t\t3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };\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\t9B54596A1AA247DB213D70D9 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DB3CD38BE4BEDD7984A4C53 /* Pods_RunnerTests.framework */; };\n\t\tFA9547A1CDFA8BA9285E4F0E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D991BFF7F7E1123F21EC9991 /* Pods_Runner.framework */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\t331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 97C146E61CF9000F007C117D /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 97C146ED1CF9000F007C117D;\n\t\t\tremoteInfo = Runner;\n\t\t};\n/* End PBXContainerItemProxy 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\t1864816EC7CE99BF3A03B425 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-RunnerTests.release.xcconfig\"; path = \"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = \"<group>\"; };\n\t\t331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; 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\t3BD433FFFD3DA36C18DC04D5 /* 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\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\t7B17DE0D71E08E74104A6DA7 /* 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\t8DB3CD38BE4BEDD7984A4C53 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };\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\t9D845508FF1BDF0DBEC753A3 /* 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\tA315059E1F405C4EDE197F3C /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-RunnerTests.profile.xcconfig\"; path = \"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tA7561FF68870DE952DF640B5 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-RunnerTests.debug.xcconfig\"; path = \"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tD991BFF7F7E1123F21EC9991 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };\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\tFA9547A1CDFA8BA9285E4F0E /* Pods_Runner.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tC30863FD7117B3791F210A0B /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t9B54596A1AA247DB213D70D9 /* Pods_RunnerTests.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\t331C8082294A63A400263BE5 /* RunnerTests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t331C807B294A618700263BE5 /* RunnerTests.swift */,\n\t\t\t);\n\t\t\tpath = RunnerTests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t575ABBC37CDFC79162ECE7F7 /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t7B17DE0D71E08E74104A6DA7 /* Pods-Runner.debug.xcconfig */,\n\t\t\t\t9D845508FF1BDF0DBEC753A3 /* Pods-Runner.release.xcconfig */,\n\t\t\t\t3BD433FFFD3DA36C18DC04D5 /* Pods-Runner.profile.xcconfig */,\n\t\t\t\tA7561FF68870DE952DF640B5 /* Pods-RunnerTests.debug.xcconfig */,\n\t\t\t\t1864816EC7CE99BF3A03B425 /* Pods-RunnerTests.release.xcconfig */,\n\t\t\t\tA315059E1F405C4EDE197F3C /* Pods-RunnerTests.profile.xcconfig */,\n\t\t\t);\n\t\t\tname = Pods;\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t78A3B507541DF7DDED4343BF /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD991BFF7F7E1123F21EC9991 /* Pods_Runner.framework */,\n\t\t\t\t8DB3CD38BE4BEDD7984A4C53 /* Pods_RunnerTests.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\t331C8082294A63A400263BE5 /* RunnerTests */,\n\t\t\t\t575ABBC37CDFC79162ECE7F7 /* Pods */,\n\t\t\t\t78A3B507541DF7DDED4343BF /* 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\t331C8081294A63A400263BE5 /* RunnerTests.xctest */,\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\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/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t331C8080294A63A400263BE5 /* RunnerTests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget \"RunnerTests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t60B65332F5596392DBC91AC5 /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t331C807D294A63A400263BE5 /* Sources */,\n\t\t\t\t331C807F294A63A400263BE5 /* Resources */,\n\t\t\t\tC30863FD7117B3791F210A0B /* Frameworks */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t331C8086294A63A400263BE5 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = RunnerTests;\n\t\t\tproductName = RunnerTests;\n\t\t\tproductReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\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\t4C22EBC450685F3AE387A893 /* [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\tB62C690B77C8F1A1692748AD /* [CP] Embed Pods Frameworks */,\n\t\t\t\tAE562F71A72CF708023D2129 /* [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\tBuildIndependentTargetsInParallel = YES;\n\t\t\t\tLastUpgradeCheck = 1510;\n\t\t\t\tORGANIZATIONNAME = \"\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t331C8080294A63A400263BE5 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 14.0;\n\t\t\t\t\t\tTestTargetID = 97C146ED1CF9000F007C117D;\n\t\t\t\t\t};\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\t331C8080294A63A400263BE5 /* RunnerTests */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t331C807F294A63A400263BE5 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\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\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\t4C22EBC450685F3AE387A893 /* [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\t60B65332F5596392DBC91AC5 /* [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-RunnerTests-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\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\t\tAE562F71A72CF708023D2129 /* [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\tB62C690B77C8F1A1692748AD /* [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/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t331C807D294A63A400263BE5 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\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 PBXTargetDependency section */\n\t\t331C8086294A63A400263BE5 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 97C146ED1CF9000F007C117D /* Runner */;\n\t\t\ttargetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency 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\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\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\tENABLE_USER_SCRIPT_SANDBOXING = NO;\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 = 12.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\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tENABLE_BITCODE = NO;\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\tPRODUCT_BUNDLE_IDENTIFIER = com.example.asmrapp;\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\t331C8088294A63A400263BE5 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = A7561FF68870DE952DF640B5 /* Pods-RunnerTests.debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.example.asmrapp.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t331C8089294A63A400263BE5 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 1864816EC7CE99BF3A03B425 /* Pods-RunnerTests.release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.example.asmrapp.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t331C808A294A63A400263BE5 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = A315059E1F405C4EDE197F3C /* Pods-RunnerTests.profile.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.example.asmrapp.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner\";\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\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;\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\tENABLE_USER_SCRIPT_SANDBOXING = NO;\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 = 12.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\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;\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\tENABLE_USER_SCRIPT_SANDBOXING = NO;\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 = 12.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\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tENABLE_BITCODE = NO;\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\tPRODUCT_BUNDLE_IDENTIFIER = com.example.asmrapp;\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\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tENABLE_BITCODE = NO;\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\tPRODUCT_BUNDLE_IDENTIFIER = com.example.asmrapp;\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\t331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget \"RunnerTests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t331C8088294A63A400263BE5 /* Debug */,\n\t\t\t\t331C8089294A63A400263BE5 /* Release */,\n\t\t\t\t331C808A294A63A400263BE5 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\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         <TestableReference\n            skipped = \"NO\"\n            parallelizable = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"331C8080294A63A400263BE5\"\n               BuildableName = \"RunnerTests.xctest\"\n               BlueprintName = \"RunnerTests\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\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": "ios/RunnerTests/RunnerTests.swift",
    "content": "import Flutter\nimport UIKit\nimport XCTest\n\nclass RunnerTests: XCTestCase {\n\n  func testExample() {\n    // If you add code to the Runner application, consider adding tests here.\n    // See https://developer.apple.com/documentation/xctest for more information about using XCTest.\n  }\n\n}\n"
  },
  {
    "path": "lib/common/constants/strings.dart",
    "content": "class Strings {\n  // App\n  static const String appName = 'asmr.one';\n\n  // Common\n  static const String loading = '加载中...';\n  static const String error = '出错了';\n  static const String retry = '重试';\n  static const String cancel = '取消';\n  static const String confirm = '确认';\n\n  // Home\n  static const String search = '搜索';\n  static const String musicList = '音乐列表将在这里显示';\n\n  // Player\n  static const String nowPlaying = '正在播放';\n  static const String playerPlaceholder = '播放器控件将在这里显示';\n\n  // Detail\n  static const String detail = '音乐详情';\n  static const String detailPlaceholder = '音乐详细信息将在这里显示';\n\n  // Drawer\n  static const String home = '主页';\n  static const String favorites = '我的收藏';\n  static const String settings = '设置';\n}\n"
  },
  {
    "path": "lib/core/audio/README.md",
    "content": "# 音频核心功能\n\n## 当前架构\n\n### 1. 事件驱动系统\n- 基于 RxDart 的事件中心\n- 统一的事件定义和处理\n- 支持事件过滤和转换\n\n### 2. 核心服务 (AudioPlayerService)\n- 实现 IAudioPlayerService 接口\n- 通过依赖注入管理依赖\n- 负责协调各个组件\n\n### 3. 状态管理\n- PlaybackStateManager 负责状态维护\n- 通过 EventHub 发送状态更新\n- 支持状态持久化\n\n### 4. 通知栏集成\n- 基于 audio_service 包\n- 响应系统媒体控制\n- 支持后台播放\n\n### 5. 依赖注入\n通过 GetIt 管理所有依赖：\n<pre>\nvoid setupServiceLocator() {\n  // 注册 EventHub\n  getIt.registerLazySingleton(() => PlaybackEventHub());\n  \n  // 注册音频服务\n  getIt.registerLazySingleton<IAudioPlayerService>(\n    () => AudioPlayerService(\n      eventHub: getIt(),\n      stateRepository: getIt(),\n    ),\n  );\n}\n</pre>\n\n## 注意事项\n\n- 所有状态更新通过 EventHub 传递\n- 避免组件间直接调用\n- 优先使用依赖注入\n- 保持组件职责单一\n "
  },
  {
    "path": "lib/core/audio/audio_player_handler.dart",
    "content": "import 'package:asmrapp/core/audio/events/playback_event_hub.dart';\nimport 'package:audio_service/audio_service.dart';\nimport 'package:just_audio/just_audio.dart';\nimport 'package:asmrapp/utils/logger.dart';\n\nclass AudioPlayerHandler extends BaseAudioHandler {\n  final AudioPlayer _player;\n  final PlaybackEventHub _eventHub;\n\n  AudioPlayerHandler(this._player, this._eventHub) {\n    AppLogger.debug('AudioPlayerHandler 初始化');\n    \n    // 改为监听 EventHub\n    _eventHub.playbackState.listen((event) {\n      final state = PlaybackState(\n        controls: [\n          MediaControl.skipToPrevious,\n          event.state.playing ? MediaControl.pause : MediaControl.play,\n          MediaControl.skipToNext,\n        ],\n        systemActions: const {\n          MediaAction.seek,\n          MediaAction.seekForward,\n          MediaAction.seekBackward,\n        },\n        androidCompactActionIndices: const [0, 1, 2],\n        processingState: const {\n          ProcessingState.idle: AudioProcessingState.idle,\n          ProcessingState.loading: AudioProcessingState.loading,\n          ProcessingState.buffering: AudioProcessingState.buffering,\n          ProcessingState.ready: AudioProcessingState.ready,\n          ProcessingState.completed: AudioProcessingState.completed,\n        }[event.state.processingState]!,\n        playing: event.state.playing,\n        updatePosition: event.position,\n        bufferedPosition: _player.bufferedPosition,\n        speed: _player.speed,\n        queueIndex: 0,\n      );\n      playbackState.add(state);\n    });\n  }\n\n  @override\n  Future<void> play() async {\n    AppLogger.debug('AudioHandler: 播放命令');\n    _player.play();\n  }\n\n  @override\n  Future<void> pause() async {\n    AppLogger.debug('AudioHandler: 暂停命令');\n    _player.pause();\n  }\n\n  @override\n  Future<void> seek(Duration position) async {\n    AppLogger.debug('AudioHandler: 跳转命令 position=$position');\n    await _player.seek(position);\n  }\n\n  @override\n  Future<void> stop() async {\n    AppLogger.debug('AudioHandler: 停止命令');\n    await _player.stop();\n  }\n}\n"
  },
  {
    "path": "lib/core/audio/audio_player_service.dart",
    "content": "import 'dart:async';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:just_audio/just_audio.dart';\nimport 'package:audio_session/audio_session.dart';\nimport './i_audio_player_service.dart';\nimport './models/audio_track_info.dart';\nimport './models/playback_context.dart';\nimport './notification/audio_notification_service.dart';\nimport './storage/i_playback_state_repository.dart';\nimport './utils/audio_error_handler.dart';\nimport './state/playback_state_manager.dart';\nimport './controllers/playback_controller.dart';\nimport './events/playback_event_hub.dart';\n\nclass AudioPlayerService implements IAudioPlayerService {\n  late final AudioPlayer _player;\n  late final AudioNotificationService _notificationService;\n  late final ConcatenatingAudioSource _playlist;\n  late final PlaybackStateManager _stateManager;\n  late final PlaybackController _playbackController;\n  final PlaybackEventHub _eventHub;\n  final IPlaybackStateRepository _stateRepository;\n\n  AudioPlayerService._internal({\n    required PlaybackEventHub eventHub,\n    required IPlaybackStateRepository stateRepository,\n  }) : _eventHub = eventHub,\n       _stateRepository = stateRepository {\n    _init();\n  }\n\n  static AudioPlayerService? _instance;\n  \n  factory AudioPlayerService({\n    required PlaybackEventHub eventHub,\n    required IPlaybackStateRepository stateRepository,\n  }) {\n    _instance ??= AudioPlayerService._internal(\n      eventHub: eventHub,\n      stateRepository: stateRepository,\n    );\n    return _instance!;\n  }\n\n  Future<void> _init() async {\n    try {\n      _player = AudioPlayer();\n      _notificationService = AudioNotificationService(\n        _player,\n        _eventHub,\n      );\n      _playlist = ConcatenatingAudioSource(children: []);\n\n      _stateManager = PlaybackStateManager(\n        player: _player,\n        stateRepository: _stateRepository,\n        eventHub: _eventHub,\n      );\n\n      _playbackController = PlaybackController(\n        player: _player,\n        stateManager: _stateManager,\n        playlist: _playlist,\n      );\n\n      final session = await AudioSession.instance;\n      await session.configure(const AudioSessionConfiguration.music());\n      await _notificationService.init();\n\n      _stateManager.initStateListeners();\n      await restorePlaybackState();\n    } catch (e, stack) {\n      AudioErrorHandler.handleError(\n        AudioErrorType.init,\n        '音频播放器初始化',\n        e,\n        stack,\n      );\n      AudioErrorHandler.throwError(\n        AudioErrorType.init,\n        '音频播放器初始化',\n        e,\n      );\n    }\n  }\n\n  // 基础播放控制\n  @override\n  Future<void> pause() => _playbackController.pause();\n\n  @override\n  Future<void> resume() => _playbackController.play();\n\n  @override\n  Future<void> stop() async {\n    await _playbackController.stop();\n    _stateManager.clearState();\n  }\n\n  @override\n  Future<void> seek(Duration position) => _playbackController.seek(position);\n\n  @override\n  Future<void> previous() => _playbackController.previous();\n\n  @override\n  Future<void> next() => _playbackController.next();\n\n  // 上下文管理\n  @override\n  Future<void> playWithContext(PlaybackContext context) async {\n    await _playbackController.setPlaybackContext(context);\n    // 添加自动播放\n    await resume();\n  }\n\n  // 状态访问\n  @override\n  AudioTrackInfo? get currentTrack => _stateManager.currentTrack;\n\n  @override\n  PlaybackContext? get currentContext => _stateManager.currentContext;\n\n  // 状态持久化\n  @override\n  Future<void> savePlaybackState() => _stateManager.saveState();\n\n  @override\n  Future<void> restorePlaybackState() async {\n    try {\n      AppLogger.debug('开始恢复播放状态');\n      final state = await _stateManager.loadState();\n      \n      if (state == null) {\n        AppLogger.debug('没有可恢复的播放状态');\n        return;\n      }\n\n      AppLogger.debug('已加载保存的状态: workId=${state.work.id}');\n      AppLogger.debug('播放列表信息: 长度=${state.playlist.length}, 索引=${state.currentIndex}');\n\n      if (state.playlist.isEmpty) {\n        AppLogger.debug('保存的播放列表为空，跳过恢复');\n        return;\n      }\n\n      final context = PlaybackContext(\n        work: state.work,\n        files: state.files,\n        currentFile: state.currentFile,\n        playMode: state.playMode,\n      );\n\n      try {\n        await _playbackController.setPlaybackContext(\n          context,\n          initialPosition: Duration(milliseconds: state.position),\n        );\n        AppLogger.debug('播放状态恢复成功');\n      } catch (e) {\n        AppLogger.error('设置播放上下文失败，跳过状态恢复', e);\n      }\n    } catch (e, stack) {\n      AudioErrorHandler.handleError(\n        AudioErrorType.init,\n        '恢复播放状态',\n        e,\n        stack,\n      );\n      rethrow;\n    }\n  }\n\n  @override\n  Future<void> dispose() async {\n    _player.dispose();\n    _notificationService.dispose();\n  }\n}\n"
  },
  {
    "path": "lib/core/audio/audio_service.dart",
    "content": "import 'package:just_audio/just_audio.dart';\n\nabstract class AudioService {\n  Future<void> play(String url);\n  Future<void> pause();\n  Future<void> resume();\n  Future<void> stop();\n  Future<void> dispose();\n\n  Stream<PlayerState> get playerState;\n}\n"
  },
  {
    "path": "lib/core/audio/cache/audio_cache_manager.dart",
    "content": "import 'dart:io';\nimport 'package:path_provider/path_provider.dart';\nimport 'package:crypto/crypto.dart';\nimport 'dart:convert';\nimport 'package:just_audio/just_audio.dart';\nimport 'package:asmrapp/utils/logger.dart';\n\n/// 音频缓存管理器\n/// 负责管理音频文件的缓存,对外隐藏具体的缓存实现\nclass AudioCacheManager {\n  static const int _maxCacheSize = 1024 * 1024 * 1024; // 总缓存限制 1024MB\n  static const Duration _cacheExpiration = Duration(days: 30);\n\n  /// 创建音频源\n  /// 内部处理缓存逻辑,对外只返回 AudioSource\n  static Future<AudioSource> createAudioSource(String url) async {\n    try {\n      final cacheFile = await _getCacheFile(url);\n      final fileName = _generateFileName(url);\n      AppLogger.debug('准备创建音频源 - URL: $url, 缓存文件名: $fileName');\n      \n      // 检查缓存文件是否存在且有效\n      final isValid = await _isCacheValid(cacheFile, fileName);\n      \n      if (isValid) {\n        AppLogger.debug('[$fileName] 使用已有缓存文件');\n        return _createCachingSource(url, cacheFile);\n      }\n\n      AppLogger.debug('[$fileName] 创建新的缓存源');\n      return _createCachingSource(url, cacheFile);\n      \n    } catch (e) {\n      AppLogger.error('创建缓存音频源失败,使用非缓存源', e);\n      return ProgressiveAudioSource(Uri.parse(url));\n    }\n  }\n\n  /// 清理过期和超量的缓存\n  static Future<void> cleanCache() async {\n    try {\n      final cacheDir = await _getCacheDir();\n      final files = await cacheDir.list().toList();\n      \n      // 按修改时间排序\n      files.sort((a, b) {\n        return a.statSync().modified.compareTo(b.statSync().modified);\n      });\n\n      var totalSize = 0;\n      for (var file in files) {\n        if (file is File) {\n          final stat = await file.stat();\n          \n          // 检查是否过期\n          if (DateTime.now().difference(stat.modified) > _cacheExpiration) {\n            await file.delete();\n            continue;\n          }\n\n          totalSize += stat.size;\n          \n          // 如果总大小超过限制,删除最旧的文件\n          if (totalSize > _maxCacheSize) {\n            await file.delete();\n          }\n        }\n      }\n    } catch (e) {\n      AppLogger.error('清理缓存失败', e);\n    }\n  }\n\n  /// 获取缓存大小\n  static Future<int> getCacheSize() async {\n    try {\n      final cacheDir = await _getCacheDir();\n      final files = await cacheDir.list().toList();\n      \n      var totalSize = 0;\n      for (var file in files) {\n        if (file is File) {\n          totalSize += (await file.stat()).size;\n        }\n      }\n      return totalSize;\n    } catch (e) {\n      AppLogger.error('获取缓存大小失败', e);\n      return 0;\n    }\n  }\n\n  // 私有方法\n\n  /// 创建缓存音频源\n  static AudioSource _createCachingSource(String url, File cacheFile) {\n    return LockCachingAudioSource(\n      Uri.parse(url),\n      cacheFile: cacheFile\n    );\n  }\n\n  /// 检查缓存是否有效\n  static Future<bool> _isCacheValid(File cacheFile, String fileName) async {\n    final exists = await cacheFile.exists();\n    if (!exists) {\n      AppLogger.debug('[$fileName] 缓存验证: 文件不存在');\n      return false;\n    }\n\n    try {\n      final stat = await cacheFile.stat();\n      final size = stat.size;\n      final age = DateTime.now().difference(stat.modified);\n      \n      AppLogger.debug('[$fileName] 缓存验证: 大小=${size}bytes, 年龄=$age');\n      \n      // 移除单个文件大小检查，只保留过期检查\n      if (age > _cacheExpiration) {\n        AppLogger.debug('[$fileName] 缓存无效: 文件过期 ($age > $_cacheExpiration)');\n        await cacheFile.delete();\n        return false;\n      }\n\n      AppLogger.debug('[$fileName] 缓存验证: 有效');\n      return true;\n    } catch (e) {\n      AppLogger.error('[$fileName] 检查缓存有效性失败', e);\n      return false;\n    }\n  }\n\n  /// 获取缓存文件\n  static Future<File> _getCacheFile(String url) async {\n    final cacheDir = await _getCacheDir();\n    final fileName = _generateFileName(url);\n    return File('${cacheDir.path}/$fileName');\n  }\n\n  /// 生成缓存文件名\n  static String _generateFileName(String url) {\n    final bytes = utf8.encode(url);\n    final digest = md5.convert(bytes);\n    return digest.toString();\n  }\n\n  /// 获取缓存目录\n  static Future<Directory> _getCacheDir() async {\n    final cacheDir = await getTemporaryDirectory();\n    final audioCacheDir = Directory('${cacheDir.path}/audio_cache');\n    if (!await audioCacheDir.exists()) {\n      await audioCacheDir.create(recursive: true);\n    }\n    return audioCacheDir;\n  }\n}"
  },
  {
    "path": "lib/core/audio/controllers/playback_controller.dart",
    "content": "import 'package:asmrapp/utils/logger.dart';\nimport 'package:just_audio/just_audio.dart';\nimport '../models/playback_context.dart';\nimport '../state/playback_state_manager.dart';\nimport '../utils/playlist_builder.dart';\nimport '../utils/audio_error_handler.dart';\nimport 'package:asmrapp/data/models/files/child.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\n\n\nclass PlaybackController {\n  final AudioPlayer _player;\n  final PlaybackStateManager _stateManager;\n  final ConcatenatingAudioSource _playlist;\n\n  PlaybackController({\n    required AudioPlayer player,\n    required PlaybackStateManager stateManager,\n    required ConcatenatingAudioSource playlist,\n  }) : _player = player,\n       _stateManager = stateManager,\n       _playlist = playlist;\n\n  // 基础播放控制\n  Future<void> play() => _player.play();\n  Future<void> pause() => _player.pause();\n  Future<void> stop() => _player.stop();\n  Future<void> seek(Duration position, {int? index}) => _player.seek(position, index: index);\n  \n  // 播放列表控制\n  Future<void> next() async {\n    try {\n      AppLogger.debug('尝试切换下一曲');\n      if (_stateManager.currentContext == null) {\n        AppLogger.debug('当前上下文为空，无法切换下一曲');\n        return;\n      }\n\n      if (_player.hasNext) {\n        AppLogger.debug('执行切换到下一曲');\n        await _player.seekToNext();\n      } else {\n        AppLogger.debug('没有下一曲可切换');\n      }\n    } catch (e, stack) {\n      AppLogger.error('切换下一曲失败', e, stack);\n      AudioErrorHandler.handleError(\n        AudioErrorType.playback,\n        '切换下一曲',\n        e,\n        stack,\n      );\n    }\n  }\n\n  Future<void> previous() async {\n    try {\n      AppLogger.debug('尝试切换上一曲');\n      if (_stateManager.currentContext == null) {\n        AppLogger.debug('当前上下文为空，无法切换上一曲');\n        return;\n      }\n\n      if (_player.hasPrevious) {\n        final previousFile = _stateManager.currentContext!.getPreviousFile();\n        AppLogger.debug('获取到上一个文件: ${previousFile?.title}');\n        if (previousFile != null) {\n          _updateTrackAndContext(\n            previousFile,\n            _stateManager.currentContext!.work\n          );\n          AppLogger.debug('执行切换到上一曲');\n          await _player.seekToPrevious();\n        }\n      } else {\n        AppLogger.debug('没有上一曲可切换');\n      }\n    } catch (e, stack) {\n      AppLogger.error('切换上一曲失败', e, stack);\n      AudioErrorHandler.handleError(\n        AudioErrorType.playback,\n        '切换上一曲',\n        e,\n        stack,\n      );\n    }\n  }\n\n  // 播放上下文设置\n  Future<void> setPlaybackContext(PlaybackContext context, {Duration? initialPosition}) async {\n    try {\n      AppLogger.debug('准备设置播放上下文: workId=${context.work.id}, file=${context.currentFile.title}');\n      AppLogger.debug('播放列表状态: 长度=${context.playlist.length}, 当前索引=${context.currentIndex}');\n      \n      // 验证上下文\n      try {\n        context.validate();\n      } catch (e) {\n        AppLogger.error('播放上下文验证失败', e);\n        rethrow;\n      }\n      \n      // 1. 先停止当前播放\n      AppLogger.debug('停止当前播放');\n      await _player.stop();\n      \n      // 2. 等待播放器就绪\n      AppLogger.debug('暂停播放器');\n      await _player.pause();\n      \n      // 3. 更新上下文\n      AppLogger.debug('更新播放上下文');\n      _stateManager.updateContext(context);\n      \n      // 4. 设置新的播放源\n      AppLogger.debug('设置播放源: 初始位置=${initialPosition?.inMilliseconds}ms');\n      try {\n        await PlaylistBuilder.setPlaylistSource(\n          player: _player,\n          playlist: _playlist,\n          files: context.playlist,\n          initialIndex: context.currentIndex,\n          initialPosition: initialPosition ?? Duration.zero,\n        );\n      } catch (e, stack) {\n        AppLogger.error('设置播放源失败', e, stack);\n        rethrow;\n      }\n\n      // 5. 等待播放器准备完成\n      // 删掉，会导致播放器索引回到0\n      // AppLogger.debug('等待播放器加载');\n      // await _player.load();\n      \n      // 6. 更新轨道信息\n      AppLogger.debug('更新轨道信息');\n      _updateTrackAndContext(context.currentFile, context.work);\n      \n      AppLogger.debug('播放上下文设置完成');\n    } catch (e, stack) {\n      AppLogger.error('设置播放上下文失败', e, stack);\n      AudioErrorHandler.handleError(\n        AudioErrorType.context,\n        '设置播放上下文',\n        e,\n        stack,\n      );\n      rethrow;\n    }\n  }\n\n  // 私有辅助方法\n  void _updateTrackAndContext(Child file, Work work) {\n    AppLogger.debug('更新轨道和上下文: file=${file.title}');\n    _stateManager.updateTrackAndContext(file, work);\n  }\n} "
  },
  {
    "path": "lib/core/audio/events/playback_event.dart",
    "content": "import 'package:just_audio/just_audio.dart';\nimport '../models/audio_track_info.dart';\nimport '../models/playback_context.dart';\nimport 'package:asmrapp/data/models/files/child.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\n\n/// 播放事件基类\nabstract class PlaybackEvent {}\n\n/// 播放状态事件\nclass PlaybackStateEvent extends PlaybackEvent {\n  final PlayerState state;\n  final Duration position;\n  final Duration? duration;\n  PlaybackStateEvent(this.state, this.position, this.duration);\n}\n\n/// 播放上下文事件\nclass PlaybackContextEvent extends PlaybackEvent {\n  final PlaybackContext context;\n  PlaybackContextEvent(this.context);\n}\n\n/// 音轨变更事件\nclass TrackChangeEvent extends PlaybackEvent {\n  final AudioTrackInfo track;\n  final Child file;\n  final Work work;\n  TrackChangeEvent(this.track, this.file, this.work);\n}\n\n/// 播放错误事件\nclass PlaybackErrorEvent extends PlaybackEvent {\n  final String operation;\n  final dynamic error;\n  final StackTrace? stackTrace;\n  PlaybackErrorEvent(this.operation, this.error, [this.stackTrace]);\n}\n\n/// 播放完成事件\nclass PlaybackCompletedEvent extends PlaybackEvent {\n  final PlaybackContext context;\n  PlaybackCompletedEvent(this.context);\n}\n\n/// 播放进度事件\nclass PlaybackProgressEvent extends PlaybackEvent {\n  final Duration position;\n  final Duration? bufferedPosition;\n  PlaybackProgressEvent(this.position, this.bufferedPosition);\n}\n\n/// 添加初始状态相关事件\nclass RequestInitialStateEvent extends PlaybackEvent {}\n\nclass InitialStateEvent extends PlaybackEvent {\n  final AudioTrackInfo? track;\n  final PlaybackContext? context;\n  InitialStateEvent(this.track, this.context);\n}"
  },
  {
    "path": "lib/core/audio/events/playback_event_hub.dart",
    "content": "import 'package:rxdart/rxdart.dart';\nimport './playback_event.dart';\n\nclass PlaybackEventHub {\n  // 统一的事件流，处理所有类型的事件\n  final _eventSubject = PublishSubject<PlaybackEvent>();\n\n  // 分类后的特定事件流\n  late final Stream<PlaybackStateEvent> playbackState = _eventSubject\n      .whereType<PlaybackStateEvent>()\n      .distinct();\n      \n  late final Stream<TrackChangeEvent> trackChange = _eventSubject\n      .whereType<TrackChangeEvent>();\n      \n  late final Stream<PlaybackContextEvent> contextChange = _eventSubject\n      .whereType<PlaybackContextEvent>();\n      \n  late final Stream<PlaybackProgressEvent> playbackProgress = _eventSubject\n      .whereType<PlaybackProgressEvent>()\n      .distinct((prev, next) => prev.position == next.position);\n      \n  late final Stream<PlaybackErrorEvent> errors = _eventSubject\n      .whereType<PlaybackErrorEvent>();\n\n  // 添加新的事件流\n  late final Stream<InitialStateEvent> initialState = _eventSubject\n      .whereType<InitialStateEvent>();\n      \n  late final Stream<RequestInitialStateEvent> requestInitialState = _eventSubject\n      .whereType<RequestInitialStateEvent>();\n\n  // 发送事件\n  void emit(PlaybackEvent event) => _eventSubject.add(event);\n\n  // 资源释放\n  void dispose() => _eventSubject.close();\n} "
  },
  {
    "path": "lib/core/audio/i_audio_player_service.dart",
    "content": "import './models/audio_track_info.dart';\nimport './models/playback_context.dart';\n\nabstract class IAudioPlayerService {\n  // 基础播放控制\n  Future<void> pause();\n  Future<void> resume();\n  Future<void> stop();\n  Future<void> seek(Duration position);\n  Future<void> previous();\n  Future<void> next();\n  Future<void> dispose();\n\n  // 上下文管理\n  Future<void> playWithContext(PlaybackContext context);\n  \n  // 状态访问\n  AudioTrackInfo? get currentTrack;\n  PlaybackContext? get currentContext;\n\n  // 状态持久化\n  Future<void> savePlaybackState();\n  Future<void> restorePlaybackState();\n}\n"
  },
  {
    "path": "lib/core/audio/models/audio_track_info.dart",
    "content": "class AudioTrackInfo {\n  final String title;\n  final String artist;\n  final String coverUrl;\n  final String url;\n  final Duration? duration;\n\n  AudioTrackInfo({\n    required this.title,\n    required this.artist,\n    required this.coverUrl,\n    required this.url,\n    this.duration,\n  });\n}\n"
  },
  {
    "path": "lib/core/audio/models/file_path.dart",
    "content": "import 'package:asmrapp/data/models/files/files.dart';\nimport 'package:asmrapp/data/models/files/child.dart';\nimport 'package:asmrapp/utils/logger.dart';\n\n/// 文件路径工具类\n/// 用于在文件树中定位文件和获取同级文件\nclass FilePath {\n  static const separator = '/';\n\n  /// 获取文件的完整路径\n  /// 返回类似 /folder1/folder2/file.mp3 的路径\n  static String? getPath(Child targetFile, Files root) {\n    AppLogger.debug('开始查找文件路径: ${targetFile.title}');\n    final segments = _findPathSegments(root.children, targetFile);\n    \n    if (segments == null) {\n      AppLogger.debug('未找到文件路径');\n      return null;\n    }\n\n    final path = separator + segments.join(separator);\n    AppLogger.debug('找到文件路径: $path');\n    return path;\n  }\n\n  /// 递归查找文件路径段\n  static List<String>? _findPathSegments(List<Child>? children, Child targetFile, [List<String> currentPath = const []]) {\n    if (children == null) return null;\n\n    for (final child in children) {\n      if (child.title == targetFile.title && \n          child.mediaDownloadUrl == targetFile.mediaDownloadUrl && \n          child.type == targetFile.type &&\n          child.size == targetFile.size) {  // size 作为额外验证\n        return [...currentPath, child.title!];\n      }\n\n      if (child.type == 'folder' && child.children != null) {\n        final result = _findPathSegments(\n          child.children, \n          targetFile, \n          [...currentPath, child.title!]\n        );\n        if (result != null) return result;\n      }\n    }\n\n    return null;\n  }\n\n  /// 获取同级文件列表\n  /// 返回与目标文件在同一目录下的所有文件\n  static List<Child> getSiblings(Child targetFile, Files root) {\n    AppLogger.debug('开始获取同级文件: ${targetFile.title}');\n    \n    // 获取目标文件的路径\n    final path = getPath(targetFile, root);\n    if (path == null) {\n      AppLogger.debug('无法获取文件路径，返回空列表');\n      return [];\n    }\n\n    // 获取父目录路径\n    final lastSeparator = path.lastIndexOf(separator);\n    final parentPath = lastSeparator > 0 ? path.substring(0, lastSeparator) : separator;\n    AppLogger.debug('父目录路径: $parentPath');\n\n    // 查找父目录内容\n    List<Child>? siblings;\n    if (parentPath == separator) {\n      // 如果是根目录，直接使用 root.children\n      AppLogger.debug('文件位于根目录，使用根目录文件列表');\n      siblings = root.children;\n    } else {\n      // 否则查找父目录\n      siblings = _findDirectoryByPath(root.children, parentPath);\n    }\n\n    if (siblings == null) {\n      AppLogger.debug('未找到父目录内容，返回空列表');\n      return [];\n    }\n\n    AppLogger.debug('找到同级文件数量: ${siblings.length}');\n    return siblings;\n  }\n\n  /// 根据路径查找目录内容\n  static List<Child>? _findDirectoryByPath(List<Child>? children, String path) {\n    if (children == null || path.isEmpty) return null;\n\n    // 如果是根路径，直接返回\n    if (path == separator) return children;\n\n    // 分割路径\n    final segments = path.split(separator)\n      ..removeWhere((s) => s.isEmpty);\n    \n    List<Child>? current = children;\n    \n    // 逐级查找目录\n    for (final segment in segments) {\n      final nextDir = current?.firstWhere(\n        (child) => child.title == segment && child.type == 'folder',\n        orElse: () => Child(),\n      );\n      \n      if (nextDir?.title == null) return null;\n      current = nextDir?.children;\n    }\n\n    return current;\n  }\n\n  /// 查找第一个包含音频文件的目录路径\n  /// 返回从根目录到目标目录的完整路径数组\n  static List<String>? findFirstAudioFolderPath(\n    List<Child>? children, {\n    List<String> formats = const ['.mp3', '.wav'],\n  }) {\n    if (children == null) return null;\n\n    List<String>? audioFolderPath;\n    \n    void findPath(Child folder, List<String> currentPath) {\n      if (audioFolderPath != null) return;\n\n      if (folder.children != null) {\n        // 首先检查当前��录是否直接包含音频文件\n        bool hasDirectAudio = folder.children!.any((child) {\n          if (child.type != 'folder') {\n            final fileName = child.title?.toLowerCase() ?? '';\n            return formats.any((format) => fileName.endsWith(format));\n          }\n          return false;\n        });\n\n        // 如果当前目录包含音频文件，记录完整路径\n        if (hasDirectAudio) {\n          audioFolderPath = currentPath;\n          return;\n        }\n\n        // 如果当前目录没有音频文件，递归检查子目录\n        for (final child in folder.children!) {\n          if (child.type == 'folder') {\n            List<String> newPath = List.from(currentPath)..add(child.title ?? '');\n            findPath(child, newPath);\n          }\n        }\n      }\n    }\n\n    // 遍历根目录下的所有文件夹\n    for (final child in children) {\n      if (child.type == 'folder') {\n        findPath(child, [child.title ?? '']);\n        if (audioFolderPath != null) break;\n      }\n    }\n\n    return audioFolderPath;\n  }\n\n  /// 检查路径是否包含指定的目录名\n  /// 用于判断某个目录是否在音频文件夹的路径上\n  static bool isInPath(List<String>? path, String? folderName) {\n    if (path == null || folderName == null) return false;\n    return path.contains(folderName);\n  }\n} "
  },
  {
    "path": "lib/core/audio/models/play_mode.dart",
    "content": "enum PlayMode {\n  single,     // 单曲循环\n  loop,       // 列表循环\n  sequence,   // 顺序播放\n} "
  },
  {
    "path": "lib/core/audio/models/playback_context.dart",
    "content": "import 'package:asmrapp/core/audio/utils/audio_error_handler.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/data/models/files/files.dart';\nimport 'package:asmrapp/data/models/files/child.dart';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:asmrapp/core/audio/models/play_mode.dart';\nimport 'package:asmrapp/core/audio/models/file_path.dart';\n\nclass PlaybackContext {\n  final Work work;\n  final Files files;\n  final Child currentFile;\n  final List<Child> playlist;\n  final int currentIndex;\n  final PlayMode playMode;\n\n  void validate() {\n    if (playlist.isEmpty) {\n      throw AudioError(\n        AudioErrorType.state,\n        '无效的播放列表状态：播放列表为空',\n      );\n    }\n    \n    if (currentIndex < 0 || currentIndex >= playlist.length) {\n      throw AudioError(\n        AudioErrorType.state,\n        '无效的播放列表索引：$currentIndex，列表长度：${playlist.length}',\n      );\n    }\n\n    if (!playlist.contains(currentFile)) {\n      throw AudioError(\n        AudioErrorType.state,\n        '当前文件不在播放列表中',\n      );\n    }\n  }\n\n  // 私有构造函数\n  const PlaybackContext._({\n    required this.work,\n    required this.files,\n    required this.currentFile,\n    required this.playlist,\n    required this.currentIndex,\n    this.playMode = PlayMode.sequence,\n  });\n\n  // 公开的工厂构造函数，只需要基本参数\n  factory PlaybackContext({\n    required Work work,\n    required Files files,\n    required Child currentFile,\n    PlayMode playMode = PlayMode.sequence,\n  }) {\n    final playlist = _getPlaylistFromSameDirectory(currentFile, files);\n    final currentIndex = playlist.indexWhere((file) => file.title == currentFile.title);\n    \n    return PlaybackContext._(\n      work: work,\n      files: files,\n      currentFile: currentFile,\n      playlist: playlist,\n      currentIndex: currentIndex,\n      playMode: playMode,\n    );\n  }\n\n  // 获取同级文件列表\n  static List<Child> _getPlaylistFromSameDirectory(Child currentFile, Files files) {\n    // AppLogger.debug('开始获取播放列表...');\n    // AppLogger.debug('当前文件: ${currentFile.title}');\n    // AppLogger.debug('当前文件类型: ${currentFile.type}');\n\n    // 获取当前文件的扩展名\n    final extension = currentFile.title?.split('.').last.toLowerCase();\n    // AppLogger.debug('当前文件扩展名: $extension');\n    \n    if (extension != 'mp3' && extension != 'wav') {\n      AppLogger.debug('不支持的文件类型: $extension');\n      return [];\n    }\n\n    // 使用 FilePath 获取同级文件\n    final siblings = FilePath.getSiblings(currentFile, files);\n    \n    // 过滤出相同扩展名的文件\n    final playlist = siblings.where((file) => \n      file.title?.toLowerCase().endsWith('.$extension') ?? false\n    ).toList();\n    \n    // AppLogger.debug('找到 ${playlist.length} 个可播放文件:');\n    // for (var file in playlist) {\n    //   AppLogger.debug('- [${file.type}] ${file.title} (URL: ${file.mediaDownloadUrl != null ? '有' : '无'})');\n    // }\n    \n    return playlist;\n  }\n\n  // 便捷方法：检查是否有下一曲\n  bool get hasNext => currentIndex < playlist.length - 1;\n\n  // 便捷方法：检查是否有上一曲\n  bool get hasPrevious => currentIndex > 0;\n\n  // 获取下一曲（考虑播放模式）\n  Child? getNextFile() {\n    if (playlist.isEmpty) return null;\n    \n    switch (playMode) {\n      case PlayMode.single:\n        return currentFile;  // 单曲循环返回当前文件\n      case PlayMode.loop:\n        // 列表循环：最后一首返回第一首，否则返回下一首\n        return hasNext ? playlist[currentIndex + 1] : playlist[0];\n      case PlayMode.sequence:\n        // 顺序播放：有下一首则返回，否则返回null\n        return hasNext ? playlist[currentIndex + 1] : null;\n    }\n  }\n\n  // 获取上一曲\n  Child? getPreviousFile() {\n    if (playlist.isEmpty) return null;\n    \n    switch (playMode) {\n      case PlayMode.single:\n        return currentFile;\n      case PlayMode.loop:\n        // 列表循环：第一首返回最后一首，否则返回上一首\n        return hasPrevious ? playlist[currentIndex - 1] : playlist[playlist.length - 1];\n      case PlayMode.sequence:\n        // 顺序播放：有上一首则返回，否则返回null\n        return hasPrevious ? playlist[currentIndex - 1] : null;\n    }\n  }\n\n  // 这两个方法 copy 的设计思路是遵循了\"不可变对象\"模式，\n  // 通过创建新的实例而不是修改现有实例来更新状态。这种模式有以下好处：\n  // 状态可预测\n  // 线程安全\n  // 便于调试\n  // 符合函数式编程思想\n\n  // 创建新的上下文（用于切换文件）\n  PlaybackContext copyWithFile(Child newFile) {\n    return PlaybackContext(\n      work: work,\n      files: files,\n      currentFile: newFile,\n      playMode: playMode,\n    );\n  }\n\n  // 创建新的上下文（用于切换播放模式）\n  PlaybackContext copyWithMode(PlayMode newMode) {\n    return PlaybackContext(\n      work: work,\n      files: files,\n      currentFile: currentFile,\n      playMode: newMode,\n    );\n  }\n\n  // 便捷方法：获取可播放文件列表\n  List<Child> getPlayableFiles() {\n    if (files.children == null) return [];\n    return files.children!.where((file) => \n      file.mediaDownloadUrl != null && \n      file.type?.toLowerCase() != 'vtt'\n    ).toList();\n  }\n\n  // 工具方法：获取文件名（不含扩展名）\n  String? _getBaseName(String? filename) {\n    if (filename == null) return null;\n    return filename.replaceAll(RegExp(r'\\.[^.]+$'), '');\n  }\n} "
  },
  {
    "path": "lib/core/audio/models/subtitle.dart",
    "content": "import 'dart:math' as math;\n\nenum SubtitleState {\n  current,  // 当前播放的字幕\n  waiting,  // 即将播放的字幕\n  passed    // 已经播放过的字幕\n}\n\nclass Subtitle {\n  final Duration start;\n  final Duration end;\n  final String text;\n  final int index;\n\n  const Subtitle({\n    required this.start,\n    required this.end,\n    required this.text,\n    required this.index,\n  });\n\n  Subtitle? getNext(SubtitleList list) {\n    if (index < list.subtitles.length - 1) {\n      return list.subtitles[index + 1];\n    }\n    return null;\n  }\n\n  Subtitle? getPrevious(SubtitleList list) {\n    if (index > 0) {\n      return list.subtitles[index - 1];\n    }\n    return null;\n  }\n\n  @override\n  String toString() => '$start --> $end: $text';\n}\n\nclass SubtitleList {\n  final List<Subtitle> subtitles;\n  int _currentIndex = -1;\n\n  SubtitleList(List<Subtitle> subtitles) \n    : subtitles = subtitles.asMap().entries.map(\n        (entry) => Subtitle(\n          start: entry.value.start,\n          end: entry.value.end,\n          text: entry.value.text,\n          index: entry.key,\n        )\n      ).toList();\n\n  SubtitleWithState? getCurrentSubtitle(Duration position) {\n    if (subtitles.isEmpty) return null;\n\n    // 如果位置在第一个字幕之前，仍然返回第一个字幕作为当前字幕\n    if (position < subtitles.first.start) {\n      return SubtitleWithState(subtitles.first, SubtitleState.current);\n    }\n\n    // 如果位置在最后一个字幕之后\n    if (position > subtitles.last.end) {\n      return SubtitleWithState(subtitles.last, SubtitleState.passed);\n    }\n\n    // 查找当前时间点对应的字幕\n    for (int i = 0; i < subtitles.length; i++) {\n      final subtitle = subtitles[i];\n      // 如果在当前字幕的时间范围内\n      if (position >= subtitle.start && position <= subtitle.end) {\n        _currentIndex = i;\n        return SubtitleWithState(subtitle, SubtitleState.current);\n      }\n      // 如果已经超过了当前字幕，但还没到下一个字幕\n      if (position > subtitle.end && \n          (i == subtitles.length - 1 || position < subtitles[i + 1].start)) {\n        return SubtitleWithState(subtitle, SubtitleState.passed);\n      }\n    }\n\n    // 正常情况下不会到达这里，因为上面的逻辑已经覆盖了所有情况\n    // 但为了安全起见，返回第一个字幕\n    return SubtitleWithState(subtitles.first, SubtitleState.waiting);\n  }\n\n  List<Subtitle> getSubtitlesInRange(int start, int count) {\n    if (start < 0 || start >= subtitles.length) return [];\n    final end = math.min(start + count, subtitles.length);\n    return subtitles.sublist(start, end);\n  }\n\n  (Subtitle?, Subtitle?, Subtitle?) getCurrentContext() {\n    if (_currentIndex == -1) return (null, null, null);\n    \n    final previous = _currentIndex > 0 ? subtitles[_currentIndex - 1] : null;\n    final current = subtitles[_currentIndex];\n    final next = _currentIndex < subtitles.length - 1 ? subtitles[_currentIndex + 1] : null;\n    \n    return (previous, current, next);\n  }\n\n  static SubtitleList parse(String vttContent) {\n    final lines = vttContent.split('\\n');\n    final subtitles = <Subtitle>[];\n    \n    int i = 0;\n    while (i < lines.length && !lines[i].contains('-->')) {\n      i++;\n    }\n\n    while (i < lines.length) {\n      final line = lines[i].trim();\n      \n      if (line.contains('-->')) {\n        final times = line.split('-->');\n        if (times.length == 2) {\n          final start = _parseTimestamp(times[0].trim());\n          final end = _parseTimestamp(times[1].trim());\n          \n          i++;\n          String text = '';\n          while (i < lines.length && lines[i].trim().isNotEmpty) {\n            if (text.isNotEmpty) text += '\\n';\n            text += lines[i].trim();\n            i++;\n          }\n          \n          if (start != null && end != null && text.isNotEmpty) {\n            subtitles.add(Subtitle(\n              start: start,\n              end: end,\n              text: text,\n              index: subtitles.length,\n            ));\n          }\n        }\n      }\n      i++;\n    }\n\n    return SubtitleList(subtitles);\n  }\n\n  static Duration? _parseTimestamp(String timestamp) {\n    try {\n      final parts = timestamp.split(':');\n      if (parts.length == 3) {\n        final seconds = parts[2].split('.');\n        return Duration(\n          hours: int.parse(parts[0]),\n          minutes: int.parse(parts[1]),\n          seconds: int.parse(seconds[0]),\n          milliseconds: seconds.length > 1 ? int.parse(seconds[1].padRight(3, '0')) : 0,\n        );\n      }\n    } catch (e) {\n      return null;\n    }\n    return null;\n  }\n}\n\nclass SubtitleWithState {\n  final Subtitle subtitle;\n  final SubtitleState state;\n\n  SubtitleWithState(this.subtitle, this.state);\n} "
  },
  {
    "path": "lib/core/audio/notification/audio_notification_service.dart",
    "content": "import 'package:asmrapp/core/audio/events/playback_event_hub.dart';\nimport 'package:audio_service/audio_service.dart';\nimport 'package:just_audio/just_audio.dart';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:rxdart/rxdart.dart';\nimport '../models/audio_track_info.dart';\nimport '../audio_player_handler.dart';\n\nclass AudioNotificationService {\n  final AudioPlayer _player;\n  final PlaybackEventHub _eventHub;\n  AudioHandler? _audioHandler;\n  final _mediaItem = BehaviorSubject<MediaItem?>();\n\n  AudioNotificationService(\n    this._player,\n    this._eventHub,\n  );\n\n  Future<void> init() async {\n    try {\n      _audioHandler = await AudioService.init(\n        builder: () => AudioPlayerHandler(_player, _eventHub),\n        config: const AudioServiceConfig(\n          androidNotificationChannelId: 'com.asmrapp.audio',\n          androidNotificationChannelName: 'ASMR One 播放器',\n          androidNotificationOngoing: true,\n          androidStopForegroundOnPause: true,\n        ),\n      );\n\n      _setupEventListeners();\n      AppLogger.debug('通知栏服务初始化成功');\n    } catch (e) {\n      AppLogger.error('通知栏服务初始化失败', e);\n      rethrow;\n    }\n  }\n\n  void _setupEventListeners() {\n    // 监听轨道变更事件来更新媒体信息\n    _eventHub.trackChange.listen((event) {\n      updateMetadata(event.track);\n    });\n  }\n\n  void updateMetadata(AudioTrackInfo trackInfo) {\n    final mediaItem = MediaItem(\n      id: trackInfo.url,\n      title: trackInfo.title,\n      artist: trackInfo.artist,\n      artUri: Uri.parse(trackInfo.coverUrl),\n      duration: trackInfo.duration,\n    );\n\n    _mediaItem.add(mediaItem);\n    if (_audioHandler != null) {\n      (_audioHandler as BaseAudioHandler).mediaItem.add(mediaItem);\n    }\n  }\n\n  Future<void> dispose() async {\n    await _audioHandler?.stop();\n    await _mediaItem.close();\n  }\n}\n"
  },
  {
    "path": "lib/core/audio/state/playback_state_manager.dart",
    "content": "import 'dart:async';\nimport 'package:just_audio/just_audio.dart';\nimport '../models/audio_track_info.dart';\nimport '../models/playback_context.dart';\nimport '../utils/audio_error_handler.dart';\nimport '../utils/track_info_creator.dart';\nimport 'package:asmrapp/data/models/playback/playback_state.dart';\nimport '../storage/i_playback_state_repository.dart';\nimport '../events/playback_event.dart';\nimport '../events/playback_event_hub.dart';\nimport 'package:asmrapp/data/models/files/child.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\n\n\nclass PlaybackStateManager {\n  final AudioPlayer _player;\n  final PlaybackEventHub _eventHub;\n  final IPlaybackStateRepository _stateRepository;\n  \n  AudioTrackInfo? _currentTrack;\n  PlaybackContext? _currentContext;\n\n  final List<StreamSubscription> _subscriptions = [];\n\n  PlaybackStateManager({\n    required AudioPlayer player,\n    required PlaybackEventHub eventHub,\n    required IPlaybackStateRepository stateRepository,\n  }) : _player = player,\n       _eventHub = eventHub,\n       _stateRepository = stateRepository;\n\n  // 初始化状态监听\n  void initStateListeners() {\n    // 监听播放器索引变化\n    _player.currentIndexStream.listen((index) {\n      if (index != null && _currentContext != null) {\n        final newFile = _currentContext!.playlist[index];\n        updateTrackAndContext(newFile, _currentContext!.work);\n      }\n    });\n\n    // 直接监听 AudioPlayer 的原始流\n    _player.playerStateStream.listen((state) async {\n      final position = _player.position;\n      final duration = _player.duration;\n      \n      // 转换并发送到 EventHub\n      _eventHub.emit(PlaybackStateEvent(state, position, duration));\n\n      if (state.processingState == ProcessingState.completed) {\n        _onPlaybackCompleted();\n      }\n      saveState();\n    });\n\n    _player.positionStream.listen((position) {\n      _eventHub.emit(PlaybackProgressEvent(\n        position,\n        _player.bufferedPosition\n      ));\n    });\n  }\n\n  // 状态更新方法\n  void updateContext(PlaybackContext? context) {\n    _currentContext = context;\n    if (context != null) {\n      _eventHub.emit(PlaybackContextEvent(context));\n    }\n  }\n\n  void updateTrackInfo(AudioTrackInfo track) {\n    _currentTrack = track;\n    _eventHub.emit(TrackChangeEvent(track, _currentContext!.currentFile, _currentContext!.work));\n  }\n\n  void updateTrackAndContext(Child file, Work work) {\n    if (_currentContext != null) {\n      final newContext = _currentContext!.copyWithFile(file);\n      updateContext(newContext);\n    }\n    \n    final trackInfo = TrackInfoCreator.createFromFile(file, work);\n    updateTrackInfo(trackInfo);\n  }\n\n  void _onPlaybackCompleted() {\n    if (_currentContext == null) return;\n    _eventHub.emit(PlaybackCompletedEvent(_currentContext!));\n  }\n\n  // 状态访问\n  AudioTrackInfo? get currentTrack => _currentTrack;\n  PlaybackContext? get currentContext => _currentContext;\n\n  void clearState() {\n    _currentTrack = null;\n    _currentContext = null;\n    updateContext(null);\n  }\n\n  // 状态持久化\n  Future<void> saveState() async {\n    if (_currentContext == null) return;\n\n    try {\n      final state = PlaybackState(\n        work: _currentContext!.work,\n        files: _currentContext!.files,\n        currentFile: _currentContext!.currentFile,\n        playlist: _currentContext!.playlist,\n        currentIndex: _currentContext!.currentIndex,\n        playMode: _currentContext!.playMode,\n        position: (_player.position).inMilliseconds,\n        timestamp: DateTime.now().toIso8601String(),\n      );\n      \n      await _stateRepository.saveState(state);\n    } catch (e, stack) {\n      AudioErrorHandler.handleError(\n        AudioErrorType.state,\n        '保存播放状态',\n        e,\n        stack,\n      );\n    }\n  }\n\n  Future<PlaybackState?> loadState() async {\n    try {\n      return await _stateRepository.loadState();\n    } catch (e, stack) {\n      AudioErrorHandler.handleError(\n        AudioErrorType.state,\n        '加载播放状态',\n        e,\n        stack,\n      );\n      return null;\n    }\n  }\n\n  void _setupEventListeners() {\n    // 处理初始状态请求\n    _subscriptions.add(\n      _eventHub.requestInitialState.listen((_) {\n        _eventHub.emit(InitialStateEvent(\n          _currentTrack,\n          _currentContext\n        ));\n      }),\n    );\n  }\n\n  void dispose() {\n    for (var subscription in _subscriptions) {\n      subscription.cancel();\n    }\n    _subscriptions.clear();\n  }\n} "
  },
  {
    "path": "lib/core/audio/storage/i_playback_state_repository.dart",
    "content": "import 'package:asmrapp/data/models/playback/playback_state.dart';\n\nabstract class IPlaybackStateRepository {\n  Future<void> saveState(PlaybackState state);\n  Future<PlaybackState?> loadState();\n} "
  },
  {
    "path": "lib/core/audio/storage/playback_state_repository.dart",
    "content": "import 'dart:convert';\nimport 'package:shared_preferences/shared_preferences.dart';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:asmrapp/data/models/playback/playback_state.dart';\nimport 'i_playback_state_repository.dart';\n\nclass PlaybackStateRepository implements IPlaybackStateRepository {\n  static const _key = 'last_playback_state';\n  final SharedPreferences _prefs;\n\n  PlaybackStateRepository(this._prefs);\n\n  @override\n  Future<void> saveState(PlaybackState state) async {\n    try {\n      final json = state.toJson();\n      final data = jsonEncode(json);\n      await _prefs.setString(_key, data);\n      AppLogger.debug('播放状态已保存');\n    } catch (e) {\n      AppLogger.error('保存播放状态失败', e);\n      rethrow;\n    }\n  }\n\n  @override\n  Future<PlaybackState?> loadState() async {\n    try {\n      final data = _prefs.getString(_key);\n      if (data == null) {\n        AppLogger.debug('没有找到保存的播放状态');\n        return null;\n      }\n\n      final json = jsonDecode(data) as Map<String, dynamic>;\n      final state = PlaybackState.fromJson(json);\n      AppLogger.debug('播放状态已加载');\n      return state;\n    } catch (e) {\n      AppLogger.error('加载播放状态失败', e);\n      return null;\n    }\n  }\n} "
  },
  {
    "path": "lib/core/audio/utils/audio_error_handler.dart",
    "content": "import 'package:asmrapp/utils/logger.dart';\n\nenum AudioErrorType {\n  playback,    // 播放错误\n  playlist,    // 播放列表错误\n  state,       // 状态错误\n  context,     // 上下文错误\n  init,        // 初始化错误\n}\n\nclass AudioError implements Exception {\n  final AudioErrorType type;\n  final String message;\n  final dynamic originalError;\n\n  AudioError(this.type, this.message, [this.originalError]);\n\n  @override\n  String toString() => '$message${originalError != null ? ': $originalError' : ''}';\n}\n\nclass AudioErrorHandler {\n  static void handleError(\n    AudioErrorType type,\n    String operation,\n    dynamic error, [\n    StackTrace? stack,\n  ]) {\n    final message = _getErrorMessage(type, operation);\n    AppLogger.error(message, error, stack);\n  }\n  \n  static Never throwError(\n    AudioErrorType type,\n    String operation,\n    dynamic error,\n  ) {\n    final message = _getErrorMessage(type, operation);\n    throw AudioError(type, message, error);\n  }\n\n  static String _getErrorMessage(AudioErrorType type, String operation) {\n    switch (type) {\n      case AudioErrorType.playback:\n        return '播放操作失败: $operation';\n      case AudioErrorType.playlist:\n        return '播放列表操作失败: $operation';\n      case AudioErrorType.state:\n        return '状态操作失败: $operation';\n      case AudioErrorType.context:\n        return '上下文操作失败: $operation';\n      case AudioErrorType.init:\n        return '初始化失败: $operation';\n    }\n  }\n} "
  },
  {
    "path": "lib/core/audio/utils/playlist_builder.dart",
    "content": "import 'package:just_audio/just_audio.dart';\nimport 'package:asmrapp/data/models/files/child.dart';\nimport 'package:asmrapp/core/audio/cache/audio_cache_manager.dart';\n\nclass PlaylistBuilder {\n  static Future<List<AudioSource>> buildAudioSources(List<Child> files) async {\n    return await Future.wait(\n      files.map((file) async {\n        return AudioCacheManager.createAudioSource(file.mediaDownloadUrl!);\n      })\n    );\n  }\n\n  static Future<void> updatePlaylist(\n    ConcatenatingAudioSource playlist,\n    List<AudioSource> sources,\n  ) async {\n    await playlist.clear();\n    await playlist.addAll(sources);\n  }\n\n  static Future<void> setPlaylistSource({\n    required AudioPlayer player,\n    required ConcatenatingAudioSource playlist,\n    required List<Child> files,\n    required int initialIndex,\n    required Duration initialPosition,\n  }) async {\n    final sources = await buildAudioSources(files);\n    await updatePlaylist(playlist, sources);\n    \n    await player.setAudioSource(\n      playlist,\n      initialIndex: initialIndex,\n      initialPosition: initialPosition,\n    );\n  }\n} "
  },
  {
    "path": "lib/core/audio/utils/track_info_creator.dart",
    "content": "import 'package:asmrapp/core/audio/models/audio_track_info.dart';\nimport 'package:asmrapp/data/models/files/child.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\n\nclass TrackInfoCreator {\n  static AudioTrackInfo createTrackInfo({\n    required String title,\n    required String? artistName,\n    required String? coverUrl,\n    required String url,\n  }) {\n    return AudioTrackInfo(\n      title: title,\n      artist: artistName ?? '',\n      coverUrl: coverUrl ?? '',\n      url: url,\n    );\n  }\n  \n  static AudioTrackInfo createFromFile(Child file, Work work) {\n    return createTrackInfo(\n      title: file.title ?? '',\n      artistName: work.circle?.name,\n      coverUrl: work.mainCoverUrl,\n      url: file.mediaDownloadUrl!,\n    );\n  }\n} "
  },
  {
    "path": "lib/core/cache/recommendation_cache_manager.dart",
    "content": "import 'dart:collection';\nimport 'package:asmrapp/data/services/api_service.dart';\nimport 'package:asmrapp/utils/logger.dart';\n\nclass RecommendationCacheManager {\n  // 单例模式\n  static final RecommendationCacheManager _instance = RecommendationCacheManager._internal();\n  factory RecommendationCacheManager() => _instance;\n  RecommendationCacheManager._internal();\n\n  // 使用 LinkedHashMap 便于按访问顺序管理缓存\n  final _cache = LinkedHashMap<String, _CacheItem>();\n  \n  // 缓存配置\n  static const int _maxCacheSize = 1000; // 最大缓存条目数\n  static const Duration _cacheDuration = Duration(hours: 24); // 缓存有效期\n\n  /// 生成缓存键\n  String _generateKey(String itemId, int page, int subtitle) {\n    return '$itemId-$page-$subtitle';\n  }\n\n  /// 获取缓存数据\n  WorksResponse? get(String itemId, int page, int subtitle) {\n    final key = _generateKey(itemId, page, subtitle);\n    final item = _cache[key];\n\n    if (item == null) {\n      return null;\n    }\n\n    // 检查是否过期\n    if (item.isExpired) {\n      _cache.remove(key);\n      AppLogger.debug('缓存已过期: $key');\n      return null;\n    }\n\n    AppLogger.debug('命中缓存: $key');\n    return item.data;\n  }\n\n  /// 存储缓存数据\n  void set(String itemId, int page, int subtitle, WorksResponse data) {\n    final key = _generateKey(itemId, page, subtitle);\n    \n    // 检查缓存大小,如果达到上限则移除最早的条目\n    if (_cache.length >= _maxCacheSize) {\n      _cache.remove(_cache.keys.first);\n    }\n\n    _cache[key] = _CacheItem(data);\n    AppLogger.debug('添加缓存: $key');\n  }\n\n  /// 清除所有缓存\n  void clear() {\n    _cache.clear();\n    AppLogger.debug('清除所有推荐缓存');\n  }\n\n  /// 移除指定作品的缓存\n  void remove(String itemId) {\n    _cache.removeWhere((key, _) => key.startsWith('$itemId-'));\n    AppLogger.debug('移除作品缓存: $itemId');\n  }\n}\n\n/// 缓存条目包装类\nclass _CacheItem {\n  final WorksResponse data;\n  final DateTime timestamp;\n\n  _CacheItem(this.data) : timestamp = DateTime.now();\n\n  bool get isExpired => \n    DateTime.now().difference(timestamp) > RecommendationCacheManager._cacheDuration;\n} "
  },
  {
    "path": "lib/core/di/service_locator.dart",
    "content": "import 'dart:io';\nimport 'package:asmrapp/core/platform/dummy_lyric_overlay_controller.dart';\nimport 'package:get_it/get_it.dart';\nimport '../audio/i_audio_player_service.dart';\nimport '../audio/audio_player_service.dart';\nimport '../../data/services/api_service.dart';\nimport '../../presentation/viewmodels/player_viewmodel.dart';\nimport '../../data/services/auth_service.dart';\nimport '../../presentation/viewmodels/auth_viewmodel.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\nimport '../../data/repositories/auth_repository.dart';\nimport '../subtitle/i_subtitle_service.dart';\nimport '../subtitle/subtitle_service.dart';\nimport '../subtitle/subtitle_loader.dart';\nimport '../../core/audio/storage/i_playback_state_repository.dart';\nimport '../../core/audio/storage/playback_state_repository.dart';\nimport '../audio/events/playback_event_hub.dart';\nimport '../../core/theme/theme_controller.dart';\nimport '../../core/platform/i_lyric_overlay_controller.dart';\nimport '../../core/platform/lyric_overlay_controller.dart';\nimport '../../core/platform/lyric_overlay_manager.dart';\nimport '../../core/platform/wakelock_controller.dart';\n\nfinal getIt = GetIt.instance;\n\nFuture<void> setupServiceLocator() async {\n  final prefs = await SharedPreferences.getInstance();\n\n  // 注册 EventHub\n  getIt.registerLazySingleton(() => PlaybackEventHub());\n\n  // 注册 SharedPreferences 实例\n  getIt.registerSingleton<SharedPreferences>(prefs);\n\n  // 注册 PlaybackStateRepository\n  getIt.registerLazySingleton<IPlaybackStateRepository>(\n    () => PlaybackStateRepository(getIt()),\n  );\n\n  // 核心服务\n  getIt.registerLazySingleton<IAudioPlayerService>(\n    () => AudioPlayerService(\n      eventHub: getIt(),\n      stateRepository: getIt(),\n    ),\n  );\n\n  // 注册 PlayerViewModel\n  getIt.registerLazySingleton<PlayerViewModel>(\n    () => PlayerViewModel(\n      audioService: getIt(),\n      eventHub: getIt(),\n      subtitleService: getIt(),\n    ),\n  );\n\n  // API 服务\n  getIt.registerLazySingleton<ApiService>(\n    () => ApiService(),\n  );\n\n  // 添加 AuthService 注册\n  getIt.registerLazySingleton<AuthService>(\n    () => AuthService(),\n  );\n\n  // 添加 AuthRepository 注册\n  getIt.registerLazySingleton<AuthRepository>(\n    () => AuthRepository(prefs),\n  );\n\n  // 修改 AuthViewModel 注册\n  getIt.registerSingleton<AuthViewModel>(\n    AuthViewModel(\n      authService: getIt<AuthService>(),\n      authRepository: getIt<AuthRepository>(),\n    ),\n  );\n\n  // 等待 AuthViewModel 完成初始化\n  await getIt<AuthViewModel>().loadSavedAuth();\n\n  // 添加字幕服务注册\n  getIt.registerLazySingleton<ISubtitleService>(\n    () => SubtitleService(),\n  );\n\n  await setupSubtitleServices();\n\n  // 注册主题控制器\n  getIt.registerLazySingleton<ThemeController>(\n    () => ThemeController(prefs),\n  );\n\n  // 注册 WakeLockController\n  getIt.registerLazySingleton(() => WakeLockController(prefs));\n}\n\nFuture<void> setupSubtitleServices() async {\n  getIt.registerLazySingleton<SubtitleLoader>(() => SubtitleLoader());\n  if (Platform.isAndroid) {\n    getIt.registerLazySingleton<ILyricOverlayController>(() => LyricOverlayController());\n  } else {\n    getIt.registerLazySingleton<ILyricOverlayController>(() => DummyLyricOverlayController());\n  }\n  getIt.registerLazySingleton(() => LyricOverlayManager(\n    controller: getIt(),\n    subtitleService: getIt(),\n  ));\n\n  // 初始化悬浮窗管理器\n  await getIt<LyricOverlayManager>().initialize();\n}\n"
  },
  {
    "path": "lib/core/platform/dummy_lyric_overlay_controller.dart",
    "content": "import 'package:asmrapp/utils/logger.dart';\nimport 'i_lyric_overlay_controller.dart';\n\nclass DummyLyricOverlayController implements ILyricOverlayController {\n  static const _tag = 'LyricOverlay';\n\n  @override\n  Future<void> initialize() async {\n  }\n\n  @override\n  Future<void> show() async {\n\n  }\n\n  @override\n  Future<void> hide() async {\n\n  }\n\n  @override\n  Future<void> updateLyric(String? text) async {\n\n  }\n\n  @override\n  Future<bool> checkPermission() async {\n    return true;\n  }\n\n  @override\n  Future<bool> requestPermission() async {\n    AppLogger.debug('[$_tag] 请求权限');\n    return true;\n  }\n\n  @override\n  Future<void> dispose() async {\n\n  }\n\n  @override\n  Future<bool> isShowing() async {\n    return false;\n  }\n}"
  },
  {
    "path": "lib/core/platform/i_lyric_overlay_controller.dart",
    "content": "abstract class ILyricOverlayController {\n  /// 初始化悬浮窗\n  Future<void> initialize();\n  \n  /// 显示悬浮窗\n  Future<void> show();\n  \n  /// 隐藏悬浮窗\n  Future<void> hide();\n  \n  /// 更新歌词内容\n  Future<void> updateLyric(String? text);\n  \n  /// 检查悬浮窗权限\n  Future<bool> checkPermission();\n  \n  /// 请求悬浮窗权限\n  Future<bool> requestPermission();\n  \n  /// 释放资源\n  Future<void> dispose();\n  \n  /// 获取悬浮窗当前显示状态\n  Future<bool> isShowing();\n} "
  },
  {
    "path": "lib/core/platform/lyric_overlay_controller.dart",
    "content": "import 'package:flutter/services.dart';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:permission_handler/permission_handler.dart';\nimport 'i_lyric_overlay_controller.dart';\n\nclass LyricOverlayController implements ILyricOverlayController {\n  static const _tag = 'LyricOverlay';\n  static const _channel = MethodChannel('one.asmr.yuro/lyric_overlay');\n  \n  @override\n  Future<void> initialize() async {\n    try {\n      AppLogger.debug('[$_tag] 初始化');\n      await _channel.invokeMethod('initialize');\n    } catch (e) {\n      AppLogger.error('[$_tag] 初始化失败', e);\n      // 这里我们不抛出异常,而是静默失败\n      // 因为这个错误不应该影响应用的主要功能\n    }\n  }\n  \n  @override\n  Future<void> show() async {\n    AppLogger.debug('[$_tag] 显示悬浮窗');\n    await _channel.invokeMethod('show');\n  }\n  \n  @override\n  Future<void> hide() async {\n    AppLogger.debug('[$_tag] 隐藏悬浮窗');\n    await _channel.invokeMethod('hide');\n  }\n  \n  @override\n  Future<void> updateLyric(String? text) async {\n    AppLogger.debug('[$_tag] 更新歌词: ${text ?? '<空>'}');\n    await _channel.invokeMethod('updateLyric', {'text': text});\n  }\n  \n  @override\n  Future<bool> checkPermission() async {\n    AppLogger.debug('[$_tag] 检查权限');\n    return await Permission.systemAlertWindow.isGranted;\n  }\n  \n  @override\n  Future<bool> requestPermission() async {\n    AppLogger.debug('[$_tag] 请求权限');\n    final status = await Permission.systemAlertWindow.request();\n    return status.isGranted;\n  }\n  \n  @override\n  Future<void> dispose() async {\n    AppLogger.debug('[$_tag] 释放资源');\n    await _channel.invokeMethod('dispose');\n  }\n  \n  @override\n  Future<bool> isShowing() async {\n    final result = await _channel.invokeMethod<bool>('isShowing') ?? false;\n    return result;\n  }\n} "
  },
  {
    "path": "lib/core/platform/lyric_overlay_manager.dart",
    "content": "import 'dart:async';\n\nimport 'package:asmrapp/core/platform/i_lyric_overlay_controller.dart';\nimport 'package:asmrapp/core/subtitle/i_subtitle_service.dart';\nimport 'package:flutter/material.dart';\n\nclass LyricOverlayManager {\n  final ILyricOverlayController _controller;\n  final ISubtitleService _subtitleService;\n  StreamSubscription? _subscription;\n  bool _isShowing = false;\n  \n  LyricOverlayManager({\n    required ILyricOverlayController controller,\n    required ISubtitleService subtitleService,\n  }) : _controller = controller,\n       _subtitleService = subtitleService;\n\n  Future<void> initialize() async {\n    await _controller.initialize();\n    _subscription = _subtitleService.currentSubtitleStream.listen((subtitle) {\n      if (_isShowing) {\n        _controller.updateLyric(subtitle?.text);\n      }\n    });\n    \n    _isShowing = await _controller.isShowing();\n    \n    if (_isShowing) {\n      await show();\n    }\n  }\n  \n  Future<void> dispose() async {\n    await _subscription?.cancel();\n    await _controller.dispose();\n  }\n  \n  Future<bool> checkPermission() async {\n    return await _controller.checkPermission();\n  }\n\n  Future<bool> requestPermission() async {\n    return await _controller.requestPermission();\n  }\n\n  Future<void> show() async {\n    await _controller.show();\n    _isShowing = true;\n    final currentSubtitle = _subtitleService.currentSubtitleWithState;\n    if (currentSubtitle != null) {\n      await _controller.updateLyric(currentSubtitle.subtitle.text);\n    }\n  }\n\n  Future<void> hide() async {\n    await _controller.hide();\n    _isShowing = false;\n  }\n\n  bool get isShowing => _isShowing;\n\n  /// 处理显示悬浮歌词的完整流程\n  Future<void> showWithPermissionCheck(BuildContext context) async {\n    final hasPermission = await checkPermission();\n    if (hasPermission) {\n      await show();\n      return;\n    }\n\n    if (!context.mounted) return;\n\n    final shouldRequest = await _showPermissionDialog(context);\n    if (shouldRequest && context.mounted) {\n      final granted = await requestPermission();\n      if (granted && context.mounted) {\n        await show();\n      }\n    }\n  }\n\n  Future<bool> _showPermissionDialog(BuildContext context) async {\n    return await showDialog<bool>(\n      context: context,\n      builder: (context) => AlertDialog(\n        title: const Text('开启悬浮歌词'),\n        content: const Text('需要悬浮窗权限来显示歌词，是否授予权限？'),\n        actions: [\n          TextButton(\n            onPressed: () => Navigator.pop(context, false),\n            child: const Text('取消'),\n          ),\n          TextButton(\n            onPressed: () => Navigator.pop(context, true),\n            child: const Text('确定'),\n          ),\n        ],\n      ),\n    ) ?? false;\n  }\n\n  /// 切换显示/隐藏状态\n  Future<void> toggle(BuildContext context) async {\n    if (_isShowing) {\n      await hide();\n    } else {\n      await showWithPermissionCheck(context);\n    }\n  }\n  \n  // 其他控制方法...\n\n  Future<void> syncState() async {\n    _isShowing = await _controller.isShowing();\n  }\n} "
  },
  {
    "path": "lib/core/platform/wakelock_controller.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\n\nimport 'package:wakelock_plus/wakelock_plus.dart';\nimport 'package:asmrapp/utils/logger.dart';\n\nclass WakeLockController extends ChangeNotifier {\n  static const _tag = 'WakeLock';\n  static const _wakeLockKey = 'wakelock_enabled';\n  final SharedPreferences _prefs;\n  bool _enabled = false;\n\n  WakeLockController(this._prefs) {\n    _loadState();\n  }\n\n  bool get enabled => _enabled;\n\n  Future<void> _loadState() async {\n    try {\n      _enabled = _prefs.getBool(_wakeLockKey) ?? false;\n      if (_enabled) {\n        await WakelockPlus.enable();\n      }\n      notifyListeners();\n    } catch (e) {\n      AppLogger.error('[$_tag] 加载状态失败', e);\n    }\n  }\n\n  Future<void> toggle() async {\n    try {\n      _enabled = !_enabled;\n      if (_enabled) {\n        await WakelockPlus.enable();\n      } else {\n        await WakelockPlus.disable();\n      }\n      await _prefs.setBool(_wakeLockKey, _enabled);\n      notifyListeners();\n    } catch (e) {\n      AppLogger.error('[$_tag] 切换状态失败', e);\n      // 恢复状态\n      _enabled = !_enabled;\n      notifyListeners();\n    }\n  }\n\n  Future<void> dispose() async {\n    try {\n      await WakelockPlus.disable();\n    } catch (e) {\n      AppLogger.error('[$_tag] 释放失败', e);\n    }\n    super.dispose();\n  }\n} "
  },
  {
    "path": "lib/core/subtitle/cache/subtitle_cache_manager.dart",
    "content": "import 'dart:convert';\nimport 'dart:typed_data';\n\nimport 'package:flutter_cache_manager/flutter_cache_manager.dart';\nimport 'package:asmrapp/utils/logger.dart';\n\nclass SubtitleCacheManager {\n  static const String key = 'subtitleCache';\n  \n  static final CacheManager instance = CacheManager(\n    Config(\n      key,\n      stalePeriod: const Duration(days: 365), // 字幕文件不会变更，设置较长的有效期\n      maxNrOfCacheObjects: 1000, // 最大缓存文件数\n      repo: JsonCacheInfoRepository(databaseName: key),\n      fileService: HttpFileService(),\n    ),\n  );\n\n  /// 获取缓存的字幕内容\n  static Future<String?> getCachedContent(String url) async {\n    try {\n      final file = await instance.getSingleFile(url);\n      AppLogger.debug('使用字幕缓存: $url');\n      return await file.readAsString();\n    } catch (e) {\n      AppLogger.error('读取字幕缓存失败', e);\n      return null;\n    }\n  }\n\n  /// 保存字幕内容到缓存\n  static Future<void> cacheContent(String url, String content) async {\n    try {\n      await instance.putFile(\n        url,\n        Uint8List.fromList(utf8.encode(content)),\n        fileExtension: 'txt',\n      );\n      AppLogger.debug('字幕已缓存: $url');\n    } catch (e) {\n      AppLogger.error('保存字幕缓存失败', e);\n    }\n  }\n\n  /// 清理缓存\n  static Future<void> clearCache() async {\n    try {\n      await instance.emptyCache();\n      AppLogger.debug('字幕缓存已清空');\n    } catch (e) {\n      AppLogger.error('清理字幕缓存失败', e);\n    }\n  }\n\n  /// 获取缓存大小\n  static Future<int> getSize() async {\n    try {\n      return instance.store.getCacheSize();\n    } catch (e) {\n      AppLogger.error('获取字幕缓存大小失败', e);\n      return 0;\n    }\n  }\n} "
  },
  {
    "path": "lib/core/subtitle/i_subtitle_service.dart",
    "content": "import 'package:asmrapp/core/audio/models/subtitle.dart';\n\nabstract class ISubtitleService {\n  // 字幕加载\n  Future<void> loadSubtitle(String url);\n  \n  // 字幕状态流\n  Stream<SubtitleList?> get subtitleStream;\n  \n  // 当前字幕流\n  Stream<Subtitle?> get currentSubtitleStream;\n  \n  // 当前字幕\n  Subtitle? get currentSubtitle;\n  \n  // 更新播放位置\n  void updatePosition(Duration position);\n  \n  // 资源释放\n  void dispose();\n  \n  // 添加这一行\n  SubtitleList? get subtitleList;  // 获取当前字幕列表\n  \n  // 添加清除字幕的方法\n  void clearSubtitle();\n  \n  Stream<SubtitleWithState?> get currentSubtitleWithStateStream;\n  SubtitleWithState? get currentSubtitleWithState;\n} "
  },
  {
    "path": "lib/core/subtitle/managers/subtitle_state_manager.dart",
    "content": "import 'dart:async';\nimport 'package:asmrapp/core/audio/models/subtitle.dart';\nimport 'package:asmrapp/utils/logger.dart';\n\nclass SubtitleStateManager {\n  SubtitleList? _subtitleList;\n  Subtitle? _currentSubtitle;\n  SubtitleWithState? _currentSubtitleWithState;\n\n  final _subtitleController = StreamController<SubtitleList?>.broadcast();\n  final _currentSubtitleController = StreamController<Subtitle?>.broadcast();\n  final _currentSubtitleWithStateController = StreamController<SubtitleWithState?>.broadcast();\n\n  Stream<SubtitleList?> get subtitleStream => _subtitleController.stream;\n  Stream<Subtitle?> get currentSubtitleStream => _currentSubtitleController.stream;\n  Stream<SubtitleWithState?> get currentSubtitleWithStateStream => \n      _currentSubtitleWithStateController.stream;\n\n  Subtitle? get currentSubtitle => _currentSubtitle;\n  SubtitleList? get subtitleList => _subtitleList;\n  SubtitleWithState? get currentSubtitleWithState => _currentSubtitleWithState;\n\n  void setSubtitleList(SubtitleList? subtitleList) {\n    _subtitleList = subtitleList;\n    _subtitleController.add(_subtitleList);\n  }\n\n  void updatePosition(Duration position) {\n    if (_subtitleList != null) {\n      final newSubtitleWithState = _subtitleList!.getCurrentSubtitle(position);\n      if (newSubtitleWithState?.subtitle != _currentSubtitleWithState?.subtitle) {\n        _currentSubtitleWithState = newSubtitleWithState;\n        _currentSubtitle = newSubtitleWithState?.subtitle;\n        AppLogger.debug('字幕更新: ${_currentSubtitle?.text ?? '无字幕'} (${newSubtitleWithState?.state})');\n        _currentSubtitleWithStateController.add(newSubtitleWithState);\n        _currentSubtitleController.add(_currentSubtitle);\n      }\n    }\n  }\n\n  void clear() {\n    _subtitleList = null;\n    _currentSubtitle = null;\n    _currentSubtitleWithState = null;\n    _subtitleController.add(null);\n    _currentSubtitleController.add(null);\n    _currentSubtitleWithStateController.add(null);\n    AppLogger.debug('字幕状态已清除');\n  }\n\n  void dispose() {\n    _subtitleController.close();\n    _currentSubtitleController.close();\n    _currentSubtitleWithStateController.close();\n  }\n} "
  },
  {
    "path": "lib/core/subtitle/parsers/lrc_parser.dart",
    "content": "import 'package:asmrapp/core/audio/models/subtitle.dart';\nimport 'package:asmrapp/core/subtitle/parsers/subtitle_parser.dart';\nimport 'package:asmrapp/utils/logger.dart';\n\nclass LrcParser extends BaseSubtitleParser {\n  static final _timeTagRegex = RegExp(r'\\[(\\d{2}):(\\d{2})\\.(\\d{2})\\]');\n  static final _idTagRegex = RegExp(r'^\\[(ar|ti|al|by|offset):(.+)\\]$');\n  \n  @override\n  bool canParse(String content) {\n    final lines = content.trim().split('\\n');\n    return lines.any((line) => _timeTagRegex.hasMatch(line));\n  }\n  \n  @override\n  SubtitleList doParse(String content) {\n    final lines = content.split('\\n');\n    final subtitles = <Subtitle>[];\n    final metadata = <String, String>{};\n    \n    for (final line in lines) {\n      final trimmedLine = line.trim();\n      if (trimmedLine.isEmpty) continue;\n      \n      // 检查是否是ID标签\n      final idMatch = _idTagRegex.firstMatch(trimmedLine);\n      if (idMatch != null) {\n        metadata[idMatch.group(1)!] = idMatch.group(2)!;\n        continue;\n      }\n      \n      // 解析时间标签和歌词\n      final timeMatches = _timeTagRegex.allMatches(trimmedLine);\n      if (timeMatches.isEmpty) continue;\n      \n      // 获取歌词内容 (移除所有时间标签)\n      final text = trimmedLine.replaceAll(_timeTagRegex, '').trim();\n      if (text.isEmpty) continue;\n      \n      // 一行可能有多个时间标签\n      for (final match in timeMatches) {\n        try {\n          final timestamp = _parseTimestamp(\n            minutes: match.group(1)!,\n            seconds: match.group(2)!,\n            milliseconds: match.group(3)!,\n          );\n          \n          subtitles.add(Subtitle(\n            start: timestamp,\n            end: timestamp + const Duration(seconds: 5), // 默认持续5秒\n            text: text,\n            index: subtitles.length,\n          ));\n        } catch (e) {\n          AppLogger.debug('解析LRC时间标签失败: $e');\n          continue;\n        }\n      }\n    }\n    \n    // 按时间排序\n    subtitles.sort((a, b) => a.start.compareTo(b.start));\n    \n    // 设置正确的结束时间\n    for (int i = 0; i < subtitles.length - 1; i++) {\n      subtitles[i] = Subtitle(\n        start: subtitles[i].start,\n        end: subtitles[i + 1].start,\n        text: subtitles[i].text,\n        index: i,\n      );\n    }\n    \n    AppLogger.debug('LRC解析完成: ${subtitles.length}条字幕, ${metadata.length}个元数据');\n    return SubtitleList(subtitles);\n  }\n  \n  Duration _parseTimestamp({\n    required String minutes,\n    required String seconds,\n    required String milliseconds,\n  }) {\n    return Duration(\n      minutes: int.parse(minutes),\n      seconds: int.parse(seconds),\n      milliseconds: int.parse(milliseconds) * 10,\n    );\n  }\n} "
  },
  {
    "path": "lib/core/subtitle/parsers/subtitle_parser.dart",
    "content": "import 'package:asmrapp/core/audio/models/subtitle.dart';\n\n/// 字幕解析器接口\nabstract class SubtitleParser {\n  /// 解析字幕内容\n  SubtitleList parse(String content);\n  \n  /// 检查内容格式是否匹配\n  bool canParse(String content);\n}\n\n/// 字幕解析器基类\nabstract class BaseSubtitleParser implements SubtitleParser {\n  @override\n  SubtitleList parse(String content) {\n    if (!canParse(content)) {\n      throw FormatException('不支持的字幕格式');\n    }\n    return doParse(content);\n  }\n  \n  /// 具体的解析实现\n  SubtitleList doParse(String content);\n} "
  },
  {
    "path": "lib/core/subtitle/parsers/subtitle_parser_factory.dart",
    "content": "import 'package:asmrapp/core/subtitle/parsers/subtitle_parser.dart';\nimport 'package:asmrapp/core/subtitle/parsers/vtt_parser.dart';\nimport 'package:asmrapp/core/subtitle/parsers/lrc_parser.dart';\nimport 'package:asmrapp/utils/logger.dart';\n\nclass SubtitleParserFactory {\n  static final List<SubtitleParser> _parsers = [\n    VttParser(),\n    LrcParser(),\n  ];\n  \n  static SubtitleParser? getParser(String content) {\n    try {\n      return _parsers.firstWhere((parser) => parser.canParse(content));\n    } catch (e) {\n      AppLogger.debug('没有找到匹配的字幕解析器');\n      return null;\n    }\n  }\n} "
  },
  {
    "path": "lib/core/subtitle/parsers/vtt_parser.dart",
    "content": "import 'package:asmrapp/core/audio/models/subtitle.dart';\nimport 'package:asmrapp/core/subtitle/parsers/subtitle_parser.dart';\n\nclass VttParser extends BaseSubtitleParser {\n  static final _vttHeaderRegex = RegExp(r'^WEBVTT');\n  \n  @override\n  bool canParse(String content) {\n    return content.trim().startsWith(_vttHeaderRegex);\n  }\n  \n  @override\n  SubtitleList doParse(String content) {\n    final lines = content.split('\\n');\n    final subtitles = <Subtitle>[];\n    int index = 0;\n    \n    // 跳过WEBVTT头部\n    while (index < lines.length && !lines[index].contains('-->')) {\n      index++;\n    }\n    \n    while (index < lines.length) {\n      final timeLine = lines[index];\n      if (timeLine.contains('-->')) {\n        final times = timeLine.split('-->');\n        if (times.length == 2) {\n          final start = _parseTimeString(times[0].trim());\n          final end = _parseTimeString(times[1].trim());\n          \n          // 收集字幕文本\n          index++;\n          String text = '';\n          while (index < lines.length && lines[index].trim().isNotEmpty) {\n            text += lines[index].trim() + '\\n';\n            index++;\n          }\n          \n          if (text.isNotEmpty) {\n            subtitles.add(Subtitle(\n              start: start,\n              end: end,\n              text: text.trim(),\n              index: subtitles.length,\n            ));\n          }\n        }\n      }\n      index++;\n    }\n    \n    return SubtitleList(subtitles);\n  }\n  \n  Duration _parseTimeString(String timeString) {\n    final parts = timeString.split(':');\n    if (parts.length != 3) throw FormatException('Invalid time format');\n    \n    final seconds = parts[2].split('.');\n    return Duration(\n      hours: int.parse(parts[0]),\n      minutes: int.parse(parts[1]),\n      seconds: int.parse(seconds[0]),\n      milliseconds: seconds.length > 1 ? int.parse(seconds[1].padRight(3, '0')) : 0,\n    );\n  }\n} "
  },
  {
    "path": "lib/core/subtitle/subtitle_loader.dart",
    "content": "import 'package:asmrapp/data/models/files/child.dart';\nimport 'package:asmrapp/data/models/files/files.dart';\nimport 'package:asmrapp/core/audio/models/file_path.dart';\nimport 'package:asmrapp/core/audio/models/subtitle.dart';\nimport 'package:dio/dio.dart';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:asmrapp/core/subtitle/utils/subtitle_matcher.dart';\nimport 'package:asmrapp/core/subtitle/parsers/subtitle_parser_factory.dart';\nimport 'package:asmrapp/core/subtitle/cache/subtitle_cache_manager.dart';\n\nclass SubtitleLoader {\n  final _dio = Dio();\n\n  // 查找字幕文件\n  Child? findSubtitleFile(Child audioFile, Files files) {\n    if (files.children == null || audioFile.title == null) {\n      AppLogger.debug('无法查找字幕文件: ${files.children == null ? '文件列表为空' : '当前文件名为空'}');\n      return null;\n    }\n\n    AppLogger.debug('开始查找字幕文件...');\n    \n    // 使用 FilePath 获取同级文件\n    final siblings = FilePath.getSiblings(audioFile, files);\n    \n    // 使用 SubtitleMatcher 查找匹配的字幕文件\n    final subtitleFile = SubtitleMatcher.findMatchingSubtitle(\n      audioFile.title!,\n      siblings\n    );\n    \n    if (subtitleFile != null) {\n      AppLogger.debug('找到字幕文件: ${subtitleFile.title}, URL: ${subtitleFile.mediaDownloadUrl}');\n    } else {\n      AppLogger.debug('在当前目录中未找到字幕文件');\n    }\n    \n    return subtitleFile;\n  }\n\n  // 修改: 加载字幕内容\n  Future<SubtitleList?> loadSubtitleContent(String url) async {\n    try {\n      // 首先尝试从缓存加载\n      final cachedContent = await SubtitleCacheManager.getCachedContent(url);\n      if (cachedContent != null) {\n        AppLogger.debug('从缓存加载字幕: $url');\n        return _parseSubtitleContent(cachedContent);\n      }\n\n      // 缓存未命中，从网络加载\n      AppLogger.debug('从网络加载字幕: $url');\n      final response = await _dio.get(url);\n      AppLogger.debug('字幕文件下载状态: ${response.statusCode}');\n      \n      if (response.statusCode == 200) {\n        final content = response.data as String;\n        \n        // 保存到缓存\n        await SubtitleCacheManager.cacheContent(url, content);\n        \n        return _parseSubtitleContent(content);\n      } else {\n        throw Exception('字幕下载失败: ${response.statusCode}');\n      }\n    } catch (e) {\n      AppLogger.debug('字幕加载失败: $e');\n      rethrow;\n    }\n  }\n\n  // 新增: 解析字幕内容的私有方法\n  SubtitleList? _parseSubtitleContent(String content) {\n    AppLogger.debug('字幕文件内容预览: ${content.substring(0, content.length > 100 ? 100 : content.length)}...');\n    \n    final parser = SubtitleParserFactory.getParser(content);\n    if (parser == null) {\n      throw Exception('不支持的字幕格式');\n    }\n    \n    final subtitleList = parser.parse(content);\n    AppLogger.debug('字幕解析完成，字幕数量: ${subtitleList.subtitles.length}');\n    \n    return subtitleList;\n  }\n} "
  },
  {
    "path": "lib/core/subtitle/subtitle_service.dart",
    "content": "import 'dart:async';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:asmrapp/core/audio/models/subtitle.dart';\nimport 'package:asmrapp/core/subtitle/i_subtitle_service.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:asmrapp/core/subtitle/subtitle_loader.dart';\nimport 'package:asmrapp/core/subtitle/managers/subtitle_state_manager.dart';\n\n\nclass SubtitleService implements ISubtitleService {\n  final _subtitleLoader = GetIt.I<SubtitleLoader>();\n  final _stateManager = SubtitleStateManager();\n  \n  @override\n  Stream<SubtitleList?> get subtitleStream => _stateManager.subtitleStream;\n  \n  @override\n  Stream<Subtitle?> get currentSubtitleStream => _stateManager.currentSubtitleStream;\n  \n  @override\n  Subtitle? get currentSubtitle => _stateManager.currentSubtitle;\n  \n  @override\n  Future<void> loadSubtitle(String url) async {\n    try {\n      clearSubtitle();\n      final subtitleList = await _subtitleLoader.loadSubtitleContent(url);\n      _stateManager.setSubtitleList(subtitleList);\n    } catch (e) {\n      AppLogger.debug('字幕加载失败: $e');\n      clearSubtitle();\n      rethrow;\n    }\n  }\n  \n  @override\n  void updatePosition(Duration position) {\n    _stateManager.updatePosition(position);\n  }\n\n  @override\n  void dispose() {\n    _stateManager.dispose();\n  }\n\n  @override\n  SubtitleList? get subtitleList => _stateManager.subtitleList;\n\n  @override\n  void clearSubtitle() {\n    _stateManager.clear();\n  }\n\n  @override\n  Stream<SubtitleWithState?> get currentSubtitleWithStateStream => \n      _stateManager.currentSubtitleWithStateStream;\n  \n  @override\n  SubtitleWithState? get currentSubtitleWithState => \n      _stateManager.currentSubtitleWithState;\n} "
  },
  {
    "path": "lib/core/subtitle/utils/subtitle_matcher.dart",
    "content": "import 'package:asmrapp/data/models/files/child.dart';\n\nclass SubtitleMatcher {\n  // 支持的字幕格式\n  static const supportedFormats = ['.vtt', '.lrc'];\n  \n  // 检查文件是否为字幕文件\n  static bool isSubtitleFile(String? fileName) {\n    if (fileName == null) return false;\n    return supportedFormats.any((format) => \n      fileName.toLowerCase().endsWith(format));\n  }\n  \n  // 获取音频文件的可能的字幕文件名列表\n  static List<String> getPossibleSubtitleNames(String audioFileName) {\n    final names = <String>[];\n    final baseName = _getBaseName(audioFileName);\n    \n    // 生成可能的字幕文件名\n    for (final format in supportedFormats) {\n      // 1. 直接替换扩展名: aaa.mp3 -> aaa.vtt\n      names.add('$baseName$format');\n      \n      // 2. 保留原扩展名: aaa.mp3 -> aaa.mp3.vtt\n      names.add('$audioFileName$format');\n    }\n    \n    return names;\n  }\n  \n  // 查找匹配的字幕文件\n  static Child? findMatchingSubtitle(String audioFileName, List<Child> siblings) {\n    final possibleNames = getPossibleSubtitleNames(audioFileName);\n    \n    // 遍历所有可能的字幕文件名\n    for (final subtitleName in possibleNames) {\n      try {\n        final subtitleFile = siblings.firstWhere(\n          (file) => file.title?.toLowerCase() == subtitleName.toLowerCase()\n        );\n        return subtitleFile;\n      } catch (_) {\n        // 继续查找下一个可能的文件名\n        continue;\n      }\n    }\n    \n    return null;\n  }\n  \n  // 获取不带扩展名的文件名\n  static String _getBaseName(String fileName) {\n    final lastDot = fileName.lastIndexOf('.');\n    if (lastDot == -1) return fileName;\n    return fileName.substring(0, lastDot);\n  }\n} "
  },
  {
    "path": "lib/core/theme/app_colors.dart",
    "content": "import 'package:flutter/material.dart';\n\n/// 应用颜色配置\nclass AppColors {\n  // 禁止实例化\n  const AppColors._();\n\n  // 亮色主题颜色\n  static const ColorScheme lightColorScheme = ColorScheme.light(\n    // 基础色调\n    primary: Color(0xFF6750A4),\n    onPrimary: Colors.white,\n    \n    // 表面颜色\n    surface: Colors.white,\n    surfaceVariant: Color(0xFFF4F4F4),\n    onSurface: Colors.black87,\n    surfaceContainerHighest: Color(0xFFE6E6E6),\n    \n    // 背景颜色\n    background: Colors.white,\n    onBackground: Colors.black87,\n    \n    // 错误状态颜色\n    error: Color(0xFFB3261E),\n    errorContainer: Color(0xFFF9DEDC),\n    onError: Colors.white,\n  );\n\n  // 暗色主题颜色\n  static const ColorScheme darkColorScheme = ColorScheme.dark(\n    // 基础色调\n    primary: Color(0xFFD0BCFF),\n    onPrimary: Color(0xFF381E72),\n    \n    // 表面颜色\n    surface: Color(0xFF1C1B1F),\n    surfaceVariant: Color(0xFF2B2930),\n    onSurface: Colors.white,\n    surfaceContainerHighest: Color(0xFF2B2B2B),\n    \n    // 背景颜色\n    background: Color(0xFF1C1B1F),\n    onBackground: Colors.white,\n    \n    // 错误状态颜色\n    error: Color(0xFFF2B8B5),\n    errorContainer: Color(0xFF8C1D18),\n    onError: Color(0xFF601410),\n  );\n} "
  },
  {
    "path": "lib/core/theme/app_theme.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'app_colors.dart';\n\n/// 应用主题配置\nclass AppTheme {\n  // 禁止实例化\n  const AppTheme._();\n\n  // 亮色主题\n  static ThemeData get light => ThemeData(\n    useMaterial3: true,\n    brightness: Brightness.light,\n    colorScheme: AppColors.lightColorScheme,\n    \n    // Card主题\n    cardTheme: const CardTheme(\n      elevation: 0,\n      shape: RoundedRectangleBorder(\n        borderRadius: BorderRadius.all(Radius.circular(12)),\n      ),\n    ),\n    \n    // AppBar主题\n    appBarTheme: const AppBarTheme(\n      centerTitle: true,\n      elevation: 0,\n      scrolledUnderElevation: 0,\n    ),\n  );\n\n  // 暗色主题\n  static ThemeData get dark => ThemeData(\n    useMaterial3: true,\n    brightness: Brightness.dark,\n    colorScheme: AppColors.darkColorScheme,\n    \n    // Card主题\n    cardTheme: const CardTheme(\n      elevation: 0,\n      shape: RoundedRectangleBorder(\n        borderRadius: BorderRadius.all(Radius.circular(12)),\n      ),\n    ),\n    \n    // AppBar主题\n    appBarTheme: const AppBarTheme(\n      centerTitle: true,\n      elevation: 0,\n      scrolledUnderElevation: 0,\n    ),\n  );\n} "
  },
  {
    "path": "lib/core/theme/theme_controller.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\n\nclass ThemeController extends ChangeNotifier {\n  static const String _themeKey = 'theme_mode';\n  final SharedPreferences _prefs;\n\n  ThemeController(this._prefs) {\n    // 从持久化存储加载主题模式\n    final savedThemeMode = _prefs.getString(_themeKey);\n    if (savedThemeMode != null) {\n      _themeMode = ThemeMode.values.firstWhere(\n        (mode) => mode.toString() == savedThemeMode,\n        orElse: () => ThemeMode.system,\n      );\n    }\n  }\n\n  ThemeMode _themeMode = ThemeMode.system;\n  \n  ThemeMode get themeMode => _themeMode;\n\n  // 切换主题模式\n  Future<void> setThemeMode(ThemeMode mode) async {\n    if (_themeMode == mode) return;\n    \n    _themeMode = mode;\n    notifyListeners();\n    \n    // 保存到持久化存储\n    await _prefs.setString(_themeKey, mode.toString());\n  }\n\n  // 切换到下一个主题模式\n  Future<void> toggleThemeMode() async {\n    final modes = ThemeMode.values;\n    final currentIndex = modes.indexOf(_themeMode);\n    final nextIndex = (currentIndex + 1) % modes.length;\n    await setThemeMode(modes[nextIndex]);\n  }\n} "
  },
  {
    "path": "lib/data/models/audio/README.md",
    "content": "# 音频数据模型\n\n此目录包含所有音频相关的数据模型定义。\n\n## 文件结构\n\n- `audio_track.dart` - 音频轨道模型\n- `playlist.dart` - 播放列表模型\n- `audio_metadata.dart` - 音频元数据模型\n\n## 说明\n\n这些模型用于：\n- 音频文件信息的封装\n- 播放列表数据的组织\n- 音频元数据的管理 "
  },
  {
    "path": "lib/data/models/auth/auth_resp/auth_resp.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'user.dart';\n\npart 'auth_resp.freezed.dart';\npart 'auth_resp.g.dart';\n\n@freezed\nclass AuthResp with _$AuthResp {\n  factory AuthResp({\n    User? user,\n    String? token,\n  }) = _AuthResp;\n\n  factory AuthResp.fromJson(Map<String, dynamic> json) =>\n      _$AuthRespFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/auth/auth_resp/auth_resp.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'auth_resp.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nAuthResp _$AuthRespFromJson(Map<String, dynamic> json) {\n  return _AuthResp.fromJson(json);\n}\n\n/// @nodoc\nmixin _$AuthResp {\n  User? get user => throw _privateConstructorUsedError;\n  String? get token => throw _privateConstructorUsedError;\n\n  /// Serializes this AuthResp to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of AuthResp\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $AuthRespCopyWith<AuthResp> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $AuthRespCopyWith<$Res> {\n  factory $AuthRespCopyWith(AuthResp value, $Res Function(AuthResp) then) =\n      _$AuthRespCopyWithImpl<$Res, AuthResp>;\n  @useResult\n  $Res call({User? user, String? token});\n\n  $UserCopyWith<$Res>? get user;\n}\n\n/// @nodoc\nclass _$AuthRespCopyWithImpl<$Res, $Val extends AuthResp>\n    implements $AuthRespCopyWith<$Res> {\n  _$AuthRespCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of AuthResp\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? user = freezed,\n    Object? token = freezed,\n  }) {\n    return _then(_value.copyWith(\n      user: freezed == user\n          ? _value.user\n          : user // ignore: cast_nullable_to_non_nullable\n              as User?,\n      token: freezed == token\n          ? _value.token\n          : token // ignore: cast_nullable_to_non_nullable\n              as String?,\n    ) as $Val);\n  }\n\n  /// Create a copy of AuthResp\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @pragma('vm:prefer-inline')\n  $UserCopyWith<$Res>? get user {\n    if (_value.user == null) {\n      return null;\n    }\n\n    return $UserCopyWith<$Res>(_value.user!, (value) {\n      return _then(_value.copyWith(user: value) as $Val);\n    });\n  }\n}\n\n/// @nodoc\nabstract class _$$AuthRespImplCopyWith<$Res>\n    implements $AuthRespCopyWith<$Res> {\n  factory _$$AuthRespImplCopyWith(\n          _$AuthRespImpl value, $Res Function(_$AuthRespImpl) then) =\n      __$$AuthRespImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call({User? user, String? token});\n\n  @override\n  $UserCopyWith<$Res>? get user;\n}\n\n/// @nodoc\nclass __$$AuthRespImplCopyWithImpl<$Res>\n    extends _$AuthRespCopyWithImpl<$Res, _$AuthRespImpl>\n    implements _$$AuthRespImplCopyWith<$Res> {\n  __$$AuthRespImplCopyWithImpl(\n      _$AuthRespImpl _value, $Res Function(_$AuthRespImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of AuthResp\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? user = freezed,\n    Object? token = freezed,\n  }) {\n    return _then(_$AuthRespImpl(\n      user: freezed == user\n          ? _value.user\n          : user // ignore: cast_nullable_to_non_nullable\n              as User?,\n      token: freezed == token\n          ? _value.token\n          : token // ignore: cast_nullable_to_non_nullable\n              as String?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$AuthRespImpl implements _AuthResp {\n  _$AuthRespImpl({this.user, this.token});\n\n  factory _$AuthRespImpl.fromJson(Map<String, dynamic> json) =>\n      _$$AuthRespImplFromJson(json);\n\n  @override\n  final User? user;\n  @override\n  final String? token;\n\n  @override\n  String toString() {\n    return 'AuthResp(user: $user, token: $token)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$AuthRespImpl &&\n            (identical(other.user, user) || other.user == user) &&\n            (identical(other.token, token) || other.token == token));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(runtimeType, user, token);\n\n  /// Create a copy of AuthResp\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$AuthRespImplCopyWith<_$AuthRespImpl> get copyWith =>\n      __$$AuthRespImplCopyWithImpl<_$AuthRespImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$AuthRespImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _AuthResp implements AuthResp {\n  factory _AuthResp({final User? user, final String? token}) = _$AuthRespImpl;\n\n  factory _AuthResp.fromJson(Map<String, dynamic> json) =\n      _$AuthRespImpl.fromJson;\n\n  @override\n  User? get user;\n  @override\n  String? get token;\n\n  /// Create a copy of AuthResp\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$AuthRespImplCopyWith<_$AuthRespImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/auth/auth_resp/auth_resp.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'auth_resp.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$AuthRespImpl _$$AuthRespImplFromJson(Map<String, dynamic> json) =>\n    _$AuthRespImpl(\n      user: json['user'] == null\n          ? null\n          : User.fromJson(json['user'] as Map<String, dynamic>),\n      token: json['token'] as String?,\n    );\n\nMap<String, dynamic> _$$AuthRespImplToJson(_$AuthRespImpl instance) =>\n    <String, dynamic>{\n      'user': instance.user,\n      'token': instance.token,\n    };\n"
  },
  {
    "path": "lib/data/models/auth/auth_resp/user.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'user.freezed.dart';\npart 'user.g.dart';\n\n@freezed\nclass User with _$User {\n  factory User({\n    bool? loggedIn,\n    String? name,\n    String? group,\n    dynamic email,\n    String? recommenderUuid,\n  }) = _User;\n\n  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/auth/auth_resp/user.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'user.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nUser _$UserFromJson(Map<String, dynamic> json) {\n  return _User.fromJson(json);\n}\n\n/// @nodoc\nmixin _$User {\n  bool? get loggedIn => throw _privateConstructorUsedError;\n  String? get name => throw _privateConstructorUsedError;\n  String? get group => throw _privateConstructorUsedError;\n  dynamic get email => throw _privateConstructorUsedError;\n  String? get recommenderUuid => throw _privateConstructorUsedError;\n\n  /// Serializes this User to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of User\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $UserCopyWith<User> get copyWith => throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $UserCopyWith<$Res> {\n  factory $UserCopyWith(User value, $Res Function(User) then) =\n      _$UserCopyWithImpl<$Res, User>;\n  @useResult\n  $Res call(\n      {bool? loggedIn,\n      String? name,\n      String? group,\n      dynamic email,\n      String? recommenderUuid});\n}\n\n/// @nodoc\nclass _$UserCopyWithImpl<$Res, $Val extends User>\n    implements $UserCopyWith<$Res> {\n  _$UserCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of User\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? loggedIn = freezed,\n    Object? name = freezed,\n    Object? group = freezed,\n    Object? email = freezed,\n    Object? recommenderUuid = freezed,\n  }) {\n    return _then(_value.copyWith(\n      loggedIn: freezed == loggedIn\n          ? _value.loggedIn\n          : loggedIn // ignore: cast_nullable_to_non_nullable\n              as bool?,\n      name: freezed == name\n          ? _value.name\n          : name // ignore: cast_nullable_to_non_nullable\n              as String?,\n      group: freezed == group\n          ? _value.group\n          : group // ignore: cast_nullable_to_non_nullable\n              as String?,\n      email: freezed == email\n          ? _value.email\n          : email // ignore: cast_nullable_to_non_nullable\n              as dynamic,\n      recommenderUuid: freezed == recommenderUuid\n          ? _value.recommenderUuid\n          : recommenderUuid // ignore: cast_nullable_to_non_nullable\n              as String?,\n    ) as $Val);\n  }\n}\n\n/// @nodoc\nabstract class _$$UserImplCopyWith<$Res> implements $UserCopyWith<$Res> {\n  factory _$$UserImplCopyWith(\n          _$UserImpl value, $Res Function(_$UserImpl) then) =\n      __$$UserImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call(\n      {bool? loggedIn,\n      String? name,\n      String? group,\n      dynamic email,\n      String? recommenderUuid});\n}\n\n/// @nodoc\nclass __$$UserImplCopyWithImpl<$Res>\n    extends _$UserCopyWithImpl<$Res, _$UserImpl>\n    implements _$$UserImplCopyWith<$Res> {\n  __$$UserImplCopyWithImpl(_$UserImpl _value, $Res Function(_$UserImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of User\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? loggedIn = freezed,\n    Object? name = freezed,\n    Object? group = freezed,\n    Object? email = freezed,\n    Object? recommenderUuid = freezed,\n  }) {\n    return _then(_$UserImpl(\n      loggedIn: freezed == loggedIn\n          ? _value.loggedIn\n          : loggedIn // ignore: cast_nullable_to_non_nullable\n              as bool?,\n      name: freezed == name\n          ? _value.name\n          : name // ignore: cast_nullable_to_non_nullable\n              as String?,\n      group: freezed == group\n          ? _value.group\n          : group // ignore: cast_nullable_to_non_nullable\n              as String?,\n      email: freezed == email\n          ? _value.email\n          : email // ignore: cast_nullable_to_non_nullable\n              as dynamic,\n      recommenderUuid: freezed == recommenderUuid\n          ? _value.recommenderUuid\n          : recommenderUuid // ignore: cast_nullable_to_non_nullable\n              as String?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$UserImpl implements _User {\n  _$UserImpl(\n      {this.loggedIn, this.name, this.group, this.email, this.recommenderUuid});\n\n  factory _$UserImpl.fromJson(Map<String, dynamic> json) =>\n      _$$UserImplFromJson(json);\n\n  @override\n  final bool? loggedIn;\n  @override\n  final String? name;\n  @override\n  final String? group;\n  @override\n  final dynamic email;\n  @override\n  final String? recommenderUuid;\n\n  @override\n  String toString() {\n    return 'User(loggedIn: $loggedIn, name: $name, group: $group, email: $email, recommenderUuid: $recommenderUuid)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$UserImpl &&\n            (identical(other.loggedIn, loggedIn) ||\n                other.loggedIn == loggedIn) &&\n            (identical(other.name, name) || other.name == name) &&\n            (identical(other.group, group) || other.group == group) &&\n            const DeepCollectionEquality().equals(other.email, email) &&\n            (identical(other.recommenderUuid, recommenderUuid) ||\n                other.recommenderUuid == recommenderUuid));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(runtimeType, loggedIn, name, group,\n      const DeepCollectionEquality().hash(email), recommenderUuid);\n\n  /// Create a copy of User\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$UserImplCopyWith<_$UserImpl> get copyWith =>\n      __$$UserImplCopyWithImpl<_$UserImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$UserImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _User implements User {\n  factory _User(\n      {final bool? loggedIn,\n      final String? name,\n      final String? group,\n      final dynamic email,\n      final String? recommenderUuid}) = _$UserImpl;\n\n  factory _User.fromJson(Map<String, dynamic> json) = _$UserImpl.fromJson;\n\n  @override\n  bool? get loggedIn;\n  @override\n  String? get name;\n  @override\n  String? get group;\n  @override\n  dynamic get email;\n  @override\n  String? get recommenderUuid;\n\n  /// Create a copy of User\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$UserImplCopyWith<_$UserImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/auth/auth_resp/user.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'user.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$UserImpl _$$UserImplFromJson(Map<String, dynamic> json) => _$UserImpl(\n      loggedIn: json['loggedIn'] as bool?,\n      name: json['name'] as String?,\n      group: json['group'] as String?,\n      email: json['email'],\n      recommenderUuid: json['recommenderUuid'] as String?,\n    );\n\nMap<String, dynamic> _$$UserImplToJson(_$UserImpl instance) =>\n    <String, dynamic>{\n      'loggedIn': instance.loggedIn,\n      'name': instance.name,\n      'group': instance.group,\n      'email': instance.email,\n      'recommenderUuid': instance.recommenderUuid,\n    };\n"
  },
  {
    "path": "lib/data/models/files/child.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'work.dart';\n\npart 'child.freezed.dart';\npart 'child.g.dart';\n\n@freezed\nclass Child with _$Child {\n  factory Child({\n    String? type,\n    String? title,\n    List<Child>? children,\n    String? hash,\n    Work? work,\n    String? workTitle,\n    String? mediaStreamUrl,\n    String? mediaDownloadUrl,\n    int? size,\n  }) = _Child;\n\n  factory Child.fromJson(Map<String, dynamic> json) => _$ChildFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/files/child.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'child.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nChild _$ChildFromJson(Map<String, dynamic> json) {\n  return _Child.fromJson(json);\n}\n\n/// @nodoc\nmixin _$Child {\n  String? get type => throw _privateConstructorUsedError;\n  String? get title => throw _privateConstructorUsedError;\n  List<Child>? get children => throw _privateConstructorUsedError;\n  String? get hash => throw _privateConstructorUsedError;\n  Work? get work => throw _privateConstructorUsedError;\n  String? get workTitle => throw _privateConstructorUsedError;\n  String? get mediaStreamUrl => throw _privateConstructorUsedError;\n  String? get mediaDownloadUrl => throw _privateConstructorUsedError;\n  int? get size => throw _privateConstructorUsedError;\n\n  /// Serializes this Child to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of Child\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $ChildCopyWith<Child> get copyWith => throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $ChildCopyWith<$Res> {\n  factory $ChildCopyWith(Child value, $Res Function(Child) then) =\n      _$ChildCopyWithImpl<$Res, Child>;\n  @useResult\n  $Res call(\n      {String? type,\n      String? title,\n      List<Child>? children,\n      String? hash,\n      Work? work,\n      String? workTitle,\n      String? mediaStreamUrl,\n      String? mediaDownloadUrl,\n      int? size});\n\n  $WorkCopyWith<$Res>? get work;\n}\n\n/// @nodoc\nclass _$ChildCopyWithImpl<$Res, $Val extends Child>\n    implements $ChildCopyWith<$Res> {\n  _$ChildCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of Child\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? type = freezed,\n    Object? title = freezed,\n    Object? children = freezed,\n    Object? hash = freezed,\n    Object? work = freezed,\n    Object? workTitle = freezed,\n    Object? mediaStreamUrl = freezed,\n    Object? mediaDownloadUrl = freezed,\n    Object? size = freezed,\n  }) {\n    return _then(_value.copyWith(\n      type: freezed == type\n          ? _value.type\n          : type // ignore: cast_nullable_to_non_nullable\n              as String?,\n      title: freezed == title\n          ? _value.title\n          : title // ignore: cast_nullable_to_non_nullable\n              as String?,\n      children: freezed == children\n          ? _value.children\n          : children // ignore: cast_nullable_to_non_nullable\n              as List<Child>?,\n      hash: freezed == hash\n          ? _value.hash\n          : hash // ignore: cast_nullable_to_non_nullable\n              as String?,\n      work: freezed == work\n          ? _value.work\n          : work // ignore: cast_nullable_to_non_nullable\n              as Work?,\n      workTitle: freezed == workTitle\n          ? _value.workTitle\n          : workTitle // ignore: cast_nullable_to_non_nullable\n              as String?,\n      mediaStreamUrl: freezed == mediaStreamUrl\n          ? _value.mediaStreamUrl\n          : mediaStreamUrl // ignore: cast_nullable_to_non_nullable\n              as String?,\n      mediaDownloadUrl: freezed == mediaDownloadUrl\n          ? _value.mediaDownloadUrl\n          : mediaDownloadUrl // ignore: cast_nullable_to_non_nullable\n              as String?,\n      size: freezed == size\n          ? _value.size\n          : size // ignore: cast_nullable_to_non_nullable\n              as int?,\n    ) as $Val);\n  }\n\n  /// Create a copy of Child\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @pragma('vm:prefer-inline')\n  $WorkCopyWith<$Res>? get work {\n    if (_value.work == null) {\n      return null;\n    }\n\n    return $WorkCopyWith<$Res>(_value.work!, (value) {\n      return _then(_value.copyWith(work: value) as $Val);\n    });\n  }\n}\n\n/// @nodoc\nabstract class _$$ChildImplCopyWith<$Res> implements $ChildCopyWith<$Res> {\n  factory _$$ChildImplCopyWith(\n          _$ChildImpl value, $Res Function(_$ChildImpl) then) =\n      __$$ChildImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call(\n      {String? type,\n      String? title,\n      List<Child>? children,\n      String? hash,\n      Work? work,\n      String? workTitle,\n      String? mediaStreamUrl,\n      String? mediaDownloadUrl,\n      int? size});\n\n  @override\n  $WorkCopyWith<$Res>? get work;\n}\n\n/// @nodoc\nclass __$$ChildImplCopyWithImpl<$Res>\n    extends _$ChildCopyWithImpl<$Res, _$ChildImpl>\n    implements _$$ChildImplCopyWith<$Res> {\n  __$$ChildImplCopyWithImpl(\n      _$ChildImpl _value, $Res Function(_$ChildImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of Child\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? type = freezed,\n    Object? title = freezed,\n    Object? children = freezed,\n    Object? hash = freezed,\n    Object? work = freezed,\n    Object? workTitle = freezed,\n    Object? mediaStreamUrl = freezed,\n    Object? mediaDownloadUrl = freezed,\n    Object? size = freezed,\n  }) {\n    return _then(_$ChildImpl(\n      type: freezed == type\n          ? _value.type\n          : type // ignore: cast_nullable_to_non_nullable\n              as String?,\n      title: freezed == title\n          ? _value.title\n          : title // ignore: cast_nullable_to_non_nullable\n              as String?,\n      children: freezed == children\n          ? _value._children\n          : children // ignore: cast_nullable_to_non_nullable\n              as List<Child>?,\n      hash: freezed == hash\n          ? _value.hash\n          : hash // ignore: cast_nullable_to_non_nullable\n              as String?,\n      work: freezed == work\n          ? _value.work\n          : work // ignore: cast_nullable_to_non_nullable\n              as Work?,\n      workTitle: freezed == workTitle\n          ? _value.workTitle\n          : workTitle // ignore: cast_nullable_to_non_nullable\n              as String?,\n      mediaStreamUrl: freezed == mediaStreamUrl\n          ? _value.mediaStreamUrl\n          : mediaStreamUrl // ignore: cast_nullable_to_non_nullable\n              as String?,\n      mediaDownloadUrl: freezed == mediaDownloadUrl\n          ? _value.mediaDownloadUrl\n          : mediaDownloadUrl // ignore: cast_nullable_to_non_nullable\n              as String?,\n      size: freezed == size\n          ? _value.size\n          : size // ignore: cast_nullable_to_non_nullable\n              as int?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$ChildImpl implements _Child {\n  _$ChildImpl(\n      {this.type,\n      this.title,\n      final List<Child>? children,\n      this.hash,\n      this.work,\n      this.workTitle,\n      this.mediaStreamUrl,\n      this.mediaDownloadUrl,\n      this.size})\n      : _children = children;\n\n  factory _$ChildImpl.fromJson(Map<String, dynamic> json) =>\n      _$$ChildImplFromJson(json);\n\n  @override\n  final String? type;\n  @override\n  final String? title;\n  final List<Child>? _children;\n  @override\n  List<Child>? get children {\n    final value = _children;\n    if (value == null) return null;\n    if (_children is EqualUnmodifiableListView) return _children;\n    // ignore: implicit_dynamic_type\n    return EqualUnmodifiableListView(value);\n  }\n\n  @override\n  final String? hash;\n  @override\n  final Work? work;\n  @override\n  final String? workTitle;\n  @override\n  final String? mediaStreamUrl;\n  @override\n  final String? mediaDownloadUrl;\n  @override\n  final int? size;\n\n  @override\n  String toString() {\n    return 'Child(type: $type, title: $title, children: $children, hash: $hash, work: $work, workTitle: $workTitle, mediaStreamUrl: $mediaStreamUrl, mediaDownloadUrl: $mediaDownloadUrl, size: $size)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$ChildImpl &&\n            (identical(other.type, type) || other.type == type) &&\n            (identical(other.title, title) || other.title == title) &&\n            const DeepCollectionEquality().equals(other._children, _children) &&\n            (identical(other.hash, hash) || other.hash == hash) &&\n            (identical(other.work, work) || other.work == work) &&\n            (identical(other.workTitle, workTitle) ||\n                other.workTitle == workTitle) &&\n            (identical(other.mediaStreamUrl, mediaStreamUrl) ||\n                other.mediaStreamUrl == mediaStreamUrl) &&\n            (identical(other.mediaDownloadUrl, mediaDownloadUrl) ||\n                other.mediaDownloadUrl == mediaDownloadUrl) &&\n            (identical(other.size, size) || other.size == size));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(\n      runtimeType,\n      type,\n      title,\n      const DeepCollectionEquality().hash(_children),\n      hash,\n      work,\n      workTitle,\n      mediaStreamUrl,\n      mediaDownloadUrl,\n      size);\n\n  /// Create a copy of Child\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$ChildImplCopyWith<_$ChildImpl> get copyWith =>\n      __$$ChildImplCopyWithImpl<_$ChildImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$ChildImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _Child implements Child {\n  factory _Child(\n      {final String? type,\n      final String? title,\n      final List<Child>? children,\n      final String? hash,\n      final Work? work,\n      final String? workTitle,\n      final String? mediaStreamUrl,\n      final String? mediaDownloadUrl,\n      final int? size}) = _$ChildImpl;\n\n  factory _Child.fromJson(Map<String, dynamic> json) = _$ChildImpl.fromJson;\n\n  @override\n  String? get type;\n  @override\n  String? get title;\n  @override\n  List<Child>? get children;\n  @override\n  String? get hash;\n  @override\n  Work? get work;\n  @override\n  String? get workTitle;\n  @override\n  String? get mediaStreamUrl;\n  @override\n  String? get mediaDownloadUrl;\n  @override\n  int? get size;\n\n  /// Create a copy of Child\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$ChildImplCopyWith<_$ChildImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/files/child.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'child.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$ChildImpl _$$ChildImplFromJson(Map<String, dynamic> json) => _$ChildImpl(\n      type: json['type'] as String?,\n      title: json['title'] as String?,\n      children: (json['children'] as List<dynamic>?)\n          ?.map((e) => Child.fromJson(e as Map<String, dynamic>))\n          .toList(),\n      hash: json['hash'] as String?,\n      work: json['work'] == null\n          ? null\n          : Work.fromJson(json['work'] as Map<String, dynamic>),\n      workTitle: json['workTitle'] as String?,\n      mediaStreamUrl: json['mediaStreamUrl'] as String?,\n      mediaDownloadUrl: json['mediaDownloadUrl'] as String?,\n      size: (json['size'] as num?)?.toInt(),\n    );\n\nMap<String, dynamic> _$$ChildImplToJson(_$ChildImpl instance) =>\n    <String, dynamic>{\n      'type': instance.type,\n      'title': instance.title,\n      'children': instance.children,\n      'hash': instance.hash,\n      'work': instance.work,\n      'workTitle': instance.workTitle,\n      'mediaStreamUrl': instance.mediaStreamUrl,\n      'mediaDownloadUrl': instance.mediaDownloadUrl,\n      'size': instance.size,\n    };\n"
  },
  {
    "path": "lib/data/models/files/files.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'child.dart';\n\npart 'files.freezed.dart';\npart 'files.g.dart';\n\n@freezed\nclass Files with _$Files {\n  factory Files({\n    String? type,\n    String? title,\n    List<Child>? children,\n  }) = _Files;\n\n  factory Files.fromJson(Map<String, dynamic> json) => _$FilesFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/files/files.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'files.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nFiles _$FilesFromJson(Map<String, dynamic> json) {\n  return _Files.fromJson(json);\n}\n\n/// @nodoc\nmixin _$Files {\n  String? get type => throw _privateConstructorUsedError;\n  String? get title => throw _privateConstructorUsedError;\n  List<Child>? get children => throw _privateConstructorUsedError;\n\n  /// Serializes this Files to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of Files\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $FilesCopyWith<Files> get copyWith => throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $FilesCopyWith<$Res> {\n  factory $FilesCopyWith(Files value, $Res Function(Files) then) =\n      _$FilesCopyWithImpl<$Res, Files>;\n  @useResult\n  $Res call({String? type, String? title, List<Child>? children});\n}\n\n/// @nodoc\nclass _$FilesCopyWithImpl<$Res, $Val extends Files>\n    implements $FilesCopyWith<$Res> {\n  _$FilesCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of Files\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? type = freezed,\n    Object? title = freezed,\n    Object? children = freezed,\n  }) {\n    return _then(_value.copyWith(\n      type: freezed == type\n          ? _value.type\n          : type // ignore: cast_nullable_to_non_nullable\n              as String?,\n      title: freezed == title\n          ? _value.title\n          : title // ignore: cast_nullable_to_non_nullable\n              as String?,\n      children: freezed == children\n          ? _value.children\n          : children // ignore: cast_nullable_to_non_nullable\n              as List<Child>?,\n    ) as $Val);\n  }\n}\n\n/// @nodoc\nabstract class _$$FilesImplCopyWith<$Res> implements $FilesCopyWith<$Res> {\n  factory _$$FilesImplCopyWith(\n          _$FilesImpl value, $Res Function(_$FilesImpl) then) =\n      __$$FilesImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call({String? type, String? title, List<Child>? children});\n}\n\n/// @nodoc\nclass __$$FilesImplCopyWithImpl<$Res>\n    extends _$FilesCopyWithImpl<$Res, _$FilesImpl>\n    implements _$$FilesImplCopyWith<$Res> {\n  __$$FilesImplCopyWithImpl(\n      _$FilesImpl _value, $Res Function(_$FilesImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of Files\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? type = freezed,\n    Object? title = freezed,\n    Object? children = freezed,\n  }) {\n    return _then(_$FilesImpl(\n      type: freezed == type\n          ? _value.type\n          : type // ignore: cast_nullable_to_non_nullable\n              as String?,\n      title: freezed == title\n          ? _value.title\n          : title // ignore: cast_nullable_to_non_nullable\n              as String?,\n      children: freezed == children\n          ? _value._children\n          : children // ignore: cast_nullable_to_non_nullable\n              as List<Child>?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$FilesImpl implements _Files {\n  _$FilesImpl({this.type, this.title, final List<Child>? children})\n      : _children = children;\n\n  factory _$FilesImpl.fromJson(Map<String, dynamic> json) =>\n      _$$FilesImplFromJson(json);\n\n  @override\n  final String? type;\n  @override\n  final String? title;\n  final List<Child>? _children;\n  @override\n  List<Child>? get children {\n    final value = _children;\n    if (value == null) return null;\n    if (_children is EqualUnmodifiableListView) return _children;\n    // ignore: implicit_dynamic_type\n    return EqualUnmodifiableListView(value);\n  }\n\n  @override\n  String toString() {\n    return 'Files(type: $type, title: $title, children: $children)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$FilesImpl &&\n            (identical(other.type, type) || other.type == type) &&\n            (identical(other.title, title) || other.title == title) &&\n            const DeepCollectionEquality().equals(other._children, _children));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(\n      runtimeType, type, title, const DeepCollectionEquality().hash(_children));\n\n  /// Create a copy of Files\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$FilesImplCopyWith<_$FilesImpl> get copyWith =>\n      __$$FilesImplCopyWithImpl<_$FilesImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$FilesImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _Files implements Files {\n  factory _Files(\n      {final String? type,\n      final String? title,\n      final List<Child>? children}) = _$FilesImpl;\n\n  factory _Files.fromJson(Map<String, dynamic> json) = _$FilesImpl.fromJson;\n\n  @override\n  String? get type;\n  @override\n  String? get title;\n  @override\n  List<Child>? get children;\n\n  /// Create a copy of Files\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$FilesImplCopyWith<_$FilesImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/files/files.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'files.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$FilesImpl _$$FilesImplFromJson(Map<String, dynamic> json) => _$FilesImpl(\n      type: json['type'] as String?,\n      title: json['title'] as String?,\n      children: (json['children'] as List<dynamic>?)\n          ?.map((e) => Child.fromJson(e as Map<String, dynamic>))\n          .toList(),\n    );\n\nMap<String, dynamic> _$$FilesImplToJson(_$FilesImpl instance) =>\n    <String, dynamic>{\n      'type': instance.type,\n      'title': instance.title,\n      'children': instance.children,\n    };\n"
  },
  {
    "path": "lib/data/models/files/work.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'work.freezed.dart';\npart 'work.g.dart';\n\n@freezed\nclass Work with _$Work {\n  factory Work({\n    int? id,\n    @JsonKey(name: 'source_id') String? sourceId,\n    @JsonKey(name: 'source_type') String? sourceType,\n  }) = _Work;\n\n  factory Work.fromJson(Map<String, dynamic> json) => _$WorkFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/files/work.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'work.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nWork _$WorkFromJson(Map<String, dynamic> json) {\n  return _Work.fromJson(json);\n}\n\n/// @nodoc\nmixin _$Work {\n  int? get id => throw _privateConstructorUsedError;\n  @JsonKey(name: 'source_id')\n  String? get sourceId => throw _privateConstructorUsedError;\n  @JsonKey(name: 'source_type')\n  String? get sourceType => throw _privateConstructorUsedError;\n\n  /// Serializes this Work to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of Work\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $WorkCopyWith<Work> get copyWith => throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $WorkCopyWith<$Res> {\n  factory $WorkCopyWith(Work value, $Res Function(Work) then) =\n      _$WorkCopyWithImpl<$Res, Work>;\n  @useResult\n  $Res call(\n      {int? id,\n      @JsonKey(name: 'source_id') String? sourceId,\n      @JsonKey(name: 'source_type') String? sourceType});\n}\n\n/// @nodoc\nclass _$WorkCopyWithImpl<$Res, $Val extends Work>\n    implements $WorkCopyWith<$Res> {\n  _$WorkCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of Work\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? id = freezed,\n    Object? sourceId = freezed,\n    Object? sourceType = freezed,\n  }) {\n    return _then(_value.copyWith(\n      id: freezed == id\n          ? _value.id\n          : id // ignore: cast_nullable_to_non_nullable\n              as int?,\n      sourceId: freezed == sourceId\n          ? _value.sourceId\n          : sourceId // ignore: cast_nullable_to_non_nullable\n              as String?,\n      sourceType: freezed == sourceType\n          ? _value.sourceType\n          : sourceType // ignore: cast_nullable_to_non_nullable\n              as String?,\n    ) as $Val);\n  }\n}\n\n/// @nodoc\nabstract class _$$WorkImplCopyWith<$Res> implements $WorkCopyWith<$Res> {\n  factory _$$WorkImplCopyWith(\n          _$WorkImpl value, $Res Function(_$WorkImpl) then) =\n      __$$WorkImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call(\n      {int? id,\n      @JsonKey(name: 'source_id') String? sourceId,\n      @JsonKey(name: 'source_type') String? sourceType});\n}\n\n/// @nodoc\nclass __$$WorkImplCopyWithImpl<$Res>\n    extends _$WorkCopyWithImpl<$Res, _$WorkImpl>\n    implements _$$WorkImplCopyWith<$Res> {\n  __$$WorkImplCopyWithImpl(_$WorkImpl _value, $Res Function(_$WorkImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of Work\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? id = freezed,\n    Object? sourceId = freezed,\n    Object? sourceType = freezed,\n  }) {\n    return _then(_$WorkImpl(\n      id: freezed == id\n          ? _value.id\n          : id // ignore: cast_nullable_to_non_nullable\n              as int?,\n      sourceId: freezed == sourceId\n          ? _value.sourceId\n          : sourceId // ignore: cast_nullable_to_non_nullable\n              as String?,\n      sourceType: freezed == sourceType\n          ? _value.sourceType\n          : sourceType // ignore: cast_nullable_to_non_nullable\n              as String?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$WorkImpl implements _Work {\n  _$WorkImpl(\n      {this.id,\n      @JsonKey(name: 'source_id') this.sourceId,\n      @JsonKey(name: 'source_type') this.sourceType});\n\n  factory _$WorkImpl.fromJson(Map<String, dynamic> json) =>\n      _$$WorkImplFromJson(json);\n\n  @override\n  final int? id;\n  @override\n  @JsonKey(name: 'source_id')\n  final String? sourceId;\n  @override\n  @JsonKey(name: 'source_type')\n  final String? sourceType;\n\n  @override\n  String toString() {\n    return 'Work(id: $id, sourceId: $sourceId, sourceType: $sourceType)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$WorkImpl &&\n            (identical(other.id, id) || other.id == id) &&\n            (identical(other.sourceId, sourceId) ||\n                other.sourceId == sourceId) &&\n            (identical(other.sourceType, sourceType) ||\n                other.sourceType == sourceType));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(runtimeType, id, sourceId, sourceType);\n\n  /// Create a copy of Work\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$WorkImplCopyWith<_$WorkImpl> get copyWith =>\n      __$$WorkImplCopyWithImpl<_$WorkImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$WorkImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _Work implements Work {\n  factory _Work(\n      {final int? id,\n      @JsonKey(name: 'source_id') final String? sourceId,\n      @JsonKey(name: 'source_type') final String? sourceType}) = _$WorkImpl;\n\n  factory _Work.fromJson(Map<String, dynamic> json) = _$WorkImpl.fromJson;\n\n  @override\n  int? get id;\n  @override\n  @JsonKey(name: 'source_id')\n  String? get sourceId;\n  @override\n  @JsonKey(name: 'source_type')\n  String? get sourceType;\n\n  /// Create a copy of Work\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$WorkImplCopyWith<_$WorkImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/files/work.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'work.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$WorkImpl _$$WorkImplFromJson(Map<String, dynamic> json) => _$WorkImpl(\n      id: (json['id'] as num?)?.toInt(),\n      sourceId: json['source_id'] as String?,\n      sourceType: json['source_type'] as String?,\n    );\n\nMap<String, dynamic> _$$WorkImplToJson(_$WorkImpl instance) =>\n    <String, dynamic>{\n      'id': instance.id,\n      'source_id': instance.sourceId,\n      'source_type': instance.sourceType,\n    };\n"
  },
  {
    "path": "lib/data/models/mark_lists/mark_lists.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'pagination.dart';\nimport 'playlist.dart';\n\npart 'mark_lists.freezed.dart';\npart 'mark_lists.g.dart';\n\n@freezed\nclass MarkLists with _$MarkLists {\n  factory MarkLists({\n    List<Playlist>? playlists,\n    Pagination? pagination,\n  }) = _MarkLists;\n\n  factory MarkLists.fromJson(Map<String, dynamic> json) =>\n      _$MarkListsFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/mark_lists/mark_lists.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'mark_lists.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nMarkLists _$MarkListsFromJson(Map<String, dynamic> json) {\n  return _MarkLists.fromJson(json);\n}\n\n/// @nodoc\nmixin _$MarkLists {\n  List<Playlist>? get playlists => throw _privateConstructorUsedError;\n  Pagination? get pagination => throw _privateConstructorUsedError;\n\n  /// Serializes this MarkLists to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of MarkLists\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $MarkListsCopyWith<MarkLists> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $MarkListsCopyWith<$Res> {\n  factory $MarkListsCopyWith(MarkLists value, $Res Function(MarkLists) then) =\n      _$MarkListsCopyWithImpl<$Res, MarkLists>;\n  @useResult\n  $Res call({List<Playlist>? playlists, Pagination? pagination});\n\n  $PaginationCopyWith<$Res>? get pagination;\n}\n\n/// @nodoc\nclass _$MarkListsCopyWithImpl<$Res, $Val extends MarkLists>\n    implements $MarkListsCopyWith<$Res> {\n  _$MarkListsCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of MarkLists\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? playlists = freezed,\n    Object? pagination = freezed,\n  }) {\n    return _then(_value.copyWith(\n      playlists: freezed == playlists\n          ? _value.playlists\n          : playlists // ignore: cast_nullable_to_non_nullable\n              as List<Playlist>?,\n      pagination: freezed == pagination\n          ? _value.pagination\n          : pagination // ignore: cast_nullable_to_non_nullable\n              as Pagination?,\n    ) as $Val);\n  }\n\n  /// Create a copy of MarkLists\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @pragma('vm:prefer-inline')\n  $PaginationCopyWith<$Res>? get pagination {\n    if (_value.pagination == null) {\n      return null;\n    }\n\n    return $PaginationCopyWith<$Res>(_value.pagination!, (value) {\n      return _then(_value.copyWith(pagination: value) as $Val);\n    });\n  }\n}\n\n/// @nodoc\nabstract class _$$MarkListsImplCopyWith<$Res>\n    implements $MarkListsCopyWith<$Res> {\n  factory _$$MarkListsImplCopyWith(\n          _$MarkListsImpl value, $Res Function(_$MarkListsImpl) then) =\n      __$$MarkListsImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call({List<Playlist>? playlists, Pagination? pagination});\n\n  @override\n  $PaginationCopyWith<$Res>? get pagination;\n}\n\n/// @nodoc\nclass __$$MarkListsImplCopyWithImpl<$Res>\n    extends _$MarkListsCopyWithImpl<$Res, _$MarkListsImpl>\n    implements _$$MarkListsImplCopyWith<$Res> {\n  __$$MarkListsImplCopyWithImpl(\n      _$MarkListsImpl _value, $Res Function(_$MarkListsImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of MarkLists\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? playlists = freezed,\n    Object? pagination = freezed,\n  }) {\n    return _then(_$MarkListsImpl(\n      playlists: freezed == playlists\n          ? _value._playlists\n          : playlists // ignore: cast_nullable_to_non_nullable\n              as List<Playlist>?,\n      pagination: freezed == pagination\n          ? _value.pagination\n          : pagination // ignore: cast_nullable_to_non_nullable\n              as Pagination?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$MarkListsImpl implements _MarkLists {\n  _$MarkListsImpl({final List<Playlist>? playlists, this.pagination})\n      : _playlists = playlists;\n\n  factory _$MarkListsImpl.fromJson(Map<String, dynamic> json) =>\n      _$$MarkListsImplFromJson(json);\n\n  final List<Playlist>? _playlists;\n  @override\n  List<Playlist>? get playlists {\n    final value = _playlists;\n    if (value == null) return null;\n    if (_playlists is EqualUnmodifiableListView) return _playlists;\n    // ignore: implicit_dynamic_type\n    return EqualUnmodifiableListView(value);\n  }\n\n  @override\n  final Pagination? pagination;\n\n  @override\n  String toString() {\n    return 'MarkLists(playlists: $playlists, pagination: $pagination)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$MarkListsImpl &&\n            const DeepCollectionEquality()\n                .equals(other._playlists, _playlists) &&\n            (identical(other.pagination, pagination) ||\n                other.pagination == pagination));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(\n      runtimeType, const DeepCollectionEquality().hash(_playlists), pagination);\n\n  /// Create a copy of MarkLists\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$MarkListsImplCopyWith<_$MarkListsImpl> get copyWith =>\n      __$$MarkListsImplCopyWithImpl<_$MarkListsImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$MarkListsImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _MarkLists implements MarkLists {\n  factory _MarkLists(\n      {final List<Playlist>? playlists,\n      final Pagination? pagination}) = _$MarkListsImpl;\n\n  factory _MarkLists.fromJson(Map<String, dynamic> json) =\n      _$MarkListsImpl.fromJson;\n\n  @override\n  List<Playlist>? get playlists;\n  @override\n  Pagination? get pagination;\n\n  /// Create a copy of MarkLists\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$MarkListsImplCopyWith<_$MarkListsImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/mark_lists/mark_lists.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'mark_lists.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$MarkListsImpl _$$MarkListsImplFromJson(Map<String, dynamic> json) =>\n    _$MarkListsImpl(\n      playlists: (json['playlists'] as List<dynamic>?)\n          ?.map((e) => Playlist.fromJson(e as Map<String, dynamic>))\n          .toList(),\n      pagination: json['pagination'] == null\n          ? null\n          : Pagination.fromJson(json['pagination'] as Map<String, dynamic>),\n    );\n\nMap<String, dynamic> _$$MarkListsImplToJson(_$MarkListsImpl instance) =>\n    <String, dynamic>{\n      'playlists': instance.playlists,\n      'pagination': instance.pagination,\n    };\n"
  },
  {
    "path": "lib/data/models/mark_lists/pagination.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'pagination.freezed.dart';\npart 'pagination.g.dart';\n\n@freezed\nclass Pagination with _$Pagination {\n  factory Pagination({\n    int? page,\n    int? pageSize,\n    int? totalCount,\n  }) = _Pagination;\n\n  factory Pagination.fromJson(Map<String, dynamic> json) =>\n      _$PaginationFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/mark_lists/pagination.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'pagination.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nPagination _$PaginationFromJson(Map<String, dynamic> json) {\n  return _Pagination.fromJson(json);\n}\n\n/// @nodoc\nmixin _$Pagination {\n  int? get page => throw _privateConstructorUsedError;\n  int? get pageSize => throw _privateConstructorUsedError;\n  int? get totalCount => throw _privateConstructorUsedError;\n\n  /// Serializes this Pagination to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of Pagination\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $PaginationCopyWith<Pagination> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $PaginationCopyWith<$Res> {\n  factory $PaginationCopyWith(\n          Pagination value, $Res Function(Pagination) then) =\n      _$PaginationCopyWithImpl<$Res, Pagination>;\n  @useResult\n  $Res call({int? page, int? pageSize, int? totalCount});\n}\n\n/// @nodoc\nclass _$PaginationCopyWithImpl<$Res, $Val extends Pagination>\n    implements $PaginationCopyWith<$Res> {\n  _$PaginationCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of Pagination\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? page = freezed,\n    Object? pageSize = freezed,\n    Object? totalCount = freezed,\n  }) {\n    return _then(_value.copyWith(\n      page: freezed == page\n          ? _value.page\n          : page // ignore: cast_nullable_to_non_nullable\n              as int?,\n      pageSize: freezed == pageSize\n          ? _value.pageSize\n          : pageSize // ignore: cast_nullable_to_non_nullable\n              as int?,\n      totalCount: freezed == totalCount\n          ? _value.totalCount\n          : totalCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n    ) as $Val);\n  }\n}\n\n/// @nodoc\nabstract class _$$PaginationImplCopyWith<$Res>\n    implements $PaginationCopyWith<$Res> {\n  factory _$$PaginationImplCopyWith(\n          _$PaginationImpl value, $Res Function(_$PaginationImpl) then) =\n      __$$PaginationImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call({int? page, int? pageSize, int? totalCount});\n}\n\n/// @nodoc\nclass __$$PaginationImplCopyWithImpl<$Res>\n    extends _$PaginationCopyWithImpl<$Res, _$PaginationImpl>\n    implements _$$PaginationImplCopyWith<$Res> {\n  __$$PaginationImplCopyWithImpl(\n      _$PaginationImpl _value, $Res Function(_$PaginationImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of Pagination\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? page = freezed,\n    Object? pageSize = freezed,\n    Object? totalCount = freezed,\n  }) {\n    return _then(_$PaginationImpl(\n      page: freezed == page\n          ? _value.page\n          : page // ignore: cast_nullable_to_non_nullable\n              as int?,\n      pageSize: freezed == pageSize\n          ? _value.pageSize\n          : pageSize // ignore: cast_nullable_to_non_nullable\n              as int?,\n      totalCount: freezed == totalCount\n          ? _value.totalCount\n          : totalCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$PaginationImpl implements _Pagination {\n  _$PaginationImpl({this.page, this.pageSize, this.totalCount});\n\n  factory _$PaginationImpl.fromJson(Map<String, dynamic> json) =>\n      _$$PaginationImplFromJson(json);\n\n  @override\n  final int? page;\n  @override\n  final int? pageSize;\n  @override\n  final int? totalCount;\n\n  @override\n  String toString() {\n    return 'Pagination(page: $page, pageSize: $pageSize, totalCount: $totalCount)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$PaginationImpl &&\n            (identical(other.page, page) || other.page == page) &&\n            (identical(other.pageSize, pageSize) ||\n                other.pageSize == pageSize) &&\n            (identical(other.totalCount, totalCount) ||\n                other.totalCount == totalCount));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(runtimeType, page, pageSize, totalCount);\n\n  /// Create a copy of Pagination\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$PaginationImplCopyWith<_$PaginationImpl> get copyWith =>\n      __$$PaginationImplCopyWithImpl<_$PaginationImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$PaginationImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _Pagination implements Pagination {\n  factory _Pagination(\n      {final int? page,\n      final int? pageSize,\n      final int? totalCount}) = _$PaginationImpl;\n\n  factory _Pagination.fromJson(Map<String, dynamic> json) =\n      _$PaginationImpl.fromJson;\n\n  @override\n  int? get page;\n  @override\n  int? get pageSize;\n  @override\n  int? get totalCount;\n\n  /// Create a copy of Pagination\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$PaginationImplCopyWith<_$PaginationImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/mark_lists/pagination.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'pagination.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$PaginationImpl _$$PaginationImplFromJson(Map<String, dynamic> json) =>\n    _$PaginationImpl(\n      page: (json['page'] as num?)?.toInt(),\n      pageSize: (json['pageSize'] as num?)?.toInt(),\n      totalCount: (json['totalCount'] as num?)?.toInt(),\n    );\n\nMap<String, dynamic> _$$PaginationImplToJson(_$PaginationImpl instance) =>\n    <String, dynamic>{\n      'page': instance.page,\n      'pageSize': instance.pageSize,\n      'totalCount': instance.totalCount,\n    };\n"
  },
  {
    "path": "lib/data/models/mark_lists/playlist.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'playlist.freezed.dart';\npart 'playlist.g.dart';\n\n@freezed\nclass Playlist with _$Playlist {\n  factory Playlist({\n    String? id,\n    @JsonKey(name: 'user_name') String? userName,\n    int? privacy,\n    String? locale,\n    @JsonKey(name: 'playback_count') int? playbackCount,\n    String? name,\n    String? description,\n    @JsonKey(name: 'created_at') String? createdAt,\n    @JsonKey(name: 'updated_at') String? updatedAt,\n    @JsonKey(name: 'works_count') int? worksCount,\n    @JsonKey(name: 'latestWorkID') dynamic latestWorkId,\n    String? mainCoverUrl,\n  }) = _Playlist;\n\n  factory Playlist.fromJson(Map<String, dynamic> json) =>\n      _$PlaylistFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/mark_lists/playlist.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'playlist.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nPlaylist _$PlaylistFromJson(Map<String, dynamic> json) {\n  return _Playlist.fromJson(json);\n}\n\n/// @nodoc\nmixin _$Playlist {\n  String? get id => throw _privateConstructorUsedError;\n  @JsonKey(name: 'user_name')\n  String? get userName => throw _privateConstructorUsedError;\n  int? get privacy => throw _privateConstructorUsedError;\n  String? get locale => throw _privateConstructorUsedError;\n  @JsonKey(name: 'playback_count')\n  int? get playbackCount => throw _privateConstructorUsedError;\n  String? get name => throw _privateConstructorUsedError;\n  String? get description => throw _privateConstructorUsedError;\n  @JsonKey(name: 'created_at')\n  String? get createdAt => throw _privateConstructorUsedError;\n  @JsonKey(name: 'updated_at')\n  String? get updatedAt => throw _privateConstructorUsedError;\n  @JsonKey(name: 'works_count')\n  int? get worksCount => throw _privateConstructorUsedError;\n  @JsonKey(name: 'latestWorkID')\n  dynamic get latestWorkId => throw _privateConstructorUsedError;\n  String? get mainCoverUrl => throw _privateConstructorUsedError;\n\n  /// Serializes this Playlist to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of Playlist\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $PlaylistCopyWith<Playlist> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $PlaylistCopyWith<$Res> {\n  factory $PlaylistCopyWith(Playlist value, $Res Function(Playlist) then) =\n      _$PlaylistCopyWithImpl<$Res, Playlist>;\n  @useResult\n  $Res call(\n      {String? id,\n      @JsonKey(name: 'user_name') String? userName,\n      int? privacy,\n      String? locale,\n      @JsonKey(name: 'playback_count') int? playbackCount,\n      String? name,\n      String? description,\n      @JsonKey(name: 'created_at') String? createdAt,\n      @JsonKey(name: 'updated_at') String? updatedAt,\n      @JsonKey(name: 'works_count') int? worksCount,\n      @JsonKey(name: 'latestWorkID') dynamic latestWorkId,\n      String? mainCoverUrl});\n}\n\n/// @nodoc\nclass _$PlaylistCopyWithImpl<$Res, $Val extends Playlist>\n    implements $PlaylistCopyWith<$Res> {\n  _$PlaylistCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of Playlist\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? id = freezed,\n    Object? userName = freezed,\n    Object? privacy = freezed,\n    Object? locale = freezed,\n    Object? playbackCount = freezed,\n    Object? name = freezed,\n    Object? description = freezed,\n    Object? createdAt = freezed,\n    Object? updatedAt = freezed,\n    Object? worksCount = freezed,\n    Object? latestWorkId = freezed,\n    Object? mainCoverUrl = freezed,\n  }) {\n    return _then(_value.copyWith(\n      id: freezed == id\n          ? _value.id\n          : id // ignore: cast_nullable_to_non_nullable\n              as String?,\n      userName: freezed == userName\n          ? _value.userName\n          : userName // ignore: cast_nullable_to_non_nullable\n              as String?,\n      privacy: freezed == privacy\n          ? _value.privacy\n          : privacy // ignore: cast_nullable_to_non_nullable\n              as int?,\n      locale: freezed == locale\n          ? _value.locale\n          : locale // ignore: cast_nullable_to_non_nullable\n              as String?,\n      playbackCount: freezed == playbackCount\n          ? _value.playbackCount\n          : playbackCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n      name: freezed == name\n          ? _value.name\n          : name // ignore: cast_nullable_to_non_nullable\n              as String?,\n      description: freezed == description\n          ? _value.description\n          : description // ignore: cast_nullable_to_non_nullable\n              as String?,\n      createdAt: freezed == createdAt\n          ? _value.createdAt\n          : createdAt // ignore: cast_nullable_to_non_nullable\n              as String?,\n      updatedAt: freezed == updatedAt\n          ? _value.updatedAt\n          : updatedAt // ignore: cast_nullable_to_non_nullable\n              as String?,\n      worksCount: freezed == worksCount\n          ? _value.worksCount\n          : worksCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n      latestWorkId: freezed == latestWorkId\n          ? _value.latestWorkId\n          : latestWorkId // ignore: cast_nullable_to_non_nullable\n              as dynamic,\n      mainCoverUrl: freezed == mainCoverUrl\n          ? _value.mainCoverUrl\n          : mainCoverUrl // ignore: cast_nullable_to_non_nullable\n              as String?,\n    ) as $Val);\n  }\n}\n\n/// @nodoc\nabstract class _$$PlaylistImplCopyWith<$Res>\n    implements $PlaylistCopyWith<$Res> {\n  factory _$$PlaylistImplCopyWith(\n          _$PlaylistImpl value, $Res Function(_$PlaylistImpl) then) =\n      __$$PlaylistImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call(\n      {String? id,\n      @JsonKey(name: 'user_name') String? userName,\n      int? privacy,\n      String? locale,\n      @JsonKey(name: 'playback_count') int? playbackCount,\n      String? name,\n      String? description,\n      @JsonKey(name: 'created_at') String? createdAt,\n      @JsonKey(name: 'updated_at') String? updatedAt,\n      @JsonKey(name: 'works_count') int? worksCount,\n      @JsonKey(name: 'latestWorkID') dynamic latestWorkId,\n      String? mainCoverUrl});\n}\n\n/// @nodoc\nclass __$$PlaylistImplCopyWithImpl<$Res>\n    extends _$PlaylistCopyWithImpl<$Res, _$PlaylistImpl>\n    implements _$$PlaylistImplCopyWith<$Res> {\n  __$$PlaylistImplCopyWithImpl(\n      _$PlaylistImpl _value, $Res Function(_$PlaylistImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of Playlist\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? id = freezed,\n    Object? userName = freezed,\n    Object? privacy = freezed,\n    Object? locale = freezed,\n    Object? playbackCount = freezed,\n    Object? name = freezed,\n    Object? description = freezed,\n    Object? createdAt = freezed,\n    Object? updatedAt = freezed,\n    Object? worksCount = freezed,\n    Object? latestWorkId = freezed,\n    Object? mainCoverUrl = freezed,\n  }) {\n    return _then(_$PlaylistImpl(\n      id: freezed == id\n          ? _value.id\n          : id // ignore: cast_nullable_to_non_nullable\n              as String?,\n      userName: freezed == userName\n          ? _value.userName\n          : userName // ignore: cast_nullable_to_non_nullable\n              as String?,\n      privacy: freezed == privacy\n          ? _value.privacy\n          : privacy // ignore: cast_nullable_to_non_nullable\n              as int?,\n      locale: freezed == locale\n          ? _value.locale\n          : locale // ignore: cast_nullable_to_non_nullable\n              as String?,\n      playbackCount: freezed == playbackCount\n          ? _value.playbackCount\n          : playbackCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n      name: freezed == name\n          ? _value.name\n          : name // ignore: cast_nullable_to_non_nullable\n              as String?,\n      description: freezed == description\n          ? _value.description\n          : description // ignore: cast_nullable_to_non_nullable\n              as String?,\n      createdAt: freezed == createdAt\n          ? _value.createdAt\n          : createdAt // ignore: cast_nullable_to_non_nullable\n              as String?,\n      updatedAt: freezed == updatedAt\n          ? _value.updatedAt\n          : updatedAt // ignore: cast_nullable_to_non_nullable\n              as String?,\n      worksCount: freezed == worksCount\n          ? _value.worksCount\n          : worksCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n      latestWorkId: freezed == latestWorkId\n          ? _value.latestWorkId\n          : latestWorkId // ignore: cast_nullable_to_non_nullable\n              as dynamic,\n      mainCoverUrl: freezed == mainCoverUrl\n          ? _value.mainCoverUrl\n          : mainCoverUrl // ignore: cast_nullable_to_non_nullable\n              as String?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$PlaylistImpl implements _Playlist {\n  _$PlaylistImpl(\n      {this.id,\n      @JsonKey(name: 'user_name') this.userName,\n      this.privacy,\n      this.locale,\n      @JsonKey(name: 'playback_count') this.playbackCount,\n      this.name,\n      this.description,\n      @JsonKey(name: 'created_at') this.createdAt,\n      @JsonKey(name: 'updated_at') this.updatedAt,\n      @JsonKey(name: 'works_count') this.worksCount,\n      @JsonKey(name: 'latestWorkID') this.latestWorkId,\n      this.mainCoverUrl});\n\n  factory _$PlaylistImpl.fromJson(Map<String, dynamic> json) =>\n      _$$PlaylistImplFromJson(json);\n\n  @override\n  final String? id;\n  @override\n  @JsonKey(name: 'user_name')\n  final String? userName;\n  @override\n  final int? privacy;\n  @override\n  final String? locale;\n  @override\n  @JsonKey(name: 'playback_count')\n  final int? playbackCount;\n  @override\n  final String? name;\n  @override\n  final String? description;\n  @override\n  @JsonKey(name: 'created_at')\n  final String? createdAt;\n  @override\n  @JsonKey(name: 'updated_at')\n  final String? updatedAt;\n  @override\n  @JsonKey(name: 'works_count')\n  final int? worksCount;\n  @override\n  @JsonKey(name: 'latestWorkID')\n  final dynamic latestWorkId;\n  @override\n  final String? mainCoverUrl;\n\n  @override\n  String toString() {\n    return 'Playlist(id: $id, userName: $userName, privacy: $privacy, locale: $locale, playbackCount: $playbackCount, name: $name, description: $description, createdAt: $createdAt, updatedAt: $updatedAt, worksCount: $worksCount, latestWorkId: $latestWorkId, mainCoverUrl: $mainCoverUrl)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$PlaylistImpl &&\n            (identical(other.id, id) || other.id == id) &&\n            (identical(other.userName, userName) ||\n                other.userName == userName) &&\n            (identical(other.privacy, privacy) || other.privacy == privacy) &&\n            (identical(other.locale, locale) || other.locale == locale) &&\n            (identical(other.playbackCount, playbackCount) ||\n                other.playbackCount == playbackCount) &&\n            (identical(other.name, name) || other.name == name) &&\n            (identical(other.description, description) ||\n                other.description == description) &&\n            (identical(other.createdAt, createdAt) ||\n                other.createdAt == createdAt) &&\n            (identical(other.updatedAt, updatedAt) ||\n                other.updatedAt == updatedAt) &&\n            (identical(other.worksCount, worksCount) ||\n                other.worksCount == worksCount) &&\n            const DeepCollectionEquality()\n                .equals(other.latestWorkId, latestWorkId) &&\n            (identical(other.mainCoverUrl, mainCoverUrl) ||\n                other.mainCoverUrl == mainCoverUrl));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(\n      runtimeType,\n      id,\n      userName,\n      privacy,\n      locale,\n      playbackCount,\n      name,\n      description,\n      createdAt,\n      updatedAt,\n      worksCount,\n      const DeepCollectionEquality().hash(latestWorkId),\n      mainCoverUrl);\n\n  /// Create a copy of Playlist\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$PlaylistImplCopyWith<_$PlaylistImpl> get copyWith =>\n      __$$PlaylistImplCopyWithImpl<_$PlaylistImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$PlaylistImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _Playlist implements Playlist {\n  factory _Playlist(\n      {final String? id,\n      @JsonKey(name: 'user_name') final String? userName,\n      final int? privacy,\n      final String? locale,\n      @JsonKey(name: 'playback_count') final int? playbackCount,\n      final String? name,\n      final String? description,\n      @JsonKey(name: 'created_at') final String? createdAt,\n      @JsonKey(name: 'updated_at') final String? updatedAt,\n      @JsonKey(name: 'works_count') final int? worksCount,\n      @JsonKey(name: 'latestWorkID') final dynamic latestWorkId,\n      final String? mainCoverUrl}) = _$PlaylistImpl;\n\n  factory _Playlist.fromJson(Map<String, dynamic> json) =\n      _$PlaylistImpl.fromJson;\n\n  @override\n  String? get id;\n  @override\n  @JsonKey(name: 'user_name')\n  String? get userName;\n  @override\n  int? get privacy;\n  @override\n  String? get locale;\n  @override\n  @JsonKey(name: 'playback_count')\n  int? get playbackCount;\n  @override\n  String? get name;\n  @override\n  String? get description;\n  @override\n  @JsonKey(name: 'created_at')\n  String? get createdAt;\n  @override\n  @JsonKey(name: 'updated_at')\n  String? get updatedAt;\n  @override\n  @JsonKey(name: 'works_count')\n  int? get worksCount;\n  @override\n  @JsonKey(name: 'latestWorkID')\n  dynamic get latestWorkId;\n  @override\n  String? get mainCoverUrl;\n\n  /// Create a copy of Playlist\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$PlaylistImplCopyWith<_$PlaylistImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/mark_lists/playlist.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'playlist.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$PlaylistImpl _$$PlaylistImplFromJson(Map<String, dynamic> json) =>\n    _$PlaylistImpl(\n      id: json['id'] as String?,\n      userName: json['user_name'] as String?,\n      privacy: (json['privacy'] as num?)?.toInt(),\n      locale: json['locale'] as String?,\n      playbackCount: (json['playback_count'] as num?)?.toInt(),\n      name: json['name'] as String?,\n      description: json['description'] as String?,\n      createdAt: json['created_at'] as String?,\n      updatedAt: json['updated_at'] as String?,\n      worksCount: (json['works_count'] as num?)?.toInt(),\n      latestWorkId: json['latestWorkID'],\n      mainCoverUrl: json['mainCoverUrl'] as String?,\n    );\n\nMap<String, dynamic> _$$PlaylistImplToJson(_$PlaylistImpl instance) =>\n    <String, dynamic>{\n      'id': instance.id,\n      'user_name': instance.userName,\n      'privacy': instance.privacy,\n      'locale': instance.locale,\n      'playback_count': instance.playbackCount,\n      'name': instance.name,\n      'description': instance.description,\n      'created_at': instance.createdAt,\n      'updated_at': instance.updatedAt,\n      'works_count': instance.worksCount,\n      'latestWorkID': instance.latestWorkId,\n      'mainCoverUrl': instance.mainCoverUrl,\n    };\n"
  },
  {
    "path": "lib/data/models/mark_status.dart",
    "content": "enum MarkStatus {\n  wantToListen('想听'),\n  listening('在听'),\n  listened('听过'),\n  relistening('重听'),\n  onHold('搁置');\n\n  final String label;\n  const MarkStatus(this.label);\n} "
  },
  {
    "path": "lib/data/models/my_lists/README.md",
    "content": "虽然已有相似结构，但为了方便管理，还是单独创建一个文件夹，专门用来处理“播放清单”这个页面的东西。"
  },
  {
    "path": "lib/data/models/my_lists/my_playlists/my_playlists.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'pagination.dart';\nimport 'playlist.dart';\n\npart 'my_playlists.freezed.dart';\npart 'my_playlists.g.dart';\n\n@freezed\nclass MyPlaylists with _$MyPlaylists {\n  factory MyPlaylists({\n    List<Playlist>? playlists,\n    Pagination? pagination,\n  }) = _MyPlaylists;\n\n  factory MyPlaylists.fromJson(Map<String, dynamic> json) =>\n      _$MyPlaylistsFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/my_lists/my_playlists/my_playlists.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'my_playlists.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nMyPlaylists _$MyPlaylistsFromJson(Map<String, dynamic> json) {\n  return _MyPlaylists.fromJson(json);\n}\n\n/// @nodoc\nmixin _$MyPlaylists {\n  List<Playlist>? get playlists => throw _privateConstructorUsedError;\n  Pagination? get pagination => throw _privateConstructorUsedError;\n\n  /// Serializes this MyPlaylists to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of MyPlaylists\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $MyPlaylistsCopyWith<MyPlaylists> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $MyPlaylistsCopyWith<$Res> {\n  factory $MyPlaylistsCopyWith(\n          MyPlaylists value, $Res Function(MyPlaylists) then) =\n      _$MyPlaylistsCopyWithImpl<$Res, MyPlaylists>;\n  @useResult\n  $Res call({List<Playlist>? playlists, Pagination? pagination});\n\n  $PaginationCopyWith<$Res>? get pagination;\n}\n\n/// @nodoc\nclass _$MyPlaylistsCopyWithImpl<$Res, $Val extends MyPlaylists>\n    implements $MyPlaylistsCopyWith<$Res> {\n  _$MyPlaylistsCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of MyPlaylists\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? playlists = freezed,\n    Object? pagination = freezed,\n  }) {\n    return _then(_value.copyWith(\n      playlists: freezed == playlists\n          ? _value.playlists\n          : playlists // ignore: cast_nullable_to_non_nullable\n              as List<Playlist>?,\n      pagination: freezed == pagination\n          ? _value.pagination\n          : pagination // ignore: cast_nullable_to_non_nullable\n              as Pagination?,\n    ) as $Val);\n  }\n\n  /// Create a copy of MyPlaylists\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @pragma('vm:prefer-inline')\n  $PaginationCopyWith<$Res>? get pagination {\n    if (_value.pagination == null) {\n      return null;\n    }\n\n    return $PaginationCopyWith<$Res>(_value.pagination!, (value) {\n      return _then(_value.copyWith(pagination: value) as $Val);\n    });\n  }\n}\n\n/// @nodoc\nabstract class _$$MyPlaylistsImplCopyWith<$Res>\n    implements $MyPlaylistsCopyWith<$Res> {\n  factory _$$MyPlaylistsImplCopyWith(\n          _$MyPlaylistsImpl value, $Res Function(_$MyPlaylistsImpl) then) =\n      __$$MyPlaylistsImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call({List<Playlist>? playlists, Pagination? pagination});\n\n  @override\n  $PaginationCopyWith<$Res>? get pagination;\n}\n\n/// @nodoc\nclass __$$MyPlaylistsImplCopyWithImpl<$Res>\n    extends _$MyPlaylistsCopyWithImpl<$Res, _$MyPlaylistsImpl>\n    implements _$$MyPlaylistsImplCopyWith<$Res> {\n  __$$MyPlaylistsImplCopyWithImpl(\n      _$MyPlaylistsImpl _value, $Res Function(_$MyPlaylistsImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of MyPlaylists\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? playlists = freezed,\n    Object? pagination = freezed,\n  }) {\n    return _then(_$MyPlaylistsImpl(\n      playlists: freezed == playlists\n          ? _value._playlists\n          : playlists // ignore: cast_nullable_to_non_nullable\n              as List<Playlist>?,\n      pagination: freezed == pagination\n          ? _value.pagination\n          : pagination // ignore: cast_nullable_to_non_nullable\n              as Pagination?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$MyPlaylistsImpl implements _MyPlaylists {\n  _$MyPlaylistsImpl({final List<Playlist>? playlists, this.pagination})\n      : _playlists = playlists;\n\n  factory _$MyPlaylistsImpl.fromJson(Map<String, dynamic> json) =>\n      _$$MyPlaylistsImplFromJson(json);\n\n  final List<Playlist>? _playlists;\n  @override\n  List<Playlist>? get playlists {\n    final value = _playlists;\n    if (value == null) return null;\n    if (_playlists is EqualUnmodifiableListView) return _playlists;\n    // ignore: implicit_dynamic_type\n    return EqualUnmodifiableListView(value);\n  }\n\n  @override\n  final Pagination? pagination;\n\n  @override\n  String toString() {\n    return 'MyPlaylists(playlists: $playlists, pagination: $pagination)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$MyPlaylistsImpl &&\n            const DeepCollectionEquality()\n                .equals(other._playlists, _playlists) &&\n            (identical(other.pagination, pagination) ||\n                other.pagination == pagination));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(\n      runtimeType, const DeepCollectionEquality().hash(_playlists), pagination);\n\n  /// Create a copy of MyPlaylists\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$MyPlaylistsImplCopyWith<_$MyPlaylistsImpl> get copyWith =>\n      __$$MyPlaylistsImplCopyWithImpl<_$MyPlaylistsImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$MyPlaylistsImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _MyPlaylists implements MyPlaylists {\n  factory _MyPlaylists(\n      {final List<Playlist>? playlists,\n      final Pagination? pagination}) = _$MyPlaylistsImpl;\n\n  factory _MyPlaylists.fromJson(Map<String, dynamic> json) =\n      _$MyPlaylistsImpl.fromJson;\n\n  @override\n  List<Playlist>? get playlists;\n  @override\n  Pagination? get pagination;\n\n  /// Create a copy of MyPlaylists\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$MyPlaylistsImplCopyWith<_$MyPlaylistsImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/my_lists/my_playlists/my_playlists.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'my_playlists.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$MyPlaylistsImpl _$$MyPlaylistsImplFromJson(Map<String, dynamic> json) =>\n    _$MyPlaylistsImpl(\n      playlists: (json['playlists'] as List<dynamic>?)\n          ?.map((e) => Playlist.fromJson(e as Map<String, dynamic>))\n          .toList(),\n      pagination: json['pagination'] == null\n          ? null\n          : Pagination.fromJson(json['pagination'] as Map<String, dynamic>),\n    );\n\nMap<String, dynamic> _$$MyPlaylistsImplToJson(_$MyPlaylistsImpl instance) =>\n    <String, dynamic>{\n      'playlists': instance.playlists,\n      'pagination': instance.pagination,\n    };\n"
  },
  {
    "path": "lib/data/models/my_lists/my_playlists/pagination.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'pagination.freezed.dart';\npart 'pagination.g.dart';\n\n@freezed\nclass Pagination with _$Pagination {\n  factory Pagination({\n    int? page,\n    int? pageSize,\n    int? totalCount,\n  }) = _Pagination;\n\n  factory Pagination.fromJson(Map<String, dynamic> json) =>\n      _$PaginationFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/my_lists/my_playlists/pagination.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'pagination.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nPagination _$PaginationFromJson(Map<String, dynamic> json) {\n  return _Pagination.fromJson(json);\n}\n\n/// @nodoc\nmixin _$Pagination {\n  int? get page => throw _privateConstructorUsedError;\n  int? get pageSize => throw _privateConstructorUsedError;\n  int? get totalCount => throw _privateConstructorUsedError;\n\n  /// Serializes this Pagination to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of Pagination\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $PaginationCopyWith<Pagination> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $PaginationCopyWith<$Res> {\n  factory $PaginationCopyWith(\n          Pagination value, $Res Function(Pagination) then) =\n      _$PaginationCopyWithImpl<$Res, Pagination>;\n  @useResult\n  $Res call({int? page, int? pageSize, int? totalCount});\n}\n\n/// @nodoc\nclass _$PaginationCopyWithImpl<$Res, $Val extends Pagination>\n    implements $PaginationCopyWith<$Res> {\n  _$PaginationCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of Pagination\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? page = freezed,\n    Object? pageSize = freezed,\n    Object? totalCount = freezed,\n  }) {\n    return _then(_value.copyWith(\n      page: freezed == page\n          ? _value.page\n          : page // ignore: cast_nullable_to_non_nullable\n              as int?,\n      pageSize: freezed == pageSize\n          ? _value.pageSize\n          : pageSize // ignore: cast_nullable_to_non_nullable\n              as int?,\n      totalCount: freezed == totalCount\n          ? _value.totalCount\n          : totalCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n    ) as $Val);\n  }\n}\n\n/// @nodoc\nabstract class _$$PaginationImplCopyWith<$Res>\n    implements $PaginationCopyWith<$Res> {\n  factory _$$PaginationImplCopyWith(\n          _$PaginationImpl value, $Res Function(_$PaginationImpl) then) =\n      __$$PaginationImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call({int? page, int? pageSize, int? totalCount});\n}\n\n/// @nodoc\nclass __$$PaginationImplCopyWithImpl<$Res>\n    extends _$PaginationCopyWithImpl<$Res, _$PaginationImpl>\n    implements _$$PaginationImplCopyWith<$Res> {\n  __$$PaginationImplCopyWithImpl(\n      _$PaginationImpl _value, $Res Function(_$PaginationImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of Pagination\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? page = freezed,\n    Object? pageSize = freezed,\n    Object? totalCount = freezed,\n  }) {\n    return _then(_$PaginationImpl(\n      page: freezed == page\n          ? _value.page\n          : page // ignore: cast_nullable_to_non_nullable\n              as int?,\n      pageSize: freezed == pageSize\n          ? _value.pageSize\n          : pageSize // ignore: cast_nullable_to_non_nullable\n              as int?,\n      totalCount: freezed == totalCount\n          ? _value.totalCount\n          : totalCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$PaginationImpl implements _Pagination {\n  _$PaginationImpl({this.page, this.pageSize, this.totalCount});\n\n  factory _$PaginationImpl.fromJson(Map<String, dynamic> json) =>\n      _$$PaginationImplFromJson(json);\n\n  @override\n  final int? page;\n  @override\n  final int? pageSize;\n  @override\n  final int? totalCount;\n\n  @override\n  String toString() {\n    return 'Pagination(page: $page, pageSize: $pageSize, totalCount: $totalCount)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$PaginationImpl &&\n            (identical(other.page, page) || other.page == page) &&\n            (identical(other.pageSize, pageSize) ||\n                other.pageSize == pageSize) &&\n            (identical(other.totalCount, totalCount) ||\n                other.totalCount == totalCount));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(runtimeType, page, pageSize, totalCount);\n\n  /// Create a copy of Pagination\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$PaginationImplCopyWith<_$PaginationImpl> get copyWith =>\n      __$$PaginationImplCopyWithImpl<_$PaginationImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$PaginationImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _Pagination implements Pagination {\n  factory _Pagination(\n      {final int? page,\n      final int? pageSize,\n      final int? totalCount}) = _$PaginationImpl;\n\n  factory _Pagination.fromJson(Map<String, dynamic> json) =\n      _$PaginationImpl.fromJson;\n\n  @override\n  int? get page;\n  @override\n  int? get pageSize;\n  @override\n  int? get totalCount;\n\n  /// Create a copy of Pagination\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$PaginationImplCopyWith<_$PaginationImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/my_lists/my_playlists/pagination.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'pagination.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$PaginationImpl _$$PaginationImplFromJson(Map<String, dynamic> json) =>\n    _$PaginationImpl(\n      page: (json['page'] as num?)?.toInt(),\n      pageSize: (json['pageSize'] as num?)?.toInt(),\n      totalCount: (json['totalCount'] as num?)?.toInt(),\n    );\n\nMap<String, dynamic> _$$PaginationImplToJson(_$PaginationImpl instance) =>\n    <String, dynamic>{\n      'page': instance.page,\n      'pageSize': instance.pageSize,\n      'totalCount': instance.totalCount,\n    };\n"
  },
  {
    "path": "lib/data/models/my_lists/my_playlists/playlist.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'playlist.freezed.dart';\npart 'playlist.g.dart';\n\n@freezed\nclass Playlist with _$Playlist {\n  factory Playlist({\n    String? id,\n    @JsonKey(name: 'user_name') String? userName,\n    int? privacy,\n    String? locale,\n    @JsonKey(name: 'playback_count') int? playbackCount,\n    String? name,\n    String? description,\n    @JsonKey(name: 'created_at') String? createdAt,\n    @JsonKey(name: 'updated_at') String? updatedAt,\n    @JsonKey(name: 'works_count') int? worksCount,\n    @JsonKey(name: 'latestWorkID') dynamic latestWorkId,\n    String? mainCoverUrl,\n  }) = _Playlist;\n\n  factory Playlist.fromJson(Map<String, dynamic> json) =>\n      _$PlaylistFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/my_lists/my_playlists/playlist.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'playlist.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nPlaylist _$PlaylistFromJson(Map<String, dynamic> json) {\n  return _Playlist.fromJson(json);\n}\n\n/// @nodoc\nmixin _$Playlist {\n  String? get id => throw _privateConstructorUsedError;\n  @JsonKey(name: 'user_name')\n  String? get userName => throw _privateConstructorUsedError;\n  int? get privacy => throw _privateConstructorUsedError;\n  String? get locale => throw _privateConstructorUsedError;\n  @JsonKey(name: 'playback_count')\n  int? get playbackCount => throw _privateConstructorUsedError;\n  String? get name => throw _privateConstructorUsedError;\n  String? get description => throw _privateConstructorUsedError;\n  @JsonKey(name: 'created_at')\n  String? get createdAt => throw _privateConstructorUsedError;\n  @JsonKey(name: 'updated_at')\n  String? get updatedAt => throw _privateConstructorUsedError;\n  @JsonKey(name: 'works_count')\n  int? get worksCount => throw _privateConstructorUsedError;\n  @JsonKey(name: 'latestWorkID')\n  dynamic get latestWorkId => throw _privateConstructorUsedError;\n  String? get mainCoverUrl => throw _privateConstructorUsedError;\n\n  /// Serializes this Playlist to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of Playlist\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $PlaylistCopyWith<Playlist> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $PlaylistCopyWith<$Res> {\n  factory $PlaylistCopyWith(Playlist value, $Res Function(Playlist) then) =\n      _$PlaylistCopyWithImpl<$Res, Playlist>;\n  @useResult\n  $Res call(\n      {String? id,\n      @JsonKey(name: 'user_name') String? userName,\n      int? privacy,\n      String? locale,\n      @JsonKey(name: 'playback_count') int? playbackCount,\n      String? name,\n      String? description,\n      @JsonKey(name: 'created_at') String? createdAt,\n      @JsonKey(name: 'updated_at') String? updatedAt,\n      @JsonKey(name: 'works_count') int? worksCount,\n      @JsonKey(name: 'latestWorkID') dynamic latestWorkId,\n      String? mainCoverUrl});\n}\n\n/// @nodoc\nclass _$PlaylistCopyWithImpl<$Res, $Val extends Playlist>\n    implements $PlaylistCopyWith<$Res> {\n  _$PlaylistCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of Playlist\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? id = freezed,\n    Object? userName = freezed,\n    Object? privacy = freezed,\n    Object? locale = freezed,\n    Object? playbackCount = freezed,\n    Object? name = freezed,\n    Object? description = freezed,\n    Object? createdAt = freezed,\n    Object? updatedAt = freezed,\n    Object? worksCount = freezed,\n    Object? latestWorkId = freezed,\n    Object? mainCoverUrl = freezed,\n  }) {\n    return _then(_value.copyWith(\n      id: freezed == id\n          ? _value.id\n          : id // ignore: cast_nullable_to_non_nullable\n              as String?,\n      userName: freezed == userName\n          ? _value.userName\n          : userName // ignore: cast_nullable_to_non_nullable\n              as String?,\n      privacy: freezed == privacy\n          ? _value.privacy\n          : privacy // ignore: cast_nullable_to_non_nullable\n              as int?,\n      locale: freezed == locale\n          ? _value.locale\n          : locale // ignore: cast_nullable_to_non_nullable\n              as String?,\n      playbackCount: freezed == playbackCount\n          ? _value.playbackCount\n          : playbackCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n      name: freezed == name\n          ? _value.name\n          : name // ignore: cast_nullable_to_non_nullable\n              as String?,\n      description: freezed == description\n          ? _value.description\n          : description // ignore: cast_nullable_to_non_nullable\n              as String?,\n      createdAt: freezed == createdAt\n          ? _value.createdAt\n          : createdAt // ignore: cast_nullable_to_non_nullable\n              as String?,\n      updatedAt: freezed == updatedAt\n          ? _value.updatedAt\n          : updatedAt // ignore: cast_nullable_to_non_nullable\n              as String?,\n      worksCount: freezed == worksCount\n          ? _value.worksCount\n          : worksCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n      latestWorkId: freezed == latestWorkId\n          ? _value.latestWorkId\n          : latestWorkId // ignore: cast_nullable_to_non_nullable\n              as dynamic,\n      mainCoverUrl: freezed == mainCoverUrl\n          ? _value.mainCoverUrl\n          : mainCoverUrl // ignore: cast_nullable_to_non_nullable\n              as String?,\n    ) as $Val);\n  }\n}\n\n/// @nodoc\nabstract class _$$PlaylistImplCopyWith<$Res>\n    implements $PlaylistCopyWith<$Res> {\n  factory _$$PlaylistImplCopyWith(\n          _$PlaylistImpl value, $Res Function(_$PlaylistImpl) then) =\n      __$$PlaylistImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call(\n      {String? id,\n      @JsonKey(name: 'user_name') String? userName,\n      int? privacy,\n      String? locale,\n      @JsonKey(name: 'playback_count') int? playbackCount,\n      String? name,\n      String? description,\n      @JsonKey(name: 'created_at') String? createdAt,\n      @JsonKey(name: 'updated_at') String? updatedAt,\n      @JsonKey(name: 'works_count') int? worksCount,\n      @JsonKey(name: 'latestWorkID') dynamic latestWorkId,\n      String? mainCoverUrl});\n}\n\n/// @nodoc\nclass __$$PlaylistImplCopyWithImpl<$Res>\n    extends _$PlaylistCopyWithImpl<$Res, _$PlaylistImpl>\n    implements _$$PlaylistImplCopyWith<$Res> {\n  __$$PlaylistImplCopyWithImpl(\n      _$PlaylistImpl _value, $Res Function(_$PlaylistImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of Playlist\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? id = freezed,\n    Object? userName = freezed,\n    Object? privacy = freezed,\n    Object? locale = freezed,\n    Object? playbackCount = freezed,\n    Object? name = freezed,\n    Object? description = freezed,\n    Object? createdAt = freezed,\n    Object? updatedAt = freezed,\n    Object? worksCount = freezed,\n    Object? latestWorkId = freezed,\n    Object? mainCoverUrl = freezed,\n  }) {\n    return _then(_$PlaylistImpl(\n      id: freezed == id\n          ? _value.id\n          : id // ignore: cast_nullable_to_non_nullable\n              as String?,\n      userName: freezed == userName\n          ? _value.userName\n          : userName // ignore: cast_nullable_to_non_nullable\n              as String?,\n      privacy: freezed == privacy\n          ? _value.privacy\n          : privacy // ignore: cast_nullable_to_non_nullable\n              as int?,\n      locale: freezed == locale\n          ? _value.locale\n          : locale // ignore: cast_nullable_to_non_nullable\n              as String?,\n      playbackCount: freezed == playbackCount\n          ? _value.playbackCount\n          : playbackCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n      name: freezed == name\n          ? _value.name\n          : name // ignore: cast_nullable_to_non_nullable\n              as String?,\n      description: freezed == description\n          ? _value.description\n          : description // ignore: cast_nullable_to_non_nullable\n              as String?,\n      createdAt: freezed == createdAt\n          ? _value.createdAt\n          : createdAt // ignore: cast_nullable_to_non_nullable\n              as String?,\n      updatedAt: freezed == updatedAt\n          ? _value.updatedAt\n          : updatedAt // ignore: cast_nullable_to_non_nullable\n              as String?,\n      worksCount: freezed == worksCount\n          ? _value.worksCount\n          : worksCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n      latestWorkId: freezed == latestWorkId\n          ? _value.latestWorkId\n          : latestWorkId // ignore: cast_nullable_to_non_nullable\n              as dynamic,\n      mainCoverUrl: freezed == mainCoverUrl\n          ? _value.mainCoverUrl\n          : mainCoverUrl // ignore: cast_nullable_to_non_nullable\n              as String?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$PlaylistImpl implements _Playlist {\n  _$PlaylistImpl(\n      {this.id,\n      @JsonKey(name: 'user_name') this.userName,\n      this.privacy,\n      this.locale,\n      @JsonKey(name: 'playback_count') this.playbackCount,\n      this.name,\n      this.description,\n      @JsonKey(name: 'created_at') this.createdAt,\n      @JsonKey(name: 'updated_at') this.updatedAt,\n      @JsonKey(name: 'works_count') this.worksCount,\n      @JsonKey(name: 'latestWorkID') this.latestWorkId,\n      this.mainCoverUrl});\n\n  factory _$PlaylistImpl.fromJson(Map<String, dynamic> json) =>\n      _$$PlaylistImplFromJson(json);\n\n  @override\n  final String? id;\n  @override\n  @JsonKey(name: 'user_name')\n  final String? userName;\n  @override\n  final int? privacy;\n  @override\n  final String? locale;\n  @override\n  @JsonKey(name: 'playback_count')\n  final int? playbackCount;\n  @override\n  final String? name;\n  @override\n  final String? description;\n  @override\n  @JsonKey(name: 'created_at')\n  final String? createdAt;\n  @override\n  @JsonKey(name: 'updated_at')\n  final String? updatedAt;\n  @override\n  @JsonKey(name: 'works_count')\n  final int? worksCount;\n  @override\n  @JsonKey(name: 'latestWorkID')\n  final dynamic latestWorkId;\n  @override\n  final String? mainCoverUrl;\n\n  @override\n  String toString() {\n    return 'Playlist(id: $id, userName: $userName, privacy: $privacy, locale: $locale, playbackCount: $playbackCount, name: $name, description: $description, createdAt: $createdAt, updatedAt: $updatedAt, worksCount: $worksCount, latestWorkId: $latestWorkId, mainCoverUrl: $mainCoverUrl)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$PlaylistImpl &&\n            (identical(other.id, id) || other.id == id) &&\n            (identical(other.userName, userName) ||\n                other.userName == userName) &&\n            (identical(other.privacy, privacy) || other.privacy == privacy) &&\n            (identical(other.locale, locale) || other.locale == locale) &&\n            (identical(other.playbackCount, playbackCount) ||\n                other.playbackCount == playbackCount) &&\n            (identical(other.name, name) || other.name == name) &&\n            (identical(other.description, description) ||\n                other.description == description) &&\n            (identical(other.createdAt, createdAt) ||\n                other.createdAt == createdAt) &&\n            (identical(other.updatedAt, updatedAt) ||\n                other.updatedAt == updatedAt) &&\n            (identical(other.worksCount, worksCount) ||\n                other.worksCount == worksCount) &&\n            const DeepCollectionEquality()\n                .equals(other.latestWorkId, latestWorkId) &&\n            (identical(other.mainCoverUrl, mainCoverUrl) ||\n                other.mainCoverUrl == mainCoverUrl));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(\n      runtimeType,\n      id,\n      userName,\n      privacy,\n      locale,\n      playbackCount,\n      name,\n      description,\n      createdAt,\n      updatedAt,\n      worksCount,\n      const DeepCollectionEquality().hash(latestWorkId),\n      mainCoverUrl);\n\n  /// Create a copy of Playlist\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$PlaylistImplCopyWith<_$PlaylistImpl> get copyWith =>\n      __$$PlaylistImplCopyWithImpl<_$PlaylistImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$PlaylistImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _Playlist implements Playlist {\n  factory _Playlist(\n      {final String? id,\n      @JsonKey(name: 'user_name') final String? userName,\n      final int? privacy,\n      final String? locale,\n      @JsonKey(name: 'playback_count') final int? playbackCount,\n      final String? name,\n      final String? description,\n      @JsonKey(name: 'created_at') final String? createdAt,\n      @JsonKey(name: 'updated_at') final String? updatedAt,\n      @JsonKey(name: 'works_count') final int? worksCount,\n      @JsonKey(name: 'latestWorkID') final dynamic latestWorkId,\n      final String? mainCoverUrl}) = _$PlaylistImpl;\n\n  factory _Playlist.fromJson(Map<String, dynamic> json) =\n      _$PlaylistImpl.fromJson;\n\n  @override\n  String? get id;\n  @override\n  @JsonKey(name: 'user_name')\n  String? get userName;\n  @override\n  int? get privacy;\n  @override\n  String? get locale;\n  @override\n  @JsonKey(name: 'playback_count')\n  int? get playbackCount;\n  @override\n  String? get name;\n  @override\n  String? get description;\n  @override\n  @JsonKey(name: 'created_at')\n  String? get createdAt;\n  @override\n  @JsonKey(name: 'updated_at')\n  String? get updatedAt;\n  @override\n  @JsonKey(name: 'works_count')\n  int? get worksCount;\n  @override\n  @JsonKey(name: 'latestWorkID')\n  dynamic get latestWorkId;\n  @override\n  String? get mainCoverUrl;\n\n  /// Create a copy of Playlist\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$PlaylistImplCopyWith<_$PlaylistImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/my_lists/my_playlists/playlist.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'playlist.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$PlaylistImpl _$$PlaylistImplFromJson(Map<String, dynamic> json) =>\n    _$PlaylistImpl(\n      id: json['id'] as String?,\n      userName: json['user_name'] as String?,\n      privacy: (json['privacy'] as num?)?.toInt(),\n      locale: json['locale'] as String?,\n      playbackCount: (json['playback_count'] as num?)?.toInt(),\n      name: json['name'] as String?,\n      description: json['description'] as String?,\n      createdAt: json['created_at'] as String?,\n      updatedAt: json['updated_at'] as String?,\n      worksCount: (json['works_count'] as num?)?.toInt(),\n      latestWorkId: json['latestWorkID'],\n      mainCoverUrl: json['mainCoverUrl'] as String?,\n    );\n\nMap<String, dynamic> _$$PlaylistImplToJson(_$PlaylistImpl instance) =>\n    <String, dynamic>{\n      'id': instance.id,\n      'user_name': instance.userName,\n      'privacy': instance.privacy,\n      'locale': instance.locale,\n      'playback_count': instance.playbackCount,\n      'name': instance.name,\n      'description': instance.description,\n      'created_at': instance.createdAt,\n      'updated_at': instance.updatedAt,\n      'works_count': instance.worksCount,\n      'latestWorkID': instance.latestWorkId,\n      'mainCoverUrl': instance.mainCoverUrl,\n    };\n"
  },
  {
    "path": "lib/data/models/playback/playback_state.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/data/models/files/files.dart';\nimport 'package:asmrapp/data/models/files/child.dart';\nimport 'package:asmrapp/core/audio/models/play_mode.dart';\n\npart 'playback_state.freezed.dart';\npart 'playback_state.g.dart';\n\n@freezed\nclass PlaybackState with _$PlaybackState {\n  const factory PlaybackState({\n    required Work work,\n    required Files files,\n    required Child currentFile,\n    required List<Child> playlist,\n    required int currentIndex,\n    required PlayMode playMode,\n    required int position,  // 使用毫秒存储\n    required String timestamp,  // ISO8601 格式\n  }) = _PlaybackState;\n\n  factory PlaybackState.fromJson(Map<String, dynamic> json) => \n      _$PlaybackStateFromJson(json);\n} "
  },
  {
    "path": "lib/data/models/playback/playback_state.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'playback_state.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nPlaybackState _$PlaybackStateFromJson(Map<String, dynamic> json) {\n  return _PlaybackState.fromJson(json);\n}\n\n/// @nodoc\nmixin _$PlaybackState {\n  Work get work => throw _privateConstructorUsedError;\n  Files get files => throw _privateConstructorUsedError;\n  Child get currentFile => throw _privateConstructorUsedError;\n  List<Child> get playlist => throw _privateConstructorUsedError;\n  int get currentIndex => throw _privateConstructorUsedError;\n  PlayMode get playMode => throw _privateConstructorUsedError;\n  int get position => throw _privateConstructorUsedError; // 使用毫秒存储\n  String get timestamp => throw _privateConstructorUsedError;\n\n  /// Serializes this PlaybackState to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of PlaybackState\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $PlaybackStateCopyWith<PlaybackState> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $PlaybackStateCopyWith<$Res> {\n  factory $PlaybackStateCopyWith(\n          PlaybackState value, $Res Function(PlaybackState) then) =\n      _$PlaybackStateCopyWithImpl<$Res, PlaybackState>;\n  @useResult\n  $Res call(\n      {Work work,\n      Files files,\n      Child currentFile,\n      List<Child> playlist,\n      int currentIndex,\n      PlayMode playMode,\n      int position,\n      String timestamp});\n\n  $WorkCopyWith<$Res> get work;\n  $FilesCopyWith<$Res> get files;\n  $ChildCopyWith<$Res> get currentFile;\n}\n\n/// @nodoc\nclass _$PlaybackStateCopyWithImpl<$Res, $Val extends PlaybackState>\n    implements $PlaybackStateCopyWith<$Res> {\n  _$PlaybackStateCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of PlaybackState\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? work = null,\n    Object? files = null,\n    Object? currentFile = null,\n    Object? playlist = null,\n    Object? currentIndex = null,\n    Object? playMode = null,\n    Object? position = null,\n    Object? timestamp = null,\n  }) {\n    return _then(_value.copyWith(\n      work: null == work\n          ? _value.work\n          : work // ignore: cast_nullable_to_non_nullable\n              as Work,\n      files: null == files\n          ? _value.files\n          : files // ignore: cast_nullable_to_non_nullable\n              as Files,\n      currentFile: null == currentFile\n          ? _value.currentFile\n          : currentFile // ignore: cast_nullable_to_non_nullable\n              as Child,\n      playlist: null == playlist\n          ? _value.playlist\n          : playlist // ignore: cast_nullable_to_non_nullable\n              as List<Child>,\n      currentIndex: null == currentIndex\n          ? _value.currentIndex\n          : currentIndex // ignore: cast_nullable_to_non_nullable\n              as int,\n      playMode: null == playMode\n          ? _value.playMode\n          : playMode // ignore: cast_nullable_to_non_nullable\n              as PlayMode,\n      position: null == position\n          ? _value.position\n          : position // ignore: cast_nullable_to_non_nullable\n              as int,\n      timestamp: null == timestamp\n          ? _value.timestamp\n          : timestamp // ignore: cast_nullable_to_non_nullable\n              as String,\n    ) as $Val);\n  }\n\n  /// Create a copy of PlaybackState\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @pragma('vm:prefer-inline')\n  $WorkCopyWith<$Res> get work {\n    return $WorkCopyWith<$Res>(_value.work, (value) {\n      return _then(_value.copyWith(work: value) as $Val);\n    });\n  }\n\n  /// Create a copy of PlaybackState\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @pragma('vm:prefer-inline')\n  $FilesCopyWith<$Res> get files {\n    return $FilesCopyWith<$Res>(_value.files, (value) {\n      return _then(_value.copyWith(files: value) as $Val);\n    });\n  }\n\n  /// Create a copy of PlaybackState\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @pragma('vm:prefer-inline')\n  $ChildCopyWith<$Res> get currentFile {\n    return $ChildCopyWith<$Res>(_value.currentFile, (value) {\n      return _then(_value.copyWith(currentFile: value) as $Val);\n    });\n  }\n}\n\n/// @nodoc\nabstract class _$$PlaybackStateImplCopyWith<$Res>\n    implements $PlaybackStateCopyWith<$Res> {\n  factory _$$PlaybackStateImplCopyWith(\n          _$PlaybackStateImpl value, $Res Function(_$PlaybackStateImpl) then) =\n      __$$PlaybackStateImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call(\n      {Work work,\n      Files files,\n      Child currentFile,\n      List<Child> playlist,\n      int currentIndex,\n      PlayMode playMode,\n      int position,\n      String timestamp});\n\n  @override\n  $WorkCopyWith<$Res> get work;\n  @override\n  $FilesCopyWith<$Res> get files;\n  @override\n  $ChildCopyWith<$Res> get currentFile;\n}\n\n/// @nodoc\nclass __$$PlaybackStateImplCopyWithImpl<$Res>\n    extends _$PlaybackStateCopyWithImpl<$Res, _$PlaybackStateImpl>\n    implements _$$PlaybackStateImplCopyWith<$Res> {\n  __$$PlaybackStateImplCopyWithImpl(\n      _$PlaybackStateImpl _value, $Res Function(_$PlaybackStateImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of PlaybackState\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? work = null,\n    Object? files = null,\n    Object? currentFile = null,\n    Object? playlist = null,\n    Object? currentIndex = null,\n    Object? playMode = null,\n    Object? position = null,\n    Object? timestamp = null,\n  }) {\n    return _then(_$PlaybackStateImpl(\n      work: null == work\n          ? _value.work\n          : work // ignore: cast_nullable_to_non_nullable\n              as Work,\n      files: null == files\n          ? _value.files\n          : files // ignore: cast_nullable_to_non_nullable\n              as Files,\n      currentFile: null == currentFile\n          ? _value.currentFile\n          : currentFile // ignore: cast_nullable_to_non_nullable\n              as Child,\n      playlist: null == playlist\n          ? _value._playlist\n          : playlist // ignore: cast_nullable_to_non_nullable\n              as List<Child>,\n      currentIndex: null == currentIndex\n          ? _value.currentIndex\n          : currentIndex // ignore: cast_nullable_to_non_nullable\n              as int,\n      playMode: null == playMode\n          ? _value.playMode\n          : playMode // ignore: cast_nullable_to_non_nullable\n              as PlayMode,\n      position: null == position\n          ? _value.position\n          : position // ignore: cast_nullable_to_non_nullable\n              as int,\n      timestamp: null == timestamp\n          ? _value.timestamp\n          : timestamp // ignore: cast_nullable_to_non_nullable\n              as String,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$PlaybackStateImpl implements _PlaybackState {\n  const _$PlaybackStateImpl(\n      {required this.work,\n      required this.files,\n      required this.currentFile,\n      required final List<Child> playlist,\n      required this.currentIndex,\n      required this.playMode,\n      required this.position,\n      required this.timestamp})\n      : _playlist = playlist;\n\n  factory _$PlaybackStateImpl.fromJson(Map<String, dynamic> json) =>\n      _$$PlaybackStateImplFromJson(json);\n\n  @override\n  final Work work;\n  @override\n  final Files files;\n  @override\n  final Child currentFile;\n  final List<Child> _playlist;\n  @override\n  List<Child> get playlist {\n    if (_playlist is EqualUnmodifiableListView) return _playlist;\n    // ignore: implicit_dynamic_type\n    return EqualUnmodifiableListView(_playlist);\n  }\n\n  @override\n  final int currentIndex;\n  @override\n  final PlayMode playMode;\n  @override\n  final int position;\n// 使用毫秒存储\n  @override\n  final String timestamp;\n\n  @override\n  String toString() {\n    return 'PlaybackState(work: $work, files: $files, currentFile: $currentFile, playlist: $playlist, currentIndex: $currentIndex, playMode: $playMode, position: $position, timestamp: $timestamp)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$PlaybackStateImpl &&\n            (identical(other.work, work) || other.work == work) &&\n            (identical(other.files, files) || other.files == files) &&\n            (identical(other.currentFile, currentFile) ||\n                other.currentFile == currentFile) &&\n            const DeepCollectionEquality().equals(other._playlist, _playlist) &&\n            (identical(other.currentIndex, currentIndex) ||\n                other.currentIndex == currentIndex) &&\n            (identical(other.playMode, playMode) ||\n                other.playMode == playMode) &&\n            (identical(other.position, position) ||\n                other.position == position) &&\n            (identical(other.timestamp, timestamp) ||\n                other.timestamp == timestamp));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(\n      runtimeType,\n      work,\n      files,\n      currentFile,\n      const DeepCollectionEquality().hash(_playlist),\n      currentIndex,\n      playMode,\n      position,\n      timestamp);\n\n  /// Create a copy of PlaybackState\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$PlaybackStateImplCopyWith<_$PlaybackStateImpl> get copyWith =>\n      __$$PlaybackStateImplCopyWithImpl<_$PlaybackStateImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$PlaybackStateImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _PlaybackState implements PlaybackState {\n  const factory _PlaybackState(\n      {required final Work work,\n      required final Files files,\n      required final Child currentFile,\n      required final List<Child> playlist,\n      required final int currentIndex,\n      required final PlayMode playMode,\n      required final int position,\n      required final String timestamp}) = _$PlaybackStateImpl;\n\n  factory _PlaybackState.fromJson(Map<String, dynamic> json) =\n      _$PlaybackStateImpl.fromJson;\n\n  @override\n  Work get work;\n  @override\n  Files get files;\n  @override\n  Child get currentFile;\n  @override\n  List<Child> get playlist;\n  @override\n  int get currentIndex;\n  @override\n  PlayMode get playMode;\n  @override\n  int get position; // 使用毫秒存储\n  @override\n  String get timestamp;\n\n  /// Create a copy of PlaybackState\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$PlaybackStateImplCopyWith<_$PlaybackStateImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/playback/playback_state.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'playback_state.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$PlaybackStateImpl _$$PlaybackStateImplFromJson(Map<String, dynamic> json) =>\n    _$PlaybackStateImpl(\n      work: Work.fromJson(json['work'] as Map<String, dynamic>),\n      files: Files.fromJson(json['files'] as Map<String, dynamic>),\n      currentFile: Child.fromJson(json['currentFile'] as Map<String, dynamic>),\n      playlist: (json['playlist'] as List<dynamic>)\n          .map((e) => Child.fromJson(e as Map<String, dynamic>))\n          .toList(),\n      currentIndex: (json['currentIndex'] as num).toInt(),\n      playMode: $enumDecode(_$PlayModeEnumMap, json['playMode']),\n      position: (json['position'] as num).toInt(),\n      timestamp: json['timestamp'] as String,\n    );\n\nMap<String, dynamic> _$$PlaybackStateImplToJson(_$PlaybackStateImpl instance) =>\n    <String, dynamic>{\n      'work': instance.work,\n      'files': instance.files,\n      'currentFile': instance.currentFile,\n      'playlist': instance.playlist,\n      'currentIndex': instance.currentIndex,\n      'playMode': _$PlayModeEnumMap[instance.playMode]!,\n      'position': instance.position,\n      'timestamp': instance.timestamp,\n    };\n\nconst _$PlayModeEnumMap = {\n  PlayMode.single: 'single',\n  PlayMode.loop: 'loop',\n  PlayMode.sequence: 'sequence',\n};\n"
  },
  {
    "path": "lib/data/models/playlists_with_exist_statu/pagination.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'pagination.freezed.dart';\npart 'pagination.g.dart';\n\n@freezed\nclass Pagination with _$Pagination {\n  factory Pagination({\n    int? page,\n    int? pageSize,\n    int? totalCount,\n  }) = _Pagination;\n\n  factory Pagination.fromJson(Map<String, dynamic> json) =>\n      _$PaginationFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/playlists_with_exist_statu/pagination.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'pagination.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nPagination _$PaginationFromJson(Map<String, dynamic> json) {\n  return _Pagination.fromJson(json);\n}\n\n/// @nodoc\nmixin _$Pagination {\n  int? get page => throw _privateConstructorUsedError;\n  int? get pageSize => throw _privateConstructorUsedError;\n  int? get totalCount => throw _privateConstructorUsedError;\n\n  /// Serializes this Pagination to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of Pagination\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $PaginationCopyWith<Pagination> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $PaginationCopyWith<$Res> {\n  factory $PaginationCopyWith(\n          Pagination value, $Res Function(Pagination) then) =\n      _$PaginationCopyWithImpl<$Res, Pagination>;\n  @useResult\n  $Res call({int? page, int? pageSize, int? totalCount});\n}\n\n/// @nodoc\nclass _$PaginationCopyWithImpl<$Res, $Val extends Pagination>\n    implements $PaginationCopyWith<$Res> {\n  _$PaginationCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of Pagination\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? page = freezed,\n    Object? pageSize = freezed,\n    Object? totalCount = freezed,\n  }) {\n    return _then(_value.copyWith(\n      page: freezed == page\n          ? _value.page\n          : page // ignore: cast_nullable_to_non_nullable\n              as int?,\n      pageSize: freezed == pageSize\n          ? _value.pageSize\n          : pageSize // ignore: cast_nullable_to_non_nullable\n              as int?,\n      totalCount: freezed == totalCount\n          ? _value.totalCount\n          : totalCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n    ) as $Val);\n  }\n}\n\n/// @nodoc\nabstract class _$$PaginationImplCopyWith<$Res>\n    implements $PaginationCopyWith<$Res> {\n  factory _$$PaginationImplCopyWith(\n          _$PaginationImpl value, $Res Function(_$PaginationImpl) then) =\n      __$$PaginationImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call({int? page, int? pageSize, int? totalCount});\n}\n\n/// @nodoc\nclass __$$PaginationImplCopyWithImpl<$Res>\n    extends _$PaginationCopyWithImpl<$Res, _$PaginationImpl>\n    implements _$$PaginationImplCopyWith<$Res> {\n  __$$PaginationImplCopyWithImpl(\n      _$PaginationImpl _value, $Res Function(_$PaginationImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of Pagination\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? page = freezed,\n    Object? pageSize = freezed,\n    Object? totalCount = freezed,\n  }) {\n    return _then(_$PaginationImpl(\n      page: freezed == page\n          ? _value.page\n          : page // ignore: cast_nullable_to_non_nullable\n              as int?,\n      pageSize: freezed == pageSize\n          ? _value.pageSize\n          : pageSize // ignore: cast_nullable_to_non_nullable\n              as int?,\n      totalCount: freezed == totalCount\n          ? _value.totalCount\n          : totalCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$PaginationImpl implements _Pagination {\n  _$PaginationImpl({this.page, this.pageSize, this.totalCount});\n\n  factory _$PaginationImpl.fromJson(Map<String, dynamic> json) =>\n      _$$PaginationImplFromJson(json);\n\n  @override\n  final int? page;\n  @override\n  final int? pageSize;\n  @override\n  final int? totalCount;\n\n  @override\n  String toString() {\n    return 'Pagination(page: $page, pageSize: $pageSize, totalCount: $totalCount)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$PaginationImpl &&\n            (identical(other.page, page) || other.page == page) &&\n            (identical(other.pageSize, pageSize) ||\n                other.pageSize == pageSize) &&\n            (identical(other.totalCount, totalCount) ||\n                other.totalCount == totalCount));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(runtimeType, page, pageSize, totalCount);\n\n  /// Create a copy of Pagination\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$PaginationImplCopyWith<_$PaginationImpl> get copyWith =>\n      __$$PaginationImplCopyWithImpl<_$PaginationImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$PaginationImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _Pagination implements Pagination {\n  factory _Pagination(\n      {final int? page,\n      final int? pageSize,\n      final int? totalCount}) = _$PaginationImpl;\n\n  factory _Pagination.fromJson(Map<String, dynamic> json) =\n      _$PaginationImpl.fromJson;\n\n  @override\n  int? get page;\n  @override\n  int? get pageSize;\n  @override\n  int? get totalCount;\n\n  /// Create a copy of Pagination\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$PaginationImplCopyWith<_$PaginationImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/playlists_with_exist_statu/pagination.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'pagination.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$PaginationImpl _$$PaginationImplFromJson(Map<String, dynamic> json) =>\n    _$PaginationImpl(\n      page: (json['page'] as num?)?.toInt(),\n      pageSize: (json['pageSize'] as num?)?.toInt(),\n      totalCount: (json['totalCount'] as num?)?.toInt(),\n    );\n\nMap<String, dynamic> _$$PaginationImplToJson(_$PaginationImpl instance) =>\n    <String, dynamic>{\n      'page': instance.page,\n      'pageSize': instance.pageSize,\n      'totalCount': instance.totalCount,\n    };\n"
  },
  {
    "path": "lib/data/models/playlists_with_exist_statu/playlist.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'playlist.freezed.dart';\npart 'playlist.g.dart';\n\n@freezed\nclass Playlist with _$Playlist {\n  factory Playlist({\n    String? id,\n    @JsonKey(name: 'user_name') String? userName,\n    int? privacy,\n    String? locale,\n    @JsonKey(name: 'playback_count') int? playbackCount,\n    String? name,\n    String? description,\n    @JsonKey(name: 'created_at') String? createdAt,\n    @JsonKey(name: 'updated_at') String? updatedAt,\n    @JsonKey(name: 'works_count') int? worksCount,\n    bool? exist,\n  }) = _Playlist;\n\n  factory Playlist.fromJson(Map<String, dynamic> json) =>\n      _$PlaylistFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/playlists_with_exist_statu/playlist.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'playlist.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nPlaylist _$PlaylistFromJson(Map<String, dynamic> json) {\n  return _Playlist.fromJson(json);\n}\n\n/// @nodoc\nmixin _$Playlist {\n  String? get id => throw _privateConstructorUsedError;\n  @JsonKey(name: 'user_name')\n  String? get userName => throw _privateConstructorUsedError;\n  int? get privacy => throw _privateConstructorUsedError;\n  String? get locale => throw _privateConstructorUsedError;\n  @JsonKey(name: 'playback_count')\n  int? get playbackCount => throw _privateConstructorUsedError;\n  String? get name => throw _privateConstructorUsedError;\n  String? get description => throw _privateConstructorUsedError;\n  @JsonKey(name: 'created_at')\n  String? get createdAt => throw _privateConstructorUsedError;\n  @JsonKey(name: 'updated_at')\n  String? get updatedAt => throw _privateConstructorUsedError;\n  @JsonKey(name: 'works_count')\n  int? get worksCount => throw _privateConstructorUsedError;\n  bool? get exist => throw _privateConstructorUsedError;\n\n  /// Serializes this Playlist to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of Playlist\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $PlaylistCopyWith<Playlist> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $PlaylistCopyWith<$Res> {\n  factory $PlaylistCopyWith(Playlist value, $Res Function(Playlist) then) =\n      _$PlaylistCopyWithImpl<$Res, Playlist>;\n  @useResult\n  $Res call(\n      {String? id,\n      @JsonKey(name: 'user_name') String? userName,\n      int? privacy,\n      String? locale,\n      @JsonKey(name: 'playback_count') int? playbackCount,\n      String? name,\n      String? description,\n      @JsonKey(name: 'created_at') String? createdAt,\n      @JsonKey(name: 'updated_at') String? updatedAt,\n      @JsonKey(name: 'works_count') int? worksCount,\n      bool? exist});\n}\n\n/// @nodoc\nclass _$PlaylistCopyWithImpl<$Res, $Val extends Playlist>\n    implements $PlaylistCopyWith<$Res> {\n  _$PlaylistCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of Playlist\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? id = freezed,\n    Object? userName = freezed,\n    Object? privacy = freezed,\n    Object? locale = freezed,\n    Object? playbackCount = freezed,\n    Object? name = freezed,\n    Object? description = freezed,\n    Object? createdAt = freezed,\n    Object? updatedAt = freezed,\n    Object? worksCount = freezed,\n    Object? exist = freezed,\n  }) {\n    return _then(_value.copyWith(\n      id: freezed == id\n          ? _value.id\n          : id // ignore: cast_nullable_to_non_nullable\n              as String?,\n      userName: freezed == userName\n          ? _value.userName\n          : userName // ignore: cast_nullable_to_non_nullable\n              as String?,\n      privacy: freezed == privacy\n          ? _value.privacy\n          : privacy // ignore: cast_nullable_to_non_nullable\n              as int?,\n      locale: freezed == locale\n          ? _value.locale\n          : locale // ignore: cast_nullable_to_non_nullable\n              as String?,\n      playbackCount: freezed == playbackCount\n          ? _value.playbackCount\n          : playbackCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n      name: freezed == name\n          ? _value.name\n          : name // ignore: cast_nullable_to_non_nullable\n              as String?,\n      description: freezed == description\n          ? _value.description\n          : description // ignore: cast_nullable_to_non_nullable\n              as String?,\n      createdAt: freezed == createdAt\n          ? _value.createdAt\n          : createdAt // ignore: cast_nullable_to_non_nullable\n              as String?,\n      updatedAt: freezed == updatedAt\n          ? _value.updatedAt\n          : updatedAt // ignore: cast_nullable_to_non_nullable\n              as String?,\n      worksCount: freezed == worksCount\n          ? _value.worksCount\n          : worksCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n      exist: freezed == exist\n          ? _value.exist\n          : exist // ignore: cast_nullable_to_non_nullable\n              as bool?,\n    ) as $Val);\n  }\n}\n\n/// @nodoc\nabstract class _$$PlaylistImplCopyWith<$Res>\n    implements $PlaylistCopyWith<$Res> {\n  factory _$$PlaylistImplCopyWith(\n          _$PlaylistImpl value, $Res Function(_$PlaylistImpl) then) =\n      __$$PlaylistImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call(\n      {String? id,\n      @JsonKey(name: 'user_name') String? userName,\n      int? privacy,\n      String? locale,\n      @JsonKey(name: 'playback_count') int? playbackCount,\n      String? name,\n      String? description,\n      @JsonKey(name: 'created_at') String? createdAt,\n      @JsonKey(name: 'updated_at') String? updatedAt,\n      @JsonKey(name: 'works_count') int? worksCount,\n      bool? exist});\n}\n\n/// @nodoc\nclass __$$PlaylistImplCopyWithImpl<$Res>\n    extends _$PlaylistCopyWithImpl<$Res, _$PlaylistImpl>\n    implements _$$PlaylistImplCopyWith<$Res> {\n  __$$PlaylistImplCopyWithImpl(\n      _$PlaylistImpl _value, $Res Function(_$PlaylistImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of Playlist\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? id = freezed,\n    Object? userName = freezed,\n    Object? privacy = freezed,\n    Object? locale = freezed,\n    Object? playbackCount = freezed,\n    Object? name = freezed,\n    Object? description = freezed,\n    Object? createdAt = freezed,\n    Object? updatedAt = freezed,\n    Object? worksCount = freezed,\n    Object? exist = freezed,\n  }) {\n    return _then(_$PlaylistImpl(\n      id: freezed == id\n          ? _value.id\n          : id // ignore: cast_nullable_to_non_nullable\n              as String?,\n      userName: freezed == userName\n          ? _value.userName\n          : userName // ignore: cast_nullable_to_non_nullable\n              as String?,\n      privacy: freezed == privacy\n          ? _value.privacy\n          : privacy // ignore: cast_nullable_to_non_nullable\n              as int?,\n      locale: freezed == locale\n          ? _value.locale\n          : locale // ignore: cast_nullable_to_non_nullable\n              as String?,\n      playbackCount: freezed == playbackCount\n          ? _value.playbackCount\n          : playbackCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n      name: freezed == name\n          ? _value.name\n          : name // ignore: cast_nullable_to_non_nullable\n              as String?,\n      description: freezed == description\n          ? _value.description\n          : description // ignore: cast_nullable_to_non_nullable\n              as String?,\n      createdAt: freezed == createdAt\n          ? _value.createdAt\n          : createdAt // ignore: cast_nullable_to_non_nullable\n              as String?,\n      updatedAt: freezed == updatedAt\n          ? _value.updatedAt\n          : updatedAt // ignore: cast_nullable_to_non_nullable\n              as String?,\n      worksCount: freezed == worksCount\n          ? _value.worksCount\n          : worksCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n      exist: freezed == exist\n          ? _value.exist\n          : exist // ignore: cast_nullable_to_non_nullable\n              as bool?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$PlaylistImpl implements _Playlist {\n  _$PlaylistImpl(\n      {this.id,\n      @JsonKey(name: 'user_name') this.userName,\n      this.privacy,\n      this.locale,\n      @JsonKey(name: 'playback_count') this.playbackCount,\n      this.name,\n      this.description,\n      @JsonKey(name: 'created_at') this.createdAt,\n      @JsonKey(name: 'updated_at') this.updatedAt,\n      @JsonKey(name: 'works_count') this.worksCount,\n      this.exist});\n\n  factory _$PlaylistImpl.fromJson(Map<String, dynamic> json) =>\n      _$$PlaylistImplFromJson(json);\n\n  @override\n  final String? id;\n  @override\n  @JsonKey(name: 'user_name')\n  final String? userName;\n  @override\n  final int? privacy;\n  @override\n  final String? locale;\n  @override\n  @JsonKey(name: 'playback_count')\n  final int? playbackCount;\n  @override\n  final String? name;\n  @override\n  final String? description;\n  @override\n  @JsonKey(name: 'created_at')\n  final String? createdAt;\n  @override\n  @JsonKey(name: 'updated_at')\n  final String? updatedAt;\n  @override\n  @JsonKey(name: 'works_count')\n  final int? worksCount;\n  @override\n  final bool? exist;\n\n  @override\n  String toString() {\n    return 'Playlist(id: $id, userName: $userName, privacy: $privacy, locale: $locale, playbackCount: $playbackCount, name: $name, description: $description, createdAt: $createdAt, updatedAt: $updatedAt, worksCount: $worksCount, exist: $exist)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$PlaylistImpl &&\n            (identical(other.id, id) || other.id == id) &&\n            (identical(other.userName, userName) ||\n                other.userName == userName) &&\n            (identical(other.privacy, privacy) || other.privacy == privacy) &&\n            (identical(other.locale, locale) || other.locale == locale) &&\n            (identical(other.playbackCount, playbackCount) ||\n                other.playbackCount == playbackCount) &&\n            (identical(other.name, name) || other.name == name) &&\n            (identical(other.description, description) ||\n                other.description == description) &&\n            (identical(other.createdAt, createdAt) ||\n                other.createdAt == createdAt) &&\n            (identical(other.updatedAt, updatedAt) ||\n                other.updatedAt == updatedAt) &&\n            (identical(other.worksCount, worksCount) ||\n                other.worksCount == worksCount) &&\n            (identical(other.exist, exist) || other.exist == exist));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(\n      runtimeType,\n      id,\n      userName,\n      privacy,\n      locale,\n      playbackCount,\n      name,\n      description,\n      createdAt,\n      updatedAt,\n      worksCount,\n      exist);\n\n  /// Create a copy of Playlist\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$PlaylistImplCopyWith<_$PlaylistImpl> get copyWith =>\n      __$$PlaylistImplCopyWithImpl<_$PlaylistImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$PlaylistImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _Playlist implements Playlist {\n  factory _Playlist(\n      {final String? id,\n      @JsonKey(name: 'user_name') final String? userName,\n      final int? privacy,\n      final String? locale,\n      @JsonKey(name: 'playback_count') final int? playbackCount,\n      final String? name,\n      final String? description,\n      @JsonKey(name: 'created_at') final String? createdAt,\n      @JsonKey(name: 'updated_at') final String? updatedAt,\n      @JsonKey(name: 'works_count') final int? worksCount,\n      final bool? exist}) = _$PlaylistImpl;\n\n  factory _Playlist.fromJson(Map<String, dynamic> json) =\n      _$PlaylistImpl.fromJson;\n\n  @override\n  String? get id;\n  @override\n  @JsonKey(name: 'user_name')\n  String? get userName;\n  @override\n  int? get privacy;\n  @override\n  String? get locale;\n  @override\n  @JsonKey(name: 'playback_count')\n  int? get playbackCount;\n  @override\n  String? get name;\n  @override\n  String? get description;\n  @override\n  @JsonKey(name: 'created_at')\n  String? get createdAt;\n  @override\n  @JsonKey(name: 'updated_at')\n  String? get updatedAt;\n  @override\n  @JsonKey(name: 'works_count')\n  int? get worksCount;\n  @override\n  bool? get exist;\n\n  /// Create a copy of Playlist\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$PlaylistImplCopyWith<_$PlaylistImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/playlists_with_exist_statu/playlist.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'playlist.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$PlaylistImpl _$$PlaylistImplFromJson(Map<String, dynamic> json) =>\n    _$PlaylistImpl(\n      id: json['id'] as String?,\n      userName: json['user_name'] as String?,\n      privacy: (json['privacy'] as num?)?.toInt(),\n      locale: json['locale'] as String?,\n      playbackCount: (json['playback_count'] as num?)?.toInt(),\n      name: json['name'] as String?,\n      description: json['description'] as String?,\n      createdAt: json['created_at'] as String?,\n      updatedAt: json['updated_at'] as String?,\n      worksCount: (json['works_count'] as num?)?.toInt(),\n      exist: json['exist'] as bool?,\n    );\n\nMap<String, dynamic> _$$PlaylistImplToJson(_$PlaylistImpl instance) =>\n    <String, dynamic>{\n      'id': instance.id,\n      'user_name': instance.userName,\n      'privacy': instance.privacy,\n      'locale': instance.locale,\n      'playback_count': instance.playbackCount,\n      'name': instance.name,\n      'description': instance.description,\n      'created_at': instance.createdAt,\n      'updated_at': instance.updatedAt,\n      'works_count': instance.worksCount,\n      'exist': instance.exist,\n    };\n"
  },
  {
    "path": "lib/data/models/playlists_with_exist_statu/playlists_with_exist_statu.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'pagination.dart';\nimport 'playlist.dart';\n\npart 'playlists_with_exist_statu.freezed.dart';\npart 'playlists_with_exist_statu.g.dart';\n\n@freezed\nclass PlaylistsWithExistStatu with _$PlaylistsWithExistStatu {\n  factory PlaylistsWithExistStatu({\n    List<Playlist>? playlists,\n    Pagination? pagination,\n  }) = _PlaylistsWithExistStatu;\n\n  factory PlaylistsWithExistStatu.fromJson(Map<String, dynamic> json) =>\n      _$PlaylistsWithExistStatuFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/playlists_with_exist_statu/playlists_with_exist_statu.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'playlists_with_exist_statu.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nPlaylistsWithExistStatu _$PlaylistsWithExistStatuFromJson(\n    Map<String, dynamic> json) {\n  return _PlaylistsWithExistStatu.fromJson(json);\n}\n\n/// @nodoc\nmixin _$PlaylistsWithExistStatu {\n  List<Playlist>? get playlists => throw _privateConstructorUsedError;\n  Pagination? get pagination => throw _privateConstructorUsedError;\n\n  /// Serializes this PlaylistsWithExistStatu to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of PlaylistsWithExistStatu\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $PlaylistsWithExistStatuCopyWith<PlaylistsWithExistStatu> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $PlaylistsWithExistStatuCopyWith<$Res> {\n  factory $PlaylistsWithExistStatuCopyWith(PlaylistsWithExistStatu value,\n          $Res Function(PlaylistsWithExistStatu) then) =\n      _$PlaylistsWithExistStatuCopyWithImpl<$Res, PlaylistsWithExistStatu>;\n  @useResult\n  $Res call({List<Playlist>? playlists, Pagination? pagination});\n\n  $PaginationCopyWith<$Res>? get pagination;\n}\n\n/// @nodoc\nclass _$PlaylistsWithExistStatuCopyWithImpl<$Res,\n        $Val extends PlaylistsWithExistStatu>\n    implements $PlaylistsWithExistStatuCopyWith<$Res> {\n  _$PlaylistsWithExistStatuCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of PlaylistsWithExistStatu\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? playlists = freezed,\n    Object? pagination = freezed,\n  }) {\n    return _then(_value.copyWith(\n      playlists: freezed == playlists\n          ? _value.playlists\n          : playlists // ignore: cast_nullable_to_non_nullable\n              as List<Playlist>?,\n      pagination: freezed == pagination\n          ? _value.pagination\n          : pagination // ignore: cast_nullable_to_non_nullable\n              as Pagination?,\n    ) as $Val);\n  }\n\n  /// Create a copy of PlaylistsWithExistStatu\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @pragma('vm:prefer-inline')\n  $PaginationCopyWith<$Res>? get pagination {\n    if (_value.pagination == null) {\n      return null;\n    }\n\n    return $PaginationCopyWith<$Res>(_value.pagination!, (value) {\n      return _then(_value.copyWith(pagination: value) as $Val);\n    });\n  }\n}\n\n/// @nodoc\nabstract class _$$PlaylistsWithExistStatuImplCopyWith<$Res>\n    implements $PlaylistsWithExistStatuCopyWith<$Res> {\n  factory _$$PlaylistsWithExistStatuImplCopyWith(\n          _$PlaylistsWithExistStatuImpl value,\n          $Res Function(_$PlaylistsWithExistStatuImpl) then) =\n      __$$PlaylistsWithExistStatuImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call({List<Playlist>? playlists, Pagination? pagination});\n\n  @override\n  $PaginationCopyWith<$Res>? get pagination;\n}\n\n/// @nodoc\nclass __$$PlaylistsWithExistStatuImplCopyWithImpl<$Res>\n    extends _$PlaylistsWithExistStatuCopyWithImpl<$Res,\n        _$PlaylistsWithExistStatuImpl>\n    implements _$$PlaylistsWithExistStatuImplCopyWith<$Res> {\n  __$$PlaylistsWithExistStatuImplCopyWithImpl(\n      _$PlaylistsWithExistStatuImpl _value,\n      $Res Function(_$PlaylistsWithExistStatuImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of PlaylistsWithExistStatu\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? playlists = freezed,\n    Object? pagination = freezed,\n  }) {\n    return _then(_$PlaylistsWithExistStatuImpl(\n      playlists: freezed == playlists\n          ? _value._playlists\n          : playlists // ignore: cast_nullable_to_non_nullable\n              as List<Playlist>?,\n      pagination: freezed == pagination\n          ? _value.pagination\n          : pagination // ignore: cast_nullable_to_non_nullable\n              as Pagination?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$PlaylistsWithExistStatuImpl implements _PlaylistsWithExistStatu {\n  _$PlaylistsWithExistStatuImpl(\n      {final List<Playlist>? playlists, this.pagination})\n      : _playlists = playlists;\n\n  factory _$PlaylistsWithExistStatuImpl.fromJson(Map<String, dynamic> json) =>\n      _$$PlaylistsWithExistStatuImplFromJson(json);\n\n  final List<Playlist>? _playlists;\n  @override\n  List<Playlist>? get playlists {\n    final value = _playlists;\n    if (value == null) return null;\n    if (_playlists is EqualUnmodifiableListView) return _playlists;\n    // ignore: implicit_dynamic_type\n    return EqualUnmodifiableListView(value);\n  }\n\n  @override\n  final Pagination? pagination;\n\n  @override\n  String toString() {\n    return 'PlaylistsWithExistStatu(playlists: $playlists, pagination: $pagination)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$PlaylistsWithExistStatuImpl &&\n            const DeepCollectionEquality()\n                .equals(other._playlists, _playlists) &&\n            (identical(other.pagination, pagination) ||\n                other.pagination == pagination));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(\n      runtimeType, const DeepCollectionEquality().hash(_playlists), pagination);\n\n  /// Create a copy of PlaylistsWithExistStatu\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$PlaylistsWithExistStatuImplCopyWith<_$PlaylistsWithExistStatuImpl>\n      get copyWith => __$$PlaylistsWithExistStatuImplCopyWithImpl<\n          _$PlaylistsWithExistStatuImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$PlaylistsWithExistStatuImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _PlaylistsWithExistStatu implements PlaylistsWithExistStatu {\n  factory _PlaylistsWithExistStatu(\n      {final List<Playlist>? playlists,\n      final Pagination? pagination}) = _$PlaylistsWithExistStatuImpl;\n\n  factory _PlaylistsWithExistStatu.fromJson(Map<String, dynamic> json) =\n      _$PlaylistsWithExistStatuImpl.fromJson;\n\n  @override\n  List<Playlist>? get playlists;\n  @override\n  Pagination? get pagination;\n\n  /// Create a copy of PlaylistsWithExistStatu\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$PlaylistsWithExistStatuImplCopyWith<_$PlaylistsWithExistStatuImpl>\n      get copyWith => throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/playlists_with_exist_statu/playlists_with_exist_statu.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'playlists_with_exist_statu.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$PlaylistsWithExistStatuImpl _$$PlaylistsWithExistStatuImplFromJson(\n        Map<String, dynamic> json) =>\n    _$PlaylistsWithExistStatuImpl(\n      playlists: (json['playlists'] as List<dynamic>?)\n          ?.map((e) => Playlist.fromJson(e as Map<String, dynamic>))\n          .toList(),\n      pagination: json['pagination'] == null\n          ? null\n          : Pagination.fromJson(json['pagination'] as Map<String, dynamic>),\n    );\n\nMap<String, dynamic> _$$PlaylistsWithExistStatuImplToJson(\n        _$PlaylistsWithExistStatuImpl instance) =>\n    <String, dynamic>{\n      'playlists': instance.playlists,\n      'pagination': instance.pagination,\n    };\n"
  },
  {
    "path": "lib/data/models/works/circle.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'circle.freezed.dart';\npart 'circle.g.dart';\n\n@freezed\nclass Circle with _$Circle {\n  factory Circle({\n    int? id,\n    String? name,\n    @JsonKey(name: 'source_id') String? sourceId,\n    @JsonKey(name: 'source_type') String? sourceType,\n  }) = _Circle;\n\n  factory Circle.fromJson(Map<String, dynamic> json) => _$CircleFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/works/circle.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'circle.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nCircle _$CircleFromJson(Map<String, dynamic> json) {\n  return _Circle.fromJson(json);\n}\n\n/// @nodoc\nmixin _$Circle {\n  int? get id => throw _privateConstructorUsedError;\n  String? get name => throw _privateConstructorUsedError;\n  @JsonKey(name: 'source_id')\n  String? get sourceId => throw _privateConstructorUsedError;\n  @JsonKey(name: 'source_type')\n  String? get sourceType => throw _privateConstructorUsedError;\n\n  /// Serializes this Circle to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of Circle\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $CircleCopyWith<Circle> get copyWith => throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $CircleCopyWith<$Res> {\n  factory $CircleCopyWith(Circle value, $Res Function(Circle) then) =\n      _$CircleCopyWithImpl<$Res, Circle>;\n  @useResult\n  $Res call(\n      {int? id,\n      String? name,\n      @JsonKey(name: 'source_id') String? sourceId,\n      @JsonKey(name: 'source_type') String? sourceType});\n}\n\n/// @nodoc\nclass _$CircleCopyWithImpl<$Res, $Val extends Circle>\n    implements $CircleCopyWith<$Res> {\n  _$CircleCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of Circle\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? id = freezed,\n    Object? name = freezed,\n    Object? sourceId = freezed,\n    Object? sourceType = freezed,\n  }) {\n    return _then(_value.copyWith(\n      id: freezed == id\n          ? _value.id\n          : id // ignore: cast_nullable_to_non_nullable\n              as int?,\n      name: freezed == name\n          ? _value.name\n          : name // ignore: cast_nullable_to_non_nullable\n              as String?,\n      sourceId: freezed == sourceId\n          ? _value.sourceId\n          : sourceId // ignore: cast_nullable_to_non_nullable\n              as String?,\n      sourceType: freezed == sourceType\n          ? _value.sourceType\n          : sourceType // ignore: cast_nullable_to_non_nullable\n              as String?,\n    ) as $Val);\n  }\n}\n\n/// @nodoc\nabstract class _$$CircleImplCopyWith<$Res> implements $CircleCopyWith<$Res> {\n  factory _$$CircleImplCopyWith(\n          _$CircleImpl value, $Res Function(_$CircleImpl) then) =\n      __$$CircleImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call(\n      {int? id,\n      String? name,\n      @JsonKey(name: 'source_id') String? sourceId,\n      @JsonKey(name: 'source_type') String? sourceType});\n}\n\n/// @nodoc\nclass __$$CircleImplCopyWithImpl<$Res>\n    extends _$CircleCopyWithImpl<$Res, _$CircleImpl>\n    implements _$$CircleImplCopyWith<$Res> {\n  __$$CircleImplCopyWithImpl(\n      _$CircleImpl _value, $Res Function(_$CircleImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of Circle\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? id = freezed,\n    Object? name = freezed,\n    Object? sourceId = freezed,\n    Object? sourceType = freezed,\n  }) {\n    return _then(_$CircleImpl(\n      id: freezed == id\n          ? _value.id\n          : id // ignore: cast_nullable_to_non_nullable\n              as int?,\n      name: freezed == name\n          ? _value.name\n          : name // ignore: cast_nullable_to_non_nullable\n              as String?,\n      sourceId: freezed == sourceId\n          ? _value.sourceId\n          : sourceId // ignore: cast_nullable_to_non_nullable\n              as String?,\n      sourceType: freezed == sourceType\n          ? _value.sourceType\n          : sourceType // ignore: cast_nullable_to_non_nullable\n              as String?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$CircleImpl implements _Circle {\n  _$CircleImpl(\n      {this.id,\n      this.name,\n      @JsonKey(name: 'source_id') this.sourceId,\n      @JsonKey(name: 'source_type') this.sourceType});\n\n  factory _$CircleImpl.fromJson(Map<String, dynamic> json) =>\n      _$$CircleImplFromJson(json);\n\n  @override\n  final int? id;\n  @override\n  final String? name;\n  @override\n  @JsonKey(name: 'source_id')\n  final String? sourceId;\n  @override\n  @JsonKey(name: 'source_type')\n  final String? sourceType;\n\n  @override\n  String toString() {\n    return 'Circle(id: $id, name: $name, sourceId: $sourceId, sourceType: $sourceType)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$CircleImpl &&\n            (identical(other.id, id) || other.id == id) &&\n            (identical(other.name, name) || other.name == name) &&\n            (identical(other.sourceId, sourceId) ||\n                other.sourceId == sourceId) &&\n            (identical(other.sourceType, sourceType) ||\n                other.sourceType == sourceType));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(runtimeType, id, name, sourceId, sourceType);\n\n  /// Create a copy of Circle\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$CircleImplCopyWith<_$CircleImpl> get copyWith =>\n      __$$CircleImplCopyWithImpl<_$CircleImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$CircleImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _Circle implements Circle {\n  factory _Circle(\n      {final int? id,\n      final String? name,\n      @JsonKey(name: 'source_id') final String? sourceId,\n      @JsonKey(name: 'source_type') final String? sourceType}) = _$CircleImpl;\n\n  factory _Circle.fromJson(Map<String, dynamic> json) = _$CircleImpl.fromJson;\n\n  @override\n  int? get id;\n  @override\n  String? get name;\n  @override\n  @JsonKey(name: 'source_id')\n  String? get sourceId;\n  @override\n  @JsonKey(name: 'source_type')\n  String? get sourceType;\n\n  /// Create a copy of Circle\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$CircleImplCopyWith<_$CircleImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/works/circle.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'circle.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$CircleImpl _$$CircleImplFromJson(Map<String, dynamic> json) => _$CircleImpl(\n      id: (json['id'] as num?)?.toInt(),\n      name: json['name'] as String?,\n      sourceId: json['source_id'] as String?,\n      sourceType: json['source_type'] as String?,\n    );\n\nMap<String, dynamic> _$$CircleImplToJson(_$CircleImpl instance) =>\n    <String, dynamic>{\n      'id': instance.id,\n      'name': instance.name,\n      'source_id': instance.sourceId,\n      'source_type': instance.sourceType,\n    };\n"
  },
  {
    "path": "lib/data/models/works/en_us.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'en_us.freezed.dart';\npart 'en_us.g.dart';\n\n@freezed\nclass EnUs with _$EnUs {\n  factory EnUs({\n    String? name,\n    List<dynamic>? history,\n  }) = _EnUs;\n\n  factory EnUs.fromJson(Map<String, dynamic> json) => _$EnUsFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/works/en_us.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'en_us.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nEnUs _$EnUsFromJson(Map<String, dynamic> json) {\n  return _EnUs.fromJson(json);\n}\n\n/// @nodoc\nmixin _$EnUs {\n  String? get name => throw _privateConstructorUsedError;\n  List<dynamic>? get history => throw _privateConstructorUsedError;\n\n  /// Serializes this EnUs to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of EnUs\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $EnUsCopyWith<EnUs> get copyWith => throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $EnUsCopyWith<$Res> {\n  factory $EnUsCopyWith(EnUs value, $Res Function(EnUs) then) =\n      _$EnUsCopyWithImpl<$Res, EnUs>;\n  @useResult\n  $Res call({String? name, List<dynamic>? history});\n}\n\n/// @nodoc\nclass _$EnUsCopyWithImpl<$Res, $Val extends EnUs>\n    implements $EnUsCopyWith<$Res> {\n  _$EnUsCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of EnUs\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? name = freezed,\n    Object? history = freezed,\n  }) {\n    return _then(_value.copyWith(\n      name: freezed == name\n          ? _value.name\n          : name // ignore: cast_nullable_to_non_nullable\n              as String?,\n      history: freezed == history\n          ? _value.history\n          : history // ignore: cast_nullable_to_non_nullable\n              as List<dynamic>?,\n    ) as $Val);\n  }\n}\n\n/// @nodoc\nabstract class _$$EnUsImplCopyWith<$Res> implements $EnUsCopyWith<$Res> {\n  factory _$$EnUsImplCopyWith(\n          _$EnUsImpl value, $Res Function(_$EnUsImpl) then) =\n      __$$EnUsImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call({String? name, List<dynamic>? history});\n}\n\n/// @nodoc\nclass __$$EnUsImplCopyWithImpl<$Res>\n    extends _$EnUsCopyWithImpl<$Res, _$EnUsImpl>\n    implements _$$EnUsImplCopyWith<$Res> {\n  __$$EnUsImplCopyWithImpl(_$EnUsImpl _value, $Res Function(_$EnUsImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of EnUs\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? name = freezed,\n    Object? history = freezed,\n  }) {\n    return _then(_$EnUsImpl(\n      name: freezed == name\n          ? _value.name\n          : name // ignore: cast_nullable_to_non_nullable\n              as String?,\n      history: freezed == history\n          ? _value._history\n          : history // ignore: cast_nullable_to_non_nullable\n              as List<dynamic>?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$EnUsImpl implements _EnUs {\n  _$EnUsImpl({this.name, final List<dynamic>? history}) : _history = history;\n\n  factory _$EnUsImpl.fromJson(Map<String, dynamic> json) =>\n      _$$EnUsImplFromJson(json);\n\n  @override\n  final String? name;\n  final List<dynamic>? _history;\n  @override\n  List<dynamic>? get history {\n    final value = _history;\n    if (value == null) return null;\n    if (_history is EqualUnmodifiableListView) return _history;\n    // ignore: implicit_dynamic_type\n    return EqualUnmodifiableListView(value);\n  }\n\n  @override\n  String toString() {\n    return 'EnUs(name: $name, history: $history)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$EnUsImpl &&\n            (identical(other.name, name) || other.name == name) &&\n            const DeepCollectionEquality().equals(other._history, _history));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(\n      runtimeType, name, const DeepCollectionEquality().hash(_history));\n\n  /// Create a copy of EnUs\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$EnUsImplCopyWith<_$EnUsImpl> get copyWith =>\n      __$$EnUsImplCopyWithImpl<_$EnUsImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$EnUsImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _EnUs implements EnUs {\n  factory _EnUs({final String? name, final List<dynamic>? history}) =\n      _$EnUsImpl;\n\n  factory _EnUs.fromJson(Map<String, dynamic> json) = _$EnUsImpl.fromJson;\n\n  @override\n  String? get name;\n  @override\n  List<dynamic>? get history;\n\n  /// Create a copy of EnUs\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$EnUsImplCopyWith<_$EnUsImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/works/en_us.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'en_us.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$EnUsImpl _$$EnUsImplFromJson(Map<String, dynamic> json) => _$EnUsImpl(\n      name: json['name'] as String?,\n      history: json['history'] as List<dynamic>?,\n    );\n\nMap<String, dynamic> _$$EnUsImplToJson(_$EnUsImpl instance) =>\n    <String, dynamic>{\n      'name': instance.name,\n      'history': instance.history,\n    };\n"
  },
  {
    "path": "lib/data/models/works/i18n.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'en_us.dart';\nimport 'ja_jp.dart';\nimport 'zh_cn.dart';\n\npart 'i18n.freezed.dart';\npart 'i18n.g.dart';\n\n@freezed\nclass I18n with _$I18n {\n  factory I18n({\n    @JsonKey(name: 'en-us') EnUs? enUs,\n    @JsonKey(name: 'ja-jp') JaJp? jaJp,\n    @JsonKey(name: 'zh-cn') ZhCn? zhCn,\n  }) = _I18n;\n\n  factory I18n.fromJson(Map<String, dynamic> json) => _$I18nFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/works/i18n.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'i18n.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nI18n _$I18nFromJson(Map<String, dynamic> json) {\n  return _I18n.fromJson(json);\n}\n\n/// @nodoc\nmixin _$I18n {\n  @JsonKey(name: 'en-us')\n  EnUs? get enUs => throw _privateConstructorUsedError;\n  @JsonKey(name: 'ja-jp')\n  JaJp? get jaJp => throw _privateConstructorUsedError;\n  @JsonKey(name: 'zh-cn')\n  ZhCn? get zhCn => throw _privateConstructorUsedError;\n\n  /// Serializes this I18n to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of I18n\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $I18nCopyWith<I18n> get copyWith => throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $I18nCopyWith<$Res> {\n  factory $I18nCopyWith(I18n value, $Res Function(I18n) then) =\n      _$I18nCopyWithImpl<$Res, I18n>;\n  @useResult\n  $Res call(\n      {@JsonKey(name: 'en-us') EnUs? enUs,\n      @JsonKey(name: 'ja-jp') JaJp? jaJp,\n      @JsonKey(name: 'zh-cn') ZhCn? zhCn});\n\n  $EnUsCopyWith<$Res>? get enUs;\n  $JaJpCopyWith<$Res>? get jaJp;\n  $ZhCnCopyWith<$Res>? get zhCn;\n}\n\n/// @nodoc\nclass _$I18nCopyWithImpl<$Res, $Val extends I18n>\n    implements $I18nCopyWith<$Res> {\n  _$I18nCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of I18n\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? enUs = freezed,\n    Object? jaJp = freezed,\n    Object? zhCn = freezed,\n  }) {\n    return _then(_value.copyWith(\n      enUs: freezed == enUs\n          ? _value.enUs\n          : enUs // ignore: cast_nullable_to_non_nullable\n              as EnUs?,\n      jaJp: freezed == jaJp\n          ? _value.jaJp\n          : jaJp // ignore: cast_nullable_to_non_nullable\n              as JaJp?,\n      zhCn: freezed == zhCn\n          ? _value.zhCn\n          : zhCn // ignore: cast_nullable_to_non_nullable\n              as ZhCn?,\n    ) as $Val);\n  }\n\n  /// Create a copy of I18n\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @pragma('vm:prefer-inline')\n  $EnUsCopyWith<$Res>? get enUs {\n    if (_value.enUs == null) {\n      return null;\n    }\n\n    return $EnUsCopyWith<$Res>(_value.enUs!, (value) {\n      return _then(_value.copyWith(enUs: value) as $Val);\n    });\n  }\n\n  /// Create a copy of I18n\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @pragma('vm:prefer-inline')\n  $JaJpCopyWith<$Res>? get jaJp {\n    if (_value.jaJp == null) {\n      return null;\n    }\n\n    return $JaJpCopyWith<$Res>(_value.jaJp!, (value) {\n      return _then(_value.copyWith(jaJp: value) as $Val);\n    });\n  }\n\n  /// Create a copy of I18n\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @pragma('vm:prefer-inline')\n  $ZhCnCopyWith<$Res>? get zhCn {\n    if (_value.zhCn == null) {\n      return null;\n    }\n\n    return $ZhCnCopyWith<$Res>(_value.zhCn!, (value) {\n      return _then(_value.copyWith(zhCn: value) as $Val);\n    });\n  }\n}\n\n/// @nodoc\nabstract class _$$I18nImplCopyWith<$Res> implements $I18nCopyWith<$Res> {\n  factory _$$I18nImplCopyWith(\n          _$I18nImpl value, $Res Function(_$I18nImpl) then) =\n      __$$I18nImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call(\n      {@JsonKey(name: 'en-us') EnUs? enUs,\n      @JsonKey(name: 'ja-jp') JaJp? jaJp,\n      @JsonKey(name: 'zh-cn') ZhCn? zhCn});\n\n  @override\n  $EnUsCopyWith<$Res>? get enUs;\n  @override\n  $JaJpCopyWith<$Res>? get jaJp;\n  @override\n  $ZhCnCopyWith<$Res>? get zhCn;\n}\n\n/// @nodoc\nclass __$$I18nImplCopyWithImpl<$Res>\n    extends _$I18nCopyWithImpl<$Res, _$I18nImpl>\n    implements _$$I18nImplCopyWith<$Res> {\n  __$$I18nImplCopyWithImpl(_$I18nImpl _value, $Res Function(_$I18nImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of I18n\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? enUs = freezed,\n    Object? jaJp = freezed,\n    Object? zhCn = freezed,\n  }) {\n    return _then(_$I18nImpl(\n      enUs: freezed == enUs\n          ? _value.enUs\n          : enUs // ignore: cast_nullable_to_non_nullable\n              as EnUs?,\n      jaJp: freezed == jaJp\n          ? _value.jaJp\n          : jaJp // ignore: cast_nullable_to_non_nullable\n              as JaJp?,\n      zhCn: freezed == zhCn\n          ? _value.zhCn\n          : zhCn // ignore: cast_nullable_to_non_nullable\n              as ZhCn?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$I18nImpl implements _I18n {\n  _$I18nImpl(\n      {@JsonKey(name: 'en-us') this.enUs,\n      @JsonKey(name: 'ja-jp') this.jaJp,\n      @JsonKey(name: 'zh-cn') this.zhCn});\n\n  factory _$I18nImpl.fromJson(Map<String, dynamic> json) =>\n      _$$I18nImplFromJson(json);\n\n  @override\n  @JsonKey(name: 'en-us')\n  final EnUs? enUs;\n  @override\n  @JsonKey(name: 'ja-jp')\n  final JaJp? jaJp;\n  @override\n  @JsonKey(name: 'zh-cn')\n  final ZhCn? zhCn;\n\n  @override\n  String toString() {\n    return 'I18n(enUs: $enUs, jaJp: $jaJp, zhCn: $zhCn)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$I18nImpl &&\n            (identical(other.enUs, enUs) || other.enUs == enUs) &&\n            (identical(other.jaJp, jaJp) || other.jaJp == jaJp) &&\n            (identical(other.zhCn, zhCn) || other.zhCn == zhCn));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(runtimeType, enUs, jaJp, zhCn);\n\n  /// Create a copy of I18n\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$I18nImplCopyWith<_$I18nImpl> get copyWith =>\n      __$$I18nImplCopyWithImpl<_$I18nImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$I18nImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _I18n implements I18n {\n  factory _I18n(\n      {@JsonKey(name: 'en-us') final EnUs? enUs,\n      @JsonKey(name: 'ja-jp') final JaJp? jaJp,\n      @JsonKey(name: 'zh-cn') final ZhCn? zhCn}) = _$I18nImpl;\n\n  factory _I18n.fromJson(Map<String, dynamic> json) = _$I18nImpl.fromJson;\n\n  @override\n  @JsonKey(name: 'en-us')\n  EnUs? get enUs;\n  @override\n  @JsonKey(name: 'ja-jp')\n  JaJp? get jaJp;\n  @override\n  @JsonKey(name: 'zh-cn')\n  ZhCn? get zhCn;\n\n  /// Create a copy of I18n\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$I18nImplCopyWith<_$I18nImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/works/i18n.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'i18n.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$I18nImpl _$$I18nImplFromJson(Map<String, dynamic> json) => _$I18nImpl(\n      enUs: json['en-us'] == null\n          ? null\n          : EnUs.fromJson(json['en-us'] as Map<String, dynamic>),\n      jaJp: json['ja-jp'] == null\n          ? null\n          : JaJp.fromJson(json['ja-jp'] as Map<String, dynamic>),\n      zhCn: json['zh-cn'] == null\n          ? null\n          : ZhCn.fromJson(json['zh-cn'] as Map<String, dynamic>),\n    );\n\nMap<String, dynamic> _$$I18nImplToJson(_$I18nImpl instance) =>\n    <String, dynamic>{\n      'en-us': instance.enUs,\n      'ja-jp': instance.jaJp,\n      'zh-cn': instance.zhCn,\n    };\n"
  },
  {
    "path": "lib/data/models/works/ja_jp.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'ja_jp.freezed.dart';\npart 'ja_jp.g.dart';\n\n@freezed\nclass JaJp with _$JaJp {\n  factory JaJp({\n    String? name,\n  }) = _JaJp;\n\n  factory JaJp.fromJson(Map<String, dynamic> json) => _$JaJpFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/works/ja_jp.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'ja_jp.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nJaJp _$JaJpFromJson(Map<String, dynamic> json) {\n  return _JaJp.fromJson(json);\n}\n\n/// @nodoc\nmixin _$JaJp {\n  String? get name => throw _privateConstructorUsedError;\n\n  /// Serializes this JaJp to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of JaJp\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $JaJpCopyWith<JaJp> get copyWith => throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $JaJpCopyWith<$Res> {\n  factory $JaJpCopyWith(JaJp value, $Res Function(JaJp) then) =\n      _$JaJpCopyWithImpl<$Res, JaJp>;\n  @useResult\n  $Res call({String? name});\n}\n\n/// @nodoc\nclass _$JaJpCopyWithImpl<$Res, $Val extends JaJp>\n    implements $JaJpCopyWith<$Res> {\n  _$JaJpCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of JaJp\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? name = freezed,\n  }) {\n    return _then(_value.copyWith(\n      name: freezed == name\n          ? _value.name\n          : name // ignore: cast_nullable_to_non_nullable\n              as String?,\n    ) as $Val);\n  }\n}\n\n/// @nodoc\nabstract class _$$JaJpImplCopyWith<$Res> implements $JaJpCopyWith<$Res> {\n  factory _$$JaJpImplCopyWith(\n          _$JaJpImpl value, $Res Function(_$JaJpImpl) then) =\n      __$$JaJpImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call({String? name});\n}\n\n/// @nodoc\nclass __$$JaJpImplCopyWithImpl<$Res>\n    extends _$JaJpCopyWithImpl<$Res, _$JaJpImpl>\n    implements _$$JaJpImplCopyWith<$Res> {\n  __$$JaJpImplCopyWithImpl(_$JaJpImpl _value, $Res Function(_$JaJpImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of JaJp\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? name = freezed,\n  }) {\n    return _then(_$JaJpImpl(\n      name: freezed == name\n          ? _value.name\n          : name // ignore: cast_nullable_to_non_nullable\n              as String?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$JaJpImpl implements _JaJp {\n  _$JaJpImpl({this.name});\n\n  factory _$JaJpImpl.fromJson(Map<String, dynamic> json) =>\n      _$$JaJpImplFromJson(json);\n\n  @override\n  final String? name;\n\n  @override\n  String toString() {\n    return 'JaJp(name: $name)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$JaJpImpl &&\n            (identical(other.name, name) || other.name == name));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(runtimeType, name);\n\n  /// Create a copy of JaJp\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$JaJpImplCopyWith<_$JaJpImpl> get copyWith =>\n      __$$JaJpImplCopyWithImpl<_$JaJpImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$JaJpImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _JaJp implements JaJp {\n  factory _JaJp({final String? name}) = _$JaJpImpl;\n\n  factory _JaJp.fromJson(Map<String, dynamic> json) = _$JaJpImpl.fromJson;\n\n  @override\n  String? get name;\n\n  /// Create a copy of JaJp\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$JaJpImplCopyWith<_$JaJpImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/works/ja_jp.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'ja_jp.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$JaJpImpl _$$JaJpImplFromJson(Map<String, dynamic> json) => _$JaJpImpl(\n      name: json['name'] as String?,\n    );\n\nMap<String, dynamic> _$$JaJpImplToJson(_$JaJpImpl instance) =>\n    <String, dynamic>{\n      'name': instance.name,\n    };\n"
  },
  {
    "path": "lib/data/models/works/language_edition.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'language_edition.freezed.dart';\npart 'language_edition.g.dart';\n\n@freezed\nclass LanguageEdition with _$LanguageEdition {\n  factory LanguageEdition({\n    String? lang,\n    String? label,\n    String? workno,\n    @JsonKey(name: 'edition_id') int? editionId,\n    @JsonKey(name: 'edition_type') String? editionType,\n    @JsonKey(name: 'display_order') int? displayOrder,\n  }) = _LanguageEdition;\n\n  factory LanguageEdition.fromJson(Map<String, dynamic> json) =>\n      _$LanguageEditionFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/works/language_edition.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'language_edition.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nLanguageEdition _$LanguageEditionFromJson(Map<String, dynamic> json) {\n  return _LanguageEdition.fromJson(json);\n}\n\n/// @nodoc\nmixin _$LanguageEdition {\n  String? get lang => throw _privateConstructorUsedError;\n  String? get label => throw _privateConstructorUsedError;\n  String? get workno => throw _privateConstructorUsedError;\n  @JsonKey(name: 'edition_id')\n  int? get editionId => throw _privateConstructorUsedError;\n  @JsonKey(name: 'edition_type')\n  String? get editionType => throw _privateConstructorUsedError;\n  @JsonKey(name: 'display_order')\n  int? get displayOrder => throw _privateConstructorUsedError;\n\n  /// Serializes this LanguageEdition to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of LanguageEdition\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $LanguageEditionCopyWith<LanguageEdition> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $LanguageEditionCopyWith<$Res> {\n  factory $LanguageEditionCopyWith(\n          LanguageEdition value, $Res Function(LanguageEdition) then) =\n      _$LanguageEditionCopyWithImpl<$Res, LanguageEdition>;\n  @useResult\n  $Res call(\n      {String? lang,\n      String? label,\n      String? workno,\n      @JsonKey(name: 'edition_id') int? editionId,\n      @JsonKey(name: 'edition_type') String? editionType,\n      @JsonKey(name: 'display_order') int? displayOrder});\n}\n\n/// @nodoc\nclass _$LanguageEditionCopyWithImpl<$Res, $Val extends LanguageEdition>\n    implements $LanguageEditionCopyWith<$Res> {\n  _$LanguageEditionCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of LanguageEdition\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? lang = freezed,\n    Object? label = freezed,\n    Object? workno = freezed,\n    Object? editionId = freezed,\n    Object? editionType = freezed,\n    Object? displayOrder = freezed,\n  }) {\n    return _then(_value.copyWith(\n      lang: freezed == lang\n          ? _value.lang\n          : lang // ignore: cast_nullable_to_non_nullable\n              as String?,\n      label: freezed == label\n          ? _value.label\n          : label // ignore: cast_nullable_to_non_nullable\n              as String?,\n      workno: freezed == workno\n          ? _value.workno\n          : workno // ignore: cast_nullable_to_non_nullable\n              as String?,\n      editionId: freezed == editionId\n          ? _value.editionId\n          : editionId // ignore: cast_nullable_to_non_nullable\n              as int?,\n      editionType: freezed == editionType\n          ? _value.editionType\n          : editionType // ignore: cast_nullable_to_non_nullable\n              as String?,\n      displayOrder: freezed == displayOrder\n          ? _value.displayOrder\n          : displayOrder // ignore: cast_nullable_to_non_nullable\n              as int?,\n    ) as $Val);\n  }\n}\n\n/// @nodoc\nabstract class _$$LanguageEditionImplCopyWith<$Res>\n    implements $LanguageEditionCopyWith<$Res> {\n  factory _$$LanguageEditionImplCopyWith(_$LanguageEditionImpl value,\n          $Res Function(_$LanguageEditionImpl) then) =\n      __$$LanguageEditionImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call(\n      {String? lang,\n      String? label,\n      String? workno,\n      @JsonKey(name: 'edition_id') int? editionId,\n      @JsonKey(name: 'edition_type') String? editionType,\n      @JsonKey(name: 'display_order') int? displayOrder});\n}\n\n/// @nodoc\nclass __$$LanguageEditionImplCopyWithImpl<$Res>\n    extends _$LanguageEditionCopyWithImpl<$Res, _$LanguageEditionImpl>\n    implements _$$LanguageEditionImplCopyWith<$Res> {\n  __$$LanguageEditionImplCopyWithImpl(\n      _$LanguageEditionImpl _value, $Res Function(_$LanguageEditionImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of LanguageEdition\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? lang = freezed,\n    Object? label = freezed,\n    Object? workno = freezed,\n    Object? editionId = freezed,\n    Object? editionType = freezed,\n    Object? displayOrder = freezed,\n  }) {\n    return _then(_$LanguageEditionImpl(\n      lang: freezed == lang\n          ? _value.lang\n          : lang // ignore: cast_nullable_to_non_nullable\n              as String?,\n      label: freezed == label\n          ? _value.label\n          : label // ignore: cast_nullable_to_non_nullable\n              as String?,\n      workno: freezed == workno\n          ? _value.workno\n          : workno // ignore: cast_nullable_to_non_nullable\n              as String?,\n      editionId: freezed == editionId\n          ? _value.editionId\n          : editionId // ignore: cast_nullable_to_non_nullable\n              as int?,\n      editionType: freezed == editionType\n          ? _value.editionType\n          : editionType // ignore: cast_nullable_to_non_nullable\n              as String?,\n      displayOrder: freezed == displayOrder\n          ? _value.displayOrder\n          : displayOrder // ignore: cast_nullable_to_non_nullable\n              as int?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$LanguageEditionImpl implements _LanguageEdition {\n  _$LanguageEditionImpl(\n      {this.lang,\n      this.label,\n      this.workno,\n      @JsonKey(name: 'edition_id') this.editionId,\n      @JsonKey(name: 'edition_type') this.editionType,\n      @JsonKey(name: 'display_order') this.displayOrder});\n\n  factory _$LanguageEditionImpl.fromJson(Map<String, dynamic> json) =>\n      _$$LanguageEditionImplFromJson(json);\n\n  @override\n  final String? lang;\n  @override\n  final String? label;\n  @override\n  final String? workno;\n  @override\n  @JsonKey(name: 'edition_id')\n  final int? editionId;\n  @override\n  @JsonKey(name: 'edition_type')\n  final String? editionType;\n  @override\n  @JsonKey(name: 'display_order')\n  final int? displayOrder;\n\n  @override\n  String toString() {\n    return 'LanguageEdition(lang: $lang, label: $label, workno: $workno, editionId: $editionId, editionType: $editionType, displayOrder: $displayOrder)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$LanguageEditionImpl &&\n            (identical(other.lang, lang) || other.lang == lang) &&\n            (identical(other.label, label) || other.label == label) &&\n            (identical(other.workno, workno) || other.workno == workno) &&\n            (identical(other.editionId, editionId) ||\n                other.editionId == editionId) &&\n            (identical(other.editionType, editionType) ||\n                other.editionType == editionType) &&\n            (identical(other.displayOrder, displayOrder) ||\n                other.displayOrder == displayOrder));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(\n      runtimeType, lang, label, workno, editionId, editionType, displayOrder);\n\n  /// Create a copy of LanguageEdition\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$LanguageEditionImplCopyWith<_$LanguageEditionImpl> get copyWith =>\n      __$$LanguageEditionImplCopyWithImpl<_$LanguageEditionImpl>(\n          this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$LanguageEditionImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _LanguageEdition implements LanguageEdition {\n  factory _LanguageEdition(\n          {final String? lang,\n          final String? label,\n          final String? workno,\n          @JsonKey(name: 'edition_id') final int? editionId,\n          @JsonKey(name: 'edition_type') final String? editionType,\n          @JsonKey(name: 'display_order') final int? displayOrder}) =\n      _$LanguageEditionImpl;\n\n  factory _LanguageEdition.fromJson(Map<String, dynamic> json) =\n      _$LanguageEditionImpl.fromJson;\n\n  @override\n  String? get lang;\n  @override\n  String? get label;\n  @override\n  String? get workno;\n  @override\n  @JsonKey(name: 'edition_id')\n  int? get editionId;\n  @override\n  @JsonKey(name: 'edition_type')\n  String? get editionType;\n  @override\n  @JsonKey(name: 'display_order')\n  int? get displayOrder;\n\n  /// Create a copy of LanguageEdition\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$LanguageEditionImplCopyWith<_$LanguageEditionImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/works/language_edition.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'language_edition.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$LanguageEditionImpl _$$LanguageEditionImplFromJson(\n        Map<String, dynamic> json) =>\n    _$LanguageEditionImpl(\n      lang: json['lang'] as String?,\n      label: json['label'] as String?,\n      workno: json['workno'] as String?,\n      editionId: (json['edition_id'] as num?)?.toInt(),\n      editionType: json['edition_type'] as String?,\n      displayOrder: (json['display_order'] as num?)?.toInt(),\n    );\n\nMap<String, dynamic> _$$LanguageEditionImplToJson(\n        _$LanguageEditionImpl instance) =>\n    <String, dynamic>{\n      'lang': instance.lang,\n      'label': instance.label,\n      'workno': instance.workno,\n      'edition_id': instance.editionId,\n      'edition_type': instance.editionType,\n      'display_order': instance.displayOrder,\n    };\n"
  },
  {
    "path": "lib/data/models/works/other_language_editions_in_db.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'other_language_editions_in_db.freezed.dart';\npart 'other_language_editions_in_db.g.dart';\n\n@freezed\nclass OtherLanguageEditionsInDb with _$OtherLanguageEditionsInDb {\n  factory OtherLanguageEditionsInDb({\n    int? id,\n    String? lang,\n    String? title,\n    @JsonKey(name: 'source_id') String? sourceId,\n    @JsonKey(name: 'is_original') bool? isOriginal,\n    @JsonKey(name: 'source_type') String? sourceType,\n  }) = _OtherLanguageEditionsInDb;\n\n  factory OtherLanguageEditionsInDb.fromJson(Map<String, dynamic> json) =>\n      _$OtherLanguageEditionsInDbFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/works/other_language_editions_in_db.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'other_language_editions_in_db.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nOtherLanguageEditionsInDb _$OtherLanguageEditionsInDbFromJson(\n    Map<String, dynamic> json) {\n  return _OtherLanguageEditionsInDb.fromJson(json);\n}\n\n/// @nodoc\nmixin _$OtherLanguageEditionsInDb {\n  int? get id => throw _privateConstructorUsedError;\n  String? get lang => throw _privateConstructorUsedError;\n  String? get title => throw _privateConstructorUsedError;\n  @JsonKey(name: 'source_id')\n  String? get sourceId => throw _privateConstructorUsedError;\n  @JsonKey(name: 'is_original')\n  bool? get isOriginal => throw _privateConstructorUsedError;\n  @JsonKey(name: 'source_type')\n  String? get sourceType => throw _privateConstructorUsedError;\n\n  /// Serializes this OtherLanguageEditionsInDb to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of OtherLanguageEditionsInDb\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $OtherLanguageEditionsInDbCopyWith<OtherLanguageEditionsInDb> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $OtherLanguageEditionsInDbCopyWith<$Res> {\n  factory $OtherLanguageEditionsInDbCopyWith(OtherLanguageEditionsInDb value,\n          $Res Function(OtherLanguageEditionsInDb) then) =\n      _$OtherLanguageEditionsInDbCopyWithImpl<$Res, OtherLanguageEditionsInDb>;\n  @useResult\n  $Res call(\n      {int? id,\n      String? lang,\n      String? title,\n      @JsonKey(name: 'source_id') String? sourceId,\n      @JsonKey(name: 'is_original') bool? isOriginal,\n      @JsonKey(name: 'source_type') String? sourceType});\n}\n\n/// @nodoc\nclass _$OtherLanguageEditionsInDbCopyWithImpl<$Res,\n        $Val extends OtherLanguageEditionsInDb>\n    implements $OtherLanguageEditionsInDbCopyWith<$Res> {\n  _$OtherLanguageEditionsInDbCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of OtherLanguageEditionsInDb\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? id = freezed,\n    Object? lang = freezed,\n    Object? title = freezed,\n    Object? sourceId = freezed,\n    Object? isOriginal = freezed,\n    Object? sourceType = freezed,\n  }) {\n    return _then(_value.copyWith(\n      id: freezed == id\n          ? _value.id\n          : id // ignore: cast_nullable_to_non_nullable\n              as int?,\n      lang: freezed == lang\n          ? _value.lang\n          : lang // ignore: cast_nullable_to_non_nullable\n              as String?,\n      title: freezed == title\n          ? _value.title\n          : title // ignore: cast_nullable_to_non_nullable\n              as String?,\n      sourceId: freezed == sourceId\n          ? _value.sourceId\n          : sourceId // ignore: cast_nullable_to_non_nullable\n              as String?,\n      isOriginal: freezed == isOriginal\n          ? _value.isOriginal\n          : isOriginal // ignore: cast_nullable_to_non_nullable\n              as bool?,\n      sourceType: freezed == sourceType\n          ? _value.sourceType\n          : sourceType // ignore: cast_nullable_to_non_nullable\n              as String?,\n    ) as $Val);\n  }\n}\n\n/// @nodoc\nabstract class _$$OtherLanguageEditionsInDbImplCopyWith<$Res>\n    implements $OtherLanguageEditionsInDbCopyWith<$Res> {\n  factory _$$OtherLanguageEditionsInDbImplCopyWith(\n          _$OtherLanguageEditionsInDbImpl value,\n          $Res Function(_$OtherLanguageEditionsInDbImpl) then) =\n      __$$OtherLanguageEditionsInDbImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call(\n      {int? id,\n      String? lang,\n      String? title,\n      @JsonKey(name: 'source_id') String? sourceId,\n      @JsonKey(name: 'is_original') bool? isOriginal,\n      @JsonKey(name: 'source_type') String? sourceType});\n}\n\n/// @nodoc\nclass __$$OtherLanguageEditionsInDbImplCopyWithImpl<$Res>\n    extends _$OtherLanguageEditionsInDbCopyWithImpl<$Res,\n        _$OtherLanguageEditionsInDbImpl>\n    implements _$$OtherLanguageEditionsInDbImplCopyWith<$Res> {\n  __$$OtherLanguageEditionsInDbImplCopyWithImpl(\n      _$OtherLanguageEditionsInDbImpl _value,\n      $Res Function(_$OtherLanguageEditionsInDbImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of OtherLanguageEditionsInDb\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? id = freezed,\n    Object? lang = freezed,\n    Object? title = freezed,\n    Object? sourceId = freezed,\n    Object? isOriginal = freezed,\n    Object? sourceType = freezed,\n  }) {\n    return _then(_$OtherLanguageEditionsInDbImpl(\n      id: freezed == id\n          ? _value.id\n          : id // ignore: cast_nullable_to_non_nullable\n              as int?,\n      lang: freezed == lang\n          ? _value.lang\n          : lang // ignore: cast_nullable_to_non_nullable\n              as String?,\n      title: freezed == title\n          ? _value.title\n          : title // ignore: cast_nullable_to_non_nullable\n              as String?,\n      sourceId: freezed == sourceId\n          ? _value.sourceId\n          : sourceId // ignore: cast_nullable_to_non_nullable\n              as String?,\n      isOriginal: freezed == isOriginal\n          ? _value.isOriginal\n          : isOriginal // ignore: cast_nullable_to_non_nullable\n              as bool?,\n      sourceType: freezed == sourceType\n          ? _value.sourceType\n          : sourceType // ignore: cast_nullable_to_non_nullable\n              as String?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$OtherLanguageEditionsInDbImpl implements _OtherLanguageEditionsInDb {\n  _$OtherLanguageEditionsInDbImpl(\n      {this.id,\n      this.lang,\n      this.title,\n      @JsonKey(name: 'source_id') this.sourceId,\n      @JsonKey(name: 'is_original') this.isOriginal,\n      @JsonKey(name: 'source_type') this.sourceType});\n\n  factory _$OtherLanguageEditionsInDbImpl.fromJson(Map<String, dynamic> json) =>\n      _$$OtherLanguageEditionsInDbImplFromJson(json);\n\n  @override\n  final int? id;\n  @override\n  final String? lang;\n  @override\n  final String? title;\n  @override\n  @JsonKey(name: 'source_id')\n  final String? sourceId;\n  @override\n  @JsonKey(name: 'is_original')\n  final bool? isOriginal;\n  @override\n  @JsonKey(name: 'source_type')\n  final String? sourceType;\n\n  @override\n  String toString() {\n    return 'OtherLanguageEditionsInDb(id: $id, lang: $lang, title: $title, sourceId: $sourceId, isOriginal: $isOriginal, sourceType: $sourceType)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$OtherLanguageEditionsInDbImpl &&\n            (identical(other.id, id) || other.id == id) &&\n            (identical(other.lang, lang) || other.lang == lang) &&\n            (identical(other.title, title) || other.title == title) &&\n            (identical(other.sourceId, sourceId) ||\n                other.sourceId == sourceId) &&\n            (identical(other.isOriginal, isOriginal) ||\n                other.isOriginal == isOriginal) &&\n            (identical(other.sourceType, sourceType) ||\n                other.sourceType == sourceType));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(\n      runtimeType, id, lang, title, sourceId, isOriginal, sourceType);\n\n  /// Create a copy of OtherLanguageEditionsInDb\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$OtherLanguageEditionsInDbImplCopyWith<_$OtherLanguageEditionsInDbImpl>\n      get copyWith => __$$OtherLanguageEditionsInDbImplCopyWithImpl<\n          _$OtherLanguageEditionsInDbImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$OtherLanguageEditionsInDbImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _OtherLanguageEditionsInDb implements OtherLanguageEditionsInDb {\n  factory _OtherLanguageEditionsInDb(\n          {final int? id,\n          final String? lang,\n          final String? title,\n          @JsonKey(name: 'source_id') final String? sourceId,\n          @JsonKey(name: 'is_original') final bool? isOriginal,\n          @JsonKey(name: 'source_type') final String? sourceType}) =\n      _$OtherLanguageEditionsInDbImpl;\n\n  factory _OtherLanguageEditionsInDb.fromJson(Map<String, dynamic> json) =\n      _$OtherLanguageEditionsInDbImpl.fromJson;\n\n  @override\n  int? get id;\n  @override\n  String? get lang;\n  @override\n  String? get title;\n  @override\n  @JsonKey(name: 'source_id')\n  String? get sourceId;\n  @override\n  @JsonKey(name: 'is_original')\n  bool? get isOriginal;\n  @override\n  @JsonKey(name: 'source_type')\n  String? get sourceType;\n\n  /// Create a copy of OtherLanguageEditionsInDb\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$OtherLanguageEditionsInDbImplCopyWith<_$OtherLanguageEditionsInDbImpl>\n      get copyWith => throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/works/other_language_editions_in_db.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'other_language_editions_in_db.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$OtherLanguageEditionsInDbImpl _$$OtherLanguageEditionsInDbImplFromJson(\n        Map<String, dynamic> json) =>\n    _$OtherLanguageEditionsInDbImpl(\n      id: (json['id'] as num?)?.toInt(),\n      lang: json['lang'] as String?,\n      title: json['title'] as String?,\n      sourceId: json['source_id'] as String?,\n      isOriginal: json['is_original'] as bool?,\n      sourceType: json['source_type'] as String?,\n    );\n\nMap<String, dynamic> _$$OtherLanguageEditionsInDbImplToJson(\n        _$OtherLanguageEditionsInDbImpl instance) =>\n    <String, dynamic>{\n      'id': instance.id,\n      'lang': instance.lang,\n      'title': instance.title,\n      'source_id': instance.sourceId,\n      'is_original': instance.isOriginal,\n      'source_type': instance.sourceType,\n    };\n"
  },
  {
    "path": "lib/data/models/works/pagination.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'pagination.freezed.dart';\npart 'pagination.g.dart';\n\n@freezed\nclass Pagination with _$Pagination {\n  factory Pagination({\n    int? currentPage,\n    int? pageSize,\n    int? totalCount,\n  }) = _Pagination;\n\n  factory Pagination.fromJson(Map<String, dynamic> json) =>\n      _$PaginationFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/works/pagination.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'pagination.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nPagination _$PaginationFromJson(Map<String, dynamic> json) {\n  return _Pagination.fromJson(json);\n}\n\n/// @nodoc\nmixin _$Pagination {\n  int? get currentPage => throw _privateConstructorUsedError;\n  int? get pageSize => throw _privateConstructorUsedError;\n  int? get totalCount => throw _privateConstructorUsedError;\n\n  /// Serializes this Pagination to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of Pagination\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $PaginationCopyWith<Pagination> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $PaginationCopyWith<$Res> {\n  factory $PaginationCopyWith(\n          Pagination value, $Res Function(Pagination) then) =\n      _$PaginationCopyWithImpl<$Res, Pagination>;\n  @useResult\n  $Res call({int? currentPage, int? pageSize, int? totalCount});\n}\n\n/// @nodoc\nclass _$PaginationCopyWithImpl<$Res, $Val extends Pagination>\n    implements $PaginationCopyWith<$Res> {\n  _$PaginationCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of Pagination\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? currentPage = freezed,\n    Object? pageSize = freezed,\n    Object? totalCount = freezed,\n  }) {\n    return _then(_value.copyWith(\n      currentPage: freezed == currentPage\n          ? _value.currentPage\n          : currentPage // ignore: cast_nullable_to_non_nullable\n              as int?,\n      pageSize: freezed == pageSize\n          ? _value.pageSize\n          : pageSize // ignore: cast_nullable_to_non_nullable\n              as int?,\n      totalCount: freezed == totalCount\n          ? _value.totalCount\n          : totalCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n    ) as $Val);\n  }\n}\n\n/// @nodoc\nabstract class _$$PaginationImplCopyWith<$Res>\n    implements $PaginationCopyWith<$Res> {\n  factory _$$PaginationImplCopyWith(\n          _$PaginationImpl value, $Res Function(_$PaginationImpl) then) =\n      __$$PaginationImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call({int? currentPage, int? pageSize, int? totalCount});\n}\n\n/// @nodoc\nclass __$$PaginationImplCopyWithImpl<$Res>\n    extends _$PaginationCopyWithImpl<$Res, _$PaginationImpl>\n    implements _$$PaginationImplCopyWith<$Res> {\n  __$$PaginationImplCopyWithImpl(\n      _$PaginationImpl _value, $Res Function(_$PaginationImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of Pagination\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? currentPage = freezed,\n    Object? pageSize = freezed,\n    Object? totalCount = freezed,\n  }) {\n    return _then(_$PaginationImpl(\n      currentPage: freezed == currentPage\n          ? _value.currentPage\n          : currentPage // ignore: cast_nullable_to_non_nullable\n              as int?,\n      pageSize: freezed == pageSize\n          ? _value.pageSize\n          : pageSize // ignore: cast_nullable_to_non_nullable\n              as int?,\n      totalCount: freezed == totalCount\n          ? _value.totalCount\n          : totalCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$PaginationImpl implements _Pagination {\n  _$PaginationImpl({this.currentPage, this.pageSize, this.totalCount});\n\n  factory _$PaginationImpl.fromJson(Map<String, dynamic> json) =>\n      _$$PaginationImplFromJson(json);\n\n  @override\n  final int? currentPage;\n  @override\n  final int? pageSize;\n  @override\n  final int? totalCount;\n\n  @override\n  String toString() {\n    return 'Pagination(currentPage: $currentPage, pageSize: $pageSize, totalCount: $totalCount)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$PaginationImpl &&\n            (identical(other.currentPage, currentPage) ||\n                other.currentPage == currentPage) &&\n            (identical(other.pageSize, pageSize) ||\n                other.pageSize == pageSize) &&\n            (identical(other.totalCount, totalCount) ||\n                other.totalCount == totalCount));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode =>\n      Object.hash(runtimeType, currentPage, pageSize, totalCount);\n\n  /// Create a copy of Pagination\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$PaginationImplCopyWith<_$PaginationImpl> get copyWith =>\n      __$$PaginationImplCopyWithImpl<_$PaginationImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$PaginationImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _Pagination implements Pagination {\n  factory _Pagination(\n      {final int? currentPage,\n      final int? pageSize,\n      final int? totalCount}) = _$PaginationImpl;\n\n  factory _Pagination.fromJson(Map<String, dynamic> json) =\n      _$PaginationImpl.fromJson;\n\n  @override\n  int? get currentPage;\n  @override\n  int? get pageSize;\n  @override\n  int? get totalCount;\n\n  /// Create a copy of Pagination\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$PaginationImplCopyWith<_$PaginationImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/works/pagination.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'pagination.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$PaginationImpl _$$PaginationImplFromJson(Map<String, dynamic> json) =>\n    _$PaginationImpl(\n      currentPage: (json['currentPage'] as num?)?.toInt(),\n      pageSize: (json['pageSize'] as num?)?.toInt(),\n      totalCount: (json['totalCount'] as num?)?.toInt(),\n    );\n\nMap<String, dynamic> _$$PaginationImplToJson(_$PaginationImpl instance) =>\n    <String, dynamic>{\n      'currentPage': instance.currentPage,\n      'pageSize': instance.pageSize,\n      'totalCount': instance.totalCount,\n    };\n"
  },
  {
    "path": "lib/data/models/works/tag.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'i18n.dart';\n\npart 'tag.freezed.dart';\npart 'tag.g.dart';\n\n@freezed\nclass Tag with _$Tag {\n  factory Tag({\n    int? id,\n    I18n? i18n,\n    String? name,\n  }) = _Tag;\n\n  factory Tag.fromJson(Map<String, dynamic> json) => _$TagFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/works/tag.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'tag.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nTag _$TagFromJson(Map<String, dynamic> json) {\n  return _Tag.fromJson(json);\n}\n\n/// @nodoc\nmixin _$Tag {\n  int? get id => throw _privateConstructorUsedError;\n  I18n? get i18n => throw _privateConstructorUsedError;\n  String? get name => throw _privateConstructorUsedError;\n\n  /// Serializes this Tag to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of Tag\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $TagCopyWith<Tag> get copyWith => throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $TagCopyWith<$Res> {\n  factory $TagCopyWith(Tag value, $Res Function(Tag) then) =\n      _$TagCopyWithImpl<$Res, Tag>;\n  @useResult\n  $Res call({int? id, I18n? i18n, String? name});\n\n  $I18nCopyWith<$Res>? get i18n;\n}\n\n/// @nodoc\nclass _$TagCopyWithImpl<$Res, $Val extends Tag> implements $TagCopyWith<$Res> {\n  _$TagCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of Tag\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? id = freezed,\n    Object? i18n = freezed,\n    Object? name = freezed,\n  }) {\n    return _then(_value.copyWith(\n      id: freezed == id\n          ? _value.id\n          : id // ignore: cast_nullable_to_non_nullable\n              as int?,\n      i18n: freezed == i18n\n          ? _value.i18n\n          : i18n // ignore: cast_nullable_to_non_nullable\n              as I18n?,\n      name: freezed == name\n          ? _value.name\n          : name // ignore: cast_nullable_to_non_nullable\n              as String?,\n    ) as $Val);\n  }\n\n  /// Create a copy of Tag\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @pragma('vm:prefer-inline')\n  $I18nCopyWith<$Res>? get i18n {\n    if (_value.i18n == null) {\n      return null;\n    }\n\n    return $I18nCopyWith<$Res>(_value.i18n!, (value) {\n      return _then(_value.copyWith(i18n: value) as $Val);\n    });\n  }\n}\n\n/// @nodoc\nabstract class _$$TagImplCopyWith<$Res> implements $TagCopyWith<$Res> {\n  factory _$$TagImplCopyWith(_$TagImpl value, $Res Function(_$TagImpl) then) =\n      __$$TagImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call({int? id, I18n? i18n, String? name});\n\n  @override\n  $I18nCopyWith<$Res>? get i18n;\n}\n\n/// @nodoc\nclass __$$TagImplCopyWithImpl<$Res> extends _$TagCopyWithImpl<$Res, _$TagImpl>\n    implements _$$TagImplCopyWith<$Res> {\n  __$$TagImplCopyWithImpl(_$TagImpl _value, $Res Function(_$TagImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of Tag\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? id = freezed,\n    Object? i18n = freezed,\n    Object? name = freezed,\n  }) {\n    return _then(_$TagImpl(\n      id: freezed == id\n          ? _value.id\n          : id // ignore: cast_nullable_to_non_nullable\n              as int?,\n      i18n: freezed == i18n\n          ? _value.i18n\n          : i18n // ignore: cast_nullable_to_non_nullable\n              as I18n?,\n      name: freezed == name\n          ? _value.name\n          : name // ignore: cast_nullable_to_non_nullable\n              as String?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$TagImpl implements _Tag {\n  _$TagImpl({this.id, this.i18n, this.name});\n\n  factory _$TagImpl.fromJson(Map<String, dynamic> json) =>\n      _$$TagImplFromJson(json);\n\n  @override\n  final int? id;\n  @override\n  final I18n? i18n;\n  @override\n  final String? name;\n\n  @override\n  String toString() {\n    return 'Tag(id: $id, i18n: $i18n, name: $name)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$TagImpl &&\n            (identical(other.id, id) || other.id == id) &&\n            (identical(other.i18n, i18n) || other.i18n == i18n) &&\n            (identical(other.name, name) || other.name == name));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(runtimeType, id, i18n, name);\n\n  /// Create a copy of Tag\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$TagImplCopyWith<_$TagImpl> get copyWith =>\n      __$$TagImplCopyWithImpl<_$TagImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$TagImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _Tag implements Tag {\n  factory _Tag({final int? id, final I18n? i18n, final String? name}) =\n      _$TagImpl;\n\n  factory _Tag.fromJson(Map<String, dynamic> json) = _$TagImpl.fromJson;\n\n  @override\n  int? get id;\n  @override\n  I18n? get i18n;\n  @override\n  String? get name;\n\n  /// Create a copy of Tag\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$TagImplCopyWith<_$TagImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/works/tag.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'tag.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$TagImpl _$$TagImplFromJson(Map<String, dynamic> json) => _$TagImpl(\n      id: (json['id'] as num?)?.toInt(),\n      i18n: json['i18n'] == null\n          ? null\n          : I18n.fromJson(json['i18n'] as Map<String, dynamic>),\n      name: json['name'] as String?,\n    );\n\nMap<String, dynamic> _$$TagImplToJson(_$TagImpl instance) => <String, dynamic>{\n      'id': instance.id,\n      'i18n': instance.i18n,\n      'name': instance.name,\n    };\n"
  },
  {
    "path": "lib/data/models/works/translation_bonus_lang.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'translation_bonus_lang.freezed.dart';\npart 'translation_bonus_lang.g.dart';\n\n@freezed\nclass TranslationBonusLang with _$TranslationBonusLang {\n  factory TranslationBonusLang({\n    int? price,\n    String? status,\n    @JsonKey(name: 'price_tax') int? priceTax,\n    @JsonKey(name: 'child_count') int? childCount,\n    @JsonKey(name: 'price_in_tax') int? priceInTax,\n    @JsonKey(name: 'recipient_max') int? recipientMax,\n    @JsonKey(name: 'recipient_available_count') int? recipientAvailableCount,\n  }) = _TranslationBonusLang;\n\n  factory TranslationBonusLang.fromJson(Map<String, dynamic> json) =>\n      _$TranslationBonusLangFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/works/translation_bonus_lang.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'translation_bonus_lang.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nTranslationBonusLang _$TranslationBonusLangFromJson(Map<String, dynamic> json) {\n  return _TranslationBonusLang.fromJson(json);\n}\n\n/// @nodoc\nmixin _$TranslationBonusLang {\n  int? get price => throw _privateConstructorUsedError;\n  String? get status => throw _privateConstructorUsedError;\n  @JsonKey(name: 'price_tax')\n  int? get priceTax => throw _privateConstructorUsedError;\n  @JsonKey(name: 'child_count')\n  int? get childCount => throw _privateConstructorUsedError;\n  @JsonKey(name: 'price_in_tax')\n  int? get priceInTax => throw _privateConstructorUsedError;\n  @JsonKey(name: 'recipient_max')\n  int? get recipientMax => throw _privateConstructorUsedError;\n  @JsonKey(name: 'recipient_available_count')\n  int? get recipientAvailableCount => throw _privateConstructorUsedError;\n\n  /// Serializes this TranslationBonusLang to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of TranslationBonusLang\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $TranslationBonusLangCopyWith<TranslationBonusLang> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $TranslationBonusLangCopyWith<$Res> {\n  factory $TranslationBonusLangCopyWith(TranslationBonusLang value,\n          $Res Function(TranslationBonusLang) then) =\n      _$TranslationBonusLangCopyWithImpl<$Res, TranslationBonusLang>;\n  @useResult\n  $Res call(\n      {int? price,\n      String? status,\n      @JsonKey(name: 'price_tax') int? priceTax,\n      @JsonKey(name: 'child_count') int? childCount,\n      @JsonKey(name: 'price_in_tax') int? priceInTax,\n      @JsonKey(name: 'recipient_max') int? recipientMax,\n      @JsonKey(name: 'recipient_available_count')\n      int? recipientAvailableCount});\n}\n\n/// @nodoc\nclass _$TranslationBonusLangCopyWithImpl<$Res,\n        $Val extends TranslationBonusLang>\n    implements $TranslationBonusLangCopyWith<$Res> {\n  _$TranslationBonusLangCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of TranslationBonusLang\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? price = freezed,\n    Object? status = freezed,\n    Object? priceTax = freezed,\n    Object? childCount = freezed,\n    Object? priceInTax = freezed,\n    Object? recipientMax = freezed,\n    Object? recipientAvailableCount = freezed,\n  }) {\n    return _then(_value.copyWith(\n      price: freezed == price\n          ? _value.price\n          : price // ignore: cast_nullable_to_non_nullable\n              as int?,\n      status: freezed == status\n          ? _value.status\n          : status // ignore: cast_nullable_to_non_nullable\n              as String?,\n      priceTax: freezed == priceTax\n          ? _value.priceTax\n          : priceTax // ignore: cast_nullable_to_non_nullable\n              as int?,\n      childCount: freezed == childCount\n          ? _value.childCount\n          : childCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n      priceInTax: freezed == priceInTax\n          ? _value.priceInTax\n          : priceInTax // ignore: cast_nullable_to_non_nullable\n              as int?,\n      recipientMax: freezed == recipientMax\n          ? _value.recipientMax\n          : recipientMax // ignore: cast_nullable_to_non_nullable\n              as int?,\n      recipientAvailableCount: freezed == recipientAvailableCount\n          ? _value.recipientAvailableCount\n          : recipientAvailableCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n    ) as $Val);\n  }\n}\n\n/// @nodoc\nabstract class _$$TranslationBonusLangImplCopyWith<$Res>\n    implements $TranslationBonusLangCopyWith<$Res> {\n  factory _$$TranslationBonusLangImplCopyWith(_$TranslationBonusLangImpl value,\n          $Res Function(_$TranslationBonusLangImpl) then) =\n      __$$TranslationBonusLangImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call(\n      {int? price,\n      String? status,\n      @JsonKey(name: 'price_tax') int? priceTax,\n      @JsonKey(name: 'child_count') int? childCount,\n      @JsonKey(name: 'price_in_tax') int? priceInTax,\n      @JsonKey(name: 'recipient_max') int? recipientMax,\n      @JsonKey(name: 'recipient_available_count')\n      int? recipientAvailableCount});\n}\n\n/// @nodoc\nclass __$$TranslationBonusLangImplCopyWithImpl<$Res>\n    extends _$TranslationBonusLangCopyWithImpl<$Res, _$TranslationBonusLangImpl>\n    implements _$$TranslationBonusLangImplCopyWith<$Res> {\n  __$$TranslationBonusLangImplCopyWithImpl(_$TranslationBonusLangImpl _value,\n      $Res Function(_$TranslationBonusLangImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of TranslationBonusLang\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? price = freezed,\n    Object? status = freezed,\n    Object? priceTax = freezed,\n    Object? childCount = freezed,\n    Object? priceInTax = freezed,\n    Object? recipientMax = freezed,\n    Object? recipientAvailableCount = freezed,\n  }) {\n    return _then(_$TranslationBonusLangImpl(\n      price: freezed == price\n          ? _value.price\n          : price // ignore: cast_nullable_to_non_nullable\n              as int?,\n      status: freezed == status\n          ? _value.status\n          : status // ignore: cast_nullable_to_non_nullable\n              as String?,\n      priceTax: freezed == priceTax\n          ? _value.priceTax\n          : priceTax // ignore: cast_nullable_to_non_nullable\n              as int?,\n      childCount: freezed == childCount\n          ? _value.childCount\n          : childCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n      priceInTax: freezed == priceInTax\n          ? _value.priceInTax\n          : priceInTax // ignore: cast_nullable_to_non_nullable\n              as int?,\n      recipientMax: freezed == recipientMax\n          ? _value.recipientMax\n          : recipientMax // ignore: cast_nullable_to_non_nullable\n              as int?,\n      recipientAvailableCount: freezed == recipientAvailableCount\n          ? _value.recipientAvailableCount\n          : recipientAvailableCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$TranslationBonusLangImpl implements _TranslationBonusLang {\n  _$TranslationBonusLangImpl(\n      {this.price,\n      this.status,\n      @JsonKey(name: 'price_tax') this.priceTax,\n      @JsonKey(name: 'child_count') this.childCount,\n      @JsonKey(name: 'price_in_tax') this.priceInTax,\n      @JsonKey(name: 'recipient_max') this.recipientMax,\n      @JsonKey(name: 'recipient_available_count')\n      this.recipientAvailableCount});\n\n  factory _$TranslationBonusLangImpl.fromJson(Map<String, dynamic> json) =>\n      _$$TranslationBonusLangImplFromJson(json);\n\n  @override\n  final int? price;\n  @override\n  final String? status;\n  @override\n  @JsonKey(name: 'price_tax')\n  final int? priceTax;\n  @override\n  @JsonKey(name: 'child_count')\n  final int? childCount;\n  @override\n  @JsonKey(name: 'price_in_tax')\n  final int? priceInTax;\n  @override\n  @JsonKey(name: 'recipient_max')\n  final int? recipientMax;\n  @override\n  @JsonKey(name: 'recipient_available_count')\n  final int? recipientAvailableCount;\n\n  @override\n  String toString() {\n    return 'TranslationBonusLang(price: $price, status: $status, priceTax: $priceTax, childCount: $childCount, priceInTax: $priceInTax, recipientMax: $recipientMax, recipientAvailableCount: $recipientAvailableCount)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$TranslationBonusLangImpl &&\n            (identical(other.price, price) || other.price == price) &&\n            (identical(other.status, status) || other.status == status) &&\n            (identical(other.priceTax, priceTax) ||\n                other.priceTax == priceTax) &&\n            (identical(other.childCount, childCount) ||\n                other.childCount == childCount) &&\n            (identical(other.priceInTax, priceInTax) ||\n                other.priceInTax == priceInTax) &&\n            (identical(other.recipientMax, recipientMax) ||\n                other.recipientMax == recipientMax) &&\n            (identical(\n                    other.recipientAvailableCount, recipientAvailableCount) ||\n                other.recipientAvailableCount == recipientAvailableCount));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(runtimeType, price, status, priceTax,\n      childCount, priceInTax, recipientMax, recipientAvailableCount);\n\n  /// Create a copy of TranslationBonusLang\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$TranslationBonusLangImplCopyWith<_$TranslationBonusLangImpl>\n      get copyWith =>\n          __$$TranslationBonusLangImplCopyWithImpl<_$TranslationBonusLangImpl>(\n              this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$TranslationBonusLangImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _TranslationBonusLang implements TranslationBonusLang {\n  factory _TranslationBonusLang(\n      {final int? price,\n      final String? status,\n      @JsonKey(name: 'price_tax') final int? priceTax,\n      @JsonKey(name: 'child_count') final int? childCount,\n      @JsonKey(name: 'price_in_tax') final int? priceInTax,\n      @JsonKey(name: 'recipient_max') final int? recipientMax,\n      @JsonKey(name: 'recipient_available_count')\n      final int? recipientAvailableCount}) = _$TranslationBonusLangImpl;\n\n  factory _TranslationBonusLang.fromJson(Map<String, dynamic> json) =\n      _$TranslationBonusLangImpl.fromJson;\n\n  @override\n  int? get price;\n  @override\n  String? get status;\n  @override\n  @JsonKey(name: 'price_tax')\n  int? get priceTax;\n  @override\n  @JsonKey(name: 'child_count')\n  int? get childCount;\n  @override\n  @JsonKey(name: 'price_in_tax')\n  int? get priceInTax;\n  @override\n  @JsonKey(name: 'recipient_max')\n  int? get recipientMax;\n  @override\n  @JsonKey(name: 'recipient_available_count')\n  int? get recipientAvailableCount;\n\n  /// Create a copy of TranslationBonusLang\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$TranslationBonusLangImplCopyWith<_$TranslationBonusLangImpl>\n      get copyWith => throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/works/translation_bonus_lang.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'translation_bonus_lang.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$TranslationBonusLangImpl _$$TranslationBonusLangImplFromJson(\n        Map<String, dynamic> json) =>\n    _$TranslationBonusLangImpl(\n      price: (json['price'] as num?)?.toInt(),\n      status: json['status'] as String?,\n      priceTax: (json['price_tax'] as num?)?.toInt(),\n      childCount: (json['child_count'] as num?)?.toInt(),\n      priceInTax: (json['price_in_tax'] as num?)?.toInt(),\n      recipientMax: (json['recipient_max'] as num?)?.toInt(),\n      recipientAvailableCount:\n          (json['recipient_available_count'] as num?)?.toInt(),\n    );\n\nMap<String, dynamic> _$$TranslationBonusLangImplToJson(\n        _$TranslationBonusLangImpl instance) =>\n    <String, dynamic>{\n      'price': instance.price,\n      'status': instance.status,\n      'price_tax': instance.priceTax,\n      'child_count': instance.childCount,\n      'price_in_tax': instance.priceInTax,\n      'recipient_max': instance.recipientMax,\n      'recipient_available_count': instance.recipientAvailableCount,\n    };\n"
  },
  {
    "path": "lib/data/models/works/translation_info.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\nimport 'translation_bonus_lang.dart';\n\npart 'translation_info.freezed.dart';\npart 'translation_info.g.dart';\n\n@freezed\nclass TranslationInfo with _$TranslationInfo {\n  factory TranslationInfo({\n    String? lang,\n    @JsonKey(name: 'is_child') bool? isChild,\n    @JsonKey(name: 'is_parent') bool? isParent,\n    @JsonKey(name: 'is_original') bool? isOriginal,\n    @JsonKey(name: 'is_volunteer') bool? isVolunteer,\n    @JsonKey(name: 'child_worknos') List<dynamic>? childWorknos,\n    @JsonKey(name: 'parent_workno') String? parentWorkno,\n    @JsonKey(name: 'original_workno') String? originalWorkno,\n    @JsonKey(name: 'is_translation_agree') bool? isTranslationAgree,\n    @JsonKey(\n      name: 'translation_bonus_langs',\n      fromJson: _translationBonusLangsFromJson,\n      toJson: _translationBonusLangsToJson,\n    )\n    Map<String, TranslationBonusLang>? translationBonusLangs,\n    @JsonKey(name: 'is_translation_bonus_child') bool? isTranslationBonusChild,\n    @JsonKey(name: 'production_trade_price_rate') int? productionTradePriceRate,\n  }) = _TranslationInfo;\n\n  factory TranslationInfo.fromJson(Map<String, dynamic> json) =>\n      _$TranslationInfoFromJson(json);\n}\n\nMap<String, TranslationBonusLang>? _translationBonusLangsFromJson(\n    dynamic json) {\n  if (json == null) return null;\n  if (json is List && json.isEmpty) return {};\n\n  if (json is Map<String, dynamic>) {\n    return json.map((key, value) => MapEntry(\n          key,\n          TranslationBonusLang.fromJson(value as Map<String, dynamic>),\n        ));\n  }\n\n  return {};\n}\n\ndynamic _translationBonusLangsToJson(Map<String, TranslationBonusLang>? map) {\n  if (map == null) return null;\n  if (map.isEmpty) return [];\n\n  return map.map((key, value) => MapEntry(key, value.toJson()));\n}\n"
  },
  {
    "path": "lib/data/models/works/translation_info.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'translation_info.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nTranslationInfo _$TranslationInfoFromJson(Map<String, dynamic> json) {\n  return _TranslationInfo.fromJson(json);\n}\n\n/// @nodoc\nmixin _$TranslationInfo {\n  String? get lang => throw _privateConstructorUsedError;\n  @JsonKey(name: 'is_child')\n  bool? get isChild => throw _privateConstructorUsedError;\n  @JsonKey(name: 'is_parent')\n  bool? get isParent => throw _privateConstructorUsedError;\n  @JsonKey(name: 'is_original')\n  bool? get isOriginal => throw _privateConstructorUsedError;\n  @JsonKey(name: 'is_volunteer')\n  bool? get isVolunteer => throw _privateConstructorUsedError;\n  @JsonKey(name: 'child_worknos')\n  List<dynamic>? get childWorknos => throw _privateConstructorUsedError;\n  @JsonKey(name: 'parent_workno')\n  String? get parentWorkno => throw _privateConstructorUsedError;\n  @JsonKey(name: 'original_workno')\n  String? get originalWorkno => throw _privateConstructorUsedError;\n  @JsonKey(name: 'is_translation_agree')\n  bool? get isTranslationAgree => throw _privateConstructorUsedError;\n  @JsonKey(\n      name: 'translation_bonus_langs',\n      fromJson: _translationBonusLangsFromJson,\n      toJson: _translationBonusLangsToJson)\n  Map<String, TranslationBonusLang>? get translationBonusLangs =>\n      throw _privateConstructorUsedError;\n  @JsonKey(name: 'is_translation_bonus_child')\n  bool? get isTranslationBonusChild => throw _privateConstructorUsedError;\n  @JsonKey(name: 'production_trade_price_rate')\n  int? get productionTradePriceRate => throw _privateConstructorUsedError;\n\n  /// Serializes this TranslationInfo to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of TranslationInfo\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $TranslationInfoCopyWith<TranslationInfo> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $TranslationInfoCopyWith<$Res> {\n  factory $TranslationInfoCopyWith(\n          TranslationInfo value, $Res Function(TranslationInfo) then) =\n      _$TranslationInfoCopyWithImpl<$Res, TranslationInfo>;\n  @useResult\n  $Res call(\n      {String? lang,\n      @JsonKey(name: 'is_child') bool? isChild,\n      @JsonKey(name: 'is_parent') bool? isParent,\n      @JsonKey(name: 'is_original') bool? isOriginal,\n      @JsonKey(name: 'is_volunteer') bool? isVolunteer,\n      @JsonKey(name: 'child_worknos') List<dynamic>? childWorknos,\n      @JsonKey(name: 'parent_workno') String? parentWorkno,\n      @JsonKey(name: 'original_workno') String? originalWorkno,\n      @JsonKey(name: 'is_translation_agree') bool? isTranslationAgree,\n      @JsonKey(\n          name: 'translation_bonus_langs',\n          fromJson: _translationBonusLangsFromJson,\n          toJson: _translationBonusLangsToJson)\n      Map<String, TranslationBonusLang>? translationBonusLangs,\n      @JsonKey(name: 'is_translation_bonus_child')\n      bool? isTranslationBonusChild,\n      @JsonKey(name: 'production_trade_price_rate')\n      int? productionTradePriceRate});\n}\n\n/// @nodoc\nclass _$TranslationInfoCopyWithImpl<$Res, $Val extends TranslationInfo>\n    implements $TranslationInfoCopyWith<$Res> {\n  _$TranslationInfoCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of TranslationInfo\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? lang = freezed,\n    Object? isChild = freezed,\n    Object? isParent = freezed,\n    Object? isOriginal = freezed,\n    Object? isVolunteer = freezed,\n    Object? childWorknos = freezed,\n    Object? parentWorkno = freezed,\n    Object? originalWorkno = freezed,\n    Object? isTranslationAgree = freezed,\n    Object? translationBonusLangs = freezed,\n    Object? isTranslationBonusChild = freezed,\n    Object? productionTradePriceRate = freezed,\n  }) {\n    return _then(_value.copyWith(\n      lang: freezed == lang\n          ? _value.lang\n          : lang // ignore: cast_nullable_to_non_nullable\n              as String?,\n      isChild: freezed == isChild\n          ? _value.isChild\n          : isChild // ignore: cast_nullable_to_non_nullable\n              as bool?,\n      isParent: freezed == isParent\n          ? _value.isParent\n          : isParent // ignore: cast_nullable_to_non_nullable\n              as bool?,\n      isOriginal: freezed == isOriginal\n          ? _value.isOriginal\n          : isOriginal // ignore: cast_nullable_to_non_nullable\n              as bool?,\n      isVolunteer: freezed == isVolunteer\n          ? _value.isVolunteer\n          : isVolunteer // ignore: cast_nullable_to_non_nullable\n              as bool?,\n      childWorknos: freezed == childWorknos\n          ? _value.childWorknos\n          : childWorknos // ignore: cast_nullable_to_non_nullable\n              as List<dynamic>?,\n      parentWorkno: freezed == parentWorkno\n          ? _value.parentWorkno\n          : parentWorkno // ignore: cast_nullable_to_non_nullable\n              as String?,\n      originalWorkno: freezed == originalWorkno\n          ? _value.originalWorkno\n          : originalWorkno // ignore: cast_nullable_to_non_nullable\n              as String?,\n      isTranslationAgree: freezed == isTranslationAgree\n          ? _value.isTranslationAgree\n          : isTranslationAgree // ignore: cast_nullable_to_non_nullable\n              as bool?,\n      translationBonusLangs: freezed == translationBonusLangs\n          ? _value.translationBonusLangs\n          : translationBonusLangs // ignore: cast_nullable_to_non_nullable\n              as Map<String, TranslationBonusLang>?,\n      isTranslationBonusChild: freezed == isTranslationBonusChild\n          ? _value.isTranslationBonusChild\n          : isTranslationBonusChild // ignore: cast_nullable_to_non_nullable\n              as bool?,\n      productionTradePriceRate: freezed == productionTradePriceRate\n          ? _value.productionTradePriceRate\n          : productionTradePriceRate // ignore: cast_nullable_to_non_nullable\n              as int?,\n    ) as $Val);\n  }\n}\n\n/// @nodoc\nabstract class _$$TranslationInfoImplCopyWith<$Res>\n    implements $TranslationInfoCopyWith<$Res> {\n  factory _$$TranslationInfoImplCopyWith(_$TranslationInfoImpl value,\n          $Res Function(_$TranslationInfoImpl) then) =\n      __$$TranslationInfoImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call(\n      {String? lang,\n      @JsonKey(name: 'is_child') bool? isChild,\n      @JsonKey(name: 'is_parent') bool? isParent,\n      @JsonKey(name: 'is_original') bool? isOriginal,\n      @JsonKey(name: 'is_volunteer') bool? isVolunteer,\n      @JsonKey(name: 'child_worknos') List<dynamic>? childWorknos,\n      @JsonKey(name: 'parent_workno') String? parentWorkno,\n      @JsonKey(name: 'original_workno') String? originalWorkno,\n      @JsonKey(name: 'is_translation_agree') bool? isTranslationAgree,\n      @JsonKey(\n          name: 'translation_bonus_langs',\n          fromJson: _translationBonusLangsFromJson,\n          toJson: _translationBonusLangsToJson)\n      Map<String, TranslationBonusLang>? translationBonusLangs,\n      @JsonKey(name: 'is_translation_bonus_child')\n      bool? isTranslationBonusChild,\n      @JsonKey(name: 'production_trade_price_rate')\n      int? productionTradePriceRate});\n}\n\n/// @nodoc\nclass __$$TranslationInfoImplCopyWithImpl<$Res>\n    extends _$TranslationInfoCopyWithImpl<$Res, _$TranslationInfoImpl>\n    implements _$$TranslationInfoImplCopyWith<$Res> {\n  __$$TranslationInfoImplCopyWithImpl(\n      _$TranslationInfoImpl _value, $Res Function(_$TranslationInfoImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of TranslationInfo\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? lang = freezed,\n    Object? isChild = freezed,\n    Object? isParent = freezed,\n    Object? isOriginal = freezed,\n    Object? isVolunteer = freezed,\n    Object? childWorknos = freezed,\n    Object? parentWorkno = freezed,\n    Object? originalWorkno = freezed,\n    Object? isTranslationAgree = freezed,\n    Object? translationBonusLangs = freezed,\n    Object? isTranslationBonusChild = freezed,\n    Object? productionTradePriceRate = freezed,\n  }) {\n    return _then(_$TranslationInfoImpl(\n      lang: freezed == lang\n          ? _value.lang\n          : lang // ignore: cast_nullable_to_non_nullable\n              as String?,\n      isChild: freezed == isChild\n          ? _value.isChild\n          : isChild // ignore: cast_nullable_to_non_nullable\n              as bool?,\n      isParent: freezed == isParent\n          ? _value.isParent\n          : isParent // ignore: cast_nullable_to_non_nullable\n              as bool?,\n      isOriginal: freezed == isOriginal\n          ? _value.isOriginal\n          : isOriginal // ignore: cast_nullable_to_non_nullable\n              as bool?,\n      isVolunteer: freezed == isVolunteer\n          ? _value.isVolunteer\n          : isVolunteer // ignore: cast_nullable_to_non_nullable\n              as bool?,\n      childWorknos: freezed == childWorknos\n          ? _value._childWorknos\n          : childWorknos // ignore: cast_nullable_to_non_nullable\n              as List<dynamic>?,\n      parentWorkno: freezed == parentWorkno\n          ? _value.parentWorkno\n          : parentWorkno // ignore: cast_nullable_to_non_nullable\n              as String?,\n      originalWorkno: freezed == originalWorkno\n          ? _value.originalWorkno\n          : originalWorkno // ignore: cast_nullable_to_non_nullable\n              as String?,\n      isTranslationAgree: freezed == isTranslationAgree\n          ? _value.isTranslationAgree\n          : isTranslationAgree // ignore: cast_nullable_to_non_nullable\n              as bool?,\n      translationBonusLangs: freezed == translationBonusLangs\n          ? _value._translationBonusLangs\n          : translationBonusLangs // ignore: cast_nullable_to_non_nullable\n              as Map<String, TranslationBonusLang>?,\n      isTranslationBonusChild: freezed == isTranslationBonusChild\n          ? _value.isTranslationBonusChild\n          : isTranslationBonusChild // ignore: cast_nullable_to_non_nullable\n              as bool?,\n      productionTradePriceRate: freezed == productionTradePriceRate\n          ? _value.productionTradePriceRate\n          : productionTradePriceRate // ignore: cast_nullable_to_non_nullable\n              as int?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$TranslationInfoImpl implements _TranslationInfo {\n  _$TranslationInfoImpl(\n      {this.lang,\n      @JsonKey(name: 'is_child') this.isChild,\n      @JsonKey(name: 'is_parent') this.isParent,\n      @JsonKey(name: 'is_original') this.isOriginal,\n      @JsonKey(name: 'is_volunteer') this.isVolunteer,\n      @JsonKey(name: 'child_worknos') final List<dynamic>? childWorknos,\n      @JsonKey(name: 'parent_workno') this.parentWorkno,\n      @JsonKey(name: 'original_workno') this.originalWorkno,\n      @JsonKey(name: 'is_translation_agree') this.isTranslationAgree,\n      @JsonKey(\n          name: 'translation_bonus_langs',\n          fromJson: _translationBonusLangsFromJson,\n          toJson: _translationBonusLangsToJson)\n      final Map<String, TranslationBonusLang>? translationBonusLangs,\n      @JsonKey(name: 'is_translation_bonus_child') this.isTranslationBonusChild,\n      @JsonKey(name: 'production_trade_price_rate')\n      this.productionTradePriceRate})\n      : _childWorknos = childWorknos,\n        _translationBonusLangs = translationBonusLangs;\n\n  factory _$TranslationInfoImpl.fromJson(Map<String, dynamic> json) =>\n      _$$TranslationInfoImplFromJson(json);\n\n  @override\n  final String? lang;\n  @override\n  @JsonKey(name: 'is_child')\n  final bool? isChild;\n  @override\n  @JsonKey(name: 'is_parent')\n  final bool? isParent;\n  @override\n  @JsonKey(name: 'is_original')\n  final bool? isOriginal;\n  @override\n  @JsonKey(name: 'is_volunteer')\n  final bool? isVolunteer;\n  final List<dynamic>? _childWorknos;\n  @override\n  @JsonKey(name: 'child_worknos')\n  List<dynamic>? get childWorknos {\n    final value = _childWorknos;\n    if (value == null) return null;\n    if (_childWorknos is EqualUnmodifiableListView) return _childWorknos;\n    // ignore: implicit_dynamic_type\n    return EqualUnmodifiableListView(value);\n  }\n\n  @override\n  @JsonKey(name: 'parent_workno')\n  final String? parentWorkno;\n  @override\n  @JsonKey(name: 'original_workno')\n  final String? originalWorkno;\n  @override\n  @JsonKey(name: 'is_translation_agree')\n  final bool? isTranslationAgree;\n  final Map<String, TranslationBonusLang>? _translationBonusLangs;\n  @override\n  @JsonKey(\n      name: 'translation_bonus_langs',\n      fromJson: _translationBonusLangsFromJson,\n      toJson: _translationBonusLangsToJson)\n  Map<String, TranslationBonusLang>? get translationBonusLangs {\n    final value = _translationBonusLangs;\n    if (value == null) return null;\n    if (_translationBonusLangs is EqualUnmodifiableMapView)\n      return _translationBonusLangs;\n    // ignore: implicit_dynamic_type\n    return EqualUnmodifiableMapView(value);\n  }\n\n  @override\n  @JsonKey(name: 'is_translation_bonus_child')\n  final bool? isTranslationBonusChild;\n  @override\n  @JsonKey(name: 'production_trade_price_rate')\n  final int? productionTradePriceRate;\n\n  @override\n  String toString() {\n    return 'TranslationInfo(lang: $lang, isChild: $isChild, isParent: $isParent, isOriginal: $isOriginal, isVolunteer: $isVolunteer, childWorknos: $childWorknos, parentWorkno: $parentWorkno, originalWorkno: $originalWorkno, isTranslationAgree: $isTranslationAgree, translationBonusLangs: $translationBonusLangs, isTranslationBonusChild: $isTranslationBonusChild, productionTradePriceRate: $productionTradePriceRate)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$TranslationInfoImpl &&\n            (identical(other.lang, lang) || other.lang == lang) &&\n            (identical(other.isChild, isChild) || other.isChild == isChild) &&\n            (identical(other.isParent, isParent) ||\n                other.isParent == isParent) &&\n            (identical(other.isOriginal, isOriginal) ||\n                other.isOriginal == isOriginal) &&\n            (identical(other.isVolunteer, isVolunteer) ||\n                other.isVolunteer == isVolunteer) &&\n            const DeepCollectionEquality()\n                .equals(other._childWorknos, _childWorknos) &&\n            (identical(other.parentWorkno, parentWorkno) ||\n                other.parentWorkno == parentWorkno) &&\n            (identical(other.originalWorkno, originalWorkno) ||\n                other.originalWorkno == originalWorkno) &&\n            (identical(other.isTranslationAgree, isTranslationAgree) ||\n                other.isTranslationAgree == isTranslationAgree) &&\n            const DeepCollectionEquality()\n                .equals(other._translationBonusLangs, _translationBonusLangs) &&\n            (identical(\n                    other.isTranslationBonusChild, isTranslationBonusChild) ||\n                other.isTranslationBonusChild == isTranslationBonusChild) &&\n            (identical(\n                    other.productionTradePriceRate, productionTradePriceRate) ||\n                other.productionTradePriceRate == productionTradePriceRate));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(\n      runtimeType,\n      lang,\n      isChild,\n      isParent,\n      isOriginal,\n      isVolunteer,\n      const DeepCollectionEquality().hash(_childWorknos),\n      parentWorkno,\n      originalWorkno,\n      isTranslationAgree,\n      const DeepCollectionEquality().hash(_translationBonusLangs),\n      isTranslationBonusChild,\n      productionTradePriceRate);\n\n  /// Create a copy of TranslationInfo\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$TranslationInfoImplCopyWith<_$TranslationInfoImpl> get copyWith =>\n      __$$TranslationInfoImplCopyWithImpl<_$TranslationInfoImpl>(\n          this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$TranslationInfoImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _TranslationInfo implements TranslationInfo {\n  factory _TranslationInfo(\n      {final String? lang,\n      @JsonKey(name: 'is_child') final bool? isChild,\n      @JsonKey(name: 'is_parent') final bool? isParent,\n      @JsonKey(name: 'is_original') final bool? isOriginal,\n      @JsonKey(name: 'is_volunteer') final bool? isVolunteer,\n      @JsonKey(name: 'child_worknos') final List<dynamic>? childWorknos,\n      @JsonKey(name: 'parent_workno') final String? parentWorkno,\n      @JsonKey(name: 'original_workno') final String? originalWorkno,\n      @JsonKey(name: 'is_translation_agree') final bool? isTranslationAgree,\n      @JsonKey(\n          name: 'translation_bonus_langs',\n          fromJson: _translationBonusLangsFromJson,\n          toJson: _translationBonusLangsToJson)\n      final Map<String, TranslationBonusLang>? translationBonusLangs,\n      @JsonKey(name: 'is_translation_bonus_child')\n      final bool? isTranslationBonusChild,\n      @JsonKey(name: 'production_trade_price_rate')\n      final int? productionTradePriceRate}) = _$TranslationInfoImpl;\n\n  factory _TranslationInfo.fromJson(Map<String, dynamic> json) =\n      _$TranslationInfoImpl.fromJson;\n\n  @override\n  String? get lang;\n  @override\n  @JsonKey(name: 'is_child')\n  bool? get isChild;\n  @override\n  @JsonKey(name: 'is_parent')\n  bool? get isParent;\n  @override\n  @JsonKey(name: 'is_original')\n  bool? get isOriginal;\n  @override\n  @JsonKey(name: 'is_volunteer')\n  bool? get isVolunteer;\n  @override\n  @JsonKey(name: 'child_worknos')\n  List<dynamic>? get childWorknos;\n  @override\n  @JsonKey(name: 'parent_workno')\n  String? get parentWorkno;\n  @override\n  @JsonKey(name: 'original_workno')\n  String? get originalWorkno;\n  @override\n  @JsonKey(name: 'is_translation_agree')\n  bool? get isTranslationAgree;\n  @override\n  @JsonKey(\n      name: 'translation_bonus_langs',\n      fromJson: _translationBonusLangsFromJson,\n      toJson: _translationBonusLangsToJson)\n  Map<String, TranslationBonusLang>? get translationBonusLangs;\n  @override\n  @JsonKey(name: 'is_translation_bonus_child')\n  bool? get isTranslationBonusChild;\n  @override\n  @JsonKey(name: 'production_trade_price_rate')\n  int? get productionTradePriceRate;\n\n  /// Create a copy of TranslationInfo\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$TranslationInfoImplCopyWith<_$TranslationInfoImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/works/translation_info.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'translation_info.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$TranslationInfoImpl _$$TranslationInfoImplFromJson(\n        Map<String, dynamic> json) =>\n    _$TranslationInfoImpl(\n      lang: json['lang'] as String?,\n      isChild: json['is_child'] as bool?,\n      isParent: json['is_parent'] as bool?,\n      isOriginal: json['is_original'] as bool?,\n      isVolunteer: json['is_volunteer'] as bool?,\n      childWorknos: json['child_worknos'] as List<dynamic>?,\n      parentWorkno: json['parent_workno'] as String?,\n      originalWorkno: json['original_workno'] as String?,\n      isTranslationAgree: json['is_translation_agree'] as bool?,\n      translationBonusLangs:\n          _translationBonusLangsFromJson(json['translation_bonus_langs']),\n      isTranslationBonusChild: json['is_translation_bonus_child'] as bool?,\n      productionTradePriceRate:\n          (json['production_trade_price_rate'] as num?)?.toInt(),\n    );\n\nMap<String, dynamic> _$$TranslationInfoImplToJson(\n        _$TranslationInfoImpl instance) =>\n    <String, dynamic>{\n      'lang': instance.lang,\n      'is_child': instance.isChild,\n      'is_parent': instance.isParent,\n      'is_original': instance.isOriginal,\n      'is_volunteer': instance.isVolunteer,\n      'child_worknos': instance.childWorknos,\n      'parent_workno': instance.parentWorkno,\n      'original_workno': instance.originalWorkno,\n      'is_translation_agree': instance.isTranslationAgree,\n      'translation_bonus_langs':\n          _translationBonusLangsToJson(instance.translationBonusLangs),\n      'is_translation_bonus_child': instance.isTranslationBonusChild,\n      'production_trade_price_rate': instance.productionTradePriceRate,\n    };\n"
  },
  {
    "path": "lib/data/models/works/work.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'circle.dart';\nimport 'language_edition.dart';\nimport 'other_language_editions_in_db.dart';\nimport 'tag.dart';\nimport 'translation_info.dart';\n\npart 'work.freezed.dart';\npart 'work.g.dart';\n\n@freezed\nclass Work with _$Work {\n  factory Work({\n    int? id,\n    String? title,\n    @JsonKey(name: 'circle_id') int? circleId,\n    String? name,\n    bool? nsfw,\n    String? release,\n    @JsonKey(name: 'dl_count') int? dlCount,\n    int? price,\n    @JsonKey(name: 'review_count') int? reviewCount,\n    @JsonKey(name: 'rate_count') int? rateCount,\n    @JsonKey(name: 'rate_average_2dp') int? rateAverage2dp,\n    @JsonKey(name: 'rate_count_detail') List<dynamic>? rateCountDetail,\n    dynamic rank,\n    @JsonKey(name: 'has_subtitle') bool? hasSubtitle,\n    @JsonKey(name: 'create_date') String? createDate,\n    List<dynamic>? vas,\n    List<Tag>? tags,\n    @JsonKey(name: 'language_editions') List<LanguageEdition>? languageEditions,\n    @JsonKey(name: 'original_workno') String? originalWorkno,\n    @JsonKey(name: 'other_language_editions_in_db')\n    List<OtherLanguageEditionsInDb>? otherLanguageEditionsInDb,\n    @JsonKey(name: 'translation_info') TranslationInfo? translationInfo,\n    @JsonKey(name: 'work_attributes') String? workAttributes,\n    @JsonKey(name: 'age_category_string') String? ageCategoryString,\n    int? duration,\n    @JsonKey(name: 'source_type') String? sourceType,\n    @JsonKey(name: 'source_id') String? sourceId,\n    @JsonKey(name: 'source_url') String? sourceUrl,\n    dynamic userRating,\n    Circle? circle,\n    String? samCoverUrl,\n    String? thumbnailCoverUrl,\n    String? mainCoverUrl,\n  }) = _Work;\n\n  factory Work.fromJson(Map<String, dynamic> json) => _$WorkFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/works/work.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'work.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nWork _$WorkFromJson(Map<String, dynamic> json) {\n  return _Work.fromJson(json);\n}\n\n/// @nodoc\nmixin _$Work {\n  int? get id => throw _privateConstructorUsedError;\n  String? get title => throw _privateConstructorUsedError;\n  @JsonKey(name: 'circle_id')\n  int? get circleId => throw _privateConstructorUsedError;\n  String? get name => throw _privateConstructorUsedError;\n  bool? get nsfw => throw _privateConstructorUsedError;\n  String? get release => throw _privateConstructorUsedError;\n  @JsonKey(name: 'dl_count')\n  int? get dlCount => throw _privateConstructorUsedError;\n  int? get price => throw _privateConstructorUsedError;\n  @JsonKey(name: 'review_count')\n  int? get reviewCount => throw _privateConstructorUsedError;\n  @JsonKey(name: 'rate_count')\n  int? get rateCount => throw _privateConstructorUsedError;\n  @JsonKey(name: 'rate_average_2dp')\n  int? get rateAverage2dp => throw _privateConstructorUsedError;\n  @JsonKey(name: 'rate_count_detail')\n  List<dynamic>? get rateCountDetail => throw _privateConstructorUsedError;\n  dynamic get rank => throw _privateConstructorUsedError;\n  @JsonKey(name: 'has_subtitle')\n  bool? get hasSubtitle => throw _privateConstructorUsedError;\n  @JsonKey(name: 'create_date')\n  String? get createDate => throw _privateConstructorUsedError;\n  List<dynamic>? get vas => throw _privateConstructorUsedError;\n  List<Tag>? get tags => throw _privateConstructorUsedError;\n  @JsonKey(name: 'language_editions')\n  List<LanguageEdition>? get languageEditions =>\n      throw _privateConstructorUsedError;\n  @JsonKey(name: 'original_workno')\n  String? get originalWorkno => throw _privateConstructorUsedError;\n  @JsonKey(name: 'other_language_editions_in_db')\n  List<OtherLanguageEditionsInDb>? get otherLanguageEditionsInDb =>\n      throw _privateConstructorUsedError;\n  @JsonKey(name: 'translation_info')\n  TranslationInfo? get translationInfo => throw _privateConstructorUsedError;\n  @JsonKey(name: 'work_attributes')\n  String? get workAttributes => throw _privateConstructorUsedError;\n  @JsonKey(name: 'age_category_string')\n  String? get ageCategoryString => throw _privateConstructorUsedError;\n  int? get duration => throw _privateConstructorUsedError;\n  @JsonKey(name: 'source_type')\n  String? get sourceType => throw _privateConstructorUsedError;\n  @JsonKey(name: 'source_id')\n  String? get sourceId => throw _privateConstructorUsedError;\n  @JsonKey(name: 'source_url')\n  String? get sourceUrl => throw _privateConstructorUsedError;\n  dynamic get userRating => throw _privateConstructorUsedError;\n  Circle? get circle => throw _privateConstructorUsedError;\n  String? get samCoverUrl => throw _privateConstructorUsedError;\n  String? get thumbnailCoverUrl => throw _privateConstructorUsedError;\n  String? get mainCoverUrl => throw _privateConstructorUsedError;\n\n  /// Serializes this Work to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of Work\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $WorkCopyWith<Work> get copyWith => throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $WorkCopyWith<$Res> {\n  factory $WorkCopyWith(Work value, $Res Function(Work) then) =\n      _$WorkCopyWithImpl<$Res, Work>;\n  @useResult\n  $Res call(\n      {int? id,\n      String? title,\n      @JsonKey(name: 'circle_id') int? circleId,\n      String? name,\n      bool? nsfw,\n      String? release,\n      @JsonKey(name: 'dl_count') int? dlCount,\n      int? price,\n      @JsonKey(name: 'review_count') int? reviewCount,\n      @JsonKey(name: 'rate_count') int? rateCount,\n      @JsonKey(name: 'rate_average_2dp') int? rateAverage2dp,\n      @JsonKey(name: 'rate_count_detail') List<dynamic>? rateCountDetail,\n      dynamic rank,\n      @JsonKey(name: 'has_subtitle') bool? hasSubtitle,\n      @JsonKey(name: 'create_date') String? createDate,\n      List<dynamic>? vas,\n      List<Tag>? tags,\n      @JsonKey(name: 'language_editions')\n      List<LanguageEdition>? languageEditions,\n      @JsonKey(name: 'original_workno') String? originalWorkno,\n      @JsonKey(name: 'other_language_editions_in_db')\n      List<OtherLanguageEditionsInDb>? otherLanguageEditionsInDb,\n      @JsonKey(name: 'translation_info') TranslationInfo? translationInfo,\n      @JsonKey(name: 'work_attributes') String? workAttributes,\n      @JsonKey(name: 'age_category_string') String? ageCategoryString,\n      int? duration,\n      @JsonKey(name: 'source_type') String? sourceType,\n      @JsonKey(name: 'source_id') String? sourceId,\n      @JsonKey(name: 'source_url') String? sourceUrl,\n      dynamic userRating,\n      Circle? circle,\n      String? samCoverUrl,\n      String? thumbnailCoverUrl,\n      String? mainCoverUrl});\n\n  $TranslationInfoCopyWith<$Res>? get translationInfo;\n  $CircleCopyWith<$Res>? get circle;\n}\n\n/// @nodoc\nclass _$WorkCopyWithImpl<$Res, $Val extends Work>\n    implements $WorkCopyWith<$Res> {\n  _$WorkCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of Work\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? id = freezed,\n    Object? title = freezed,\n    Object? circleId = freezed,\n    Object? name = freezed,\n    Object? nsfw = freezed,\n    Object? release = freezed,\n    Object? dlCount = freezed,\n    Object? price = freezed,\n    Object? reviewCount = freezed,\n    Object? rateCount = freezed,\n    Object? rateAverage2dp = freezed,\n    Object? rateCountDetail = freezed,\n    Object? rank = freezed,\n    Object? hasSubtitle = freezed,\n    Object? createDate = freezed,\n    Object? vas = freezed,\n    Object? tags = freezed,\n    Object? languageEditions = freezed,\n    Object? originalWorkno = freezed,\n    Object? otherLanguageEditionsInDb = freezed,\n    Object? translationInfo = freezed,\n    Object? workAttributes = freezed,\n    Object? ageCategoryString = freezed,\n    Object? duration = freezed,\n    Object? sourceType = freezed,\n    Object? sourceId = freezed,\n    Object? sourceUrl = freezed,\n    Object? userRating = freezed,\n    Object? circle = freezed,\n    Object? samCoverUrl = freezed,\n    Object? thumbnailCoverUrl = freezed,\n    Object? mainCoverUrl = freezed,\n  }) {\n    return _then(_value.copyWith(\n      id: freezed == id\n          ? _value.id\n          : id // ignore: cast_nullable_to_non_nullable\n              as int?,\n      title: freezed == title\n          ? _value.title\n          : title // ignore: cast_nullable_to_non_nullable\n              as String?,\n      circleId: freezed == circleId\n          ? _value.circleId\n          : circleId // ignore: cast_nullable_to_non_nullable\n              as int?,\n      name: freezed == name\n          ? _value.name\n          : name // ignore: cast_nullable_to_non_nullable\n              as String?,\n      nsfw: freezed == nsfw\n          ? _value.nsfw\n          : nsfw // ignore: cast_nullable_to_non_nullable\n              as bool?,\n      release: freezed == release\n          ? _value.release\n          : release // ignore: cast_nullable_to_non_nullable\n              as String?,\n      dlCount: freezed == dlCount\n          ? _value.dlCount\n          : dlCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n      price: freezed == price\n          ? _value.price\n          : price // ignore: cast_nullable_to_non_nullable\n              as int?,\n      reviewCount: freezed == reviewCount\n          ? _value.reviewCount\n          : reviewCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n      rateCount: freezed == rateCount\n          ? _value.rateCount\n          : rateCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n      rateAverage2dp: freezed == rateAverage2dp\n          ? _value.rateAverage2dp\n          : rateAverage2dp // ignore: cast_nullable_to_non_nullable\n              as int?,\n      rateCountDetail: freezed == rateCountDetail\n          ? _value.rateCountDetail\n          : rateCountDetail // ignore: cast_nullable_to_non_nullable\n              as List<dynamic>?,\n      rank: freezed == rank\n          ? _value.rank\n          : rank // ignore: cast_nullable_to_non_nullable\n              as dynamic,\n      hasSubtitle: freezed == hasSubtitle\n          ? _value.hasSubtitle\n          : hasSubtitle // ignore: cast_nullable_to_non_nullable\n              as bool?,\n      createDate: freezed == createDate\n          ? _value.createDate\n          : createDate // ignore: cast_nullable_to_non_nullable\n              as String?,\n      vas: freezed == vas\n          ? _value.vas\n          : vas // ignore: cast_nullable_to_non_nullable\n              as List<dynamic>?,\n      tags: freezed == tags\n          ? _value.tags\n          : tags // ignore: cast_nullable_to_non_nullable\n              as List<Tag>?,\n      languageEditions: freezed == languageEditions\n          ? _value.languageEditions\n          : languageEditions // ignore: cast_nullable_to_non_nullable\n              as List<LanguageEdition>?,\n      originalWorkno: freezed == originalWorkno\n          ? _value.originalWorkno\n          : originalWorkno // ignore: cast_nullable_to_non_nullable\n              as String?,\n      otherLanguageEditionsInDb: freezed == otherLanguageEditionsInDb\n          ? _value.otherLanguageEditionsInDb\n          : otherLanguageEditionsInDb // ignore: cast_nullable_to_non_nullable\n              as List<OtherLanguageEditionsInDb>?,\n      translationInfo: freezed == translationInfo\n          ? _value.translationInfo\n          : translationInfo // ignore: cast_nullable_to_non_nullable\n              as TranslationInfo?,\n      workAttributes: freezed == workAttributes\n          ? _value.workAttributes\n          : workAttributes // ignore: cast_nullable_to_non_nullable\n              as String?,\n      ageCategoryString: freezed == ageCategoryString\n          ? _value.ageCategoryString\n          : ageCategoryString // ignore: cast_nullable_to_non_nullable\n              as String?,\n      duration: freezed == duration\n          ? _value.duration\n          : duration // ignore: cast_nullable_to_non_nullable\n              as int?,\n      sourceType: freezed == sourceType\n          ? _value.sourceType\n          : sourceType // ignore: cast_nullable_to_non_nullable\n              as String?,\n      sourceId: freezed == sourceId\n          ? _value.sourceId\n          : sourceId // ignore: cast_nullable_to_non_nullable\n              as String?,\n      sourceUrl: freezed == sourceUrl\n          ? _value.sourceUrl\n          : sourceUrl // ignore: cast_nullable_to_non_nullable\n              as String?,\n      userRating: freezed == userRating\n          ? _value.userRating\n          : userRating // ignore: cast_nullable_to_non_nullable\n              as dynamic,\n      circle: freezed == circle\n          ? _value.circle\n          : circle // ignore: cast_nullable_to_non_nullable\n              as Circle?,\n      samCoverUrl: freezed == samCoverUrl\n          ? _value.samCoverUrl\n          : samCoverUrl // ignore: cast_nullable_to_non_nullable\n              as String?,\n      thumbnailCoverUrl: freezed == thumbnailCoverUrl\n          ? _value.thumbnailCoverUrl\n          : thumbnailCoverUrl // ignore: cast_nullable_to_non_nullable\n              as String?,\n      mainCoverUrl: freezed == mainCoverUrl\n          ? _value.mainCoverUrl\n          : mainCoverUrl // ignore: cast_nullable_to_non_nullable\n              as String?,\n    ) as $Val);\n  }\n\n  /// Create a copy of Work\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @pragma('vm:prefer-inline')\n  $TranslationInfoCopyWith<$Res>? get translationInfo {\n    if (_value.translationInfo == null) {\n      return null;\n    }\n\n    return $TranslationInfoCopyWith<$Res>(_value.translationInfo!, (value) {\n      return _then(_value.copyWith(translationInfo: value) as $Val);\n    });\n  }\n\n  /// Create a copy of Work\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @pragma('vm:prefer-inline')\n  $CircleCopyWith<$Res>? get circle {\n    if (_value.circle == null) {\n      return null;\n    }\n\n    return $CircleCopyWith<$Res>(_value.circle!, (value) {\n      return _then(_value.copyWith(circle: value) as $Val);\n    });\n  }\n}\n\n/// @nodoc\nabstract class _$$WorkImplCopyWith<$Res> implements $WorkCopyWith<$Res> {\n  factory _$$WorkImplCopyWith(\n          _$WorkImpl value, $Res Function(_$WorkImpl) then) =\n      __$$WorkImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call(\n      {int? id,\n      String? title,\n      @JsonKey(name: 'circle_id') int? circleId,\n      String? name,\n      bool? nsfw,\n      String? release,\n      @JsonKey(name: 'dl_count') int? dlCount,\n      int? price,\n      @JsonKey(name: 'review_count') int? reviewCount,\n      @JsonKey(name: 'rate_count') int? rateCount,\n      @JsonKey(name: 'rate_average_2dp') int? rateAverage2dp,\n      @JsonKey(name: 'rate_count_detail') List<dynamic>? rateCountDetail,\n      dynamic rank,\n      @JsonKey(name: 'has_subtitle') bool? hasSubtitle,\n      @JsonKey(name: 'create_date') String? createDate,\n      List<dynamic>? vas,\n      List<Tag>? tags,\n      @JsonKey(name: 'language_editions')\n      List<LanguageEdition>? languageEditions,\n      @JsonKey(name: 'original_workno') String? originalWorkno,\n      @JsonKey(name: 'other_language_editions_in_db')\n      List<OtherLanguageEditionsInDb>? otherLanguageEditionsInDb,\n      @JsonKey(name: 'translation_info') TranslationInfo? translationInfo,\n      @JsonKey(name: 'work_attributes') String? workAttributes,\n      @JsonKey(name: 'age_category_string') String? ageCategoryString,\n      int? duration,\n      @JsonKey(name: 'source_type') String? sourceType,\n      @JsonKey(name: 'source_id') String? sourceId,\n      @JsonKey(name: 'source_url') String? sourceUrl,\n      dynamic userRating,\n      Circle? circle,\n      String? samCoverUrl,\n      String? thumbnailCoverUrl,\n      String? mainCoverUrl});\n\n  @override\n  $TranslationInfoCopyWith<$Res>? get translationInfo;\n  @override\n  $CircleCopyWith<$Res>? get circle;\n}\n\n/// @nodoc\nclass __$$WorkImplCopyWithImpl<$Res>\n    extends _$WorkCopyWithImpl<$Res, _$WorkImpl>\n    implements _$$WorkImplCopyWith<$Res> {\n  __$$WorkImplCopyWithImpl(_$WorkImpl _value, $Res Function(_$WorkImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of Work\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? id = freezed,\n    Object? title = freezed,\n    Object? circleId = freezed,\n    Object? name = freezed,\n    Object? nsfw = freezed,\n    Object? release = freezed,\n    Object? dlCount = freezed,\n    Object? price = freezed,\n    Object? reviewCount = freezed,\n    Object? rateCount = freezed,\n    Object? rateAverage2dp = freezed,\n    Object? rateCountDetail = freezed,\n    Object? rank = freezed,\n    Object? hasSubtitle = freezed,\n    Object? createDate = freezed,\n    Object? vas = freezed,\n    Object? tags = freezed,\n    Object? languageEditions = freezed,\n    Object? originalWorkno = freezed,\n    Object? otherLanguageEditionsInDb = freezed,\n    Object? translationInfo = freezed,\n    Object? workAttributes = freezed,\n    Object? ageCategoryString = freezed,\n    Object? duration = freezed,\n    Object? sourceType = freezed,\n    Object? sourceId = freezed,\n    Object? sourceUrl = freezed,\n    Object? userRating = freezed,\n    Object? circle = freezed,\n    Object? samCoverUrl = freezed,\n    Object? thumbnailCoverUrl = freezed,\n    Object? mainCoverUrl = freezed,\n  }) {\n    return _then(_$WorkImpl(\n      id: freezed == id\n          ? _value.id\n          : id // ignore: cast_nullable_to_non_nullable\n              as int?,\n      title: freezed == title\n          ? _value.title\n          : title // ignore: cast_nullable_to_non_nullable\n              as String?,\n      circleId: freezed == circleId\n          ? _value.circleId\n          : circleId // ignore: cast_nullable_to_non_nullable\n              as int?,\n      name: freezed == name\n          ? _value.name\n          : name // ignore: cast_nullable_to_non_nullable\n              as String?,\n      nsfw: freezed == nsfw\n          ? _value.nsfw\n          : nsfw // ignore: cast_nullable_to_non_nullable\n              as bool?,\n      release: freezed == release\n          ? _value.release\n          : release // ignore: cast_nullable_to_non_nullable\n              as String?,\n      dlCount: freezed == dlCount\n          ? _value.dlCount\n          : dlCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n      price: freezed == price\n          ? _value.price\n          : price // ignore: cast_nullable_to_non_nullable\n              as int?,\n      reviewCount: freezed == reviewCount\n          ? _value.reviewCount\n          : reviewCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n      rateCount: freezed == rateCount\n          ? _value.rateCount\n          : rateCount // ignore: cast_nullable_to_non_nullable\n              as int?,\n      rateAverage2dp: freezed == rateAverage2dp\n          ? _value.rateAverage2dp\n          : rateAverage2dp // ignore: cast_nullable_to_non_nullable\n              as int?,\n      rateCountDetail: freezed == rateCountDetail\n          ? _value._rateCountDetail\n          : rateCountDetail // ignore: cast_nullable_to_non_nullable\n              as List<dynamic>?,\n      rank: freezed == rank\n          ? _value.rank\n          : rank // ignore: cast_nullable_to_non_nullable\n              as dynamic,\n      hasSubtitle: freezed == hasSubtitle\n          ? _value.hasSubtitle\n          : hasSubtitle // ignore: cast_nullable_to_non_nullable\n              as bool?,\n      createDate: freezed == createDate\n          ? _value.createDate\n          : createDate // ignore: cast_nullable_to_non_nullable\n              as String?,\n      vas: freezed == vas\n          ? _value._vas\n          : vas // ignore: cast_nullable_to_non_nullable\n              as List<dynamic>?,\n      tags: freezed == tags\n          ? _value._tags\n          : tags // ignore: cast_nullable_to_non_nullable\n              as List<Tag>?,\n      languageEditions: freezed == languageEditions\n          ? _value._languageEditions\n          : languageEditions // ignore: cast_nullable_to_non_nullable\n              as List<LanguageEdition>?,\n      originalWorkno: freezed == originalWorkno\n          ? _value.originalWorkno\n          : originalWorkno // ignore: cast_nullable_to_non_nullable\n              as String?,\n      otherLanguageEditionsInDb: freezed == otherLanguageEditionsInDb\n          ? _value._otherLanguageEditionsInDb\n          : otherLanguageEditionsInDb // ignore: cast_nullable_to_non_nullable\n              as List<OtherLanguageEditionsInDb>?,\n      translationInfo: freezed == translationInfo\n          ? _value.translationInfo\n          : translationInfo // ignore: cast_nullable_to_non_nullable\n              as TranslationInfo?,\n      workAttributes: freezed == workAttributes\n          ? _value.workAttributes\n          : workAttributes // ignore: cast_nullable_to_non_nullable\n              as String?,\n      ageCategoryString: freezed == ageCategoryString\n          ? _value.ageCategoryString\n          : ageCategoryString // ignore: cast_nullable_to_non_nullable\n              as String?,\n      duration: freezed == duration\n          ? _value.duration\n          : duration // ignore: cast_nullable_to_non_nullable\n              as int?,\n      sourceType: freezed == sourceType\n          ? _value.sourceType\n          : sourceType // ignore: cast_nullable_to_non_nullable\n              as String?,\n      sourceId: freezed == sourceId\n          ? _value.sourceId\n          : sourceId // ignore: cast_nullable_to_non_nullable\n              as String?,\n      sourceUrl: freezed == sourceUrl\n          ? _value.sourceUrl\n          : sourceUrl // ignore: cast_nullable_to_non_nullable\n              as String?,\n      userRating: freezed == userRating\n          ? _value.userRating\n          : userRating // ignore: cast_nullable_to_non_nullable\n              as dynamic,\n      circle: freezed == circle\n          ? _value.circle\n          : circle // ignore: cast_nullable_to_non_nullable\n              as Circle?,\n      samCoverUrl: freezed == samCoverUrl\n          ? _value.samCoverUrl\n          : samCoverUrl // ignore: cast_nullable_to_non_nullable\n              as String?,\n      thumbnailCoverUrl: freezed == thumbnailCoverUrl\n          ? _value.thumbnailCoverUrl\n          : thumbnailCoverUrl // ignore: cast_nullable_to_non_nullable\n              as String?,\n      mainCoverUrl: freezed == mainCoverUrl\n          ? _value.mainCoverUrl\n          : mainCoverUrl // ignore: cast_nullable_to_non_nullable\n              as String?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$WorkImpl implements _Work {\n  _$WorkImpl(\n      {this.id,\n      this.title,\n      @JsonKey(name: 'circle_id') this.circleId,\n      this.name,\n      this.nsfw,\n      this.release,\n      @JsonKey(name: 'dl_count') this.dlCount,\n      this.price,\n      @JsonKey(name: 'review_count') this.reviewCount,\n      @JsonKey(name: 'rate_count') this.rateCount,\n      @JsonKey(name: 'rate_average_2dp') this.rateAverage2dp,\n      @JsonKey(name: 'rate_count_detail') final List<dynamic>? rateCountDetail,\n      this.rank,\n      @JsonKey(name: 'has_subtitle') this.hasSubtitle,\n      @JsonKey(name: 'create_date') this.createDate,\n      final List<dynamic>? vas,\n      final List<Tag>? tags,\n      @JsonKey(name: 'language_editions')\n      final List<LanguageEdition>? languageEditions,\n      @JsonKey(name: 'original_workno') this.originalWorkno,\n      @JsonKey(name: 'other_language_editions_in_db')\n      final List<OtherLanguageEditionsInDb>? otherLanguageEditionsInDb,\n      @JsonKey(name: 'translation_info') this.translationInfo,\n      @JsonKey(name: 'work_attributes') this.workAttributes,\n      @JsonKey(name: 'age_category_string') this.ageCategoryString,\n      this.duration,\n      @JsonKey(name: 'source_type') this.sourceType,\n      @JsonKey(name: 'source_id') this.sourceId,\n      @JsonKey(name: 'source_url') this.sourceUrl,\n      this.userRating,\n      this.circle,\n      this.samCoverUrl,\n      this.thumbnailCoverUrl,\n      this.mainCoverUrl})\n      : _rateCountDetail = rateCountDetail,\n        _vas = vas,\n        _tags = tags,\n        _languageEditions = languageEditions,\n        _otherLanguageEditionsInDb = otherLanguageEditionsInDb;\n\n  factory _$WorkImpl.fromJson(Map<String, dynamic> json) =>\n      _$$WorkImplFromJson(json);\n\n  @override\n  final int? id;\n  @override\n  final String? title;\n  @override\n  @JsonKey(name: 'circle_id')\n  final int? circleId;\n  @override\n  final String? name;\n  @override\n  final bool? nsfw;\n  @override\n  final String? release;\n  @override\n  @JsonKey(name: 'dl_count')\n  final int? dlCount;\n  @override\n  final int? price;\n  @override\n  @JsonKey(name: 'review_count')\n  final int? reviewCount;\n  @override\n  @JsonKey(name: 'rate_count')\n  final int? rateCount;\n  @override\n  @JsonKey(name: 'rate_average_2dp')\n  final int? rateAverage2dp;\n  final List<dynamic>? _rateCountDetail;\n  @override\n  @JsonKey(name: 'rate_count_detail')\n  List<dynamic>? get rateCountDetail {\n    final value = _rateCountDetail;\n    if (value == null) return null;\n    if (_rateCountDetail is EqualUnmodifiableListView) return _rateCountDetail;\n    // ignore: implicit_dynamic_type\n    return EqualUnmodifiableListView(value);\n  }\n\n  @override\n  final dynamic rank;\n  @override\n  @JsonKey(name: 'has_subtitle')\n  final bool? hasSubtitle;\n  @override\n  @JsonKey(name: 'create_date')\n  final String? createDate;\n  final List<dynamic>? _vas;\n  @override\n  List<dynamic>? get vas {\n    final value = _vas;\n    if (value == null) return null;\n    if (_vas is EqualUnmodifiableListView) return _vas;\n    // ignore: implicit_dynamic_type\n    return EqualUnmodifiableListView(value);\n  }\n\n  final List<Tag>? _tags;\n  @override\n  List<Tag>? get tags {\n    final value = _tags;\n    if (value == null) return null;\n    if (_tags is EqualUnmodifiableListView) return _tags;\n    // ignore: implicit_dynamic_type\n    return EqualUnmodifiableListView(value);\n  }\n\n  final List<LanguageEdition>? _languageEditions;\n  @override\n  @JsonKey(name: 'language_editions')\n  List<LanguageEdition>? get languageEditions {\n    final value = _languageEditions;\n    if (value == null) return null;\n    if (_languageEditions is EqualUnmodifiableListView)\n      return _languageEditions;\n    // ignore: implicit_dynamic_type\n    return EqualUnmodifiableListView(value);\n  }\n\n  @override\n  @JsonKey(name: 'original_workno')\n  final String? originalWorkno;\n  final List<OtherLanguageEditionsInDb>? _otherLanguageEditionsInDb;\n  @override\n  @JsonKey(name: 'other_language_editions_in_db')\n  List<OtherLanguageEditionsInDb>? get otherLanguageEditionsInDb {\n    final value = _otherLanguageEditionsInDb;\n    if (value == null) return null;\n    if (_otherLanguageEditionsInDb is EqualUnmodifiableListView)\n      return _otherLanguageEditionsInDb;\n    // ignore: implicit_dynamic_type\n    return EqualUnmodifiableListView(value);\n  }\n\n  @override\n  @JsonKey(name: 'translation_info')\n  final TranslationInfo? translationInfo;\n  @override\n  @JsonKey(name: 'work_attributes')\n  final String? workAttributes;\n  @override\n  @JsonKey(name: 'age_category_string')\n  final String? ageCategoryString;\n  @override\n  final int? duration;\n  @override\n  @JsonKey(name: 'source_type')\n  final String? sourceType;\n  @override\n  @JsonKey(name: 'source_id')\n  final String? sourceId;\n  @override\n  @JsonKey(name: 'source_url')\n  final String? sourceUrl;\n  @override\n  final dynamic userRating;\n  @override\n  final Circle? circle;\n  @override\n  final String? samCoverUrl;\n  @override\n  final String? thumbnailCoverUrl;\n  @override\n  final String? mainCoverUrl;\n\n  @override\n  String toString() {\n    return 'Work(id: $id, title: $title, circleId: $circleId, name: $name, nsfw: $nsfw, release: $release, dlCount: $dlCount, price: $price, reviewCount: $reviewCount, rateCount: $rateCount, rateAverage2dp: $rateAverage2dp, rateCountDetail: $rateCountDetail, rank: $rank, hasSubtitle: $hasSubtitle, createDate: $createDate, vas: $vas, tags: $tags, languageEditions: $languageEditions, originalWorkno: $originalWorkno, otherLanguageEditionsInDb: $otherLanguageEditionsInDb, translationInfo: $translationInfo, workAttributes: $workAttributes, ageCategoryString: $ageCategoryString, duration: $duration, sourceType: $sourceType, sourceId: $sourceId, sourceUrl: $sourceUrl, userRating: $userRating, circle: $circle, samCoverUrl: $samCoverUrl, thumbnailCoverUrl: $thumbnailCoverUrl, mainCoverUrl: $mainCoverUrl)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$WorkImpl &&\n            (identical(other.id, id) || other.id == id) &&\n            (identical(other.title, title) || other.title == title) &&\n            (identical(other.circleId, circleId) ||\n                other.circleId == circleId) &&\n            (identical(other.name, name) || other.name == name) &&\n            (identical(other.nsfw, nsfw) || other.nsfw == nsfw) &&\n            (identical(other.release, release) || other.release == release) &&\n            (identical(other.dlCount, dlCount) || other.dlCount == dlCount) &&\n            (identical(other.price, price) || other.price == price) &&\n            (identical(other.reviewCount, reviewCount) ||\n                other.reviewCount == reviewCount) &&\n            (identical(other.rateCount, rateCount) ||\n                other.rateCount == rateCount) &&\n            (identical(other.rateAverage2dp, rateAverage2dp) ||\n                other.rateAverage2dp == rateAverage2dp) &&\n            const DeepCollectionEquality()\n                .equals(other._rateCountDetail, _rateCountDetail) &&\n            const DeepCollectionEquality().equals(other.rank, rank) &&\n            (identical(other.hasSubtitle, hasSubtitle) ||\n                other.hasSubtitle == hasSubtitle) &&\n            (identical(other.createDate, createDate) ||\n                other.createDate == createDate) &&\n            const DeepCollectionEquality().equals(other._vas, _vas) &&\n            const DeepCollectionEquality().equals(other._tags, _tags) &&\n            const DeepCollectionEquality()\n                .equals(other._languageEditions, _languageEditions) &&\n            (identical(other.originalWorkno, originalWorkno) ||\n                other.originalWorkno == originalWorkno) &&\n            const DeepCollectionEquality().equals(\n                other._otherLanguageEditionsInDb, _otherLanguageEditionsInDb) &&\n            (identical(other.translationInfo, translationInfo) ||\n                other.translationInfo == translationInfo) &&\n            (identical(other.workAttributes, workAttributes) ||\n                other.workAttributes == workAttributes) &&\n            (identical(other.ageCategoryString, ageCategoryString) ||\n                other.ageCategoryString == ageCategoryString) &&\n            (identical(other.duration, duration) ||\n                other.duration == duration) &&\n            (identical(other.sourceType, sourceType) ||\n                other.sourceType == sourceType) &&\n            (identical(other.sourceId, sourceId) ||\n                other.sourceId == sourceId) &&\n            (identical(other.sourceUrl, sourceUrl) ||\n                other.sourceUrl == sourceUrl) &&\n            const DeepCollectionEquality()\n                .equals(other.userRating, userRating) &&\n            (identical(other.circle, circle) || other.circle == circle) &&\n            (identical(other.samCoverUrl, samCoverUrl) ||\n                other.samCoverUrl == samCoverUrl) &&\n            (identical(other.thumbnailCoverUrl, thumbnailCoverUrl) ||\n                other.thumbnailCoverUrl == thumbnailCoverUrl) &&\n            (identical(other.mainCoverUrl, mainCoverUrl) ||\n                other.mainCoverUrl == mainCoverUrl));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hashAll([\n        runtimeType,\n        id,\n        title,\n        circleId,\n        name,\n        nsfw,\n        release,\n        dlCount,\n        price,\n        reviewCount,\n        rateCount,\n        rateAverage2dp,\n        const DeepCollectionEquality().hash(_rateCountDetail),\n        const DeepCollectionEquality().hash(rank),\n        hasSubtitle,\n        createDate,\n        const DeepCollectionEquality().hash(_vas),\n        const DeepCollectionEquality().hash(_tags),\n        const DeepCollectionEquality().hash(_languageEditions),\n        originalWorkno,\n        const DeepCollectionEquality().hash(_otherLanguageEditionsInDb),\n        translationInfo,\n        workAttributes,\n        ageCategoryString,\n        duration,\n        sourceType,\n        sourceId,\n        sourceUrl,\n        const DeepCollectionEquality().hash(userRating),\n        circle,\n        samCoverUrl,\n        thumbnailCoverUrl,\n        mainCoverUrl\n      ]);\n\n  /// Create a copy of Work\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$WorkImplCopyWith<_$WorkImpl> get copyWith =>\n      __$$WorkImplCopyWithImpl<_$WorkImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$WorkImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _Work implements Work {\n  factory _Work(\n      {final int? id,\n      final String? title,\n      @JsonKey(name: 'circle_id') final int? circleId,\n      final String? name,\n      final bool? nsfw,\n      final String? release,\n      @JsonKey(name: 'dl_count') final int? dlCount,\n      final int? price,\n      @JsonKey(name: 'review_count') final int? reviewCount,\n      @JsonKey(name: 'rate_count') final int? rateCount,\n      @JsonKey(name: 'rate_average_2dp') final int? rateAverage2dp,\n      @JsonKey(name: 'rate_count_detail') final List<dynamic>? rateCountDetail,\n      final dynamic rank,\n      @JsonKey(name: 'has_subtitle') final bool? hasSubtitle,\n      @JsonKey(name: 'create_date') final String? createDate,\n      final List<dynamic>? vas,\n      final List<Tag>? tags,\n      @JsonKey(name: 'language_editions')\n      final List<LanguageEdition>? languageEditions,\n      @JsonKey(name: 'original_workno') final String? originalWorkno,\n      @JsonKey(name: 'other_language_editions_in_db')\n      final List<OtherLanguageEditionsInDb>? otherLanguageEditionsInDb,\n      @JsonKey(name: 'translation_info') final TranslationInfo? translationInfo,\n      @JsonKey(name: 'work_attributes') final String? workAttributes,\n      @JsonKey(name: 'age_category_string') final String? ageCategoryString,\n      final int? duration,\n      @JsonKey(name: 'source_type') final String? sourceType,\n      @JsonKey(name: 'source_id') final String? sourceId,\n      @JsonKey(name: 'source_url') final String? sourceUrl,\n      final dynamic userRating,\n      final Circle? circle,\n      final String? samCoverUrl,\n      final String? thumbnailCoverUrl,\n      final String? mainCoverUrl}) = _$WorkImpl;\n\n  factory _Work.fromJson(Map<String, dynamic> json) = _$WorkImpl.fromJson;\n\n  @override\n  int? get id;\n  @override\n  String? get title;\n  @override\n  @JsonKey(name: 'circle_id')\n  int? get circleId;\n  @override\n  String? get name;\n  @override\n  bool? get nsfw;\n  @override\n  String? get release;\n  @override\n  @JsonKey(name: 'dl_count')\n  int? get dlCount;\n  @override\n  int? get price;\n  @override\n  @JsonKey(name: 'review_count')\n  int? get reviewCount;\n  @override\n  @JsonKey(name: 'rate_count')\n  int? get rateCount;\n  @override\n  @JsonKey(name: 'rate_average_2dp')\n  int? get rateAverage2dp;\n  @override\n  @JsonKey(name: 'rate_count_detail')\n  List<dynamic>? get rateCountDetail;\n  @override\n  dynamic get rank;\n  @override\n  @JsonKey(name: 'has_subtitle')\n  bool? get hasSubtitle;\n  @override\n  @JsonKey(name: 'create_date')\n  String? get createDate;\n  @override\n  List<dynamic>? get vas;\n  @override\n  List<Tag>? get tags;\n  @override\n  @JsonKey(name: 'language_editions')\n  List<LanguageEdition>? get languageEditions;\n  @override\n  @JsonKey(name: 'original_workno')\n  String? get originalWorkno;\n  @override\n  @JsonKey(name: 'other_language_editions_in_db')\n  List<OtherLanguageEditionsInDb>? get otherLanguageEditionsInDb;\n  @override\n  @JsonKey(name: 'translation_info')\n  TranslationInfo? get translationInfo;\n  @override\n  @JsonKey(name: 'work_attributes')\n  String? get workAttributes;\n  @override\n  @JsonKey(name: 'age_category_string')\n  String? get ageCategoryString;\n  @override\n  int? get duration;\n  @override\n  @JsonKey(name: 'source_type')\n  String? get sourceType;\n  @override\n  @JsonKey(name: 'source_id')\n  String? get sourceId;\n  @override\n  @JsonKey(name: 'source_url')\n  String? get sourceUrl;\n  @override\n  dynamic get userRating;\n  @override\n  Circle? get circle;\n  @override\n  String? get samCoverUrl;\n  @override\n  String? get thumbnailCoverUrl;\n  @override\n  String? get mainCoverUrl;\n\n  /// Create a copy of Work\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$WorkImplCopyWith<_$WorkImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/works/work.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'work.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$WorkImpl _$$WorkImplFromJson(Map<String, dynamic> json) => _$WorkImpl(\n      id: (json['id'] as num?)?.toInt(),\n      title: json['title'] as String?,\n      circleId: (json['circle_id'] as num?)?.toInt(),\n      name: json['name'] as String?,\n      nsfw: json['nsfw'] as bool?,\n      release: json['release'] as String?,\n      dlCount: (json['dl_count'] as num?)?.toInt(),\n      price: (json['price'] as num?)?.toInt(),\n      reviewCount: (json['review_count'] as num?)?.toInt(),\n      rateCount: (json['rate_count'] as num?)?.toInt(),\n      rateAverage2dp: (json['rate_average_2dp'] as num?)?.toInt(),\n      rateCountDetail: json['rate_count_detail'] as List<dynamic>?,\n      rank: json['rank'],\n      hasSubtitle: json['has_subtitle'] as bool?,\n      createDate: json['create_date'] as String?,\n      vas: json['vas'] as List<dynamic>?,\n      tags: (json['tags'] as List<dynamic>?)\n          ?.map((e) => Tag.fromJson(e as Map<String, dynamic>))\n          .toList(),\n      languageEditions: (json['language_editions'] as List<dynamic>?)\n          ?.map((e) => LanguageEdition.fromJson(e as Map<String, dynamic>))\n          .toList(),\n      originalWorkno: json['original_workno'] as String?,\n      otherLanguageEditionsInDb:\n          (json['other_language_editions_in_db'] as List<dynamic>?)\n              ?.map((e) =>\n                  OtherLanguageEditionsInDb.fromJson(e as Map<String, dynamic>))\n              .toList(),\n      translationInfo: json['translation_info'] == null\n          ? null\n          : TranslationInfo.fromJson(\n              json['translation_info'] as Map<String, dynamic>),\n      workAttributes: json['work_attributes'] as String?,\n      ageCategoryString: json['age_category_string'] as String?,\n      duration: (json['duration'] as num?)?.toInt(),\n      sourceType: json['source_type'] as String?,\n      sourceId: json['source_id'] as String?,\n      sourceUrl: json['source_url'] as String?,\n      userRating: json['userRating'],\n      circle: json['circle'] == null\n          ? null\n          : Circle.fromJson(json['circle'] as Map<String, dynamic>),\n      samCoverUrl: json['samCoverUrl'] as String?,\n      thumbnailCoverUrl: json['thumbnailCoverUrl'] as String?,\n      mainCoverUrl: json['mainCoverUrl'] as String?,\n    );\n\nMap<String, dynamic> _$$WorkImplToJson(_$WorkImpl instance) =>\n    <String, dynamic>{\n      'id': instance.id,\n      'title': instance.title,\n      'circle_id': instance.circleId,\n      'name': instance.name,\n      'nsfw': instance.nsfw,\n      'release': instance.release,\n      'dl_count': instance.dlCount,\n      'price': instance.price,\n      'review_count': instance.reviewCount,\n      'rate_count': instance.rateCount,\n      'rate_average_2dp': instance.rateAverage2dp,\n      'rate_count_detail': instance.rateCountDetail,\n      'rank': instance.rank,\n      'has_subtitle': instance.hasSubtitle,\n      'create_date': instance.createDate,\n      'vas': instance.vas,\n      'tags': instance.tags,\n      'language_editions': instance.languageEditions,\n      'original_workno': instance.originalWorkno,\n      'other_language_editions_in_db': instance.otherLanguageEditionsInDb,\n      'translation_info': instance.translationInfo,\n      'work_attributes': instance.workAttributes,\n      'age_category_string': instance.ageCategoryString,\n      'duration': instance.duration,\n      'source_type': instance.sourceType,\n      'source_id': instance.sourceId,\n      'source_url': instance.sourceUrl,\n      'userRating': instance.userRating,\n      'circle': instance.circle,\n      'samCoverUrl': instance.samCoverUrl,\n      'thumbnailCoverUrl': instance.thumbnailCoverUrl,\n      'mainCoverUrl': instance.mainCoverUrl,\n    };\n"
  },
  {
    "path": "lib/data/models/works/works.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'pagination.dart';\nimport 'work.dart';\n\npart 'works.freezed.dart';\npart 'works.g.dart';\n\n@freezed\nclass Works with _$Works {\n  factory Works({\n    List<Work>? works,\n    Pagination? pagination,\n  }) = _Works;\n\n  factory Works.fromJson(Map<String, dynamic> json) => _$WorksFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/works/works.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'works.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nWorks _$WorksFromJson(Map<String, dynamic> json) {\n  return _Works.fromJson(json);\n}\n\n/// @nodoc\nmixin _$Works {\n  List<Work>? get works => throw _privateConstructorUsedError;\n  Pagination? get pagination => throw _privateConstructorUsedError;\n\n  /// Serializes this Works to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of Works\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $WorksCopyWith<Works> get copyWith => throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $WorksCopyWith<$Res> {\n  factory $WorksCopyWith(Works value, $Res Function(Works) then) =\n      _$WorksCopyWithImpl<$Res, Works>;\n  @useResult\n  $Res call({List<Work>? works, Pagination? pagination});\n\n  $PaginationCopyWith<$Res>? get pagination;\n}\n\n/// @nodoc\nclass _$WorksCopyWithImpl<$Res, $Val extends Works>\n    implements $WorksCopyWith<$Res> {\n  _$WorksCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of Works\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? works = freezed,\n    Object? pagination = freezed,\n  }) {\n    return _then(_value.copyWith(\n      works: freezed == works\n          ? _value.works\n          : works // ignore: cast_nullable_to_non_nullable\n              as List<Work>?,\n      pagination: freezed == pagination\n          ? _value.pagination\n          : pagination // ignore: cast_nullable_to_non_nullable\n              as Pagination?,\n    ) as $Val);\n  }\n\n  /// Create a copy of Works\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @pragma('vm:prefer-inline')\n  $PaginationCopyWith<$Res>? get pagination {\n    if (_value.pagination == null) {\n      return null;\n    }\n\n    return $PaginationCopyWith<$Res>(_value.pagination!, (value) {\n      return _then(_value.copyWith(pagination: value) as $Val);\n    });\n  }\n}\n\n/// @nodoc\nabstract class _$$WorksImplCopyWith<$Res> implements $WorksCopyWith<$Res> {\n  factory _$$WorksImplCopyWith(\n          _$WorksImpl value, $Res Function(_$WorksImpl) then) =\n      __$$WorksImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call({List<Work>? works, Pagination? pagination});\n\n  @override\n  $PaginationCopyWith<$Res>? get pagination;\n}\n\n/// @nodoc\nclass __$$WorksImplCopyWithImpl<$Res>\n    extends _$WorksCopyWithImpl<$Res, _$WorksImpl>\n    implements _$$WorksImplCopyWith<$Res> {\n  __$$WorksImplCopyWithImpl(\n      _$WorksImpl _value, $Res Function(_$WorksImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of Works\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? works = freezed,\n    Object? pagination = freezed,\n  }) {\n    return _then(_$WorksImpl(\n      works: freezed == works\n          ? _value._works\n          : works // ignore: cast_nullable_to_non_nullable\n              as List<Work>?,\n      pagination: freezed == pagination\n          ? _value.pagination\n          : pagination // ignore: cast_nullable_to_non_nullable\n              as Pagination?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$WorksImpl implements _Works {\n  _$WorksImpl({final List<Work>? works, this.pagination}) : _works = works;\n\n  factory _$WorksImpl.fromJson(Map<String, dynamic> json) =>\n      _$$WorksImplFromJson(json);\n\n  final List<Work>? _works;\n  @override\n  List<Work>? get works {\n    final value = _works;\n    if (value == null) return null;\n    if (_works is EqualUnmodifiableListView) return _works;\n    // ignore: implicit_dynamic_type\n    return EqualUnmodifiableListView(value);\n  }\n\n  @override\n  final Pagination? pagination;\n\n  @override\n  String toString() {\n    return 'Works(works: $works, pagination: $pagination)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$WorksImpl &&\n            const DeepCollectionEquality().equals(other._works, _works) &&\n            (identical(other.pagination, pagination) ||\n                other.pagination == pagination));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(\n      runtimeType, const DeepCollectionEquality().hash(_works), pagination);\n\n  /// Create a copy of Works\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$WorksImplCopyWith<_$WorksImpl> get copyWith =>\n      __$$WorksImplCopyWithImpl<_$WorksImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$WorksImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _Works implements Works {\n  factory _Works({final List<Work>? works, final Pagination? pagination}) =\n      _$WorksImpl;\n\n  factory _Works.fromJson(Map<String, dynamic> json) = _$WorksImpl.fromJson;\n\n  @override\n  List<Work>? get works;\n  @override\n  Pagination? get pagination;\n\n  /// Create a copy of Works\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$WorksImplCopyWith<_$WorksImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/works/works.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'works.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$WorksImpl _$$WorksImplFromJson(Map<String, dynamic> json) => _$WorksImpl(\n      works: (json['works'] as List<dynamic>?)\n          ?.map((e) => Work.fromJson(e as Map<String, dynamic>))\n          .toList(),\n      pagination: json['pagination'] == null\n          ? null\n          : Pagination.fromJson(json['pagination'] as Map<String, dynamic>),\n    );\n\nMap<String, dynamic> _$$WorksImplToJson(_$WorksImpl instance) =>\n    <String, dynamic>{\n      'works': instance.works,\n      'pagination': instance.pagination,\n    };\n"
  },
  {
    "path": "lib/data/models/works/zh_cn.dart",
    "content": "import 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'zh_cn.freezed.dart';\npart 'zh_cn.g.dart';\n\n@freezed\nclass ZhCn with _$ZhCn {\n  factory ZhCn({\n    String? name,\n    List<dynamic>? history,\n  }) = _ZhCn;\n\n  factory ZhCn.fromJson(Map<String, dynamic> json) => _$ZhCnFromJson(json);\n}\n"
  },
  {
    "path": "lib/data/models/works/zh_cn.freezed.dart",
    "content": "// coverage:ignore-file\n// GENERATED CODE - DO NOT MODIFY BY HAND\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of 'zh_cn.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\nT _$identity<T>(T value) => value;\n\nfinal _privateConstructorUsedError = UnsupportedError(\n    'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');\n\nZhCn _$ZhCnFromJson(Map<String, dynamic> json) {\n  return _ZhCn.fromJson(json);\n}\n\n/// @nodoc\nmixin _$ZhCn {\n  String? get name => throw _privateConstructorUsedError;\n  List<dynamic>? get history => throw _privateConstructorUsedError;\n\n  /// Serializes this ZhCn to a JSON map.\n  Map<String, dynamic> toJson() => throw _privateConstructorUsedError;\n\n  /// Create a copy of ZhCn\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  $ZhCnCopyWith<ZhCn> get copyWith => throw _privateConstructorUsedError;\n}\n\n/// @nodoc\nabstract class $ZhCnCopyWith<$Res> {\n  factory $ZhCnCopyWith(ZhCn value, $Res Function(ZhCn) then) =\n      _$ZhCnCopyWithImpl<$Res, ZhCn>;\n  @useResult\n  $Res call({String? name, List<dynamic>? history});\n}\n\n/// @nodoc\nclass _$ZhCnCopyWithImpl<$Res, $Val extends ZhCn>\n    implements $ZhCnCopyWith<$Res> {\n  _$ZhCnCopyWithImpl(this._value, this._then);\n\n  // ignore: unused_field\n  final $Val _value;\n  // ignore: unused_field\n  final $Res Function($Val) _then;\n\n  /// Create a copy of ZhCn\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? name = freezed,\n    Object? history = freezed,\n  }) {\n    return _then(_value.copyWith(\n      name: freezed == name\n          ? _value.name\n          : name // ignore: cast_nullable_to_non_nullable\n              as String?,\n      history: freezed == history\n          ? _value.history\n          : history // ignore: cast_nullable_to_non_nullable\n              as List<dynamic>?,\n    ) as $Val);\n  }\n}\n\n/// @nodoc\nabstract class _$$ZhCnImplCopyWith<$Res> implements $ZhCnCopyWith<$Res> {\n  factory _$$ZhCnImplCopyWith(\n          _$ZhCnImpl value, $Res Function(_$ZhCnImpl) then) =\n      __$$ZhCnImplCopyWithImpl<$Res>;\n  @override\n  @useResult\n  $Res call({String? name, List<dynamic>? history});\n}\n\n/// @nodoc\nclass __$$ZhCnImplCopyWithImpl<$Res>\n    extends _$ZhCnCopyWithImpl<$Res, _$ZhCnImpl>\n    implements _$$ZhCnImplCopyWith<$Res> {\n  __$$ZhCnImplCopyWithImpl(_$ZhCnImpl _value, $Res Function(_$ZhCnImpl) _then)\n      : super(_value, _then);\n\n  /// Create a copy of ZhCn\n  /// with the given fields replaced by the non-null parameter values.\n  @pragma('vm:prefer-inline')\n  @override\n  $Res call({\n    Object? name = freezed,\n    Object? history = freezed,\n  }) {\n    return _then(_$ZhCnImpl(\n      name: freezed == name\n          ? _value.name\n          : name // ignore: cast_nullable_to_non_nullable\n              as String?,\n      history: freezed == history\n          ? _value._history\n          : history // ignore: cast_nullable_to_non_nullable\n              as List<dynamic>?,\n    ));\n  }\n}\n\n/// @nodoc\n@JsonSerializable()\nclass _$ZhCnImpl implements _ZhCn {\n  _$ZhCnImpl({this.name, final List<dynamic>? history}) : _history = history;\n\n  factory _$ZhCnImpl.fromJson(Map<String, dynamic> json) =>\n      _$$ZhCnImplFromJson(json);\n\n  @override\n  final String? name;\n  final List<dynamic>? _history;\n  @override\n  List<dynamic>? get history {\n    final value = _history;\n    if (value == null) return null;\n    if (_history is EqualUnmodifiableListView) return _history;\n    // ignore: implicit_dynamic_type\n    return EqualUnmodifiableListView(value);\n  }\n\n  @override\n  String toString() {\n    return 'ZhCn(name: $name, history: $history)';\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return identical(this, other) ||\n        (other.runtimeType == runtimeType &&\n            other is _$ZhCnImpl &&\n            (identical(other.name, name) || other.name == name) &&\n            const DeepCollectionEquality().equals(other._history, _history));\n  }\n\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  int get hashCode => Object.hash(\n      runtimeType, name, const DeepCollectionEquality().hash(_history));\n\n  /// Create a copy of ZhCn\n  /// with the given fields replaced by the non-null parameter values.\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  @override\n  @pragma('vm:prefer-inline')\n  _$$ZhCnImplCopyWith<_$ZhCnImpl> get copyWith =>\n      __$$ZhCnImplCopyWithImpl<_$ZhCnImpl>(this, _$identity);\n\n  @override\n  Map<String, dynamic> toJson() {\n    return _$$ZhCnImplToJson(\n      this,\n    );\n  }\n}\n\nabstract class _ZhCn implements ZhCn {\n  factory _ZhCn({final String? name, final List<dynamic>? history}) =\n      _$ZhCnImpl;\n\n  factory _ZhCn.fromJson(Map<String, dynamic> json) = _$ZhCnImpl.fromJson;\n\n  @override\n  String? get name;\n  @override\n  List<dynamic>? get history;\n\n  /// Create a copy of ZhCn\n  /// with the given fields replaced by the non-null parameter values.\n  @override\n  @JsonKey(includeFromJson: false, includeToJson: false)\n  _$$ZhCnImplCopyWith<_$ZhCnImpl> get copyWith =>\n      throw _privateConstructorUsedError;\n}\n"
  },
  {
    "path": "lib/data/models/works/zh_cn.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of 'zh_cn.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_$ZhCnImpl _$$ZhCnImplFromJson(Map<String, dynamic> json) => _$ZhCnImpl(\n      name: json['name'] as String?,\n      history: json['history'] as List<dynamic>?,\n    );\n\nMap<String, dynamic> _$$ZhCnImplToJson(_$ZhCnImpl instance) =>\n    <String, dynamic>{\n      'name': instance.name,\n      'history': instance.history,\n    };\n"
  },
  {
    "path": "lib/data/repositories/audio/README.md",
    "content": "# 音频数据仓库\n\n此目录包含音频数据访问的仓库实现。\n\n## 文件结构\n\n- `audio_repository.dart` - 音频数据仓库实现\n- `audio_repository_impl.dart` - 音频数据仓库具体实现\n- `audio_cache_repository.dart` - 音频缓存仓库\n\n## 职责\n\n- 音频数据的获取和存储\n- 播放历史记录的管理\n- 音频缓存的处理 "
  },
  {
    "path": "lib/data/repositories/auth_repository.dart",
    "content": "import 'dart:convert';\nimport 'package:shared_preferences/shared_preferences.dart';\nimport 'package:asmrapp/data/models/auth/auth_resp/auth_resp.dart';\nimport 'package:asmrapp/utils/logger.dart';\n\nclass AuthRepository {\n  static const _authDataKey = 'auth_data';\n  final SharedPreferences _prefs;\n\n  AuthRepository(this._prefs);\n\n  Future<void> saveAuthData(AuthResp authData) async {\n    try {\n      final jsonStr = json.encode(authData.toJson());\n      await _prefs.setString(_authDataKey, jsonStr);\n      AppLogger.info('保存认证数据成功');\n    } catch (e) {\n      AppLogger.error('保存认证数据失败', e);\n      rethrow;\n    }\n  }\n\n  Future<AuthResp?> getAuthData() async {\n    try {\n      final jsonStr = _prefs.getString(_authDataKey);\n      if (jsonStr == null) return null;\n\n      final authData = AuthResp.fromJson(json.decode(jsonStr));\n      AppLogger.info('读取认证数据成功: ${authData.user?.name}');\n      return authData;\n    } catch (e) {\n      AppLogger.error('读取认证数据失败', e);\n      return null;\n    }\n  }\n\n  Future<void> clearAuthData() async {\n    try {\n      await _prefs.remove(_authDataKey);\n      AppLogger.info('清除认证数据成功');\n    } catch (e) {\n      AppLogger.error('清除认证数据失败', e);\n      rethrow;\n    }\n  }\n} "
  },
  {
    "path": "lib/data/services/api_service.dart",
    "content": "import 'package:asmrapp/core/cache/recommendation_cache_manager.dart';\nimport 'package:asmrapp/data/models/mark_status.dart';\nimport 'package:asmrapp/data/models/playlists_with_exist_statu/playlists_with_exist_statu.dart';\nimport 'package:dio/dio.dart';\nimport 'package:asmrapp/data/models/files/files.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/data/models/works/pagination.dart';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:asmrapp/data/services/interceptors/auth_interceptor.dart';\nimport 'package:asmrapp/data/models/playlists_with_exist_statu/playlist.dart';\nimport 'package:asmrapp/data/models/my_lists/my_playlists/my_playlists.dart';\n\n\nclass WorksResponse {\n  final List<Work> works;\n  final Pagination pagination;\n\n  WorksResponse({required this.works, required this.pagination});\n}\n\nclass ApiService {\n  final Dio _dio;\n  final _recommendationCache = RecommendationCacheManager();\n\n  ApiService()\n      : _dio = Dio(BaseOptions(\n          baseUrl: 'https://api.asmr.one/api',\n        )) {\n    _dio.interceptors.add(AuthInterceptor());\n  }\n\n  /// 获取作品文件列表\n  Future<Files> getWorkFiles(String workId, {CancelToken? cancelToken}) async {\n    try {\n      final response = await _dio.get(\n        '/tracks/$workId', \n        queryParameters: {\n          'v': '1',\n        },\n        cancelToken: cancelToken,  // 添加 cancelToken 支持\n      );\n\n      if (response.statusCode == 200) {\n        final filesData = {\n          'type': 'root',\n          'title': 'Root',\n          'children': response.data,\n        };\n\n        return Files.fromJson(filesData);\n      }\n\n      throw Exception('获取文件列表失败: ${response.statusCode}');\n    } on DioException catch (e) {\n      AppLogger.error('网络请求失败', e, e.stackTrace);\n      throw Exception('网络请求失败: ${e.message}');\n    } catch (e, stackTrace) {\n      AppLogger.error('解析数据失败', e, stackTrace);\n      throw Exception('解析数据失败: $e');\n    }\n  }\n\n  /// 获取作品列表\n  Future<WorksResponse> getWorks({\n    int page = 1,\n    bool hasSubtitle = false,\n    String order = 'create_date',\n    String sort = 'desc',\n    String playlistId = '',\n  }) async {\n    try {\n      final queryParams = {\n        'page': page,\n        'subtitle': hasSubtitle ? 1 : 0,\n        'order': order,\n        'sort': sort,\n      };\n\n      // 如果提供了收藏夹ID，添加到查询参数\n      if (playlistId.isNotEmpty) {\n        queryParams['withPlaylistStatus[]'] = playlistId;\n      }\n\n      final response = await _dio.get(\n        '/works',\n        queryParameters: queryParams,\n      );\n\n      if (response.statusCode == 200) {\n        final List<dynamic> works = response.data['works'] ?? [];\n        final pagination = Pagination.fromJson(response.data['pagination']);\n\n        return WorksResponse(\n          works: works.map((work) => Work.fromJson(work)).toList(),\n          pagination: pagination,\n        );\n      }\n\n      throw Exception('获取作品列表失败: ${response.statusCode}');\n    } on DioException catch (e) {\n      AppLogger.error('网络请求失败', e, e.stackTrace);\n      throw Exception('网络请求失败: ${e.message}');\n    } catch (e, stackTrace) {\n      AppLogger.error('解析数据失败', e, stackTrace);\n      throw Exception('解析数据失败: $e');\n    }\n  }\n\n  /// 搜索作品\n  Future<WorksResponse> searchWorks({\n    required String keyword,\n    int page = 1,\n    String order = 'create_date',\n    String sort = 'desc',\n    bool hasSubtitle = false,\n  }) async {\n    try {\n      final response = await _dio.get(\n        '/search/${Uri.encodeComponent(keyword)}',\n        queryParameters: {\n          'page': page,\n          'order': order,\n          'sort': sort,\n          'subtitle': hasSubtitle ? 1 : 0,\n          'includeTranslationWorks': true,\n        },\n      );\n\n      if (response.statusCode == 200) {\n        AppLogger.debug('搜索返回数据: ${response.data}');\n\n        final works = (response.data['works'] as List)\n            .map((work) => Work.fromJson(work))\n            .toList();\n\n        final pagination = Pagination.fromJson(response.data['pagination']);\n\n        return WorksResponse(\n          works: works,\n          pagination: pagination,\n        );\n      }\n\n      throw Exception('搜索失败: ${response.statusCode}');\n    } catch (e) {\n      throw Exception('搜索请求失败: $e');\n    }\n  }\n\n  /// 获取收藏列表\n  Future<WorksResponse> getFavorites({int page = 1}) async {\n    try {\n      final response = await _dio.get('/review', queryParameters: {\n        'page': page,\n        'order': 'updated_at',\n        'sort': 'desc',\n      });\n\n      if (response.statusCode == 200) {\n        final List<dynamic> works = response.data['works'] ?? [];\n        final pagination = Pagination.fromJson(response.data['pagination']);\n\n        return WorksResponse(\n          works: works.map((work) => Work.fromJson(work)).toList(),\n          pagination: pagination,\n        );\n      }\n\n      throw Exception('获取收藏列表失败: ${response.statusCode}');\n    } on DioException catch (e) {\n      AppLogger.error('网络请求失败', e, e.stackTrace);\n      throw Exception('网络请求失败: ${e.message}');\n    } catch (e, stackTrace) {\n      AppLogger.error('解析数据失败', e, stackTrace);\n      throw Exception('解析数据失败: $e');\n    }\n  }\n\n  /// 获取推荐作品\n  Future<WorksResponse> getRecommendations({\n    required String uuid,\n    int page = 1,\n    bool hasSubtitle = false,\n  }) async {\n    try {\n      final response = await _dio.post(\n        '/recommender/recommend-for-user',\n        data: {\n          'keyword': ' ',\n          'userId': uuid,\n          'page': page,\n          'subtitle': hasSubtitle ? 1 : 0,\n          'localSubtitledWorks': [],\n          'withPlaylistStatus': [],\n        },\n      );\n\n      if (response.statusCode == 200) {\n        final List<dynamic> works = response.data['works'] ?? [];\n        final pagination = Pagination.fromJson(response.data['pagination']);\n\n        return WorksResponse(\n          works: works.map((work) => Work.fromJson(work)).toList(),\n          pagination: pagination,\n        );\n      }\n\n      throw Exception('获取推荐列表失败: ${response.statusCode}');\n    } on DioException catch (e) {\n      AppLogger.error('网络请求失败', e, e.stackTrace);\n      throw Exception('网络请求失败: ${e.message}');\n    } catch (e, stackTrace) {\n      AppLogger.error('解析数据失败', e, stackTrace);\n      throw Exception('解析数据失败: $e');\n    }\n  }\n\n  /// 获取热门作品\n  Future<WorksResponse> getPopular({\n    int page = 1,\n    bool hasSubtitle = false,\n  }) async {\n    try {\n      final response = await _dio.post(\n        '/recommender/popular',\n        data: {\n          'keyword': ' ',\n          'page': page,\n          'subtitle': hasSubtitle ? 1 : 0,\n          'localSubtitledWorks': [],\n          'withPlaylistStatus': [],\n        },\n      );\n\n      if (response.statusCode == 200) {\n        final List<dynamic> works = response.data['works'] ?? [];\n        final pagination = Pagination.fromJson(response.data['pagination']);\n\n        return WorksResponse(\n          works: works.map((work) => Work.fromJson(work)).toList(),\n          pagination: pagination,\n        );\n      }\n\n      throw Exception('获取热门列表失败: ${response.statusCode}');\n    } on DioException catch (e) {\n      AppLogger.error('网络请求失败', e, e.stackTrace);\n      throw Exception('网络请求失败: ${e.message}');\n    } catch (e, stackTrace) {\n      AppLogger.error('解析数据失败', e, stackTrace);\n      throw Exception('解析数据失败: $e');\n    }\n  }\n\n  /// 获取相关推荐作品\n  Future<WorksResponse> getItemNeighbors({\n    required String itemId,\n    int page = 1,\n    bool hasSubtitle = false,\n  }) async {\n    try {\n      // 先尝试从缓存获取\n      final cachedData = _recommendationCache.get(itemId, page, hasSubtitle ? 1 : 0);\n      if (cachedData != null) {\n        return cachedData;\n      }\n\n      // 缓存未命中，从网络获取\n      final response = await _dio.post(\n        '/recommender/item-neighbors',\n        data: {\n          'keyword': '',\n          'itemId': itemId,\n          'page': page,\n          'subtitle': hasSubtitle ? 1 : 0,\n          'localSubtitledWorks': [],\n          'withPlaylistStatus': [],\n        },\n      );\n\n      if (response.statusCode == 200) {\n        final List<dynamic> works = response.data['works'] ?? [];\n        final pagination = Pagination.fromJson(response.data['pagination']);\n\n        final worksResponse = WorksResponse(\n          works: works.map((work) => Work.fromJson(work)).toList(),\n          pagination: pagination,\n        );\n\n        // 存入缓存\n        _recommendationCache.set(itemId, page, hasSubtitle ? 1 : 0, worksResponse);\n\n        return worksResponse;\n      }\n\n      throw Exception('获取相关推荐失败: ${response.statusCode}');\n    } on DioException catch (e) {\n      AppLogger.error('网络请求失败', e, e.stackTrace);\n      throw Exception('网络请求失败: ${e.message}');\n    } catch (e, stackTrace) {\n      AppLogger.error('解析数据失败', e, stackTrace);\n      throw Exception('解析数据失败: $e');\n    }\n  }\n\n  /// 获取作品在收藏夹中的状态\n  Future<PlaylistsWithExistStatu> getWorkExistStatusInPlaylists({\n    required String workId,\n    int page = 1,\n  }) async {\n    try {\n      final response = await _dio.get(\n        '/playlist/get-work-exist-status-in-my-playlists',\n        queryParameters: {\n          'workID': workId,\n          'page': page,\n          'version': 2,\n        },\n      );\n\n      if (response.statusCode == 200) {\n        return PlaylistsWithExistStatu.fromJson(response.data);\n      }\n\n      throw Exception('获取收藏夹列表失败: ${response.statusCode}');\n    } on DioException catch (e) {\n      AppLogger.error('网络请求失败', e, e.stackTrace);\n      throw Exception('网络请求失败: ${e.message}');\n    } catch (e, stackTrace) {\n      AppLogger.error('解析数据失败', e, stackTrace);\n      throw Exception('解析数据失败: $e');\n    }\n  }\n\n  /// 添加作品到收藏夹\n  Future<void> addWorkToPlaylist({\n    required String playlistId,\n    required String workId,\n  }) async {\n    try {\n      await _dio.post(\n        '/playlist/add-works-to-playlist',\n        data: {\n          'id': playlistId,\n          'works': [int.parse(workId)],\n        },\n      );\n    } on DioException catch (e) {\n      AppLogger.error('网络请求失败', e, e.stackTrace);\n      throw Exception('网络请求失败: ${e.message}');\n    } catch (e, stackTrace) {\n      AppLogger.error('添加到收藏夹失败', e, stackTrace);\n      throw Exception('添加到收藏夹失败: $e');\n    }\n  }\n\n  /// 从收藏夹移除作品\n  Future<void> removeWorkFromPlaylist({\n    required String playlistId,\n    required String workId,\n  }) async {\n    try {\n      await _dio.post(\n        '/playlist/remove-works-from-playlist',\n        data: {\n          'id': playlistId,\n          'works': [int.parse(workId)],\n        },\n      );\n    } on DioException catch (e) {\n      AppLogger.error('网络请求失败', e, e.stackTrace);\n      throw Exception('网络请求失败: ${e.message}');\n    } catch (e, stackTrace) {\n      AppLogger.error('从收藏夹移除失败', e, stackTrace);\n      throw Exception('从收藏夹移除失败: $e');\n    }\n  }\n\n  /// 更新作品的标记状态\n  Future<void> updateWorkMarkStatus(String workId, String status) async {\n    try {\n      final response = await _dio.put(\n        '/review',\n        data: {\n          'work_id': int.parse(workId),\n          'progress': status,\n        },\n      );\n\n      if (response.statusCode != 200) {\n        throw Exception('标记失败: ${response.statusCode}');\n      }\n    } catch (e) {\n      AppLogger.error('更新标记状态失败', e);\n      rethrow;\n    }\n  }\n\n  /// 将 MarkStatus 枚举转换为 API 参数\n  String convertMarkStatusToApi(MarkStatus status) {\n    switch (status) {\n      case MarkStatus.wantToListen:\n        return 'marked';\n      case MarkStatus.listening:\n        return 'listening';\n      case MarkStatus.listened:\n        return 'listened';\n      case MarkStatus.relistening:\n        return 'replay';\n      case MarkStatus.onHold:\n        return 'postponed';\n    }\n  }\n\n  /// 获取默认标记目标收藏夹\n  Future<Playlist> getDefaultMarkTargetPlaylist() async {\n    try {\n      final response = await _dio.get('/playlist/get-default-mark-target-playlist');\n\n      if (response.statusCode == 200) {\n        final playlist = Playlist.fromJson(response.data);\n        AppLogger.info('获取默认标记目标收藏夹成功: id=${playlist.id}, name=${playlist.name}');\n        return playlist;\n      }\n\n      throw Exception('获取默认标记目标收藏夹失败: ${response.statusCode}');\n    } on DioException catch (e) {\n      AppLogger.error('网络请求失败', e, e.stackTrace);\n      throw Exception('网络请求失败: ${e.message}');\n    } catch (e, stackTrace) {\n      AppLogger.error('解析数据失败', e, stackTrace);\n      throw Exception('解析数据失败: $e');\n    }\n  }\n\n  /// 获取用户的播放列表\n  Future<MyPlaylists> getMyPlaylists({int page = 1}) async {\n    try {\n      final response = await _dio.get(\n        '/playlist/get-playlists',\n        queryParameters: {\n          'page': page,\n        },\n      );\n\n      if (response.statusCode == 200) {\n        final myPlaylists = MyPlaylists.fromJson(response.data);\n        AppLogger.info('获取播放列表成功: ${myPlaylists.playlists?.length ?? 0}个播放列表');\n        return myPlaylists;\n      }\n\n      throw Exception('获取播放列表失败: ${response.statusCode}');\n    } on DioException catch (e) {\n      AppLogger.error('网络请求失败', e, e.stackTrace);\n      throw Exception('网络请求失败: ${e.message}');\n    } catch (e, stackTrace) {\n      AppLogger.error('解析数据失败', e, stackTrace);\n      throw Exception('解析数据失败: $e');\n    }\n  }\n\n  /// 获取播放列表中的作品\n  Future<WorksResponse> getPlaylistWorks({\n    required String playlistId,\n    int page = 1,\n    int pageSize = 12,\n  }) async {\n    try {\n      final response = await _dio.get(\n        '/playlist/get-playlist-works',\n        queryParameters: {\n          'id': playlistId,\n          'page': page,\n          'pageSize': pageSize,\n        },\n      );\n\n      if (response.statusCode == 200) {\n        final List<dynamic> works = response.data['works'] ?? [];\n        final pagination = Pagination.fromJson(response.data['pagination']);\n\n        return WorksResponse(\n          works: works.map((work) => Work.fromJson(work)).toList(),\n          pagination: pagination,\n        );\n      }\n\n      throw Exception('获取播放列表作品失败: ${response.statusCode}');\n    } on DioException catch (e) {\n      AppLogger.error('网络请求失败', e, e.stackTrace);\n      throw Exception('网络请求失败: ${e.message}');\n    } catch (e, stackTrace) {\n      AppLogger.error('解析数据失败', e, stackTrace);\n      throw Exception('解析数据失败: $e');\n    }\n  }\n}\n"
  },
  {
    "path": "lib/data/services/auth_service.dart",
    "content": "import 'package:asmrapp/data/models/auth/auth_resp/auth_resp.dart';\nimport 'package:dio/dio.dart';\nimport '../../utils/logger.dart';\n\nclass AuthService {\n  final Dio _dio;\n\n  AuthService() \n    : _dio = Dio(BaseOptions(\n        baseUrl: 'https://api.asmr.one/api',\n      ));\n\n  Future<AuthResp> login(String name, String password) async {\n    try {\n      AppLogger.info('开始登录请求: name=$name');\n      final response = await _dio.post('/auth/me', \n        data: {\n          'name': name,\n          'password': password,\n        },\n      );\n\n      AppLogger.info('收到登录响应: statusCode=${response.statusCode}');\n      AppLogger.info('响应数据: ${response.data}');\n\n      if (response.statusCode == 200) {\n        final authResp = AuthResp.fromJson(response.data);\n        AppLogger.info('登录成功: username=${authResp.user?.name}, group=${authResp.user?.group}');\n        return authResp;\n      }\n\n      throw Exception('登录失败: ${response.statusCode}');\n    } on DioException catch (e) {\n      AppLogger.error('登录请求失败', e);\n      AppLogger.error('错误详情: ${e.response?.data}');\n      throw Exception('网络请求失败: ${e.message}');\n    } catch (e) {\n      AppLogger.error('登录失败', e);\n      throw Exception('登录失败: $e');\n    }\n  }\n} "
  },
  {
    "path": "lib/data/services/interceptors/auth_interceptor.dart",
    "content": "import 'package:dio/dio.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:asmrapp/data/repositories/auth_repository.dart';\nimport 'package:asmrapp/utils/logger.dart';\n\nclass AuthInterceptor extends Interceptor {\n  @override\n  Future<void> onRequest(\n    RequestOptions options, \n    RequestInterceptorHandler handler,\n  ) async {\n    try {\n      final authRepository = GetIt.I<AuthRepository>();\n      final authData = await authRepository.getAuthData();\n      \n      if (authData?.token != null) {\n        options.headers['Authorization'] = 'Bearer ${authData!.token}';\n      }\n      \n      handler.next(options);\n    } catch (e) {\n      AppLogger.error('AuthInterceptor: 处理请求失败', e);\n      handler.next(options);  // 即使出错也继续请求\n    }\n  }\n} "
  },
  {
    "path": "lib/main.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/common/constants/strings.dart';\nimport 'package:asmrapp/presentation/viewmodels/auth_viewmodel.dart';\nimport 'core/di/service_locator.dart';\nimport 'package:provider/provider.dart';\nimport 'screens/main_screen.dart';\nimport 'package:asmrapp/core/theme/app_theme.dart';\nimport 'package:asmrapp/core/theme/theme_controller.dart';\nimport 'screens/search_screen.dart';\n\nvoid main() async {\n  WidgetsFlutterBinding.ensureInitialized();\n\n  // 初始化服务定位器\n  await setupServiceLocator();\n\n  runApp(const MyApp());\n}\n\nclass MyApp extends StatelessWidget {\n  const MyApp({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return MultiProvider(\n      providers: [\n        ChangeNotifierProvider(\n          create: (_) => getIt<AuthViewModel>(),\n        ),\n        ChangeNotifierProvider(\n          create: (_) => getIt<ThemeController>(),\n        ),\n      ],\n      child: Consumer<ThemeController>(\n        builder: (context, themeController, child) {\n          return MaterialApp(\n            title: Strings.appName,\n            theme: AppTheme.light,\n            darkTheme: AppTheme.dark,\n            themeMode: themeController.themeMode,\n            home: const MainScreen(),\n            routes: {\n              // '/player': (context) => const PlayerScreen(),\n              '/search': (context) {\n                final keyword =\n                    ModalRoute.of(context)?.settings.arguments as String?;\n                return SearchScreen(initialKeyword: keyword);\n              },\n            },\n          );\n        },\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/presentation/layouts/work_layout_config.dart",
    "content": "import 'package:flutter/material.dart';\n\n/// 设备类型\nenum DeviceType {\n  mobile,\n  tablet,\n  desktop;\n\n  /// 根据屏幕宽度获取设备类型\n  static DeviceType fromWidth(double width) {\n    if (width >= WorkLayoutConfig.desktopBreakpoint) return DeviceType.desktop;\n    if (width >= WorkLayoutConfig.tabletBreakpoint) return DeviceType.tablet;\n    return DeviceType.mobile;\n  }\n}\n\n/// 作品布局配置\nclass WorkLayoutConfig {\n  // 断点\n  static const double desktopBreakpoint = 1200;\n  static const double tabletBreakpoint = 800;\n\n  // 列数\n  static const int desktopColumns = 4;\n  static const int tabletColumns = 3;\n  static const int mobileColumns = 2;\n\n  // 间距\n  static const double desktopSpacing = 16;\n  static const double tabletSpacing = 12;\n  static const double mobileSpacing = 8;\n\n  // 内边距\n  static const EdgeInsets desktopPadding = EdgeInsets.all(16);\n  static const EdgeInsets tabletPadding = EdgeInsets.all(12);\n  static const EdgeInsets mobilePadding = EdgeInsets.all(8);\n\n  const WorkLayoutConfig._();\n\n  /// 根据设备类型获取列数\n  static int getColumnsCount(DeviceType deviceType) {\n    switch (deviceType) {\n      case DeviceType.desktop:\n        return desktopColumns;\n      case DeviceType.tablet:\n        return tabletColumns;\n      case DeviceType.mobile:\n        return mobileColumns;\n    }\n  }\n\n  /// 根据设备类型获取间距\n  static double getSpacing(DeviceType deviceType) {\n    switch (deviceType) {\n      case DeviceType.desktop:\n        return desktopSpacing;\n      case DeviceType.tablet:\n        return tabletSpacing;\n      case DeviceType.mobile:\n        return mobileSpacing;\n    }\n  }\n\n  /// 根据设备类型获取内边距\n  static EdgeInsets getPadding(DeviceType deviceType) {\n    switch (deviceType) {\n      case DeviceType.desktop:\n        return desktopPadding;\n      case DeviceType.tablet:\n        return tabletPadding;\n      case DeviceType.mobile:\n        return mobilePadding;\n    }\n  }\n}\n"
  },
  {
    "path": "lib/presentation/layouts/work_layout_strategy.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/presentation/layouts/work_layout_config.dart';\n\n/// 作品布局策略\nclass WorkLayoutStrategy {\n  const WorkLayoutStrategy();\n\n  /// 获取设备类型\n  DeviceType _getDeviceType(BuildContext context) {\n    return DeviceType.fromWidth(MediaQuery.of(context).size.width);\n  }\n\n  /// 获取每行的列数\n  int getColumnsCount(BuildContext context) {\n    return WorkLayoutConfig.getColumnsCount(_getDeviceType(context));\n  }\n\n  /// 获取行间距\n  double getRowSpacing(BuildContext context) {\n    return WorkLayoutConfig.getSpacing(_getDeviceType(context));\n  }\n\n  /// 获取列间距\n  double getColumnSpacing(BuildContext context) {\n    return WorkLayoutConfig.getSpacing(_getDeviceType(context));\n  }\n\n  /// 获取内边距\n  EdgeInsets getPadding(BuildContext context) {\n    return WorkLayoutConfig.getPadding(_getDeviceType(context));\n  }\n\n  /// 将作品列表分组为行\n  List<List<Work>> groupWorksIntoRows(List<Work> works, int columnsCount) {\n    final List<List<Work>> rows = [];\n    for (var i = 0; i < works.length; i += columnsCount) {\n      final end = i + columnsCount;\n      rows.add(works.sublist(i, end > works.length ? works.length : end));\n    }\n    return rows;\n  }\n}\n"
  },
  {
    "path": "lib/presentation/models/filter_state.dart",
    "content": "class FilterState {\n  final String orderField;\n  final bool isDescending;\n\n  const FilterState({\n    this.orderField = 'create_date',\n    this.isDescending = true,\n  });\n\n  bool get showSortDirection => orderField != 'random';\n\n  String get sortValue => orderField == 'random' ? 'desc' : (isDescending ? 'desc' : 'asc');\n\n  FilterState copyWith({\n    String? orderField,\n    bool? isDescending,\n  }) {\n    return FilterState(\n      orderField: orderField ?? this.orderField,\n      isDescending: isDescending ?? this.isDescending,\n    );\n  }\n\n  // 用于持久化\n  Map<String, dynamic> toJson() => {\n    'orderField': orderField,\n    'isDescending': isDescending,\n  };\n\n  // 从持久化恢复\n  factory FilterState.fromJson(Map<String, dynamic> json) => FilterState(\n    orderField: json['orderField'] ?? 'create_date',\n    isDescending: json['isDescending'] ?? true,\n  );\n} "
  },
  {
    "path": "lib/presentation/viewmodels/auth_viewmodel.dart",
    "content": "import 'package:flutter/foundation.dart';\nimport 'package:asmrapp/data/models/auth/auth_resp/auth_resp.dart';\nimport 'package:asmrapp/data/services/auth_service.dart';\nimport 'package:asmrapp/data/repositories/auth_repository.dart';\nimport 'package:asmrapp/utils/logger.dart';\n\nclass AuthViewModel extends ChangeNotifier {\n  final AuthService _authService;\n  final AuthRepository _authRepository;\n  AuthResp? _authData;\n  bool _isLoading = false;\n  String? _error;\n\n  AuthViewModel({\n    required AuthService authService,\n    required AuthRepository authRepository,\n  })  : _authService = authService,\n        _authRepository = authRepository {\n    _loadSavedAuth();\n  }\n\n  Future<void> _loadSavedAuth() async {\n    _authData = await _authRepository.getAuthData();\n    if (_authData != null) {\n      AppLogger.info('加载保存的认证数据: ${_authData?.user?.name}');\n    }\n    notifyListeners();\n  }\n\n  Future<void> login(String name, String password) async {\n    if (_isLoading) return;\n\n    _isLoading = true;\n    _error = null;\n    notifyListeners();\n\n    try {\n      AppLogger.info('AuthViewModel: 开始登录流程');\n      _authData = await _authService.login(name, password);\n      \n      // 保存认证数据\n      await _authRepository.saveAuthData(_authData!);\n      \n      AppLogger.info('''\n登录成功，完整数据:\n- token: ${_authData?.token}\n- loggedIn: ${_authData?.user?.loggedIn}\n- name: ${_authData?.user?.name}\n- group: ${_authData?.user?.group}\n- email: ${_authData?.user?.email}\n- recommenderUuid: ${_authData?.user?.recommenderUuid}\n      ''');\n\n    } catch (e) {\n      AppLogger.error('AuthViewModel: 登录失败', e);\n      _error = e.toString();\n      _authData = null;\n    } finally {\n      _isLoading = false;\n      notifyListeners();\n    }\n  }\n\n  Future<void> logout() async {\n    AppLogger.info('AuthViewModel: 执行登出');\n    AppLogger.info('''\n登出用户信息:\n- name: ${_authData?.user?.name}\n- group: ${_authData?.user?.group}\n- token: ${_authData?.token}\n    ''');\n    \n    await _authRepository.clearAuthData();\n    _authData = null;\n    notifyListeners();\n  }\n\n  bool get isLoggedIn => _authData?.user != null;\n  bool get isLoading => _isLoading;\n  String? get error => _error;\n  String? get username => _authData?.user?.name;\n  String? get token => _authData?.token;\n  String? get group => _authData?.user?.group;\n  bool? get isUserLoggedIn => _authData?.user?.loggedIn;\n  String? get recommenderUuid => _authData?.user?.recommenderUuid;\n\n  Future<void> loadSavedAuth() async {\n    _authData = await _authRepository.getAuthData();\n    if (_authData != null) {\n      AppLogger.info('加载保存的认证数据: ${_authData?.user?.name}');\n    }\n    notifyListeners();\n  }\n} "
  },
  {
    "path": "lib/presentation/viewmodels/base/paginated_works_viewmodel.dart",
    "content": "import 'package:flutter/foundation.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/data/models/works/pagination.dart';\nimport 'package:asmrapp/data/services/api_service.dart';\nimport 'package:asmrapp/utils/logger.dart';\n\nabstract class PaginatedWorksViewModel extends ChangeNotifier {\n  final ApiService _apiService;\n  List<Work> _works = [];\n  bool _isLoading = false;\n  String? _error;\n  Pagination? _pagination;\n  int _currentPage = 1;\n\n  PaginatedWorksViewModel(this._apiService) {\n    _init();\n  }\n\n  // 修改为异步初始化\n  Future<void> _init() async {\n    await onInit(); // 添加初始化钩子\n    loadPage(1);\n  }\n\n  // 添加初始化钩子，供子类重写\n  Future<void> onInit() async {}\n\n  // Getters\n  List<Work> get works => _works;\n  bool get isLoading => _isLoading;\n  String? get error => _error;\n  int get currentPage => _currentPage;\n  int? get totalPages => _pagination?.totalCount != null && _pagination?.pageSize != null\n      ? (_pagination!.totalCount! / _pagination!.pageSize!).ceil()\n      : null;\n\n  // 获取页面名称，用于日志\n  String get pageName;\n\n  // 子类必须实现的方法\n  Future<WorksResponse> fetchPage(int page);\n\n  // 获取 ApiService 实例，供子类使用\n  ApiService get apiService => _apiService;\n\n  // 通用的加载逻辑\n  Future<void> loadPage(int page) async {\n    if (_isLoading) return;\n    if (page < 1 || (totalPages != null && page > totalPages!)) return;\n\n    _isLoading = true;\n    _error = null;\n    notifyListeners();\n\n    try {\n      AppLogger.info('加载$pageName: 第$page页');\n      final response = await fetchPage(page);\n      _works = response.works;\n      _pagination = response.pagination;\n      _currentPage = page;\n      AppLogger.info('第$page页$pageName加载成功: ${response.works.length}个作品');\n    } catch (e) {\n      AppLogger.error('加载$pageName失败', e);\n      _error = e.toString();\n    } finally {\n      _isLoading = false;\n      notifyListeners();\n    }\n  }\n\n  // 刷新方法\n  Future<void> refresh() async {\n    AppLogger.info('刷新$pageName');\n    await loadPage(1);\n  }\n\n  @override\n  void dispose() {\n    AppLogger.info('销毁$pageName ViewModel');\n    super.dispose();\n  }\n\n  // 添加 pagination getter\n  Pagination? get pagination => _pagination;\n} "
  },
  {
    "path": "lib/presentation/viewmodels/detail_viewmodel.dart",
    "content": "import 'package:asmrapp/data/models/playlists_with_exist_statu/pagination.dart';\nimport 'package:asmrapp/data/models/playlists_with_exist_statu/playlist.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/files/files.dart';\nimport 'package:asmrapp/data/models/files/child.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/data/services/api_service.dart';\nimport 'package:asmrapp/core/audio/i_audio_player_service.dart';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:asmrapp/core/audio/models/playback_context.dart';\nimport 'package:asmrapp/widgets/detail/playlist_selection_dialog.dart';\nimport 'package:asmrapp/data/models/mark_status.dart';\nimport 'package:asmrapp/widgets/detail/mark_selection_dialog.dart';\nimport 'package:dio/dio.dart';\n\nclass DetailViewModel extends ChangeNotifier {\n  late final ApiService _apiService;\n  late final IAudioPlayerService _audioService;\n  final Work work;\n\n  Files? _files;\n  bool _isLoading = false;\n  String? _error;\n  bool _disposed = false;\n  \n  bool _hasRecommendations = false;\n  bool _checkingRecommendations = false;\n\n  // 收藏夹相关状态\n  bool _loadingPlaylists = false;\n  String? _playlistsError;\n  List<Playlist>? _playlists;\n  Pagination? _playlistsPagination;\n\n  bool _loadingFavorite = false;\n  bool get loadingFavorite => _loadingFavorite;\n\n  MarkStatus? _currentMarkStatus;\n  MarkStatus? get currentMarkStatus => _currentMarkStatus;\n\n  bool _loadingMark = false;\n  bool get loadingMark => _loadingMark;\n\n  // 添加取消标记\n  final _cancelToken = CancelToken();\n\n  DetailViewModel({\n    required this.work,\n  }) {\n    _audioService = GetIt.I<IAudioPlayerService>();\n    _apiService = GetIt.I<ApiService>();\n    _checkRecommendations();\n  }\n\n  Files? get files => _files;\n  bool get isLoading => _isLoading;\n  String? get error => _error;\n  bool get hasRecommendations => _hasRecommendations;\n  bool get checkingRecommendations => _checkingRecommendations;\n\n  // 收藏夹相关 getters\n  bool get loadingPlaylists => _loadingPlaylists;\n  String? get playlistsError => _playlistsError;\n  List<Playlist>? get playlists => _playlists;\n  int? get playlistsTotalPages => \n      _playlistsPagination?.totalCount != null && _playlistsPagination?.pageSize != null\n          ? (_playlistsPagination!.totalCount! / _playlistsPagination!.pageSize!).ceil()\n          : null;\n\n  Future<void> _checkRecommendations() async {\n    _checkingRecommendations = true;\n    notifyListeners();\n\n    try {\n      final response = await _apiService.getItemNeighbors(\n        itemId: work.id.toString(),\n        page: 1,\n      );\n      _hasRecommendations = (response.pagination.totalCount ?? 0) > 0;\n    } catch (e) {\n      AppLogger.error('检查相关推荐失败', e);\n      _hasRecommendations = false;\n    } finally {\n      if (!_disposed) {\n        _checkingRecommendations = false;\n        notifyListeners();\n      }\n    }\n  }\n\n  Future<void> loadFiles() async {\n    if (_isLoading) return;\n\n    _isLoading = true;\n    _error = null;\n    notifyListeners();\n\n    try {\n      AppLogger.info('开始加载作品文件: ${work.id}');\n      _files = await _apiService.getWorkFiles(\n        work.id.toString(),\n        cancelToken: _cancelToken,\n      );\n      AppLogger.info('文件加载成功: ${work.id}');\n    } catch (e) {\n      if (e is! DioException || e.type != DioExceptionType.cancel) {\n        AppLogger.info('加载文件失败，用户取消请求');\n        _error = e.toString();\n      }\n    } finally {\n      if (!_disposed) {\n        _isLoading = false;\n        notifyListeners();\n      }\n    }\n  }\n\n  Future<void> playFile(Child file, BuildContext context) async {\n    if (file.type?.toLowerCase() != 'audio') {\n      throw Exception('不支持的文件类型: ${file.type}');\n    }\n\n    if (file.mediaDownloadUrl == null) {\n      throw Exception('无法播放：文件URL不存在');\n    }\n\n    if (_files == null) {\n      throw Exception('文件列表未加载');\n    }\n\n    try {\n      final playbackContext = PlaybackContext(\n        work: work,\n        files: _files!,\n        currentFile: file,\n      );\n\n      await _audioService.playWithContext(playbackContext);\n    } catch (e) {\n      if (!_disposed) {\n        AppLogger.error('播放失败', e);\n      }\n      rethrow;\n    }\n  }\n\n  /// 加载收藏夹列表\n  Future<void> loadPlaylists({int page = 1}) async {\n    if (_loadingPlaylists) return;\n\n    _loadingPlaylists = true;\n    _playlistsError = null;\n    notifyListeners();\n\n    try {\n      final response = await _apiService.getWorkExistStatusInPlaylists(\n        workId: work.id.toString(),\n        page: page,\n      );\n      \n      _playlists = response.playlists;\n      _playlistsPagination = response.pagination;\n      AppLogger.info('收藏夹列表加载成功: ${_playlists?.length ?? 0}个收藏夹');\n    } catch (e) {\n      AppLogger.error('加载收藏夹列表失败', e);\n      _playlistsError = e.toString();\n    } finally {\n      _loadingPlaylists = false;\n      notifyListeners();\n    }\n  }\n\n  Future<void> showPlaylistsDialog(BuildContext context) async {\n    _loadingFavorite = true;\n    notifyListeners();\n    \n    try {\n      await loadPlaylists();\n      _loadingFavorite = false;\n      notifyListeners();\n      \n      if (!context.mounted) return;\n      \n      await showDialog(\n        context: context,\n        builder: (context) => PlaylistSelectionDialog(\n          playlists: playlists,\n          isLoading: loadingPlaylists,\n          error: playlistsError,\n          onRetry: () => loadPlaylists(),\n          onPlaylistTap: (playlist) async {\n            try {\n              await togglePlaylistWork(playlist);\n            } catch (e) {\n              if (context.mounted) {\n                ScaffoldMessenger.of(context).showSnackBar(\n                  SnackBar(content: Text('操作失败: $e')),\n                );\n              }\n            }\n          },\n        ),\n      );\n    } catch (e) {\n      _loadingFavorite = false;\n      notifyListeners();\n      rethrow;\n    }\n  }\n\n  Future<void> togglePlaylistWork(Playlist playlist) async {\n    try {\n      if (playlist.exist ?? false) {\n        await _apiService.removeWorkFromPlaylist(\n          playlistId: playlist.id!,\n          workId: work.id.toString(),\n        );\n      } else {\n        await _apiService.addWorkToPlaylist(\n          playlistId: playlist.id!,\n          workId: work.id.toString(),\n        );\n      }\n      \n      // 更新本地收藏夹状态\n      final index = _playlists?.indexWhere((p) => p.id == playlist.id);\n      if (index != null && index != -1) {\n        _playlists = List<Playlist>.from(_playlists!)\n          ..[index] = playlist.copyWith(exist: !(playlist.exist ?? false));\n        notifyListeners();\n      }\n      \n      final action = (playlist.exist ?? false) ? '移除' : '添加';\n      AppLogger.info('$action收藏成功: ${playlist.name}');\n    } catch (e) {\n      AppLogger.error('切换收藏状态失败', e);\n      rethrow;\n    }\n  }\n\n  Future<void> updateMarkStatus(MarkStatus status) async {\n    _loadingMark = true;\n    notifyListeners();\n\n    try {\n      await _apiService.updateWorkMarkStatus(\n        work.id.toString(),\n        _apiService.convertMarkStatusToApi(status),\n      );\n      \n      _currentMarkStatus = status;\n      AppLogger.info('更新标记状态成功: ${status.label}');\n    } catch (e) {\n      AppLogger.error('更新标记状态失败', e);\n      rethrow;\n    } finally {\n      _loadingMark = false;\n      notifyListeners();\n    }\n  }\n\n  void showMarkDialog(BuildContext context) {\n    showDialog(\n      context: context,\n      builder: (dialogContext) => MarkSelectionDialog(\n        currentStatus: _currentMarkStatus,\n        loading: _loadingMark,\n        onMarkSelected: (status) async {\n          try {\n            await updateMarkStatus(status);\n            if (context.mounted) {\n              ScaffoldMessenger.of(context).showSnackBar(\n                SnackBar(\n                  content: Text('已标记为${status.label}'),\n                  duration: const Duration(seconds: 2),\n                  behavior: SnackBarBehavior.floating,\n                ),\n              );\n            }\n          } catch (e) {\n            if (context.mounted) {\n              ScaffoldMessenger.of(context).showSnackBar(\n                SnackBar(content: Text('标记失败: $e')),\n              );\n            }\n          }\n        },\n      ),\n    );\n  }\n\n  @override\n  void dispose() {\n    // 取消所有正在进行的请求\n    _cancelToken.cancel('ViewModel disposed');\n    _disposed = true;\n    super.dispose();\n  }\n}\n"
  },
  {
    "path": "lib/presentation/viewmodels/favorites_viewmodel.dart",
    "content": "import 'package:flutter/foundation.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/data/models/works/pagination.dart';\nimport 'package:asmrapp/data/services/api_service.dart';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:get_it/get_it.dart';\n\nclass FavoritesViewModel extends ChangeNotifier {\n  final ApiService _apiService;\n  List<Work> _works = [];\n  bool _isLoading = false;\n  String? _error;\n  Pagination? _pagination;\n  int _currentPage = 1;\n\n  FavoritesViewModel() : _apiService = GetIt.I<ApiService>();\n\n  List<Work> get works => _works;\n  bool get isLoading => _isLoading;\n  String? get error => _error;\n  int get currentPage => _currentPage;\n  int? get totalPages =>\n      _pagination?.totalCount != null && _pagination?.pageSize != null\n          ? (_pagination!.totalCount! / _pagination!.pageSize!).ceil()\n          : null;\n\n  /// 加载指定页面的数据\n  Future<void> loadPage(int page) async {\n    if (_isLoading) return;\n    if (page < 1 || (totalPages != null && page > totalPages!)) return;\n\n    _isLoading = true;\n    _error = null;\n    notifyListeners();\n\n    try {\n      final response = await _apiService.getFavorites(page: page);\n      _works = response.works;\n      _pagination = response.pagination;\n      _currentPage = page;\n      AppLogger.info('第$page页收藏列表加载成功: ${response.works.length}个作品');\n    } catch (e) {\n      AppLogger.error('加载收藏列表失败', e);\n      _error = e.toString();\n    } finally {\n      _isLoading = false;\n      notifyListeners();\n    }\n  }\n\n  /// 加载收藏列表(用于初始加载和刷新)\n  Future<void> loadFavorites({bool refresh = false}) async {\n    await loadPage(1);\n  }\n} "
  },
  {
    "path": "lib/presentation/viewmodels/home_viewmodel.dart",
    "content": "import 'dart:convert';\nimport 'package:asmrapp/presentation/viewmodels/base/paginated_works_viewmodel.dart';\nimport 'package:asmrapp/data/services/api_service.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:asmrapp/presentation/models/filter_state.dart';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\n\nclass HomeViewModel extends PaginatedWorksViewModel {\n  static const String _filterStateKey = 'home_filter_state';\n  static const String _subtitleFilterKey = 'subtitle_filter';\n  \n  bool _filterPanelExpanded = false;\n  bool _hasSubtitle = false;\n  FilterState _filterState = const FilterState();\n  \n  bool get filterPanelExpanded => _filterPanelExpanded;\n  bool get hasSubtitle => _hasSubtitle;\n  FilterState get filterState => _filterState;\n\n  HomeViewModel() : super(GetIt.I<ApiService>());\n\n  @override\n  Future<void> onInit() async {\n    await _loadFilterState();\n    await _loadSubtitleFilter();\n  }\n\n  Future<void> _loadFilterState() async {\n    try {\n      final prefs = await SharedPreferences.getInstance();\n      final jsonStr = prefs.getString(_filterStateKey);\n      if (jsonStr != null) {\n        _filterState = FilterState.fromJson(jsonDecode(jsonStr));\n        notifyListeners();\n      }\n    } catch (e) {\n      AppLogger.error('加载筛选状态失败', e);\n    }\n  }\n\n  Future<void> _loadSubtitleFilter() async {\n    try {\n      final prefs = await SharedPreferences.getInstance();\n      _hasSubtitle = prefs.getBool(_subtitleFilterKey) ?? false;\n      notifyListeners();\n    } catch (e) {\n      AppLogger.error('加载字幕筛选状态失败', e);\n    }\n  }\n\n  Future<void> _saveSubtitleFilter() async {\n    try {\n      final prefs = await SharedPreferences.getInstance();\n      await prefs.setBool(_subtitleFilterKey, _hasSubtitle);\n    } catch (e) {\n      AppLogger.error('保存字幕筛选状态失败', e);\n    }\n  }\n\n  Future<void> _saveFilterState() async {\n    try {\n      final prefs = await SharedPreferences.getInstance();\n      await prefs.setString(_filterStateKey, jsonEncode(_filterState.toJson()));\n    } catch (e) {\n      AppLogger.error('保存筛选状态失败', e);\n    }\n  }\n\n  void toggleFilterPanel() {\n    _filterPanelExpanded = !_filterPanelExpanded;\n    notifyListeners();\n  }\n\n  void updateSubtitle(bool value) {\n    _hasSubtitle = value;\n    _saveSubtitleFilter();\n    notifyListeners();\n    refresh();\n  }\n\n  void updateOrderField(String value) {\n    // 如果切换到随机排序，强制设置为降序\n    final newState = _filterState.copyWith(\n      orderField: value,\n      isDescending: value == 'random' ? true : _filterState.isDescending,\n    );\n    _filterState = newState;\n    _saveFilterState();\n    notifyListeners();\n    refresh();\n  }\n\n  void updateSortDirection(bool isDescending) {\n    if (_filterState.orderField == 'random') return;\n    _filterState = _filterState.copyWith(isDescending: isDescending);\n    _saveFilterState();\n    notifyListeners();\n    refresh();\n  }\n\n  void closeFilterPanel() {\n    if (_filterPanelExpanded) {\n      _filterPanelExpanded = false;\n      notifyListeners();\n    }\n  }\n\n  @override\n  String get pageName => '主页';\n\n  @override\n  Future<WorksResponse> fetchPage(int page) {\n    return apiService.getWorks(\n      page: page,\n      hasSubtitle: _hasSubtitle,\n      order: _filterState.orderField,\n      sort: _filterState.sortValue,\n    );\n  }\n\n  @override\n  void dispose() {\n    _saveFilterState();\n    super.dispose();\n  }\n}\n"
  },
  {
    "path": "lib/presentation/viewmodels/player_viewmodel.dart",
    "content": "import 'package:asmrapp/core/audio/events/playback_event.dart';\nimport 'package:asmrapp/core/audio/models/audio_track_info.dart';\nimport 'package:asmrapp/core/audio/models/playback_context.dart';\nimport 'package:asmrapp/core/subtitle/i_subtitle_service.dart';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:asmrapp/core/audio/i_audio_player_service.dart';\nimport 'package:asmrapp/core/audio/models/subtitle.dart';\nimport 'dart:async';\nimport 'package:asmrapp/core/subtitle/subtitle_loader.dart';\nimport 'package:asmrapp/core/audio/events/playback_event_hub.dart';\n\nclass PlayerViewModel extends ChangeNotifier {\n  final IAudioPlayerService _audioService;\n  final PlaybackEventHub _eventHub;\n  final ISubtitleService _subtitleService;\n  final _subtitleLoader = SubtitleLoader();\n\n  bool _isPlaying = false;\n  Duration? _position;\n  Duration? _duration;\n  Subtitle? _currentSubtitle;\n\n  final List<StreamSubscription> _subscriptions = [];\n\n  static const _tag = 'PlayerViewModel';\n\n  PlayerViewModel({\n    required IAudioPlayerService audioService,\n    required PlaybackEventHub eventHub,\n    required ISubtitleService subtitleService,\n  }) : _audioService = audioService,\n       _eventHub = eventHub,\n       _subtitleService = subtitleService {\n    _initStreams();\n    _requestInitialState();\n  }\n\n  void _initStreams() {\n    // 播放状态事件\n    _subscriptions.add(\n      _eventHub.playbackState.listen(\n        (event) {\n          _isPlaying = event.state.playing;\n          _position = event.position;\n          _duration = event.duration;\n          notifyListeners();\n        },\n        onError: (error) => debugPrint('$_tag - 播放状态流错误: $error'),\n      ),\n    );\n\n    // 音轨变更事件\n    _subscriptions.add(\n      _eventHub.trackChange.listen(\n        (event) {\n          notifyListeners();\n        },\n        onError: (error) => debugPrint('$_tag - 音轨变更流错误: $error'),\n      ),\n    );\n\n    // 播放进度事件\n    _subscriptions.add(\n      _eventHub.playbackProgress.listen(\n        (event) {\n          _position = event.position;\n          if (_position != null) {\n            _subtitleService.updatePosition(_position!);\n          }\n          notifyListeners();\n        },\n        onError: (error) => debugPrint('$_tag - 播放进度流错误: $error'),\n      ),\n    );\n\n    // 上下文变更事件\n    _subscriptions.add(\n      _eventHub.contextChange.listen(\n        (event) async {\n          await _loadSubtitleIfAvailable(event.context);\n          // 如果有保存的位置，在字幕加载完成后更新位置\n          if (_position != null) {\n            _subtitleService.updatePosition(_position!);\n          }\n        },\n        onError: (error) => debugPrint('$_tag - 上下文流错误: $error'),\n      ),\n    );\n\n    // 使用新添加的 initialState 流\n    _subscriptions.add(\n      _eventHub.initialState.listen(\n        (event) {\n          if (event.track != null) {\n            notifyListeners();\n          }\n          if (event.context != null) {\n            _loadSubtitleIfAvailable(event.context!);\n          }\n        },\n        onError: (error) => debugPrint('$_tag - 初始状态流错误: $error'),\n      ),\n    );\n\n    _initSubtitleStreams();\n  }\n\n  void _initSubtitleStreams() {\n    _subscriptions.add(\n      _subtitleService.subtitleStream.listen(\n        (subtitleList) {\n          debugPrint('$_tag - 字幕列表更新: ${subtitleList != null ? '已加载' : '未加载'}');\n        },\n        onError: (error) => debugPrint('$_tag - 字幕流错误: $error'),\n      ),\n    );\n\n    _subscriptions.add(\n      _subtitleService.currentSubtitleStream.listen(\n        (subtitle) {\n          _currentSubtitle = subtitle;\n          notifyListeners();\n        },\n        onError: (error) => debugPrint('$_tag - 当前字幕流错误: $error'),\n      ),\n    );\n  }\n\n  bool get isPlaying => _isPlaying;\n  Duration? get position => _position;\n  Duration? get duration => _duration;\n  Subtitle? get currentSubtitle => _currentSubtitle;\n\n  Future<void> playPause() async {\n    if (_isPlaying) {\n      _audioService.pause();\n    } else {\n      _audioService.resume();\n    }\n  }\n\n  Future<void> seek(Duration position) async {\n    await _audioService.seek(position);\n  }\n\n  Future<void> previous() async {\n    await _audioService.previous();\n  }\n\n  Future<void> next() async {\n    await _audioService.next();\n  }\n\n  Future<void> stop() async {\n    await _audioService.stop();\n    _position = Duration.zero;\n    notifyListeners();\n  }\n\n  @override\n  void dispose() {\n    for (var subscription in _subscriptions) {\n      subscription.cancel();\n    }\n    _subscriptions.clear();\n    super.dispose();\n  }\n\n  // 请求初始状态\n  void _requestInitialState() {\n    _eventHub.emit(RequestInitialStateEvent());\n  }\n\n  // 修改字幕加载方法，返回 Future 以便等待加载完成\n  Future<void> _loadSubtitleIfAvailable(PlaybackContext context) async {\n    final subtitleFile = _subtitleLoader.findSubtitleFile(\n      context.currentFile,\n      context.files\n    );\n    if (subtitleFile?.mediaDownloadUrl != null) {\n      await _subtitleService.loadSubtitle(subtitleFile!.mediaDownloadUrl!);\n    } else {\n      _subtitleService.clearSubtitle();\n      AppLogger.debug('未找到字幕文件，清除现有字幕');\n    }\n  }\n\n  AudioTrackInfo? get currentTrackInfo => _audioService.currentTrack;\n  PlaybackContext? get currentContext => _audioService.currentContext;\n\n  Future<void> seekToNextLyric() async {\n    final currentSubtitle = _subtitleService.currentSubtitleWithState;\n    final subtitleList = _subtitleService.subtitleList;\n    \n    if (currentSubtitle != null && subtitleList != null) {\n      final nextSubtitle = currentSubtitle.subtitle.getNext(subtitleList);\n      if (nextSubtitle != null) {\n        await seek(nextSubtitle.start);\n      }\n    }\n  }\n\n  Future<void> seekToPreviousLyric() async {\n    final currentSubtitle = _subtitleService.currentSubtitleWithState;\n    final subtitleList = _subtitleService.subtitleList;\n    \n    if (currentSubtitle != null && subtitleList != null) {\n      final previousSubtitle = currentSubtitle.subtitle.getPrevious(subtitleList);\n      if (previousSubtitle != null) {\n        await seek(previousSubtitle.start);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/presentation/viewmodels/playlist_works_viewmodel.dart",
    "content": "import 'package:asmrapp/data/models/my_lists/my_playlists/playlist.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/data/models/works/pagination.dart';\nimport 'package:asmrapp/data/services/api_service.dart';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:get_it/get_it.dart';\n\nclass PlaylistWorksViewModel extends ChangeNotifier {\n  final ApiService _apiService = GetIt.I<ApiService>();\n  final Playlist playlist;\n  \n  List<Work> _works = [];\n  bool _isLoading = false;\n  String? _error;\n  Pagination? _pagination;\n  int _currentPage = 1;\n\n  PlaylistWorksViewModel(this.playlist);\n\n  List<Work> get works => _works;\n  bool get isLoading => _isLoading;\n  String? get error => _error;\n  int get currentPage => _currentPage;\n  int? get totalPages => _pagination?.totalCount != null && _pagination?.pageSize != null\n      ? (_pagination!.totalCount! / _pagination!.pageSize!).ceil()\n      : null;\n\n  Future<void> loadWorks({int page = 1}) async {\n    if (_isLoading) return;\n    if (page < 1 || (totalPages != null && page > totalPages!)) return;\n\n    _isLoading = true;\n    _error = null;\n    notifyListeners();\n\n    try {\n      final response = await _apiService.getPlaylistWorks(\n        playlistId: playlist.id!,\n        page: page,\n      );\n      \n      _works = response.works;\n      _pagination = response.pagination;\n      _currentPage = page;\n      AppLogger.info('第$page页播放列表作品加载成功: ${response.works.length}个作品');\n    } catch (e) {\n      AppLogger.error('加载播放列表作品失败', e);\n      _error = e.toString();\n    } finally {\n      _isLoading = false;\n      notifyListeners();\n    }\n  }\n\n  Future<void> refresh() => loadWorks(page: 1);\n}\n"
  },
  {
    "path": "lib/presentation/viewmodels/playlists_viewmodel.dart",
    "content": "import 'package:asmrapp/data/models/works/work.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:asmrapp/data/models/my_lists/my_playlists/my_playlists.dart';\nimport 'package:asmrapp/data/models/my_lists/my_playlists/playlist.dart';\nimport 'package:asmrapp/data/models/my_lists/my_playlists/pagination.dart';\nimport 'package:asmrapp/data/services/api_service.dart';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:get_it/get_it.dart';\n\nclass PlaylistsViewModel extends ChangeNotifier {\n  final ApiService _apiService = GetIt.I<ApiService>();\n  \n  List<Playlist>? _playlists;\n  bool _isLoading = false;\n  String? _error;\n  Pagination? _pagination;\n  int _currentPage = 1;\n\n  // 添加视图切换相关状态\n  Playlist? _selectedPlaylist;\n  List<Work> _playlistWorks = [];\n  bool _loadingWorks = false;\n  String? _worksError;\n  Pagination? _worksPagination;\n  int _worksCurrentPage = 1;\n\n  // Getters\n  List<Playlist> get playlists => _playlists ?? [];\n  bool get isLoading => _isLoading;\n  String? get error => _error;\n  int get currentPage => _currentPage;\n  int? get totalPages => _pagination?.totalCount != null && _pagination?.pageSize != null\n      ? (_pagination!.totalCount! / _pagination!.pageSize!).ceil()\n      : null;\n  Playlist? get selectedPlaylist => _selectedPlaylist;\n  List<Work> get playlistWorks => _playlistWorks;\n  bool get loadingWorks => _loadingWorks;\n  String? get worksError => _worksError;\n  int get worksCurrentPage => _worksCurrentPage;\n  int? get worksTotalPages => _worksPagination?.totalCount != null && _worksPagination?.pageSize != null\n      ? (_worksPagination!.totalCount! / _worksPagination!.pageSize!).ceil()\n      : null;\n\n  PlaylistsViewModel() {\n    loadPlaylists();\n  }\n\n  /// 加载播放列表\n  Future<void> loadPlaylists({int page = 1}) async {\n    if (_isLoading) return;\n    if (page < 1 || (totalPages != null && page > totalPages!)) return;\n\n    _isLoading = true;\n    _error = null;\n    notifyListeners();\n\n    try {\n      final response = await _apiService.getMyPlaylists(page: page);\n      _playlists = response.playlists;\n      _pagination = response.pagination;\n      _currentPage = page;\n      AppLogger.info('第$page页播放列表加载成功: ${_playlists?.length ?? 0}个播放列表');\n    } catch (e) {\n      AppLogger.error('加载播放列表失败', e);\n      _error = e.toString();\n    } finally {\n      _isLoading = false;\n      notifyListeners();\n    }\n  }\n\n  /// 刷新播放列表\n  Future<void> refresh() async {\n    await loadPlaylists(page: 1);\n  }\n\n  /// 选择播放列表并加载作品\n  Future<void> selectPlaylist(Playlist playlist) async {\n    _selectedPlaylist = playlist;\n    _playlistWorks = [];\n    _worksError = null;\n    _worksPagination = null;\n    _worksCurrentPage = 1;\n    notifyListeners();\n    \n    await loadPlaylistWorks();\n  }\n\n  /// 清除选中的播放列表\n  void clearSelectedPlaylist() {\n    _selectedPlaylist = null;\n    _playlistWorks = [];\n    _worksError = null;\n    _worksPagination = null;\n    _worksCurrentPage = 1;\n    notifyListeners();\n  }\n\n  /// 加载播放列表作品\n  Future<void> loadPlaylistWorks({int page = 1}) async {\n    if (_loadingWorks || _selectedPlaylist == null) return;\n    if (page < 1 || (worksTotalPages != null && page > worksTotalPages!)) return;\n\n    _loadingWorks = true;\n    _worksError = null;\n    notifyListeners();\n\n    try {\n      final response = await _apiService.getPlaylistWorks(\n        playlistId: _selectedPlaylist!.id!,\n        page: page,\n      );\n      \n      _playlistWorks = response.works;\n      _worksPagination = response.pagination as Pagination?;\n      _worksCurrentPage = page;\n      AppLogger.info('第$page页播放列表作品加载成功: ${response.works.length}个作品');\n    } catch (e) {\n      AppLogger.error('加载播放列表作品失败', e);\n      _worksError = e.toString();\n    } finally {\n      _loadingWorks = false;\n      notifyListeners();\n    }\n  }\n\n  /// 刷新播放列表作品\n  Future<void> refreshWorks() => loadPlaylistWorks(page: 1);\n\n  /// 获取播放列表显示名称\n  String getDisplayName(String? name) {\n    switch (name) {\n      case '__SYS_PLAYLIST_MARKED':\n        return '我标记的';\n      case '__SYS_PLAYLIST_LIKED':\n        return '我喜欢的';\n      default:\n        return name ?? '';\n    }\n  }\n\n  @override\n  void dispose() {\n    AppLogger.info('销毁 PlaylistsViewModel');\n    super.dispose();\n  }\n} "
  },
  {
    "path": "lib/presentation/viewmodels/popular_viewmodel.dart",
    "content": "import 'package:asmrapp/presentation/viewmodels/base/paginated_works_viewmodel.dart';\nimport 'package:asmrapp/data/services/api_service.dart';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\n\nclass PopularViewModel extends PaginatedWorksViewModel {\n  static const _subtitleFilterKey = 'subtitle_filter';\n  bool _hasSubtitle = false;\n  bool _filterPanelExpanded = false;\n\n  PopularViewModel() : super(GetIt.I<ApiService>()) {\n    _loadFilterState();\n  }\n\n  @override\n  Future<void> onInit() async {\n    await _loadSubtitleFilter();  // 使用 onInit 钩子加载状态\n  }\n\n  Future<void> _loadSubtitleFilter() async {\n    try {\n      final prefs = await SharedPreferences.getInstance();\n      _hasSubtitle = prefs.getBool(_subtitleFilterKey) ?? false;\n      notifyListeners();\n    } catch (e) {\n      AppLogger.error('加载字幕筛选状态失败', e);\n    }\n  }\n\n  Future<void> _loadFilterState() async {\n    try {\n      final prefs = await SharedPreferences.getInstance();\n      _hasSubtitle = prefs.getBool(_subtitleFilterKey) ?? false;\n      notifyListeners();\n    } catch (e) {\n      AppLogger.error('加载筛选状态失败', e);\n    }\n  }\n\n  Future<void> _saveFilterState() async {\n    try {\n      final prefs = await SharedPreferences.getInstance();\n      await prefs.setBool(_subtitleFilterKey, _hasSubtitle);\n    } catch (e) {\n      AppLogger.error('保存筛选状态失败', e);\n    }\n  }\n\n  bool get hasSubtitle => _hasSubtitle;\n  bool get filterPanelExpanded => _filterPanelExpanded;\n\n  void toggleSubtitleFilter() {\n    _hasSubtitle = !_hasSubtitle;\n    _saveFilterState();\n    notifyListeners();\n    refresh(); // 刷新列表\n  }\n\n  void toggleFilterPanel() {\n    _filterPanelExpanded = !_filterPanelExpanded;\n    notifyListeners();\n  }\n\n  void closeFilterPanel() {\n    if (_filterPanelExpanded) {\n      _filterPanelExpanded = false;\n      notifyListeners();\n    }\n  }\n\n  @override\n  String get pageName => '热门列表';\n\n  @override\n  Future<WorksResponse> fetchPage(int page) {\n    return apiService.getPopular(\n      page: page,\n      hasSubtitle: _hasSubtitle,\n    );\n  }\n\n  // 保持原有的便捷方法\n  Future<void> loadPopular({bool refresh = false}) => \n    refresh ? this.refresh() : loadPage(1);\n\n  @override\n  void dispose() {\n    _saveFilterState();\n    super.dispose();\n  }\n} "
  },
  {
    "path": "lib/presentation/viewmodels/recommend_viewmodel.dart",
    "content": "import 'package:flutter/foundation.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/data/models/works/pagination.dart';\nimport 'package:asmrapp/data/services/api_service.dart';\nimport 'package:asmrapp/presentation/viewmodels/auth_viewmodel.dart';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\n\nclass RecommendViewModel extends ChangeNotifier {\n  static const _subtitleFilterKey = 'subtitle_filter'; // 与 PopularViewModel 使用相同的 key 实现全局共享\n  final ApiService _apiService;\n  final AuthViewModel _authViewModel;\n  List<Work> _works = [];\n  bool _isLoading = false;\n  String? _error;\n  Pagination? _pagination;\n  int _currentPage = 1;\n  bool _hasSubtitle = false;\n  bool _filterPanelExpanded = false;\n\n  RecommendViewModel(this._authViewModel) : _apiService = GetIt.I<ApiService>() {\n    _loadFilterState();\n  }\n\n  // 加载筛选状态\n  Future<void> _loadFilterState() async {\n    try {\n      final prefs = await SharedPreferences.getInstance();\n      _hasSubtitle = prefs.getBool(_subtitleFilterKey) ?? false;\n      notifyListeners();\n      // 首次加载时应用筛选状态\n      loadRecommendations(refresh: true);\n    } catch (e) {\n      AppLogger.error('加载筛选状态失败', e);\n    }\n  }\n\n  // 保存筛选状态\n  Future<void> _saveFilterState() async {\n    try {\n      final prefs = await SharedPreferences.getInstance();\n      await prefs.setBool(_subtitleFilterKey, _hasSubtitle);\n    } catch (e) {\n      AppLogger.error('保存筛选状态失败', e);\n    }\n  }\n\n  // Getters\n  List<Work> get works => _works;\n  bool get isLoading => _isLoading;\n  String? get error => _error;\n  int get currentPage => _currentPage;\n  int? get totalPages =>\n      _pagination?.totalCount != null && _pagination?.pageSize != null\n          ? (_pagination!.totalCount! / _pagination!.pageSize!).ceil()\n          : null;\n  bool get hasSubtitle => _hasSubtitle;\n  bool get filterPanelExpanded => _filterPanelExpanded;\n\n  Pagination? get pagination => _pagination;\n\n  // 切换字幕筛选\n  void toggleSubtitleFilter() {\n    _hasSubtitle = !_hasSubtitle;\n    _saveFilterState(); // 保存状态\n    notifyListeners();\n    loadRecommendations(refresh: true); // 刷新列表\n  }\n\n  void toggleFilterPanel() {\n    _filterPanelExpanded = !_filterPanelExpanded;\n    notifyListeners();\n  }\n\n  void closeFilterPanel() {\n    if (_filterPanelExpanded) {\n      _filterPanelExpanded = false;\n      notifyListeners();\n    }\n  }\n\n  /// 加载指定页面的数据\n  Future<void> loadPage(int page) async {\n    if (_isLoading) return;\n    if (page < 1 || (totalPages != null && page > totalPages!)) return;\n    \n    // 检查是否已登录\n    final uuid = _authViewModel.recommenderUuid;\n    if (uuid == null) {\n      _error = '请先登录';\n      notifyListeners();\n      return;\n    }\n\n    _isLoading = true;\n    _error = null;\n    notifyListeners();\n\n    try {\n      final response = await _apiService.getRecommendations(\n        uuid: uuid,\n        page: page,\n        hasSubtitle: _hasSubtitle, // 添加字幕筛选参数\n      );\n      _works = response.works;\n      _pagination = response.pagination;\n      _currentPage = page;\n      AppLogger.info('第$page页推荐列表加载成功: ${response.works.length}个作品');\n    } catch (e) {\n      AppLogger.error('加载推荐列表失败', e);\n      _error = e.toString();\n    } finally {\n      _isLoading = false;\n      notifyListeners();\n    }\n  }\n\n  /// 加载推荐列表(用于初始加载和刷新)\n  Future<void> loadRecommendations({bool refresh = false}) async {\n    await loadPage(1);\n  }\n\n  @override\n  void dispose() {\n    _saveFilterState(); // 在销毁时保存状态\n    super.dispose();\n  }\n} "
  },
  {
    "path": "lib/presentation/viewmodels/search_viewmodel.dart",
    "content": "import 'package:flutter/foundation.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/data/models/works/pagination.dart';\nimport 'package:asmrapp/data/services/api_service.dart';\nimport 'package:asmrapp/utils/logger.dart';\n\nclass SearchViewModel extends ChangeNotifier {\n  final _apiService = GetIt.I<ApiService>();\n\n  List<Work> _works = [];\n  List<Work> get works => _works;\n\n  String _keyword = '';\n  String get keyword => _keyword;\n\n  bool _isLoading = false;\n  bool get isLoading => _isLoading;\n\n  String? _error;\n  String? get error => _error;\n\n  Pagination? _pagination;\n  int get totalPages =>\n      _pagination?.totalCount != null && _pagination?.pageSize != null\n          ? (_pagination!.totalCount! / _pagination!.pageSize!).ceil()\n          : 1;\n  int _currentPage = 1;\n  int get currentPage => _currentPage;\n\n  bool _hasSubtitle = false;\n  bool get hasSubtitle => _hasSubtitle;\n\n  String _order = 'create_date'; // 默认按创建时间\n  String get order => _order;\n\n  String _sort = 'desc'; // 默认降序\n  String get sort => _sort;\n\n  void toggleSubtitle() {\n    _hasSubtitle = !_hasSubtitle;\n    notifyListeners();\n    if (_keyword.isNotEmpty) {\n      search(_keyword);\n    }\n  }\n\n  void setOrder(String order, String sort) {\n    _order = order;\n    _sort = sort;\n    notifyListeners();\n    if (_keyword.isNotEmpty) {\n      search(_keyword);\n    }\n  }\n\n  /// 执行搜索\n  Future<void> search(String keyword, {int page = 1}) async {\n    if (keyword.isEmpty) return;\n\n    _keyword = keyword;\n    _isLoading = true;\n    _error = null;\n    notifyListeners();\n\n    try {\n      AppLogger.info('搜索关键词: $keyword, 页码: $page');\n      final response = await _apiService.searchWorks(\n        keyword: keyword,\n        page: page,\n        order: _order,\n        sort: _sort,\n        hasSubtitle: _hasSubtitle, // 添加字幕过滤\n      );\n\n      _works = response.works;\n      _pagination = response.pagination;\n      _currentPage = page;\n      AppLogger.info('搜索成功: ${response.works.length}个结果');\n    } catch (e) {\n      AppLogger.error('搜索失败', e);\n      _error = e.toString();\n    } finally {\n      _isLoading = false;\n      notifyListeners();\n    }\n  }\n\n  /// 加载指定页\n  Future<void> loadPage(int page) async {\n    if (_keyword.isEmpty) return;\n    await search(_keyword, page: page);\n  }\n\n  /// 清空搜索结果\n  void clear() {\n    _works = [];\n    _keyword = '';\n    _error = null;\n    _pagination = null;\n    _currentPage = 1;\n    notifyListeners();\n  }\n}\n"
  },
  {
    "path": "lib/presentation/viewmodels/settings/cache_manager_viewmodel.dart",
    "content": "import 'package:flutter/foundation.dart';\nimport 'package:asmrapp/core/audio/cache/audio_cache_manager.dart';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:asmrapp/core/subtitle/cache/subtitle_cache_manager.dart';\n\nclass CacheManagerViewModel extends ChangeNotifier {\n  bool _isLoading = false;\n  int _audioCacheSize = 0;\n  int _subtitleCacheSize = 0;\n  String? _error;\n\n  bool get isLoading => _isLoading;\n  int get audioCacheSize => _audioCacheSize;\n  int get subtitleCacheSize => _subtitleCacheSize;\n  int get totalCacheSize => _audioCacheSize + _subtitleCacheSize;\n  String? get error => _error;\n\n  // 格式化缓存大小显示\n  String _formatSize(int size) {\n    if (size < 1024) return '${size}B';\n    if (size < 1024 * 1024) return '${(size / 1024).toStringAsFixed(2)}KB';\n    return '${(size / (1024 * 1024)).toStringAsFixed(2)}MB';\n  }\n\n  String get audioCacheSizeFormatted => _formatSize(_audioCacheSize);\n  String get subtitleCacheSizeFormatted => _formatSize(_subtitleCacheSize);\n  String get totalCacheSizeFormatted => _formatSize(totalCacheSize);\n\n  // 加载缓存大小\n  Future<void> loadCacheSize() async {\n    try {\n      _isLoading = true;\n      notifyListeners();\n      \n      // 获取音频缓存大小\n      _audioCacheSize = await AudioCacheManager.getCacheSize();\n      // 获取字幕缓存大小\n      _subtitleCacheSize = await SubtitleCacheManager.getSize();\n      \n      _error = null;\n    } catch (e) {\n      AppLogger.error('加载缓存大小失败', e);\n      _error = '加载失败: $e';\n    } finally {\n      _isLoading = false;\n      notifyListeners();\n    }\n  }\n\n  // 清理音频缓存\n  Future<void> clearAudioCache() async {\n    try {\n      _isLoading = true;\n      notifyListeners();\n      \n      await AudioCacheManager.cleanCache();\n      await loadCacheSize();\n      _error = null;\n    } catch (e) {\n      AppLogger.error('清理音频缓存失败', e);\n      _error = '清理失败: $e';\n    } finally {\n      _isLoading = false;\n      notifyListeners();\n    }\n  }\n\n  // 清理字幕缓存\n  Future<void> clearSubtitleCache() async {\n    try {\n      _isLoading = true;\n      notifyListeners();\n      \n      await SubtitleCacheManager.clearCache();\n      await loadCacheSize();\n      _error = null;\n    } catch (e) {\n      AppLogger.error('清理字幕缓存失败', e);\n      _error = '清理失败: $e';\n    } finally {\n      _isLoading = false;\n      notifyListeners();\n    }\n  }\n\n  // 清理所有缓存\n  Future<void> clearAllCache() async {\n    try {\n      _isLoading = true;\n      notifyListeners();\n      \n      await Future.wait([\n        AudioCacheManager.cleanCache(),\n        SubtitleCacheManager.clearCache(),\n      ]);\n      \n      await loadCacheSize();\n      _error = null;\n    } catch (e) {\n      AppLogger.error('清理缓存失败', e);\n      _error = '清理失败: $e';\n    } finally {\n      _isLoading = false;\n      notifyListeners();\n    }\n  }\n} "
  },
  {
    "path": "lib/presentation/viewmodels/similar_works_viewmodel.dart",
    "content": "import 'package:flutter/foundation.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/data/models/works/pagination.dart';\nimport 'package:asmrapp/data/services/api_service.dart';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\n\nclass SimilarWorksViewModel extends ChangeNotifier {\n  static const _subtitleFilterKey = 'subtitle_filter'; // 与其他 ViewModel 使用相同的 key\n  final ApiService _apiService;\n  final Work work;\n  List<Work> _works = [];\n  bool _isLoading = false;\n  String? _error;\n  Pagination? _pagination;\n  int _currentPage = 1;\n  bool _hasSubtitle = false;\n  bool _filterPanelExpanded = false;\n\n  SimilarWorksViewModel(this.work) : _apiService = GetIt.I<ApiService>() {\n    _loadFilterState();\n  }\n\n  // Getters\n  List<Work> get works => _works;\n  bool get isLoading => _isLoading;\n  String? get error => _error;\n  int get currentPage => _currentPage;\n  bool get hasSubtitle => _hasSubtitle;\n  bool get filterPanelExpanded => _filterPanelExpanded;\n  int? get totalPages =>\n      _pagination?.totalCount != null && _pagination?.pageSize != null\n          ? (_pagination!.totalCount! / _pagination!.pageSize!).ceil()\n          : null;\n\n  // 加载筛选状态\n  Future<void> _loadFilterState() async {\n    try {\n      final prefs = await SharedPreferences.getInstance();\n      _hasSubtitle = prefs.getBool(_subtitleFilterKey) ?? false;\n      notifyListeners();\n      // 首次加载时应用筛选状态\n      loadSimilarWorks(refresh: true);\n    } catch (e) {\n      AppLogger.error('加载筛选状态失败', e);\n    }\n  }\n\n  // 保存筛选状态\n  Future<void> _saveFilterState() async {\n    try {\n      final prefs = await SharedPreferences.getInstance();\n      await prefs.setBool(_subtitleFilterKey, _hasSubtitle);\n    } catch (e) {\n      AppLogger.error('保存筛选状态失败', e);\n    }\n  }\n\n  // 切换字幕筛选\n  void toggleSubtitleFilter() {\n    _hasSubtitle = !_hasSubtitle;\n    _saveFilterState();\n    notifyListeners();\n    loadSimilarWorks(refresh: true);\n  }\n\n  void toggleFilterPanel() {\n    _filterPanelExpanded = !_filterPanelExpanded;\n    notifyListeners();\n  }\n\n  void closeFilterPanel() {\n    if (_filterPanelExpanded) {\n      _filterPanelExpanded = false;\n      notifyListeners();\n    }\n  }\n\n  /// 加载指定页面的数据\n  Future<void> loadPage(int page) async {\n    if (_isLoading) return;\n    if (page < 1 || (totalPages != null && page > totalPages!)) return;\n\n    _isLoading = true;\n    _error = null;\n    notifyListeners();\n\n    try {\n      final response = await _apiService.getItemNeighbors(\n        itemId: work.id.toString(),\n        page: page,\n        hasSubtitle: _hasSubtitle, // 添加字幕筛选参数\n      );\n      _works = response.works;\n      _pagination = response.pagination;\n      _currentPage = page;\n      AppLogger.info('第$page页相关推荐加载成功: ${response.works.length}个作品');\n    } catch (e) {\n      AppLogger.error('加载相关推荐失败', e);\n      _error = e.toString();\n    } finally {\n      _isLoading = false;\n      notifyListeners();\n    }\n  }\n\n  /// 加载相关推荐(用于初始加载和刷新)\n  Future<void> loadSimilarWorks({bool refresh = false}) async {\n    await loadPage(1);\n  }\n\n  @override\n  void dispose() {\n    _saveFilterState();\n    super.dispose();\n  }\n} "
  },
  {
    "path": "lib/presentation/widgets/auth/login_dialog.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:provider/provider.dart';\nimport 'package:asmrapp/presentation/viewmodels/auth_viewmodel.dart';\nimport 'package:asmrapp/utils/logger.dart';\n\nclass LoginDialog extends StatefulWidget {\n  const LoginDialog({super.key});\n\n  @override\n  State<LoginDialog> createState() => _LoginDialogState();\n}\n\nclass _LoginDialogState extends State<LoginDialog> {\n  final _nameController = TextEditingController();\n  final _passwordController = TextEditingController();\n  bool _obscurePassword = true;\n\n  @override\n  void dispose() {\n    _nameController.dispose();\n    _passwordController.dispose();\n    super.dispose();\n  }\n\n  Future<void> _handleLogin() async {\n    final name = _nameController.text.trim();\n    AppLogger.info('LoginDialog: 尝试登录: name=$name');\n    \n    final authVM = context.read<AuthViewModel>();\n    await authVM.login(name, _passwordController.text);\n    \n    if (mounted) {\n      if (authVM.error == null) {\n        AppLogger.info('LoginDialog: 登录成功，关闭对话框');\n        Navigator.of(context).pop();\n      } else {\n        AppLogger.error('LoginDialog: 登录失败: ${authVM.error}');\n      }\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return AlertDialog(\n      title: const Text('登录'),\n      content: Column(\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          TextField(\n            controller: _nameController,\n            decoration: const InputDecoration(\n              labelText: '用户名',\n              border: OutlineInputBorder(),\n            ),\n            textInputAction: TextInputAction.next,\n          ),\n          const SizedBox(height: 16),\n          TextField(\n            controller: _passwordController,\n            decoration: InputDecoration(\n              labelText: '密码',\n              border: const OutlineInputBorder(),\n              suffixIcon: IconButton(\n                icon: Icon(\n                  _obscurePassword ? Icons.visibility : Icons.visibility_off,\n                ),\n                onPressed: () {\n                  setState(() {\n                    _obscurePassword = !_obscurePassword;\n                  });\n                },\n              ),\n            ),\n            obscureText: _obscurePassword,\n            textInputAction: TextInputAction.done,\n            onSubmitted: (_) => _handleLogin(),\n          ),\n          const SizedBox(height: 8),\n          Consumer<AuthViewModel>(\n            builder: (context, authVM, _) {\n              if (authVM.error != null) {\n                return Text(\n                  authVM.error!,\n                  style: TextStyle(\n                    color: Theme.of(context).colorScheme.error,\n                  ),\n                );\n              }\n              return const SizedBox.shrink();\n            },\n          ),\n        ],\n      ),\n      actions: [\n        TextButton(\n          onPressed: () => Navigator.pop(context),\n          child: const Text('取消'),\n        ),\n        Consumer<AuthViewModel>(\n          builder: (context, authVM, _) {\n            return FilledButton(\n              onPressed: authVM.isLoading ? null : _handleLogin,\n              child: authVM.isLoading\n                  ? const SizedBox(\n                      width: 20,\n                      height: 20,\n                      child: CircularProgressIndicator(\n                        strokeWidth: 2,\n                      ),\n                    )\n                  : const Text('登录'),\n            );\n          },\n        ),\n      ],\n    );\n  }\n} "
  },
  {
    "path": "lib/screens/contents/home_content.dart",
    "content": "import 'package:asmrapp/widgets/filter/filter_panel.dart';\nimport 'package:flutter/material.dart';\nimport 'package:provider/provider.dart';\nimport 'package:asmrapp/presentation/viewmodels/home_viewmodel.dart';\nimport 'package:asmrapp/presentation/layouts/work_layout_strategy.dart';\nimport 'package:asmrapp/widgets/work_grid/enhanced_work_grid_view.dart';\n\nclass HomeContent extends StatefulWidget {\n  const HomeContent({super.key});\n\n  @override\n  State<HomeContent> createState() => _HomeContentState();\n}\n\nclass _HomeContentState extends State<HomeContent>\n    with AutomaticKeepAliveClientMixin {\n  final _layoutStrategy = const WorkLayoutStrategy();\n  final _scrollController = ScrollController();\n\n  @override\n  bool get wantKeepAlive => true;\n\n  @override\n  void initState() {\n    super.initState();\n    // 添加滚动监听\n    _scrollController.addListener(_onScroll);\n  }\n\n  @override\n  void dispose() {\n    _scrollController.removeListener(_onScroll);\n    _scrollController.dispose();\n    super.dispose();\n  }\n\n  void _onScroll() {\n    // 当滚动开始时收起筛选面板\n    if (_scrollController.position.pixels !=\n        _scrollController.position.minScrollExtent) {\n      final viewModel = context.read<HomeViewModel>();\n      if (viewModel.filterPanelExpanded) {\n        viewModel.closeFilterPanel(); // 需要在 ViewModel 中添加这个方法\n      }\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    super.build(context);\n    return Consumer<HomeViewModel>(\n      builder: (context, viewModel, child) {\n        return Stack(\n          children: [\n            // 作品列表\n            EnhancedWorkGridView(\n              works: viewModel.works,\n              isLoading: viewModel.isLoading,\n              error: viewModel.error,\n              currentPage: viewModel.currentPage,\n              totalPages: viewModel.totalPages,\n              onPageChanged: (page) => viewModel.loadPage(page),\n              onRefresh: () => viewModel.refresh(),\n              onRetry: () => viewModel.refresh(),\n              layoutStrategy: _layoutStrategy,\n              scrollController: _scrollController,\n            ),\n            // 筛选面板\n            Positioned(\n              top: 0,\n              left: 0,\n              right: 0,\n              child: AnimatedSlide(\n                duration: const Duration(milliseconds: 200),\n                curve: Curves.easeInOut,\n                offset: Offset(0, viewModel.filterPanelExpanded ? 0 : -1),\n                child: Container(\n                  decoration: BoxDecoration(\n                    color: Theme.of(context).colorScheme.surface,\n                    boxShadow: [\n                      BoxShadow(\n                        color: Colors.black.withOpacity(0.05),\n                        blurRadius: 8,\n                        spreadRadius: 1,\n                        offset: const Offset(0, 1),\n                      ),\n                    ],\n                  ),\n                  child: FilterPanel(\n                    hasSubtitle: viewModel.hasSubtitle,\n                    onSubtitleChanged: viewModel.updateSubtitle,\n                    orderField: viewModel.filterState.orderField,\n                    isDescending: viewModel.filterState.isDescending,\n                    onOrderFieldChanged: viewModel.updateOrderField,\n                    onSortDirectionChanged: viewModel.updateSortDirection,\n                  ),\n                ),\n              ),\n            ),\n          ],\n        );\n      },\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/contents/playlists/playlist_works_view.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:provider/provider.dart';\nimport 'package:asmrapp/data/models/my_lists/my_playlists/playlist.dart';\nimport 'package:asmrapp/presentation/viewmodels/playlist_works_viewmodel.dart';\nimport 'package:asmrapp/presentation/viewmodels/playlists_viewmodel.dart';\nimport 'package:asmrapp/widgets/work_grid/enhanced_work_grid_view.dart';\nimport 'package:asmrapp/presentation/layouts/work_layout_strategy.dart';\n\nclass PlaylistWorksView extends StatelessWidget {\n  final Playlist playlist;\n  final VoidCallback onBack;\n  final WorkLayoutStrategy _layoutStrategy = const WorkLayoutStrategy();\n\n  const PlaylistWorksView({\n    super.key,\n    required this.playlist,\n    required this.onBack,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final playlistsViewModel = context.read<PlaylistsViewModel>();\n    \n    return ChangeNotifierProvider(\n      create: (_) => PlaylistWorksViewModel(playlist)..loadWorks(),\n      child: Consumer<PlaylistWorksViewModel>(\n        builder: (context, viewModel, child) {\n          return Column(\n            children: [\n              Material(\n                elevation: 2,\n                child: Container(\n                  padding: const EdgeInsets.all(8),\n                  child: Row(\n                    children: [\n                      IconButton(\n                        icon: const Icon(Icons.arrow_back),\n                        onPressed: onBack,\n                      ),\n                      Expanded(\n                        child: Text(\n                          playlistsViewModel.getDisplayName(playlist.name),\n                          style: Theme.of(context).textTheme.titleMedium,\n                          overflow: TextOverflow.ellipsis,\n                        ),\n                      ),\n                    ],\n                  ),\n                ),\n              ),\n              Expanded(\n                child: EnhancedWorkGridView(\n                  works: viewModel.works,\n                  isLoading: viewModel.isLoading,\n                  error: viewModel.error,\n                  onRetry: () => viewModel.refresh(),\n                  onRefresh: () => viewModel.refresh(),\n                  currentPage: viewModel.currentPage,\n                  totalPages: viewModel.totalPages,\n                  onPageChanged: (page) => viewModel.loadWorks(page: page),\n                  layoutStrategy: _layoutStrategy,\n                  emptyMessage: '暂无作品',\n                ),\n              ),\n            ],\n          );\n        },\n      ),\n    );\n  }\n} "
  },
  {
    "path": "lib/screens/contents/playlists/playlists_list_view.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:provider/provider.dart';\nimport 'package:asmrapp/presentation/viewmodels/playlists_viewmodel.dart';\nimport 'package:asmrapp/data/models/my_lists/my_playlists/playlist.dart';\nimport 'package:asmrapp/widgets/pagination_controls.dart';\n\nclass PlaylistsListView extends StatelessWidget {\n  final Function(Playlist) onPlaylistSelected;\n\n  const PlaylistsListView({\n    super.key,\n    required this.onPlaylistSelected,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return Consumer<PlaylistsViewModel>(\n      builder: (context, viewModel, child) {\n        if (viewModel.isLoading && viewModel.playlists.isEmpty) {\n          return const Center(child: CircularProgressIndicator());\n        }\n\n        if (viewModel.error != null && viewModel.playlists.isEmpty) {\n          return Center(\n            child: Column(\n              mainAxisAlignment: MainAxisAlignment.center,\n              children: [\n                Text(viewModel.error!),\n                const SizedBox(height: 16),\n                ElevatedButton(\n                  onPressed: viewModel.refresh,\n                  child: const Text('重试'),\n                ),\n              ],\n            ),\n          );\n        }\n\n        return RefreshIndicator(\n          onRefresh: viewModel.refresh,\n          child: Column(\n            children: [\n              Expanded(\n                child: ListView.builder(\n                  itemCount: viewModel.playlists.length,\n                  itemBuilder: (context, index) {\n                    final playlist = viewModel.playlists[index];\n                    return ListTile(\n                      leading: const Icon(Icons.playlist_play),\n                      title: Text(viewModel.getDisplayName(playlist.name)),\n                      subtitle: Text('${playlist.worksCount ?? 0} 个作品'),\n                      onTap: () => onPlaylistSelected(playlist),\n                    );\n                  },\n                ),\n              ),\n              if (viewModel.playlists.isNotEmpty)\n                PaginationControls(\n                  currentPage: viewModel.currentPage,\n                  totalPages: viewModel.totalPages ?? 1,\n                  isLoading: viewModel.isLoading,\n                  onPageChanged: (page) => viewModel.loadPlaylists(page: page),\n                ),\n            ],\n          ),\n        );\n      },\n    );\n  }\n} "
  },
  {
    "path": "lib/screens/contents/playlists_content.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:provider/provider.dart';\nimport 'package:asmrapp/data/models/my_lists/my_playlists/playlist.dart';\nimport 'package:asmrapp/screens/contents/playlists/playlists_list_view.dart';\nimport 'package:asmrapp/screens/contents/playlists/playlist_works_view.dart';\nimport 'package:asmrapp/presentation/viewmodels/playlists_viewmodel.dart';\n\nclass PlaylistsContent extends StatefulWidget {\n  const PlaylistsContent({super.key});\n\n  @override\n  State<PlaylistsContent> createState() => _PlaylistsContentState();\n}\n\nclass _PlaylistsContentState extends State<PlaylistsContent> with AutomaticKeepAliveClientMixin {\n  Playlist? _selectedPlaylist;\n\n  @override\n  bool get wantKeepAlive => true;\n\n  void _handlePlaylistSelected(Playlist playlist) {\n    setState(() {\n      _selectedPlaylist = playlist;\n    });\n  }\n\n  void _handleBack() {\n    setState(() {\n      _selectedPlaylist = null;\n    });\n  }\n\n  Future<bool> _onWillPop() async {\n    if (_selectedPlaylist != null) {\n      _handleBack();\n      return false;\n    }\n    return true;\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    super.build(context);\n    \n    return PopScope(\n      canPop: _selectedPlaylist == null,\n      onPopInvokedWithResult: (didPop, result) {\n        if (!didPop) {\n          _handleBack();\n        }\n      },\n      child: _selectedPlaylist != null\n          ? PlaylistWorksView(\n              playlist: _selectedPlaylist!,\n              onBack: _handleBack,\n            )\n          : PlaylistsListView(\n              onPlaylistSelected: _handlePlaylistSelected,\n            ),\n    );\n  }\n}"
  },
  {
    "path": "lib/screens/contents/popular_content.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:provider/provider.dart';\nimport 'package:asmrapp/presentation/viewmodels/popular_viewmodel.dart';\nimport 'package:asmrapp/presentation/layouts/work_layout_strategy.dart';\nimport 'package:asmrapp/widgets/work_grid/enhanced_work_grid_view.dart';\nimport 'package:asmrapp/widgets/filter/filter_with_keyword.dart';\n\nclass PopularContent extends StatefulWidget {\n  const PopularContent({super.key});\n\n  @override\n  State<PopularContent> createState() => _PopularContentState();\n}\n\nclass _PopularContentState extends State<PopularContent> with AutomaticKeepAliveClientMixin {\n  final _layoutStrategy = const WorkLayoutStrategy();\n  final _scrollController = ScrollController();\n\n  @override\n  bool get wantKeepAlive => true;\n\n  @override\n  void initState() {\n    super.initState();\n    _scrollController.addListener(_onScroll);\n  }\n\n  @override\n  void dispose() {\n    _scrollController.dispose();\n    super.dispose();\n  }\n\n  void _onScroll() {\n    if (_scrollController.position.pixels != _scrollController.position.minScrollExtent) {\n      final viewModel = context.read<PopularViewModel>();\n      if (viewModel.filterPanelExpanded) {\n        viewModel.closeFilterPanel();\n      }\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    super.build(context);\n    return Consumer<PopularViewModel>(\n      builder: (context, viewModel, child) {\n        return Stack(\n          children: [\n            // 作品列表\n            EnhancedWorkGridView(\n              works: viewModel.works,\n              isLoading: viewModel.isLoading,\n              error: viewModel.error,\n              currentPage: viewModel.currentPage,\n              totalPages: viewModel.totalPages,\n              onPageChanged: (page) => viewModel.loadPage(page),\n              onRefresh: () => viewModel.loadPopular(refresh: true),\n              onRetry: () => viewModel.loadPopular(refresh: true),\n              layoutStrategy: _layoutStrategy,\n              scrollController: _scrollController,\n            ),\n            // 筛选面板\n            Positioned(\n              top: 0,\n              left: 0,\n              right: 0,\n              child: AnimatedSlide(\n                duration: const Duration(milliseconds: 200),\n                curve: Curves.easeInOut,\n                offset: Offset(0, viewModel.filterPanelExpanded ? 0 : -1),\n                child: FilterWithKeyword(\n                  hasSubtitle: viewModel.hasSubtitle,\n                  onSubtitleChanged: (_) => viewModel.toggleSubtitleFilter(),\n                ),\n              ),\n            ),\n          ],\n        );\n      },\n    );\n  }\n} "
  },
  {
    "path": "lib/screens/contents/recommend_content.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:provider/provider.dart';\nimport 'package:asmrapp/presentation/viewmodels/recommend_viewmodel.dart';\nimport 'package:asmrapp/presentation/layouts/work_layout_strategy.dart';\nimport 'package:asmrapp/widgets/work_grid/enhanced_work_grid_view.dart';\nimport 'package:asmrapp/presentation/viewmodels/auth_viewmodel.dart';\nimport 'package:asmrapp/widgets/filter/filter_with_keyword.dart';\n\nclass RecommendContent extends StatefulWidget {\n  const RecommendContent({super.key});\n\n  @override\n  State<RecommendContent> createState() => _RecommendContentState();\n}\n\nclass _RecommendContentState extends State<RecommendContent> with AutomaticKeepAliveClientMixin {\n  final _layoutStrategy = const WorkLayoutStrategy();\n  final _scrollController = ScrollController();\n\n  @override\n  bool get wantKeepAlive => true;\n\n  void _onScroll() {\n    if (_scrollController.position.pixels != _scrollController.position.minScrollExtent) {\n      final viewModel = context.read<RecommendViewModel>();\n      if (viewModel.filterPanelExpanded) {\n        viewModel.closeFilterPanel();\n      }\n    }\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    _scrollController.addListener(_onScroll);\n    // 初始加载\n    WidgetsBinding.instance.addPostFrameCallback((_) {\n      context.read<RecommendViewModel>().loadRecommendations();\n    });\n  }\n\n  @override\n  void dispose() {\n    _scrollController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    super.build(context);\n    return Consumer<RecommendViewModel>(\n      builder: (context, viewModel, child) {\n        return Stack(\n          children: [\n            // 作品列表\n            EnhancedWorkGridView(\n              works: viewModel.works,\n              isLoading: viewModel.isLoading,\n              error: viewModel.error,\n              currentPage: viewModel.currentPage,\n              totalPages: viewModel.totalPages,\n              onPageChanged: (page) => viewModel.loadPage(page),\n              onRefresh: () => viewModel.loadRecommendations(refresh: true),\n              onRetry: () => viewModel.loadRecommendations(refresh: true),\n              layoutStrategy: _layoutStrategy,\n              scrollController: _scrollController,\n            ),\n            // 筛选面板\n            Positioned(\n              top: 0,\n              left: 0,\n              right: 0,\n              child: AnimatedSlide(\n                duration: const Duration(milliseconds: 200),\n                curve: Curves.easeInOut,\n                offset: Offset(0, viewModel.filterPanelExpanded ? 0 : -1),\n                child: FilterWithKeyword(\n                  hasSubtitle: viewModel.hasSubtitle,\n                  onSubtitleChanged: (_) => viewModel.toggleSubtitleFilter(),\n                ),\n              ),\n            ),\n          ],\n        );\n      },\n    );\n  }\n} "
  },
  {
    "path": "lib/screens/detail_screen.dart",
    "content": "import 'package:asmrapp/widgets/mini_player/mini_player.dart';\nimport 'package:flutter/material.dart';\nimport 'package:provider/provider.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/widgets/detail/work_cover.dart';\nimport 'package:asmrapp/widgets/detail/work_info.dart';\nimport 'package:asmrapp/widgets/detail/work_files_list.dart';\nimport 'package:asmrapp/widgets/detail/work_files_skeleton.dart';\nimport 'package:asmrapp/presentation/viewmodels/detail_viewmodel.dart';\nimport 'package:asmrapp/widgets/detail/work_action_buttons.dart';\nimport 'package:asmrapp/screens/similar_works_screen.dart';\n\nclass DetailScreen extends StatelessWidget {\n  final Work work;\n  final bool fromPlayer;\n\n  const DetailScreen({\n    super.key,\n    required this.work,\n    this.fromPlayer = false,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return ChangeNotifierProvider(\n      create: (_) => DetailViewModel(\n        work: work,\n      )..loadFiles(),\n      child: Scaffold(\n        appBar: AppBar(\n          title: Text(work.sourceId ?? ''),\n        ),\n        body: SingleChildScrollView(\n          padding: const EdgeInsets.only(bottom: MiniPlayer.height),\n          child: Column(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: [\n              WorkCover(\n                imageUrl: work.mainCoverUrl ?? '',\n                workId: work.id ?? 0,\n                sourceId: work.sourceId ?? '',\n                releaseDate: work.release,\n                heroTag: 'work-cover-${work.id}',\n              ),\n              WorkInfo(work: work),\n              Consumer<DetailViewModel>(\n                builder: (context, viewModel, _) => WorkActionButtons(\n                  hasRecommendations: viewModel.hasRecommendations,\n                  checkingRecommendations: viewModel.checkingRecommendations,\n                  onRecommendationsTap: () {\n                    Navigator.of(context).push(\n                      PageRouteBuilder(\n                        pageBuilder: (context, animation, secondaryAnimation) =>\n                            SimilarWorksScreen(work: work),\n                        transitionsBuilder: (context, animation, secondaryAnimation, child) {\n                          const begin = Offset(1.0, 0.0);\n                          const end = Offset.zero;\n                          const curve = Curves.easeInOut;\n                          var tween = Tween(begin: begin, end: end).chain(\n                            CurveTween(curve: curve),\n                          );\n                          return SlideTransition(\n                            position: animation.drive(tween),\n                            child: child,\n                          );\n                        },\n                      ),\n                    );\n                  },\n                  onFavoriteTap: () => viewModel.showPlaylistsDialog(context),\n                  loadingFavorite: viewModel.loadingFavorite,\n                  onMarkTap: () => viewModel.showMarkDialog(context),\n                  currentMarkStatus: viewModel.currentMarkStatus,\n                  loadingMark: viewModel.loadingMark,\n                ),\n              ),\n              Consumer<DetailViewModel>(\n                builder: (context, viewModel, _) {\n                  if (viewModel.isLoading) {\n                    return const WorkFilesSkeleton();\n                  }\n\n                  if (viewModel.error != null) {\n                    return Center(\n                      child: Text(\n                        viewModel.error!,\n                        style: TextStyle(\n                            color: Theme.of(context).colorScheme.error),\n                      ),\n                    );\n                  }\n\n                  if (viewModel.files != null) {\n                    return WorkFilesList(\n                      files: viewModel.files!,\n                      onFileTap: (file) async {\n                        try {\n                          await viewModel.playFile(file, context);\n                        } catch (e) {\n                          if (context.mounted) {\n                            ScaffoldMessenger.of(context).showSnackBar(\n                              SnackBar(content: Text('播放失败: $e')),\n                            );\n                          }\n                        }\n                      },\n                    );\n                  }\n\n                  return const SizedBox.shrink();\n                },\n              ),\n            ],\n          ),\n        ),\n        bottomSheet: const MiniPlayer(),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/docs/main_screen.md",
    "content": "# 应用架构说明\n\n## MainScreen 架构\n\n### 概述\nMainScreen 采用集中式的状态管理架构，作为应用的主要页面容器，它负责：\n1. 管理所有主要页面的 ViewModel\n2. 提供统一的状态管理入口\n3. 确保 ViewModel 的单一实例\n\n### 核心原则\n\n1. **ViewModel 单一实例**\n   - 所有页面的 ViewModel 都在 MainScreen 中初始化\n   - 子页面通过 Provider 获取 ViewModel，不创建自己的实例\n   - 确保状态的一致性和可预测性\n\n2. **状态提供机制**\n   - 使用 MultiProvider 在顶层提供所有 ViewModel\n   - 子页面使用 context.read 或 Provider.of 获取 ViewModel\n   - 避免重复创建 ViewModel 实例\n\n3. **生命周期管理**\n   - MainScreen 负责 ViewModel 的创建和销毁\n   - 在 initState 中初始化所有 ViewModel\n   - 在 dispose 中释放所有资源\n\n### 子页面开发指南\n\n1. **ViewModel 访问**   ```dart\n   // 推荐使用 context.read 获取 ViewModel\n   final viewModel = context.read<HomeViewModel>();\n   \n   // 或者使用 Provider.of（效果相同）\n   final viewModel = Provider.of<HomeViewModel>(context, listen: false);   ```\n\n2. **状态监听**   ```dart\n   // 使用 Consumer 监听状态变化\n   Consumer<HomeViewModel>(\n     builder: (context, viewModel, child) {\n       // 使用 viewModel 的状态\n     },\n   )   ```\n\n3. **注意事项**\n   - 不要在子页面中创建新的 ViewModel 实例\n   - 使用 AutomaticKeepAliveClientMixin 保持页面状态\n   - 在 initState 中进行必要的初始化\n\n### 常见问题\n\n1. **重复实例问题**\n   - 症状：状态更新不生效\n   - 原因：子页面创建了新的 ViewModel 实例\n   - 解决：使用 MainScreen 提供的 ViewModel\n\n2. **状态同步问题**\n   - 症状：不同页面状态不同步\n   - 原因：使用了多个 ViewModel 实例\n   - 解决：确保使用 MainScreen 提供的单一实例"
  },
  {
    "path": "lib/screens/favorites_screen.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:provider/provider.dart';\nimport 'package:asmrapp/widgets/drawer_menu.dart';\nimport 'package:asmrapp/presentation/viewmodels/favorites_viewmodel.dart';\nimport 'package:asmrapp/presentation/layouts/work_layout_strategy.dart';\nimport 'package:asmrapp/widgets/pagination_controls.dart';\nimport 'package:asmrapp/widgets/work_grid_view.dart';\n\nclass FavoritesScreen extends StatefulWidget {\n  const FavoritesScreen({super.key});\n\n  @override\n  State<FavoritesScreen> createState() => _FavoritesScreenState();\n}\n\nclass _FavoritesScreenState extends State<FavoritesScreen> {\n  final _layoutStrategy = const WorkLayoutStrategy();\n  final _scrollController = ScrollController();\n  late FavoritesViewModel _viewModel;\n\n  @override\n  void initState() {\n    super.initState();\n    _viewModel = FavoritesViewModel();\n    _viewModel.loadFavorites();\n  }\n\n  @override\n  void dispose() {\n    _scrollController.dispose();\n    super.dispose();\n  }\n\n  void _onPageChanged(int page) async {\n    await _viewModel.loadPage(page);\n    if (_scrollController.hasClients) {\n      _scrollController.animateTo(\n        0,\n        duration: const Duration(milliseconds: 300),\n        curve: Curves.easeOut,\n      );\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return ChangeNotifierProvider.value(\n      value: _viewModel,\n      child: Scaffold(\n        appBar: AppBar(\n          title: const Text('我的收藏'),\n        ),\n        drawer: const DrawerMenu(),\n        body: Consumer<FavoritesViewModel>(\n          builder: (context, viewModel, child) {\n            return Column(\n              children: [\n                Expanded(\n                  child: WorkGridView(\n                    works: viewModel.works,\n                    isLoading: viewModel.isLoading,\n                    error: viewModel.error,\n                    onRetry: () => viewModel.loadFavorites(),\n                    layoutStrategy: _layoutStrategy,\n                    scrollController: _scrollController,\n                    bottomWidget: viewModel.works.isNotEmpty\n                        ? PaginationControls(\n                            currentPage: viewModel.currentPage,\n                            totalPages: viewModel.totalPages ?? 1,\n                            onPageChanged: _onPageChanged,\n                            isLoading: viewModel.isLoading,\n                          )\n                        : null,\n                  ),\n                ),\n              ],\n            );\n          },\n        ),\n      ),\n    );\n  }\n} "
  },
  {
    "path": "lib/screens/main_screen.dart",
    "content": "import 'package:asmrapp/screens/contents/playlists_content.dart';\nimport 'package:flutter/material.dart';\nimport 'package:asmrapp/widgets/mini_player/mini_player.dart';\nimport 'package:asmrapp/widgets/drawer_menu.dart';\nimport 'package:asmrapp/screens/contents/home_content.dart';\nimport 'package:asmrapp/screens/contents/recommend_content.dart';\nimport 'package:asmrapp/screens/contents/popular_content.dart';\nimport 'package:asmrapp/screens/search_screen.dart';\nimport 'package:provider/provider.dart';\nimport 'package:asmrapp/presentation/viewmodels/home_viewmodel.dart';\nimport 'package:asmrapp/presentation/viewmodels/popular_viewmodel.dart';\nimport 'package:asmrapp/presentation/viewmodels/recommend_viewmodel.dart';\nimport 'package:asmrapp/presentation/viewmodels/auth_viewmodel.dart';\nimport 'package:asmrapp/presentation/viewmodels/playlists_viewmodel.dart';\n\n/// MainScreen 是应用的主界面，负责管理底部导航栏和对应的内容页面。\n/// 它采用了集中式的状态管理架构，所有子页面的 ViewModel 都在这里初始化和提供。\n///\n/// 架构说明：\n/// 1. ViewModel 初始化：所有页面的 ViewModel 都在 MainScreen 中初始化，确保单一实例\n/// 2. 状态提供：通过 MultiProvider 将 ViewModel 提供给整个子树\n/// 3. 生命周期管理：负责所有 ViewModel 的创建和销毁\nclass MainScreen extends StatefulWidget {\n  const MainScreen({super.key});\n\n  @override\n  State<MainScreen> createState() => _MainScreenState();\n}\n\nclass _MainScreenState extends State<MainScreen> {\n  final _pageController = PageController(initialPage: 1);\n  int _currentIndex = 1;\n\n  // 集中管理所有页面的 ViewModel\n  // 这些 ViewModel 将通过 Provider 提供给子页面\n  late final HomeViewModel _homeViewModel;\n  late final PopularViewModel _popularViewModel;\n  late final RecommendViewModel _recommendViewModel;\n  late final PlaylistsViewModel _playlistsViewModel;\n\n  final _titles = const ['收藏', '主页', '为你推荐', '热门作品'];\n\n  // 页面内容列表\n  // 注意：这些页面不应该创建自己的 ViewModel 实例\n  // 而是应该通过 Provider.of 或 context.read 获取 MainScreen 提供的实例\n  final _pages = const [\n    PlaylistsContent(),\n    HomeContent(),\n    RecommendContent(),\n    PopularContent(),\n  ];\n\n  @override\n  void initState() {\n    super.initState();\n    // 初始化所有 ViewModel\n    // 注意初始化顺序，如果有依赖关系需要先初始化依赖项\n    _homeViewModel = HomeViewModel();\n    _popularViewModel = PopularViewModel();\n    _recommendViewModel = RecommendViewModel(\n      Provider.of<AuthViewModel>(context, listen: false),\n    );\n    _playlistsViewModel = PlaylistsViewModel();\n  }\n\n  void _onPageChanged(int index) {\n    setState(() {\n      _currentIndex = index;\n    });\n  }\n\n  void _onTabTapped(int index) {\n    _pageController.animateToPage(\n      index,\n      duration: const Duration(milliseconds: 300),\n      curve: Curves.easeInOut,\n    );\n  }\n\n  @override\n  void dispose() {\n    // 确保所有 ViewModel 都被正确释放\n    _pageController.dispose();\n    _homeViewModel.dispose();\n    _popularViewModel.dispose();\n    _recommendViewModel.dispose();\n    _playlistsViewModel.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return MultiProvider(\n      // 通过 MultiProvider 将所有 ViewModel 提供给子树\n      // 这样子页面就可以通过 Provider.of 或 context.read 获取对应的 ViewModel\n      providers: [\n        ChangeNotifierProvider.value(value: _homeViewModel),\n        ChangeNotifierProvider.value(value: _popularViewModel),\n        ChangeNotifierProvider.value(value: _recommendViewModel),\n        ChangeNotifierProvider.value(value: _playlistsViewModel),\n      ],\n      child: Builder(\n        builder: (context) {\n          // 根据当前页面获取对应的总数\n          final totalCount = _currentIndex == 1\n              ? context.watch<HomeViewModel>().pagination?.totalCount\n              : _currentIndex == 2\n                  ? context.watch<RecommendViewModel>().pagination?.totalCount\n                  : _currentIndex == 3\n                      ? context.watch<PopularViewModel>().pagination?.totalCount\n                      : null;\n\n          // 构建标题文本\n          final title = totalCount != null\n              ? '${_titles[_currentIndex]} (${totalCount})'\n              : _titles[_currentIndex];\n\n          return Scaffold(\n            appBar: AppBar(\n              title: Text(title),\n              actions: [\n                IconButton(\n                  icon: const Icon(Icons.filter_list),\n                  onPressed: () {\n                    if (_currentIndex == 1) {\n                      context.read<HomeViewModel>().toggleFilterPanel();\n                    } else if (_currentIndex == 2) {\n                      context.read<RecommendViewModel>().toggleFilterPanel();\n                    } else if (_currentIndex == 3) {\n                      context.read<PopularViewModel>().toggleFilterPanel();\n                    }\n                  },\n                ),\n                IconButton(\n                  icon: const Icon(Icons.search),\n                  onPressed: () {\n                    Navigator.push(\n                      context,\n                      MaterialPageRoute(\n                        builder: (context) => const SearchScreen(),\n                      ),\n                    );\n                  },\n                ),\n              ],\n            ),\n            drawer: const DrawerMenu(),\n            body: PageView(\n              controller: _pageController,\n              physics: const ClampingScrollPhysics(),\n              onPageChanged: _onPageChanged,\n              children: _pages,\n            ),\n            bottomNavigationBar: Column(\n              mainAxisSize: MainAxisSize.min,\n              children: [\n                const MiniPlayer(),\n                NavigationBar(\n                  height: 60,\n                  labelBehavior: NavigationDestinationLabelBehavior.alwaysHide,\n                  backgroundColor: Theme.of(context).colorScheme.surface,\n                  elevation: 0,\n                  selectedIndex: _currentIndex,\n                  onDestinationSelected: _onTabTapped,\n                  destinations: const [\n                    NavigationDestination(\n                      icon: Icon(Icons.favorite_outline),\n                      selectedIcon: Icon(Icons.favorite),\n                      label: '收藏',\n                    ),\n                    NavigationDestination(\n                      icon: Icon(Icons.home_outlined),\n                      selectedIcon: Icon(Icons.home),\n                      label: '主页',\n                    ),\n                    NavigationDestination(\n                      icon: Icon(Icons.recommend_outlined),\n                      selectedIcon: Icon(Icons.recommend),\n                      label: '推荐',\n                    ),\n                    NavigationDestination(\n                      icon: Icon(Icons.trending_up_outlined),\n                      selectedIcon: Icon(Icons.trending_up),\n                      label: '热门',\n                    ),\n                  ],\n                ),\n              ],\n            ),\n          );\n        },\n      ),\n    );\n  }\n} "
  },
  {
    "path": "lib/screens/player_screen.dart",
    "content": "import 'package:asmrapp/core/platform/lyric_overlay_manager.dart';\nimport 'package:flutter/material.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:asmrapp/presentation/viewmodels/player_viewmodel.dart';\nimport 'package:asmrapp/widgets/player/player_controls.dart';\nimport 'package:asmrapp/widgets/player/player_progress.dart';\nimport 'package:asmrapp/widgets/player/player_cover.dart';\nimport 'package:asmrapp/screens/detail_screen.dart';\nimport 'package:asmrapp/widgets/lyrics/components/player_lyric_view.dart';\nimport 'package:asmrapp/widgets/player/player_work_info.dart';\nimport 'package:asmrapp/core/platform/wakelock_controller.dart';\n\nclass PlayerScreen extends StatefulWidget {\n  const PlayerScreen({super.key});\n\n  @override\n  State<PlayerScreen> createState() => _PlayerScreenState();\n}\n\nclass _PlayerScreenState extends State<PlayerScreen> {\n  bool _showLyrics = false;\n  bool _canSwitchView = true;\n  late final PlayerViewModel _viewModel;\n\n  @override\n  void initState() {\n    super.initState();\n    _viewModel = GetIt.I<PlayerViewModel>();\n  }\n\n  Widget _buildContent() {\n    return AnimatedSwitcher(\n      duration: const Duration(milliseconds: 400),\n      switchInCurve: Curves.easeOutQuart,\n      switchOutCurve: Curves.easeInQuart,\n      transitionBuilder: (Widget child, Animation<double> animation) {\n        final isLyrics = (child as dynamic).key == const ValueKey('lyrics');\n        \n        return FadeTransition(\n          opacity: animation,\n          child: SlideTransition(\n            position: Tween<Offset>(\n              begin: Offset(0, isLyrics ? 0.1 : -0.1),\n              end: Offset.zero,\n            ).animate(animation),\n            child: ScaleTransition(\n              scale: Tween<double>(\n                begin: 0.95,\n                end: 1.0,\n              ).animate(animation),\n              child: child,\n            ),\n          ),\n        );\n      },\n      layoutBuilder: (currentChild, previousChildren) {\n        return Stack(\n          alignment: Alignment.center,\n          children: <Widget>[\n            ...previousChildren,\n            if (currentChild != null) currentChild,\n          ],\n        );\n      },\n      child: _showLyrics\n          ? LayoutBuilder(\n              key: const ValueKey('lyrics'),\n              builder: (context, constraints) {\n                return PlayerLyricView(\n                  onScrollStateChanged: (canSwitch) {\n                    setState(() {\n                      _canSwitchView = canSwitch;\n                    });\n                  },\n                );\n              },\n            )\n          : ListenableBuilder(\n              listenable: _viewModel,\n              builder: (context, _) {\n                return Column(\n                  key: const ValueKey('cover'),\n                  mainAxisAlignment: MainAxisAlignment.center,\n                  children: [\n                    const SizedBox(height: 32),\n                    Padding(\n                      padding: const EdgeInsets.symmetric(horizontal: 32),\n                      child: Hero(\n                        tag: 'mini-player-cover',\n                        child: PlayerCover(\n                          coverUrl: _viewModel.currentTrackInfo?.coverUrl,\n                        ),\n                      ),\n                    ),\n                    const SizedBox(height: 32),\n                    Padding(\n                      padding: const EdgeInsets.symmetric(horizontal: 32),\n                      child: Column(\n                        children: [\n                          Hero(\n                            tag: 'player-title',\n                            child: Material(\n                              color: Colors.transparent,\n                              child: Text(\n                                _viewModel.currentTrackInfo?.title ?? '未在播放',\n                                style: Theme.of(context).textTheme.titleLarge?.copyWith(\n                                      fontWeight: FontWeight.w600,\n                                    ),\n                                textAlign: TextAlign.center,\n                              ),\n                            ),\n                          ),\n                          const SizedBox(height: 8),\n                          if (_viewModel.currentTrackInfo?.artist != null)\n                            Text(\n                              _viewModel.currentTrackInfo!.artist,\n                              style: Theme.of(context).textTheme.bodyLarge?.copyWith(\n                                    color: Theme.of(context)\n                                        .colorScheme\n                                        .onSurface\n                                        .withOpacity(0.7),\n                                  ),\n                              textAlign: TextAlign.center,\n                            ),\n                        ],\n                      ),\n                    ),\n                    const Spacer(),\n                    PlayerWorkInfo(context: _viewModel.currentContext),\n                  ],\n                );\n              },\n            ),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final lyricManager = GetIt.I<LyricOverlayManager>();\n    final wakeLockController = GetIt.I<WakeLockController>();\n\n    return Scaffold(\n      appBar: AppBar(\n        leading: IconButton(\n          icon: const Icon(Icons.expand_more),\n          onPressed: () {\n            Navigator.of(context).pop();\n          },\n        ),\n        actions: [\n          IconButton(\n            icon: const Icon(Icons.info_outline),\n            onPressed: () {\n              final currentWork = _viewModel.currentContext?.work;\n              if (currentWork != null) {\n                Navigator.of(context).push(\n                  MaterialPageRoute(\n                    builder: (context) => DetailScreen(\n                      work: currentWork,\n                      fromPlayer: true,\n                    ),\n                  ),\n                );\n              }\n            },\n          ),\n          IconButton(\n            icon: Icon(\n              lyricManager.isShowing ? Icons.lyrics : Icons.lyrics_outlined,\n            ),\n            onPressed: () => lyricManager.toggle(context),\n          ),\n          ListenableBuilder(\n            listenable: wakeLockController,\n            builder: (context, _) {\n              return IconButton(\n                icon: Icon(\n                  wakeLockController.enabled \n                    ? Icons.lightbulb\n                    : Icons.lightbulb_outline,\n                ),\n                tooltip: wakeLockController.enabled ? '关闭屏幕常亮' : '开启屏幕常亮',\n                onPressed: () => wakeLockController.toggle(),\n              );\n            },\n          ),\n        ],\n        backgroundColor: Colors.transparent,\n        elevation: 0,\n      ),\n      body: SafeArea(\n        child: Column(\n          children: [\n            Expanded(\n              child: GestureDetector(\n                onTap: () {\n                  if (_canSwitchView) {\n                    setState(() {\n                      _showLyrics = !_showLyrics;\n                    });\n                  }\n                },\n                behavior: HitTestBehavior.opaque,\n                child: _buildContent(),\n              ),\n            ),\n            Container(\n              padding: const EdgeInsets.fromLTRB(12, 0, 12, 32),\n              child: Column(\n                children: const [\n                  PlayerProgress(),\n                  SizedBox(height: 8),\n                  SizedBox(height: 8),\n                  PlayerControls(),\n                ],\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/search_screen.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:provider/provider.dart';\nimport 'package:asmrapp/presentation/viewmodels/search_viewmodel.dart';\nimport 'package:asmrapp/widgets/work_grid_view.dart';\nimport 'package:asmrapp/presentation/layouts/work_layout_strategy.dart';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:asmrapp/widgets/pagination_controls.dart';\n\nclass SearchScreen extends StatelessWidget {\n  final String? initialKeyword;\n\n  const SearchScreen({\n    super.key,\n    this.initialKeyword,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return ChangeNotifierProvider(\n      create: (_) => SearchViewModel(),\n      child: SearchScreenContent(initialKeyword: initialKeyword),\n    );\n  }\n}\n\nclass SearchScreenContent extends StatefulWidget {\n  final String? initialKeyword;\n\n  const SearchScreenContent({\n    super.key,\n    this.initialKeyword,\n  });\n\n  @override\n  State<SearchScreenContent> createState() => _SearchScreenContentState();\n}\n\nclass _SearchScreenContentState extends State<SearchScreenContent> {\n  late final TextEditingController _searchController;\n  final _layoutStrategy = const WorkLayoutStrategy();\n  final _scrollController = ScrollController();\n\n  @override\n  void initState() {\n    super.initState();\n    _searchController = TextEditingController(text: widget.initialKeyword);\n    \n    // 如果有初始关键词，自动执行搜索\n    if (widget.initialKeyword?.isNotEmpty == true) {\n      WidgetsBinding.instance.addPostFrameCallback((_) {\n        _onSearch();\n      });\n    }\n  }\n\n  @override\n  void dispose() {\n    _searchController.dispose();\n    _scrollController.dispose();\n    super.dispose();\n  }\n\n  void _onSearch() {\n    final keyword = _searchController.text.trim();\n    if (keyword.isEmpty) return;\n\n    AppLogger.debug('执行搜索: $keyword');\n    context.read<SearchViewModel>().search(keyword);\n  }\n\n  void _onPageChanged(int page) async {\n    final viewModel = context.read<SearchViewModel>();\n    await viewModel.loadPage(page);\n    if (_scrollController.hasClients) {\n      _scrollController.animateTo(\n        0,\n        duration: const Duration(milliseconds: 300),\n        curve: Curves.easeOut,\n      );\n    }\n  }\n\n  String _getOrderText(String order, String sort) {\n    switch (order) {\n      case 'create_date':\n        return sort == 'desc' ? '最新收录' : '最早收录';\n      case 'release':\n        return sort == 'desc' ? '发售日期倒序' : '发售日期顺序';\n      case 'dl_count':\n        return sort == 'desc' ? '销量倒序' : '销量顺序';\n      case 'price':\n        return sort == 'desc' ? '价格倒序' : '价格顺序';\n      case 'rate_average_2dp':\n        return '评价倒序';\n      case 'review_count':\n        return '评论数量倒序';\n      case 'id':\n        return sort == 'desc' ? 'RJ号倒序' : 'RJ号顺序';\n      case 'random':\n        return '随机排序';\n      default:\n        return '排序';\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      body: Column(\n        children: [\n          Container(\n            color: Theme.of(context).scaffoldBackgroundColor,\n            padding: EdgeInsets.only(\n              top: MediaQuery.of(context).padding.top + 8,\n              bottom: 8,\n            ),\n            child: Column(\n              children: [\n                Padding(\n                  padding: const EdgeInsets.symmetric(horizontal: 16),\n                  child: TextField(\n                    controller: _searchController,\n                    decoration: InputDecoration(\n                      hintText: '搜索...',\n                      filled: true,\n                      fillColor: Theme.of(context)\n                          .colorScheme\n                          .surfaceContainerHighest\n                          .withOpacity(0.5),\n                      border: OutlineInputBorder(\n                        borderRadius: BorderRadius.circular(24),\n                        borderSide: BorderSide.none,\n                      ),\n                      contentPadding: const EdgeInsets.symmetric(\n                          horizontal: 16, vertical: 8),\n                      suffixIcon: _searchController.text.isNotEmpty\n                          ? IconButton(\n                              icon: const Icon(Icons.clear, size: 20),\n                              onPressed: () {\n                                _searchController.clear();\n                                context.read<SearchViewModel>().clear();\n                              },\n                            )\n                          : null,\n                      prefixIcon: const Icon(Icons.search, size: 20),\n                      isDense: true,\n                    ),\n                    textInputAction: TextInputAction.search,\n                    onSubmitted: (_) => _onSearch(),\n                    onChanged: (value) => setState(() {}),\n                  ),\n                ),\n                const SizedBox(height: 8),\n                Padding(\n                  padding: const EdgeInsets.symmetric(horizontal: 16),\n                  child: Row(\n                    children: [\n                      // 字幕选项\n                      Consumer<SearchViewModel>(\n                        builder: (context, viewModel, _) => FilterChip(\n                          label: const Text('字幕'),\n                          selected: viewModel.hasSubtitle,\n                          onSelected: (_) => viewModel.toggleSubtitle(),\n                          showCheckmark: true,\n                        ),\n                      ),\n                      const SizedBox(width: 8),\n                      // 排序选项\n                      Consumer<SearchViewModel>(\n                        builder: (context, viewModel, _) =>\n                            PopupMenuButton<(String, String)>(\n                          child: Chip(\n                            label: Text(\n                                _getOrderText(viewModel.order, viewModel.sort)),\n                            deleteIcon:\n                                const Icon(Icons.arrow_drop_down, size: 18),\n                            onDeleted: null,\n                          ),\n                          itemBuilder: (context) => [\n                            const PopupMenuItem(\n                              value: ('create_date', 'desc'),\n                              child: Text('最新收录'),\n                            ),\n                            const PopupMenuItem(\n                              value: ('release', 'desc'),\n                              child: Text('发售日期倒序'),\n                            ),\n                            const PopupMenuItem(\n                              value: ('release', 'asc'),\n                              child: Text('发售日期顺序'),\n                            ),\n                            const PopupMenuItem(\n                              value: ('dl_count', 'desc'),\n                              child: Text('销量倒序'),\n                            ),\n                            const PopupMenuItem(\n                              value: ('price', 'asc'),\n                              child: Text('价格顺序'),\n                            ),\n                            const PopupMenuItem(\n                              value: ('price', 'desc'),\n                              child: Text('价格倒序'),\n                            ),\n                            const PopupMenuItem(\n                              value: ('rate_average_2dp', 'desc'),\n                              child: Text('评价倒序'),\n                            ),\n                            const PopupMenuItem(\n                              value: ('review_count', 'desc'),\n                              child: Text('评论数量倒序'),\n                            ),\n                            const PopupMenuItem(\n                              value: ('id', 'desc'),\n                              child: Text('RJ号倒序'),\n                            ),\n                            const PopupMenuItem(\n                              value: ('id', 'asc'),\n                              child: Text('RJ号顺序'),\n                            ),\n                            const PopupMenuItem(\n                              value: ('random', 'desc'),\n                              child: Text('随机排序'),\n                            ),\n                          ],\n                          onSelected: (value) =>\n                              viewModel.setOrder(value.$1, value.$2),\n                        ),\n                      ),\n                    ],\n                  ),\n                ),\n              ],\n            ),\n          ),\n          Expanded(\n            child: Consumer<SearchViewModel>(\n              builder: (context, viewModel, child) {\n                Widget? emptyWidget;\n                if (viewModel.works.isEmpty && viewModel.keyword.isEmpty) {\n                  emptyWidget = const Center(\n                    child: Text('输入关键词开始搜索'),\n                  );\n                } else if (viewModel.works.isEmpty) {\n                  emptyWidget = const Center(\n                    child: Text('没有找到相关结果'),\n                  );\n                }\n\n                return WorkGridView(\n                  works: viewModel.works,\n                  isLoading: viewModel.isLoading,\n                  error: viewModel.error,\n                  onRetry: _onSearch,\n                  customEmptyWidget: emptyWidget,\n                  layoutStrategy: _layoutStrategy,\n                  scrollController: _scrollController,\n                  bottomWidget: viewModel.works.isNotEmpty\n                      ? PaginationControls(\n                          currentPage: viewModel.currentPage,\n                          totalPages: viewModel.totalPages,\n                          isLoading: viewModel.isLoading,\n                          onPageChanged: _onPageChanged,\n                        )\n                      : null,\n                );\n              },\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screens/settings/cache_manager_screen.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:provider/provider.dart';\nimport 'package:asmrapp/presentation/viewmodels/settings/cache_manager_viewmodel.dart';\n\nclass CacheManagerScreen extends StatelessWidget {\n  const CacheManagerScreen({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return ChangeNotifierProvider(\n      create: (_) => CacheManagerViewModel()..loadCacheSize(),\n      child: Scaffold(\n        appBar: AppBar(\n          title: const Text('缓存管理'),\n        ),\n        body: Consumer<CacheManagerViewModel>(\n          builder: (context, viewModel, _) {\n            if (viewModel.isLoading) {\n              return const Center(child: CircularProgressIndicator());\n            }\n\n            if (viewModel.error != null) {\n              return Center(\n                child: Text(\n                  viewModel.error!,\n                  style: TextStyle(color: Theme.of(context).colorScheme.error),\n                ),\n              );\n            }\n\n            return ListView(\n              children: [\n                // 音频缓存\n                ListTile(\n                  title: const Text('音频缓存'),\n                  subtitle: Text(viewModel.audioCacheSizeFormatted),\n                  trailing: TextButton(\n                    onPressed: viewModel.isLoading \n                      ? null \n                      : () => viewModel.clearAudioCache(),\n                    child: const Text('清理'),\n                  ),\n                ),\n                const Divider(),\n                \n                // 字幕缓存\n                ListTile(\n                  title: const Text('字幕缓存'),\n                  subtitle: Text(viewModel.subtitleCacheSizeFormatted),\n                  trailing: TextButton(\n                    onPressed: viewModel.isLoading \n                      ? null \n                      : () => viewModel.clearSubtitleCache(),\n                    child: const Text('清理'),\n                  ),\n                ),\n                const Divider(),\n                \n                // 总缓存大小\n                ListTile(\n                  title: const Text('总缓存大小'),\n                  subtitle: Text(viewModel.totalCacheSizeFormatted),\n                  trailing: TextButton(\n                    onPressed: viewModel.isLoading \n                      ? null \n                      : () => viewModel.clearAllCache(),\n                    child: const Text('清理全部'),\n                  ),\n                ),\n                const Divider(),\n                \n                // 缓存说明\n                const ListTile(\n                  title: Text('缓存说明'),\n                  subtitle: Text(\n                    '缓存用于存储最近播放的音频文件和字幕文件，以提高再次播放时的加载速度。'\n                    '系统会自动清理过期和超量的缓存。'\n                  ),\n                ),\n              ],\n            );\n          },\n        ),\n      ),\n    );\n  }\n} "
  },
  {
    "path": "lib/screens/similar_works_screen.dart",
    "content": "import 'package:asmrapp/widgets/filter/filter_with_keyword.dart';\nimport 'package:flutter/material.dart';\nimport 'package:provider/provider.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/presentation/viewmodels/similar_works_viewmodel.dart';\nimport 'package:asmrapp/widgets/work_grid_view.dart';\nimport 'package:asmrapp/presentation/layouts/work_layout_strategy.dart';\nimport 'package:asmrapp/widgets/pagination_controls.dart';\n\nclass SimilarWorksScreen extends StatefulWidget {\n  final Work work;\n\n  const SimilarWorksScreen({\n    super.key,\n    required this.work,\n  });\n\n  @override\n  State<SimilarWorksScreen> createState() => _SimilarWorksScreenState();\n}\n\nclass _SimilarWorksScreenState extends State<SimilarWorksScreen> {\n  final _layoutStrategy = const WorkLayoutStrategy();\n  final _scrollController = ScrollController();\n  late SimilarWorksViewModel _viewModel;\n\n  @override\n  void initState() {\n    super.initState();\n    _viewModel = SimilarWorksViewModel(widget.work);\n    _scrollController.addListener(_onScroll);\n  }\n\n  @override\n  void dispose() {\n    _scrollController.removeListener(_onScroll);\n    _scrollController.dispose();\n    super.dispose();\n  }\n\n  void _onScroll() {\n    if (_scrollController.position.pixels != _scrollController.position.minScrollExtent) {\n      if (_viewModel.filterPanelExpanded) {\n        _viewModel.closeFilterPanel();\n      }\n    }\n  }\n\n  void _scrollToTop() {\n    if (_scrollController.hasClients) {\n      _scrollController.animateTo(\n        0,\n        duration: const Duration(milliseconds: 300),\n        curve: Curves.easeOut,\n      );\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return ChangeNotifierProvider.value(\n      value: _viewModel,\n      child: Scaffold(\n        appBar: AppBar(\n          title: const Text('相关推荐'),\n          actions: [\n            Consumer<SimilarWorksViewModel>(\n              builder: (context, viewModel, _) => IconButton(\n                icon: const Icon(Icons.filter_list),\n                onPressed: viewModel.toggleFilterPanel,\n              ),\n            ),\n          ],\n        ),\n        body: Consumer<SimilarWorksViewModel>(\n          builder: (context, viewModel, child) {\n            return Stack(\n              children: [\n                Column(\n                  children: [\n                    Expanded(\n                      child: WorkGridView(\n                        works: viewModel.works,\n                        isLoading: viewModel.isLoading,\n                        error: viewModel.error,\n                        onRetry: () => viewModel.loadSimilarWorks(),\n                        layoutStrategy: _layoutStrategy,\n                        scrollController: _scrollController,\n                        bottomWidget: viewModel.works.isNotEmpty\n                            ? PaginationControls(\n                                currentPage: viewModel.currentPage,\n                                totalPages: viewModel.totalPages ?? 1,\n                                onPageChanged: (page) {\n                                  viewModel.loadPage(page);\n                                  _scrollToTop();\n                                },\n                                isLoading: viewModel.isLoading,\n                              )\n                            : null,\n                      ),\n                    ),\n                  ],\n                ),\n                Positioned(\n                  top: 0,\n                  left: 0,\n                  right: 0,\n                  child: AnimatedSlide(\n                    duration: const Duration(milliseconds: 200),\n                    curve: Curves.easeInOut,\n                    offset: Offset(0, viewModel.filterPanelExpanded ? 0 : -1),\n                    child: FilterWithKeyword(\n                      hasSubtitle: viewModel.hasSubtitle,\n                      onSubtitleChanged: (_) => viewModel.toggleSubtitleFilter(),\n                    ),\n                  ),\n                ),\n              ],\n            );\n          },\n        ),\n      ),\n    );\n  }\n} "
  },
  {
    "path": "lib/utils/file_size_formatter.dart",
    "content": "class FileSizeFormatter {\n  static String format(int? size) {\n    if (size == null) return '';\n    const kb = 1024;\n    const mb = kb * 1024;\n    if (size > mb) {\n      return '${(size / mb).toStringAsFixed(2)} MB';\n    }\n    return '${(size / kb).toStringAsFixed(2)} KB';\n  }\n}\n"
  },
  {
    "path": "lib/utils/logger.dart",
    "content": "import 'package:logger/logger.dart';\n\nclass AppLogger {\n  static final Logger _logger = Logger(\n    printer: PrettyPrinter(\n      methodCount: 0,\n      errorMethodCount: 8,\n      lineLength: 120,\n      colors: true,\n      printEmojis: true,\n      printTime: true,\n    ),\n  );\n\n  static void init() {\n    Logger.level = Level.debug;\n  }\n\n  static void debug(String message) => _logger.d(message);\n  static void info(String message) => _logger.i(message);\n  static void warning(String message) => _logger.w(message);\n  static void error(String message, [Object? error, StackTrace? stackTrace]) =>\n      _logger.e(message, error: error, stackTrace: stackTrace);\n}\n"
  },
  {
    "path": "lib/widgets/common/tag_chip.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass TagChip extends StatelessWidget {\n  final String text;\n  final Color? backgroundColor;\n  final Color? textColor;\n  final VoidCallback? onTap;\n\n  const TagChip({\n    super.key,\n    required this.text,\n    this.backgroundColor,\n    this.textColor,\n    this.onTap,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return InkWell(\n      onTap: onTap,\n      borderRadius: BorderRadius.circular(16),\n      child: Container(\n        padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),\n        decoration: BoxDecoration(\n          color: backgroundColor ?? Theme.of(context).colorScheme.surfaceContainerHighest,\n          borderRadius: BorderRadius.circular(4),\n        ),\n        child: Text(\n          text,\n          style: Theme.of(context).textTheme.bodyMedium?.copyWith(\n                color: textColor ?? Theme.of(context).colorScheme.onSurfaceVariant,\n                fontSize: 13,\n              ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/detail/mark_selection_dialog.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/mark_status.dart';\n\nclass MarkSelectionDialog extends StatelessWidget {\n  final MarkStatus? currentStatus;\n  final Function(MarkStatus) onMarkSelected;\n  final bool loading;\n\n  const MarkSelectionDialog({\n    super.key,\n    this.currentStatus,\n    required this.onMarkSelected,\n    this.loading = false,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final isDark = Theme.of(context).brightness == Brightness.dark;\n    \n    return AlertDialog(\n      backgroundColor: isDark ? const Color(0xFF2C2C2C) : Colors.white,\n      title: Text(\n        '标记状态',\n        style: TextStyle(\n          color: isDark ? Colors.white70 : Colors.black87,\n        ),\n      ),\n      content: Column(\n        mainAxisSize: MainAxisSize.min,\n        children: MarkStatus.values.map((status) {\n          final isSelected = status == currentStatus;\n          return ListTile(\n            enabled: !loading,\n            leading: Radio<MarkStatus>(\n              value: status,\n              groupValue: currentStatus,\n              onChanged: loading ? null : (MarkStatus? value) {\n                if (value != null) {\n                  onMarkSelected(value);\n                  Navigator.of(context).pop();\n                }\n              },\n              fillColor: MaterialStateProperty.resolveWith((states) {\n                if (states.contains(MaterialState.disabled)) {\n                  return isDark ? Colors.white24 : Colors.black26;\n                }\n                if (states.contains(MaterialState.selected)) {\n                  return isDark ? Colors.white70 : Colors.black87;\n                }\n                return isDark ? Colors.white38 : Colors.black45;\n              }),\n            ),\n            title: Text(\n              status.label,\n              style: TextStyle(\n                color: loading\n                    ? (isDark ? Colors.white38 : Colors.black38)\n                    : (isSelected\n                        ? (isDark ? Colors.white : Colors.black87)\n                        : (isDark ? Colors.white70 : Colors.black54)),\n              ),\n            ),\n            onTap: loading ? null : () {\n              onMarkSelected(status);\n              Navigator.of(context).pop();\n            },\n            hoverColor: isDark \n                ? Colors.white.withOpacity(0.05) \n                : Colors.black.withOpacity(0.05),\n          );\n        }).toList(),\n      ),\n      shape: RoundedRectangleBorder(\n        borderRadius: BorderRadius.circular(12),\n      ),\n    );\n  }\n} "
  },
  {
    "path": "lib/widgets/detail/playlist_selection_dialog.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/playlists_with_exist_statu/playlist.dart';\n\nclass PlaylistSelectionDialog extends StatefulWidget {\n  final List<Playlist>? playlists;\n  final bool isLoading;\n  final String? error;\n  final Future<void> Function(Playlist playlist)? onPlaylistTap;\n  final VoidCallback? onRetry;\n\n  const PlaylistSelectionDialog({\n    super.key,\n    this.playlists,\n    required this.isLoading,\n    this.error,\n    this.onPlaylistTap,\n    this.onRetry,\n  });\n\n  @override\n  State<PlaylistSelectionDialog> createState() => _PlaylistSelectionDialogState();\n}\n\nclass _PlaylistSelectionDialogState extends State<PlaylistSelectionDialog> {\n  final Map<String, _PlaylistItemState> _itemStates = {};\n\n  @override\n  void initState() {\n    super.initState();\n    _updateItemStates();\n  }\n\n  @override\n  void didUpdateWidget(PlaylistSelectionDialog oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    if (widget.playlists != oldWidget.playlists) {\n      _updateItemStates();\n    }\n  }\n\n  void _updateItemStates() {\n    if (widget.playlists == null) return;\n    \n    final newStates = <String, _PlaylistItemState>{};\n    for (final playlist in widget.playlists!) {\n      newStates[playlist.id!] = _PlaylistItemState(\n        playlist: playlist,\n        isLoading: _itemStates[playlist.id!]?.isLoading ?? false,\n      );\n    }\n    _itemStates.clear();\n    _itemStates.addAll(newStates);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Dialog(\n      child: ConstrainedBox(\n        constraints: const BoxConstraints(\n          maxWidth: 400,\n          maxHeight: 500,\n        ),\n        child: Padding(\n          padding: const EdgeInsets.all(16),\n          child: Column(\n            mainAxisSize: MainAxisSize.min,\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: [\n              Text(\n                '添加到收藏夹',\n                style: Theme.of(context).textTheme.titleLarge,\n              ),\n              const SizedBox(height: 16),\n              Flexible(\n                child: _buildContent(),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget _buildContent() {\n    if (widget.isLoading) {\n      return const Center(\n        child: CircularProgressIndicator(),\n      );\n    }\n\n    if (widget.error != null) {\n      return Center(\n        child: Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: [\n            Text(widget.error!),\n            if (widget.onRetry != null) ...[\n              const SizedBox(height: 8),\n              ElevatedButton(\n                onPressed: widget.onRetry,\n                child: const Text('重试'),\n              ),\n            ],\n          ],\n        ),\n      );\n    }\n\n    if (widget.playlists == null || widget.playlists!.isEmpty) {\n      return const Center(\n        child: Text('暂无收藏夹'),\n      );\n    }\n\n    return ListView.builder(\n      shrinkWrap: true,\n      itemCount: widget.playlists!.length,\n      itemBuilder: (context, index) {\n        final playlist = widget.playlists![index];\n        final state = _itemStates[playlist.id!]!;\n        return _PlaylistItem(\n          state: state,\n          onTap: () => _handlePlaylistTap(state),\n        );\n      },\n    );\n  }\n\n  Future<void> _handlePlaylistTap(_PlaylistItemState state) async {\n    if (state.isLoading || widget.onPlaylistTap == null) return;\n\n    setState(() {\n      state.isLoading = true;\n    });\n\n    try {\n      await widget.onPlaylistTap!(state.playlist);\n      \n      if (mounted) {\n        final newPlaylist = state.playlist.copyWith(\n          exist: !(state.playlist.exist ?? false),\n        );\n        \n        _itemStates[state.playlist.id!] = _PlaylistItemState(\n          playlist: newPlaylist,\n          isLoading: false,\n        );\n\n        ScaffoldMessenger.of(context).showSnackBar(\n          SnackBar(\n            content: Text(\n              '${newPlaylist.exist! ? '添加成功' : '移除成功'}: ${_getDisplayName(newPlaylist.name)}',\n              style: TextStyle(\n                color: Theme.of(context).colorScheme.onSurface,\n              ),\n            ),\n            duration: const Duration(seconds: 1),\n            backgroundColor: Theme.of(context).colorScheme.surfaceVariant,\n            showCloseIcon: true,\n            closeIconColor: Theme.of(context).colorScheme.onSurface,\n            behavior: SnackBarBehavior.floating,\n          ),\n        );\n        \n        setState(() {});\n      }\n    } finally {\n      if (mounted) {\n        setState(() {\n          state.isLoading = false;\n        });\n      }\n    }\n  }\n\n  String _getDisplayName(String? name) {\n    switch (name) {\n      case '__SYS_PLAYLIST_MARKED':\n        return '我标记的';\n      case '__SYS_PLAYLIST_LIKED':\n        return '我喜欢的';\n      default:\n        return name ?? '';\n    }\n  }\n}\n\nclass _PlaylistItem extends StatelessWidget {\n  final _PlaylistItemState state;\n  final VoidCallback? onTap;\n\n  const _PlaylistItem({\n    required this.state,\n    this.onTap,\n  });\n\n  String _getDisplayName(String? name) {\n    switch (name) {\n      case '__SYS_PLAYLIST_MARKED':\n        return '我标记的';\n      case '__SYS_PLAYLIST_LIKED':\n        return '我喜欢的';\n      default:\n        return name ?? '';\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return ListTile(\n      title: Text(_getDisplayName(state.playlist.name)),\n      subtitle: Text('${state.playlist.worksCount ?? 0} 个作品'),\n      trailing: state.isLoading\n          ? const SizedBox(\n              width: 24,\n              height: 24,\n              child: CircularProgressIndicator(\n                strokeWidth: 2,\n              ),\n            )\n          : Checkbox(\n              value: state.playlist.exist ?? false,\n              onChanged: (_) => onTap?.call(),\n            ),\n      onTap: onTap,\n    );\n  }\n}\n\nclass _PlaylistItemState {\n  final Playlist playlist;\n  bool isLoading;\n  \n  _PlaylistItemState({\n    required this.playlist,\n    this.isLoading = false,\n  });\n} "
  },
  {
    "path": "lib/widgets/detail/work_action_buttons.dart",
    "content": "import 'package:asmrapp/data/models/mark_status.dart';\nimport 'package:flutter/material.dart';\n\nclass WorkActionButtons extends StatelessWidget {\n  final VoidCallback onRecommendationsTap;\n  final bool hasRecommendations;\n  final bool checkingRecommendations;\n  final VoidCallback onFavoriteTap;\n  final bool loadingFavorite;\n  final VoidCallback onMarkTap;\n  final MarkStatus? currentMarkStatus;\n  final bool loadingMark;\n\n  const WorkActionButtons({\n    super.key,\n    required this.onRecommendationsTap,\n    required this.hasRecommendations,\n    required this.checkingRecommendations,\n    required this.onFavoriteTap,\n    this.loadingFavorite = false,\n    required this.onMarkTap,\n    this.currentMarkStatus,\n    this.loadingMark = false,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return Padding(\n      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),\n      child: Row(\n        mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n        children: [\n          _ActionButton(\n            icon: Icons.favorite_border,\n            label: '收藏',\n            onTap: onFavoriteTap,\n            loading: loadingFavorite,\n          ),\n          _ActionButton(\n            icon: Icons.bookmark_border,\n            label: currentMarkStatus?.label ?? '标记',\n            onTap: onMarkTap,\n            loading: loadingMark,\n          ),\n          _ActionButton(\n            icon: Icons.star_border,\n            label: '评分',\n            onTap: () {\n              // TODO: 实现评分功能\n            },\n          ),\n          _ActionButton(\n            icon: Icons.recommend,\n            label: checkingRecommendations ? '检查中' : (hasRecommendations ? '相关推荐' : '暂无推荐'),\n            onTap: hasRecommendations ? onRecommendationsTap : null,\n            loading: checkingRecommendations,\n          ),\n        ],\n      ),\n    );\n  }\n}\n\nclass _ActionButton extends StatelessWidget {\n  final IconData icon;\n  final String label;\n  final VoidCallback? onTap;\n  final bool loading;\n\n  const _ActionButton({\n    required this.icon,\n    required this.label,\n    this.onTap,\n    this.loading = false,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final theme = Theme.of(context);\n    final disabled = onTap == null && !loading;\n    \n    return InkWell(\n      onTap: onTap,\n      borderRadius: BorderRadius.circular(8),\n      child: Padding(\n        padding: const EdgeInsets.all(8.0),\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          children: [\n            if (loading)\n              SizedBox(\n                width: 24,\n                height: 24,\n                child: CircularProgressIndicator(\n                  strokeWidth: 2,\n                  color: theme.colorScheme.primary,\n                ),\n              )\n            else\n              Icon(\n                icon,\n                color: disabled \n                    ? theme.colorScheme.onSurface.withOpacity(0.38)\n                    : null,\n              ),\n            const SizedBox(height: 4),\n            Text(\n              label,\n              style: theme.textTheme.bodySmall?.copyWith(\n                color: disabled\n                    ? theme.colorScheme.onSurface.withOpacity(0.38)\n                    : null,\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n} "
  },
  {
    "path": "lib/widgets/detail/work_cover.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:cached_network_image/cached_network_image.dart';\n\nclass WorkCover extends StatelessWidget {\n  final String imageUrl;\n  final int workId;\n  final String sourceId;\n  final String? releaseDate;\n  final String? heroTag;\n\n\n  const WorkCover({\n    super.key,\n    required this.imageUrl,\n    required this.workId,\n    required this.sourceId,\n    this.releaseDate,\n    this.heroTag,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    Widget content = Stack(\n      children: [\n        AspectRatio(\n          aspectRatio: 195 / 146,\n          child: CachedNetworkImage(\n            imageUrl: imageUrl,\n            fit: BoxFit.cover,\n            width: double.infinity,\n            height: double.infinity,\n          ),\n        ),\n        Positioned(\n          left: 8,\n          top: 8,\n          child: Container(\n            padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),\n            decoration: BoxDecoration(\n              color: Colors.black.withOpacity(0.7),\n              borderRadius: BorderRadius.circular(4),\n            ),\n            child: Text(\n              sourceId,\n              style: Theme.of(context).textTheme.bodySmall?.copyWith(\n                    color: Colors.white,\n                    fontSize: 12,\n                  ),\n            ),\n          ),\n        ),\n        if (releaseDate != null)\n          Positioned(\n            right: 8,\n            bottom: 8,\n            child: Container(\n              padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),\n              decoration: BoxDecoration(\n                color: Colors.black.withOpacity(0.7),\n                borderRadius: BorderRadius.circular(4),\n              ),\n              child: Text(\n                releaseDate!,\n                style: Theme.of(context).textTheme.bodySmall?.copyWith(\n                      color: Colors.white,\n                      fontSize: 12,\n                    ),\n              ),\n            ),\n          ),\n      ],\n    );\n\n    if (heroTag != null) {\n      return Hero(\n        tag: heroTag!,\n        child: content,\n      );\n    }\n\n    return content;\n  }\n}\n"
  },
  {
    "path": "lib/widgets/detail/work_file_item.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/files/child.dart';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:asmrapp/utils/file_size_formatter.dart';\n\nclass WorkFileItem extends StatelessWidget {\n  final Child file;\n  final double indentation;\n  final Function(Child file)? onFileTap;\n\n  const WorkFileItem({\n    super.key,\n    required this.file,\n    required this.indentation,\n    this.onFileTap,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final bool isAudio = file.type?.toLowerCase() == 'audio';\n    final colorScheme = Theme.of(context).colorScheme;\n    \n    return Padding(\n      padding: EdgeInsets.only(left: indentation),\n      child: ListTile(\n        title: Text(\n          file.title ?? '',\n          style: TextStyle(\n            color: colorScheme.onSurface,\n          ),\n        ),\n        subtitle: Text(\n          FileSizeFormatter.format(file.size),\n          style: TextStyle(\n            color: colorScheme.onSurfaceVariant,\n          ),\n        ),\n        leading: Icon(\n          isAudio ? Icons.audio_file : Icons.insert_drive_file,\n          color: isAudio ? Colors.green : Colors.blue,\n        ),\n        dense: true,\n        onTap: isAudio ? () {\n          AppLogger.debug('点击音频文件: ${file.title}');\n          onFileTap?.call(file);\n        } : null,\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/detail/work_files_list.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/files/files.dart';\nimport 'package:asmrapp/data/models/files/child.dart';\nimport 'package:asmrapp/widgets/detail/work_folder_item.dart';\nimport 'package:asmrapp/widgets/detail/work_file_item.dart';\n\nclass WorkFilesList extends StatelessWidget {\n  final Files files;\n  final Function(Child file)? onFileTap;\n\n  const WorkFilesList({\n    super.key,\n    required this.files,\n    this.onFileTap,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    // 重置文件夹展开状态\n    WorkFolderItem.resetExpandState();\n    \n    return Card(\n      margin: const EdgeInsets.all(8),\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: [\n          Padding(\n            padding: const EdgeInsets.all(16),\n            child: Text(\n              '文件列表',\n              style: Theme.of(context).textTheme.titleMedium?.copyWith(\n                    fontWeight: FontWeight.bold,\n                  ),\n            ),\n          ),\n          Divider(\n            height: 1,\n            color: Theme.of(context).colorScheme.surfaceVariant,\n          ),\n          ...files.children\n                  ?.map((child) => child.type == 'folder'\n                      ? WorkFolderItem(\n                          folder: child,\n                          indentation: 0,\n                          onFileTap: onFileTap,\n                        )\n                      : WorkFileItem(\n                          file: child,\n                          indentation: 0,\n                          onFileTap: onFileTap,\n                        ))\n                  .toList() ??\n              [],\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/detail/work_files_skeleton.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:shimmer/shimmer.dart';\n\nclass WorkFilesSkeleton extends StatelessWidget {\n  const WorkFilesSkeleton({super.key});\n\n  Widget _buildShimmerItem() {\n    return Padding(\n      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),\n      child: Row(\n        children: [\n          // 图标占位\n          Container(\n            width: 24,\n            height: 24,\n            decoration: BoxDecoration(\n              color: Colors.white,\n              borderRadius: BorderRadius.circular(4),\n            ),\n          ),\n          const SizedBox(width: 16),\n          // 标题占位\n          Expanded(\n            child: Column(\n              crossAxisAlignment: CrossAxisAlignment.start,\n              children: [\n                Container(\n                  height: 14,\n                  width: double.infinity,\n                  decoration: BoxDecoration(\n                    color: Colors.white,\n                    borderRadius: BorderRadius.circular(2),\n                  ),\n                ),\n                const SizedBox(height: 8),\n                Container(\n                  height: 10,\n                  width: 100,\n                  decoration: BoxDecoration(\n                    color: Colors.white,\n                    borderRadius: BorderRadius.circular(2),\n                  ),\n                ),\n              ],\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Card(\n      margin: const EdgeInsets.all(8),\n      child: Shimmer.fromColors(\n        baseColor: Theme.of(context).colorScheme.surfaceContainerHighest,\n        highlightColor: Theme.of(context).colorScheme.surface,\n        child: Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            // 标题占位\n            Padding(\n              padding: const EdgeInsets.all(16),\n              child: Container(\n                height: 24,\n                width: 120,\n                decoration: BoxDecoration(\n                  color: Colors.white,\n                  borderRadius: BorderRadius.circular(4),\n                ),\n              ),\n            ),\n            const Divider(height: 1),\n            // 列表项占位\n            ListView.builder(\n              shrinkWrap: true,\n              physics: const NeverScrollableScrollPhysics(),\n              itemCount: 6, // 显示6个占位项\n              itemBuilder: (context, index) => _buildShimmerItem(),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/detail/work_folder_item.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/files/child.dart';\nimport 'package:asmrapp/utils/logger.dart';\nimport 'package:asmrapp/widgets/detail/work_file_item.dart';\nimport 'package:asmrapp/core/audio/models/file_path.dart';\n\nclass WorkFolderItem extends StatelessWidget {\n  final Child folder;\n  final double indentation;\n  final Function(Child file)? onFileTap;\n\n  // 支持的音频格式列表，按优先级排序\n  static const _audioFormats = ['.mp3', '.wav'];\n\n  // 静态变量用于跟踪第一个包含音频的文件夹的完整路径\n  static List<String>? _audioFolderPath;\n\n  // 静态方法用于重置展开状态\n  static void resetExpandState() {\n    _audioFolderPath = null;\n  }\n\n  const WorkFolderItem({\n    super.key,\n    required this.folder,\n    required this.indentation,\n    this.onFileTap,\n  });\n\n  bool _shouldExpandFolder(Child folder) {\n    // 如果还没有找到第一个音频文件夹，就搜索并记录\n    _audioFolderPath ??= FilePath.findFirstAudioFolderPath(\n        [folder],\n        formats: _audioFormats,\n      );\n\n    // 判断当前文件夹是否在音频文件夹的路径上\n    return FilePath.isInPath(_audioFolderPath, folder.title);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final colorScheme = Theme.of(context).colorScheme;\n    final shouldExpand = _shouldExpandFolder(folder);\n\n    return Padding(\n      padding: EdgeInsets.only(left: indentation),\n      child: Theme(\n        data: Theme.of(context).copyWith(\n          dividerColor: Colors.transparent,\n          // 确保子组件也能继承正确的文字颜色\n          textTheme: Theme.of(context).textTheme.apply(\n            bodyColor: colorScheme.onSurface,\n            displayColor: colorScheme.onSurface,\n          ),\n        ),\n        child: ExpansionTile(\n          title: Text(\n            folder.title ?? '',\n            style: TextStyle(\n              color: colorScheme.onSurface,\n            ),\n          ),\n          leading: Icon(\n            Icons.folder,\n            color: colorScheme.primary,\n          ),\n          initiallyExpanded: shouldExpand,\n          children: folder.children\n                  ?.map((child) => child.type == 'folder'\n                      ? WorkFolderItem(\n                          folder: child,\n                          indentation: indentation + 16.0,\n                          onFileTap: onFileTap,\n                        )\n                      : WorkFileItem(\n                          file: child,\n                          indentation: indentation + 16.0,\n                          onFileTap: onFileTap,\n                        ))\n                  .toList() ??\n              [],\n          onExpansionChanged: (expanded) {\n            AppLogger.debug(\n              '${expanded ? \"展开\" : \"折叠\"}文件夹: ${folder.title}',\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/detail/work_info.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/data/models/works/tag.dart';\nimport 'package:asmrapp/widgets/common/tag_chip.dart';\nimport 'package:asmrapp/widgets/detail/work_info_header.dart';\nimport 'package:asmrapp/utils/logger.dart';\n\nclass WorkInfo extends StatelessWidget {\n  final Work work;\n\n  const WorkInfo({\n    super.key,\n    required this.work,\n  });\n\n  String _getLocalizedTagName(Tag tag) {\n    final zhName = tag.i18n?.zhCn?.name;\n    if (zhName != null) return zhName;\n    final jaName = tag.i18n?.jaJp?.name;\n    if (jaName != null) return jaName;\n    return tag.name ?? '';\n  }\n\n  void _onTagTap(BuildContext context, Tag tag) {\n    final keyword = tag.name ?? '';\n    if (keyword.isEmpty) return;\n\n    AppLogger.debug('点击标签: $keyword');\n    Navigator.pushNamed(\n      context,\n      '/search',\n      arguments: keyword,\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Padding(\n      padding: const EdgeInsets.all(16.0),\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: [\n          WorkInfoHeader(work: work),\n          const SizedBox(height: 8),\n          if (work.tags != null && work.tags!.isNotEmpty)\n            Wrap(\n              spacing: 8,\n              runSpacing: 8,\n              children: work.tags!\n                  .map((tag) => TagChip(\n                        text: _getLocalizedTagName(tag),\n                        onTap: () => _onTagTap(context, tag),\n                      ))\n                  .toList(),\n            ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/detail/work_info_header.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/widgets/common/tag_chip.dart';\nimport 'package:asmrapp/widgets/detail/work_stats_info.dart';\nimport 'package:asmrapp/utils/logger.dart';\n\nclass WorkInfoHeader extends StatelessWidget {\n  final Work work;\n\n  const WorkInfoHeader({\n    super.key,\n    required this.work,\n  });\n\n  void _onTagTap(BuildContext context, String keyword) {\n    if (keyword.isEmpty) return;\n\n    AppLogger.debug('点击标签: $keyword');\n    Navigator.pushNamed(\n      context,\n      '/search',\n      arguments: keyword,\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: [\n        Text(\n          work.title ?? '',\n          style: Theme.of(context).textTheme.titleMedium,\n        ),\n        const SizedBox(height: 8),\n        WorkStatsInfo(work: work),\n        const SizedBox(height: 8),\n        Wrap(\n          spacing: 8,\n          runSpacing: 8,\n          children: [\n            if (work.circle?.name != null)\n              TagChip(\n                text: work.circle?.name ?? '',\n                backgroundColor: Colors.orange.withOpacity(0.2),\n                textColor: Colors.orange[700],\n                onTap: () => _onTagTap(context, work.circle?.name ?? ''),\n              ),\n            ...?work.vas?.map(\n              (va) => TagChip(\n                text: va['name'] ?? '',\n                backgroundColor: Colors.green.withOpacity(0.2),\n                textColor: Colors.green[700],\n                onTap: () => _onTagTap(context, va['name'] ?? ''),\n              ),\n            ),\n            if (work.hasSubtitle == true)\n              TagChip(\n                text: '字幕',\n                backgroundColor: Colors.blue.withOpacity(0.2),\n                textColor: Colors.blue[700],\n              ),\n          ],\n        ),\n      ],\n    );\n  }\n} "
  },
  {
    "path": "lib/widgets/detail/work_stats_info.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\n\nclass WorkStatsInfo extends StatelessWidget {\n  final Work work;\n\n  const WorkStatsInfo({\n    super.key,\n    required this.work,\n  });\n\n  String _formatDuration(int? seconds) {\n    if (seconds == null) return '';\n    final duration = Duration(seconds: seconds);\n    final hours = duration.inHours;\n    final minutes = duration.inMinutes.remainder(60);\n\n    if (hours > 0) {\n      return '${hours}h ${minutes}m';\n    } else {\n      return '${minutes}m';\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Row(\n      children: [\n        if (work.duration != null) ...[\n          Icon(\n            Icons.access_time,\n            size: 16,\n            color: Theme.of(context).colorScheme.onSurfaceVariant,\n          ),\n          const SizedBox(width: 4),\n          Text(\n            _formatDuration(work.duration),\n            style: Theme.of(context).textTheme.bodyMedium?.copyWith(\n                  color: Theme.of(context).colorScheme.onSurfaceVariant,\n                ),\n          ),\n          const SizedBox(width: 16),\n        ],\n        if ((work.rateCount ?? 0) > 0) ...[\n          const Icon(Icons.star, size: 16, color: Colors.amber),\n          const SizedBox(width: 4),\n          Text(\n            (work.rateAverage2dp ?? 0.0).toStringAsFixed(1),\n            style: Theme.of(context).textTheme.bodyMedium,\n          ),\n          const SizedBox(width: 16),\n        ],\n        Icon(\n          Icons.download,\n          size: 16,\n          color: Theme.of(context).colorScheme.onSurfaceVariant,\n        ),\n        const SizedBox(width: 4),\n        Text(\n          '${work.dlCount ?? 0}',\n          style: Theme.of(context).textTheme.bodyMedium?.copyWith(\n                color: Theme.of(context).colorScheme.onSurfaceVariant,\n              ),\n        ),\n      ],\n    );\n  }\n} "
  },
  {
    "path": "lib/widgets/drawer_menu.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:provider/provider.dart';\nimport 'package:asmrapp/common/constants/strings.dart';\nimport 'package:asmrapp/presentation/viewmodels/auth_viewmodel.dart';\nimport 'package:asmrapp/presentation/widgets/auth/login_dialog.dart';\nimport 'package:asmrapp/screens/favorites_screen.dart';\nimport 'package:asmrapp/screens/settings/cache_manager_screen.dart';\nimport 'package:asmrapp/core/theme/theme_controller.dart';\nimport 'package:asmrapp/core/platform/wakelock_controller.dart';\nimport 'package:get_it/get_it.dart';\n\nclass DrawerMenu extends StatelessWidget {\n  const DrawerMenu({super.key});\n\n  void _showLoginDialog(BuildContext context) {\n    showDialog(\n      context: context,\n      builder: (context) => const LoginDialog(),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Drawer(\n      backgroundColor: Theme.of(context).colorScheme.surface,\n      child: ListTileTheme(\n        style: ListTileStyle.drawer,\n        child: ListView(\n          padding: EdgeInsets.zero,\n          children: [\n            Theme(\n              data: Theme.of(context).copyWith(\n                dividerTheme: const DividerThemeData(color: Colors.transparent),\n              ),\n              child: const DrawerHeader(\n                decoration: BoxDecoration(\n                  color: Colors.deepPurple,\n                ),\n                child: Text(\n                  Strings.appName,\n                  style: TextStyle(\n                    color: Colors.white,\n                    fontSize: 24,\n                  ),\n                ),\n              ),\n            ),\n            Consumer<AuthViewModel>(\n              builder: (context, authVM, _) {\n                return ListTile(\n                  leading: const Icon(Icons.person),\n                  title: Text(\n                    authVM.isLoggedIn ? authVM.username ?? '' : '登录',\n                  ),\n                  onTap: () {\n                    Navigator.pop(context);\n                    if (authVM.isLoggedIn) {\n                      authVM.logout();\n                    } else {\n                      _showLoginDialog(context);\n                    }\n                  },\n                );\n              },\n            ),\n\n            ListTile(\n              leading: const Icon(Icons.favorite),\n              title: const Text(Strings.favorites),\n              onTap: () {\n                Navigator.pop(context);\n                // 检查用户是否已登录\n                final authVM = context.read<AuthViewModel>();\n                if (!authVM.isLoggedIn) {\n                  // 如果未登录，显示登录对话框\n                  _showLoginDialog(context);\n                  return;\n                }\n                // 导航到收藏页面\n                Navigator.push(\n                  context,\n                  MaterialPageRoute(\n                    builder: (context) => const FavoritesScreen(),\n                  ),\n                );\n              },\n            ),\n            ListTile(\n              leading: const Icon(Icons.settings),\n              title: const Text(Strings.settings),\n              onTap: () {\n                Navigator.pop(context);\n                // TODO: 导航到设置页面\n              },\n            ),\n            ListTile(\n              leading: const Icon(Icons.storage),\n              title: const Text('缓存管理'),\n              onTap: () {\n                Navigator.pop(context);\n                Navigator.push(\n                  context,\n                  MaterialPageRoute(\n                    builder: (context) => const CacheManagerScreen(),\n                  ),\n                );\n              },\n            ),\n              Divider(\n                color: Theme.of(context).colorScheme.surfaceVariant,\n                height: 1,\n              ),\n            Consumer<ThemeController>(\n              builder: (context, themeController, _) {\n                return ListTile(\n                  leading: Icon(_getThemeIcon(themeController.themeMode)),\n                  title: Text(_getThemeText(themeController.themeMode)),\n                  onTap: () => themeController.toggleThemeMode(),\n                );\n              },\n            ),\n            ListenableBuilder(\n              listenable: GetIt.I<WakeLockController>(),\n              builder: (context, _) {\n                final controller = GetIt.I<WakeLockController>();\n                return SwitchListTile(\n                  title: const Text('屏幕常亮'),\n                  value: controller.enabled,\n                  onChanged: (_) => controller.toggle(),\n                );\n              },\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n\n  IconData _getThemeIcon(ThemeMode mode) {\n    switch (mode) {\n      case ThemeMode.system:\n        return Icons.brightness_auto;\n      case ThemeMode.light:\n        return Icons.brightness_high;\n      case ThemeMode.dark:\n        return Icons.brightness_2;\n    }\n  }\n\n  String _getThemeText(ThemeMode mode) {\n    switch (mode) {\n      case ThemeMode.system:\n        return '跟随系统主题';\n      case ThemeMode.light:\n        return '浅色模式';\n      case ThemeMode.dark:\n        return '深色模式';\n    }\n  }\n}\n"
  },
  {
    "path": "lib/widgets/filter/filter_panel.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass FilterPanel extends StatelessWidget {\n  final bool expanded;\n  final bool hasSubtitle;\n  final String orderField;\n  final bool isDescending;\n  final ValueChanged<bool> onSubtitleChanged;\n  final ValueChanged<String> onOrderFieldChanged;\n  final ValueChanged<bool> onSortDirectionChanged;\n\n  const FilterPanel({\n    super.key,\n    this.expanded = false,\n    required this.hasSubtitle,\n    required this.orderField,\n    required this.isDescending,\n    required this.onSubtitleChanged,\n    required this.onOrderFieldChanged,\n    required this.onSortDirectionChanged,\n  });\n\n  String _getOrderFieldText(String field) {\n    switch (field) {\n      case 'create_date':\n        return '收录时间';\n      case 'release':\n        return '发售日期';\n      case 'dl_count':\n        return '销量';\n      case 'price':\n        return '价格';\n      case 'rate_average_2dp':\n        return '评价';\n      case 'review_count':\n        return '评论数量';\n      case 'id':\n        return 'RJ号';\n      case 'rating':\n        return '我的评价';\n      case 'nsfw':\n        return '全年龄';\n      case 'random':\n        return '随机';\n      default:\n        return '排序';\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return SingleChildScrollView(\n      child: Padding(\n        padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),\n        child: Row(\n          children: [\n            // 字幕过滤\n            Container(\n              decoration: BoxDecoration(\n                border: Border.all(\n                  color: Theme.of(context).colorScheme.outline.withOpacity(0.5),\n                ),\n                borderRadius: BorderRadius.circular(8),\n              ),\n              child: Material(\n                type: MaterialType.transparency,\n                child: InkWell(\n                  onTap: () => onSubtitleChanged(!hasSubtitle),\n                  borderRadius: BorderRadius.circular(7),\n                  child: Padding(\n                    padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),\n                    child: Row(\n                      mainAxisSize: MainAxisSize.min,\n                      children: [\n                        Icon(\n                          hasSubtitle ? Icons.check_box : Icons.check_box_outline_blank,\n                          size: 20,\n                          color: hasSubtitle \n                              ? Theme.of(context).colorScheme.primary \n                              : Theme.of(context).colorScheme.onSurfaceVariant,\n                        ),\n                        const SizedBox(width: 8),\n                        Text(\n                          '有字幕',\n                          style: TextStyle(\n                            color: hasSubtitle \n                                ? Theme.of(context).colorScheme.primary \n                                : Theme.of(context).colorScheme.onSurface,\n                          ),\n                        ),\n                      ],\n                    ),\n                  ),\n                ),\n              ),\n            ),\n            const SizedBox(width: 8),\n            // 排序字段\n            Container(\n              decoration: BoxDecoration(\n                border: Border.all(\n                  color: Theme.of(context).colorScheme.outline.withOpacity(0.5),\n                ),\n                borderRadius: BorderRadius.circular(8),\n              ),\n              child: PopupMenuButton<String>(\n                child: Padding(\n                  padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),\n                  child: Row(\n                    mainAxisSize: MainAxisSize.min,\n                    children: [\n                      Text(_getOrderFieldText(orderField)),\n                      const SizedBox(width: 4),\n                      const Icon(Icons.arrow_drop_down, size: 20),\n                    ],\n                  ),\n                ),\n                itemBuilder: (context) => [\n                  _buildOrderMenuItem('收录时间', 'create_date'),\n                  _buildOrderMenuItem('发售日期', 'release'),\n                  _buildOrderMenuItem('销量', 'dl_count'),\n                  _buildOrderMenuItem('价格', 'price'),\n                  _buildOrderMenuItem('评价', 'rate_average_2dp'),\n                  _buildOrderMenuItem('评论数量', 'review_count'),\n                  _buildOrderMenuItem('RJ号', 'id'),\n                  _buildOrderMenuItem('我的评价', 'rating'),\n                  _buildOrderMenuItem('全年龄', 'nsfw'),\n                  _buildOrderMenuItem('随机', 'random'),\n                ],\n              ),\n            ),\n            const SizedBox(width: 8),\n            // 排序方向\n            Container(\n              decoration: BoxDecoration(\n                border: Border.all(\n                  color: Theme.of(context).colorScheme.outline.withOpacity(0.5),\n                ),\n                borderRadius: BorderRadius.circular(8),\n              ),\n              child: Material(\n                type: MaterialType.transparency,\n                child: InkWell(\n                  onTap: () => onSortDirectionChanged(!isDescending),\n                  borderRadius: BorderRadius.circular(7),\n                  child: Padding(\n                    padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),\n                    child: Row(\n                      mainAxisSize: MainAxisSize.min,\n                      children: [\n                        Text(isDescending ? '降序' : '升序'),\n                        const SizedBox(width: 4),\n                        Icon(\n                          isDescending ? Icons.arrow_downward : Icons.arrow_upward,\n                          size: 20,\n                          color: Theme.of(context).colorScheme.onSurface,\n                        ),\n                      ],\n                    ),\n                  ),\n                ),\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n\n  PopupMenuItem<String> _buildOrderMenuItem(String text, String value) {\n    return PopupMenuItem(\n      value: value,\n      child: Text(text),\n    );\n  }\n} "
  },
  {
    "path": "lib/widgets/filter/filter_with_keyword.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass FilterWithKeyword extends StatelessWidget {\n  final bool hasSubtitle;\n  final Function(bool) onSubtitleChanged;\n  final bool showSearchField;\n  final String? keyword;\n  final Function(String)? onSearch;\n  final VoidCallback? onClear;\n\n  const FilterWithKeyword({\n    super.key,\n    required this.hasSubtitle,\n    required this.onSubtitleChanged,\n    this.showSearchField = false,\n    this.keyword,\n    this.onSearch,\n    this.onClear,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final colorScheme = Theme.of(context).colorScheme;\n\n    return Material(\n      elevation: 2,\n      color: colorScheme.surface,\n      child: Padding(\n        padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),\n        child: Row(\n          children: [\n            Container(\n              decoration: BoxDecoration(\n                border: Border.all(\n                  color: Theme.of(context).colorScheme.outline.withOpacity(0.5),\n                ),\n                borderRadius: BorderRadius.circular(8),\n              ),\n              child: Material(\n                type: MaterialType.transparency,\n                child: InkWell(\n                  onTap: () => onSubtitleChanged(!hasSubtitle),\n                  borderRadius: BorderRadius.circular(7),\n                  child: Padding(\n                    padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),\n                    child: Row(\n                      mainAxisSize: MainAxisSize.min,\n                      children: [\n                        Icon(\n                          hasSubtitle \n                              ? Icons.check_box \n                              : Icons.check_box_outline_blank,\n                          size: 20,\n                          color: hasSubtitle \n                              ? colorScheme.primary \n                              : colorScheme.onSurfaceVariant,\n                        ),\n                        const SizedBox(width: 8),\n                        Text(\n                          '有字幕',\n                          style: TextStyle(\n                            color: hasSubtitle \n                                ? colorScheme.primary \n                                : colorScheme.onSurface,\n                          ),\n                        ),\n                      ],\n                    ),\n                  ),\n                ),\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n} "
  },
  {
    "path": "lib/widgets/lyrics/components/lyric_line.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/core/audio/models/subtitle.dart';\n\nclass LyricLine extends StatelessWidget {\n  final Subtitle subtitle;\n  final bool isActive;\n  final double opacity;\n  final VoidCallback? onTap;\n\n  const LyricLine({\n    super.key,\n    required this.subtitle,\n    this.isActive = false,\n    this.opacity = 1.0,\n    this.onTap,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return Center(\n      child: AnimatedOpacity(\n        duration: const Duration(milliseconds: 300),\n        opacity: opacity,\n        child: GestureDetector(\n          behavior: HitTestBehavior.translucent,\n          onTap: onTap,\n          child: Padding(\n            padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),\n            child: Text(\n              subtitle.text,\n              style: Theme.of(context).textTheme.bodyLarge?.copyWith(\n                fontSize: 20,\n                height: 1.3,\n                color: isActive \n                  ? Theme.of(context).colorScheme.primary\n                  : Theme.of(context).colorScheme.onSurface.withOpacity(0.7),\n                fontWeight: isActive ? FontWeight.w600 : FontWeight.normal,\n              ),\n              textAlign: TextAlign.center,\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n} "
  },
  {
    "path": "lib/widgets/lyrics/components/player_lyric_view.dart",
    "content": "import 'dart:async';\n\nimport 'package:flutter/material.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:scrollable_positioned_list/scrollable_positioned_list.dart';\nimport 'package:asmrapp/core/subtitle/i_subtitle_service.dart';\nimport 'package:asmrapp/core/audio/models/subtitle.dart';\nimport 'lyric_line.dart';\nimport 'package:asmrapp/presentation/viewmodels/player_viewmodel.dart';\n\nclass PlayerLyricView extends StatefulWidget {\n  final bool immediateScroll;\n  final Function(bool canSwitch) onScrollStateChanged;\n\n  const PlayerLyricView({\n    super.key,\n    this.immediateScroll = false,\n    required this.onScrollStateChanged,\n  });\n\n  @override\n  State<PlayerLyricView> createState() => _PlayerLyricViewState();\n}\n\nclass _PlayerLyricViewState extends State<PlayerLyricView> {\n  final ISubtitleService _subtitleService = GetIt.I<ISubtitleService>();\n  final PlayerViewModel _viewModel = GetIt.I<PlayerViewModel>();\n  final ItemScrollController _itemScrollController = ItemScrollController();\n  final ItemPositionsListener _itemPositionsListener = ItemPositionsListener.create();\n  \n  bool _isFirstBuild = true;\n  Subtitle? _lastScrolledSubtitle;\n  \n  // 用于控制视图切换的计时器和状态\n  // 当用户手动滚动时，暂时禁用视图切换功能，防止切换到封面\n  Timer? _scrollDebounceTimer;\n  \n  // 用于控制自动滚动的计时器和状态\n  // 当用户手动滚动时，暂时禁用自动滚动功能，让用户可以自由浏览歌词\n  bool _allowAutoScroll = true;\n  Timer? _autoScrollDebounceTimer;\n\n  @override\n  void initState() {\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    // 清理所有计时器\n    _scrollDebounceTimer?.cancel();    // 视图切换计时器\n    _autoScrollDebounceTimer?.cancel(); // 自动滚动计时器\n    super.dispose();\n  }\n\n  void _scrollToCurrentLyric(SubtitleWithState current) {\n    if (!_itemScrollController.isAttached) return;\n    \n    // 如果当前禁用了自动滚动（用户正在手动浏览），则不执行自动滚动\n    if (!_allowAutoScroll) return;\n    \n    // 避免重复滚动到同一句歌词\n    if (_lastScrolledSubtitle == current.subtitle) return;\n    _lastScrolledSubtitle = current.subtitle;\n    \n    if (_isFirstBuild) {\n      _isFirstBuild = false;\n      // 首次加载时直接跳转，不使用动画\n      _itemScrollController.jumpTo(\n        index: current.subtitle.index,\n        alignment: 0.5,\n      );\n    } else {\n      // 正常播放时使用平滑滚动动画\n      _itemScrollController.scrollTo(\n        index: current.subtitle.index,\n        duration: const Duration(milliseconds: 300),\n        curve: Curves.easeOutQuart,\n        alignment: 0.5,\n      );\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final screenHeight = MediaQuery.of(context).size.height;\n    final baseUnit = screenHeight * 0.04;\n    \n    return StreamBuilder<SubtitleWithState?>(\n      stream: _subtitleService.currentSubtitleWithStateStream,\n      initialData: _subtitleService.currentSubtitleWithState,\n      builder: (context, snapshot) {\n        final currentSubtitle = snapshot.data;\n        final subtitleList = _subtitleService.subtitleList;\n\n        if (subtitleList == null || subtitleList.subtitles.isEmpty) {\n          return const Center(\n            child: Text('无歌词'),\n          );\n        }\n\n        if (currentSubtitle != null) {\n          WidgetsBinding.instance.addPostFrameCallback((_) {\n            _scrollToCurrentLyric(currentSubtitle);\n          });\n        }\n\n        return NotificationListener<ScrollNotification>(\n          onNotification: (notification) {\n            if (notification is ScrollStartNotification && \n                notification.dragDetails != null) {  // 用户开始手动滚动\n              // 立即禁用视图切换功能\n              widget.onScrollStateChanged(false);\n              \n              // 禁用自动滚动功能\n              _allowAutoScroll = false;\n              \n              // 取消所有待执行的计时器\n              _scrollDebounceTimer?.cancel();\n              _autoScrollDebounceTimer?.cancel();\n            } else if (notification is ScrollEndNotification) {  // 用户结束滚动\n              // 延长视图切换的禁用时间到1秒\n              _scrollDebounceTimer?.cancel();\n              _scrollDebounceTimer = Timer(const Duration(milliseconds: 1000), () {\n                if (mounted) {\n                  widget.onScrollStateChanged(true);\n                }\n              });\n              \n              // 自动滚动计时器保持3秒\n              _autoScrollDebounceTimer?.cancel();\n              _autoScrollDebounceTimer = Timer(const Duration(milliseconds: 3000), () {\n                if (mounted) {\n                  setState(() {\n                    _allowAutoScroll = true;\n                    // 恢复时立即滚动到当前播放位置\n                    if (_subtitleService.currentSubtitleWithState != null) {\n                      _scrollToCurrentLyric(_subtitleService.currentSubtitleWithState!);\n                    }\n                  });\n                }\n              });\n            }\n            return false;\n          },\n          child: ScrollablePositionedList.builder(\n            itemCount: subtitleList.subtitles.length,\n            itemScrollController: _itemScrollController,\n            itemPositionsListener: _itemPositionsListener,\n            padding: EdgeInsets.symmetric(\n              vertical: screenHeight * 0.3,\n              horizontal: baseUnit * 0.8,\n            ),\n            itemBuilder: (context, index) {\n              final subtitle = subtitleList.subtitles[index];\n              final isActive = currentSubtitle?.subtitle == subtitle;\n              \n              return Padding(\n                padding: EdgeInsets.symmetric(\n                  vertical: baseUnit * 0.35,\n                ),\n                child: LyricLine(\n                  subtitle: subtitle,\n                  isActive: isActive,\n                  opacity: isActive ? 1.0 : 0.5,\n                  onTap: () async {\n                    widget.onScrollStateChanged(false);\n                    \n                    await _viewModel.seek(subtitle.start);\n                    \n                    Future.delayed(const Duration(milliseconds: 500), () {\n                      if (mounted) {\n                        widget.onScrollStateChanged(true);\n                      }\n                    });\n                  },\n                ),\n              );\n            },\n          ),\n        );\n      },\n    );\n  }\n} "
  },
  {
    "path": "lib/widgets/mini_player/mini_player.dart",
    "content": "import 'package:asmrapp/screens/player_screen.dart';\nimport 'package:flutter/material.dart';\nimport 'package:asmrapp/presentation/viewmodels/player_viewmodel.dart';\nimport 'mini_player_controls.dart';\nimport 'mini_player_progress.dart';\nimport 'package:get_it/get_it.dart';\nimport 'mini_player_cover.dart';\n\nclass MiniPlayer extends StatelessWidget {\n  static const height = 48.0;\n  \n  const MiniPlayer({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    final viewModel = GetIt.I<PlayerViewModel>();\n    return ListenableBuilder(\n      listenable: viewModel,\n      builder: (context, _) {\n        return GestureDetector(\n          onTap: () {\n            Navigator.of(context).push(\n              PageRouteBuilder(\n                pageBuilder: (context, animation, secondaryAnimation) {\n                  return const PlayerScreen();\n                },\n                transitionsBuilder: (context, animation, secondaryAnimation, child) {\n                  // 创建一个曲线动画\n                  final curvedAnimation = CurvedAnimation(\n                    parent: animation,\n                    curve: Curves.easeOutQuart,\n                  );\n                  \n                  return Stack(\n                    children: [\n                      // 背景淡入效果\n                      FadeTransition(\n                        opacity: curvedAnimation,\n                        child: Container(\n                          color: Theme.of(context).scaffoldBackgroundColor,\n                        ),\n                      ),\n                      // 内容从底部滑入并淡入\n                      FadeTransition(\n                        opacity: Tween<double>(\n                          begin: 0.3,\n                          end: 1.0,\n                        ).animate(curvedAnimation),\n                        child: SlideTransition(\n                          position: Tween<Offset>(\n                            begin: const Offset(0, 0.3),\n                            end: Offset.zero,\n                          ).animate(curvedAnimation),\n                          child: child,\n                        ),\n                      ),\n                    ],\n                  );\n                },\n                transitionDuration: const Duration(milliseconds: 400),\n              ),\n            );\n          },\n          child: Container(\n            height: height,\n            decoration: BoxDecoration(\n              color: Theme.of(context).colorScheme.surface,\n              boxShadow: [\n                BoxShadow(\n                  color: Colors.black.withOpacity(0.1),\n                  blurRadius: 4,\n                  offset: const Offset(0, -1),\n                ),\n              ],\n            ),\n            child: Column(\n              children: [\n                const MiniPlayerProgress(),\n                Expanded(\n                  child: Row(\n                    children: [\n                      Padding(\n                        padding: const EdgeInsets.fromLTRB(16, 4, 8, 4),\n                        child: Hero(\n                          tag: 'mini-player-cover',\n                          child: MiniPlayerCover(\n                            coverUrl: viewModel.currentTrackInfo?.coverUrl,\n                          ),\n                        ),\n                      ),\n                      Expanded(\n                        child: Padding(\n                          padding: const EdgeInsets.symmetric(horizontal: 16),\n                          child: Hero(\n                            tag: 'player-title',\n                            child: Material(\n                              color: Colors.transparent,\n                              child: Text(\n                                viewModel.currentTrackInfo?.title ?? '未在播放',\n                                maxLines: 1,\n                                overflow: TextOverflow.ellipsis,\n                                style: Theme.of(context).textTheme.titleSmall,\n                              ),\n                            ),\n                          ),\n                        ),\n                      ),\n                      const MiniPlayerControls(),\n                    ],\n                  ),\n                ),\n              ],\n            ),\n          ),\n        );\n      },\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/mini_player/mini_player_controls.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/presentation/viewmodels/player_viewmodel.dart';\nimport 'package:get_it/get_it.dart';\n\nclass MiniPlayerControls extends StatelessWidget {\n  const MiniPlayerControls({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    final viewModel = GetIt.I<PlayerViewModel>();\n    return ListenableBuilder(\n      listenable: viewModel,\n      builder: (context, _) {\n        return IconButton(\n          icon: Icon(\n            viewModel.isPlaying ? Icons.pause : Icons.play_arrow,\n          ),\n          onPressed: viewModel.playPause,\n        );\n      },\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/mini_player/mini_player_cover.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:cached_network_image/cached_network_image.dart';\nimport 'package:shimmer/shimmer.dart';\n\nclass MiniPlayerCover extends StatelessWidget {\n  final String? coverUrl;\n  final double size;\n\n  const MiniPlayerCover({\n    super.key,\n    this.coverUrl,\n    this.size = 48,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    if (coverUrl == null) {\n      return _buildEmptyPlaceholder();\n    }\n\n    return ClipRRect(\n      borderRadius: BorderRadius.circular(4),\n      child: CachedNetworkImage(\n        imageUrl: coverUrl!,\n        width: size,\n        height: size,\n        fit: BoxFit.cover,\n        placeholder: (context, url) => _buildPlaceholder(context),\n        errorWidget: (context, url, error) => _buildErrorWidget(),\n      ),\n    );\n  }\n\n  Widget _buildEmptyPlaceholder() {\n    return Container(\n      width: size,\n      height: size,\n      decoration: BoxDecoration(\n        color: Colors.grey[200],\n        borderRadius: BorderRadius.circular(4),\n      ),\n      child: const Icon(Icons.music_note, color: Colors.grey),\n    );\n  }\n\n  Widget _buildPlaceholder(BuildContext context) {\n    return Shimmer.fromColors(\n      baseColor: Theme.of(context).colorScheme.surfaceContainerHighest,\n      highlightColor: Theme.of(context).colorScheme.surface,\n      child: Container(\n        width: size,\n        height: size,\n        decoration: BoxDecoration(\n          color: Colors.white,\n          borderRadius: BorderRadius.circular(4),\n        ),\n      ),\n    );\n  }\n\n  Widget _buildErrorWidget() {\n    return Container(\n      width: size,\n      height: size,\n      decoration: BoxDecoration(\n        color: Colors.grey[300],\n        borderRadius: BorderRadius.circular(4),\n      ),\n      child: const Icon(Icons.broken_image, color: Colors.grey),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/mini_player/mini_player_progress.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:asmrapp/presentation/viewmodels/player_viewmodel.dart';\n\nclass MiniPlayerProgress extends StatelessWidget {\n  const MiniPlayerProgress({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    final viewModel = GetIt.I<PlayerViewModel>();\n    return ListenableBuilder(\n      listenable: viewModel,\n      builder: (context, _) {\n        final position = viewModel.position?.inMilliseconds.toDouble() ?? 0.0;\n        final duration = viewModel.duration?.inMilliseconds.toDouble() ?? 0.0;\n        final progress = duration > 0 ? position / duration : 0.0;\n\n        return SizedBox(\n          height: 2,\n          child: LinearProgressIndicator(\n            value: progress,\n            backgroundColor:\n                Theme.of(context).colorScheme.surfaceContainerHighest,\n            valueColor: AlwaysStoppedAnimation<Color>(\n              Theme.of(context).colorScheme.primary,\n            ),\n          ),\n        );\n      },\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/pagination_controls.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass PaginationControls extends StatelessWidget {\n  final int currentPage;\n  final int? totalPages;\n  final bool isLoading;\n  final Function(int) onPageChanged;\n\n  const PaginationControls({\n    super.key,\n    required this.currentPage,\n    required this.totalPages,\n    required this.isLoading,\n    required this.onPageChanged,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      padding: const EdgeInsets.all(16),\n      child: Row(\n        mainAxisAlignment: MainAxisAlignment.center,\n        children: [\n          const SizedBox(width: 32),\n          IconButton(\n            onPressed: currentPage > 1 && !isLoading\n                ? () => onPageChanged(currentPage - 1)\n                : null,\n            icon: const Icon(Icons.chevron_left),\n          ),\n          const SizedBox(width: 16),\n          Text('$currentPage/${totalPages ?? \"?\"}'),\n          const SizedBox(width: 16),\n          IconButton(\n            onPressed:\n                totalPages != null && currentPage < totalPages! && !isLoading\n                    ? () => onPageChanged(currentPage + 1)\n                    : null,\n            icon: const Icon(Icons.chevron_right),\n          ),\n          SizedBox(\n            width: 32,\n            child: isLoading\n                ? const Padding(\n                    padding: EdgeInsets.only(left: 16),\n                    child: SizedBox(\n                      width: 16,\n                      height: 16,\n                      child: CircularProgressIndicator(\n                        strokeWidth: 2,\n                      ),\n                    ),\n                  )\n                : null,\n          ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/player/player_controls.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:asmrapp/presentation/viewmodels/player_viewmodel.dart';\n\nclass PlayerControls extends StatelessWidget {\n  const PlayerControls({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    final viewModel = GetIt.I<PlayerViewModel>();\n    \n    return ListenableBuilder(\n      listenable: viewModel,\n      builder: (context, _) {\n        return Row(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: [\n            IconButton(\n              iconSize: 32,\n              icon: const Icon(Icons.skip_previous),\n              onPressed: viewModel.previous,\n            ),\n            const SizedBox(width: 16),\n            Container(\n              width: 64,\n              height: 64,\n              decoration: BoxDecoration(\n                shape: BoxShape.circle,\n                color: Theme.of(context).primaryColor,\n              ),\n              child: IconButton(\n                iconSize: 32,\n                color: Colors.white,\n                icon: Icon(\n                  viewModel.isPlaying ? Icons.pause : Icons.play_arrow,\n                ),\n                onPressed: viewModel.playPause,\n              ),\n            ),\n            const SizedBox(width: 16),\n            IconButton(\n              iconSize: 32,\n              icon: const Icon(Icons.skip_next),\n              onPressed: viewModel.next,\n            ),\n          ],\n        );\n      },\n    );\n  }\n} "
  },
  {
    "path": "lib/widgets/player/player_cover.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:cached_network_image/cached_network_image.dart';\nimport 'package:shimmer/shimmer.dart';\n\nclass PlayerCover extends StatelessWidget {\n  final String? coverUrl;\n  final double? maxWidth;\n  \n  const PlayerCover({\n    super.key,\n    this.coverUrl,\n    this.maxWidth = 480,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return AspectRatio(\n      aspectRatio: 4/3,\n      child: Container(\n        constraints: BoxConstraints(\n          maxWidth: maxWidth ?? 480,\n        ),\n        decoration: BoxDecoration(\n          color: Colors.grey[300],\n          borderRadius: BorderRadius.circular(12),\n          boxShadow: [\n            BoxShadow(\n              color: Colors.black.withOpacity(0.1),\n              blurRadius: 20,\n              offset: const Offset(0, 8),\n            ),\n          ],\n        ),\n        child: ClipRRect(\n          borderRadius: BorderRadius.circular(12),\n          child: coverUrl != null\n              ? CachedNetworkImage(\n                  imageUrl: coverUrl!,\n                  fit: BoxFit.cover,\n                  placeholder: (context, url) => Shimmer.fromColors(\n                    baseColor: Theme.of(context).colorScheme.surfaceContainerHighest,\n                    highlightColor: Theme.of(context).colorScheme.surface,\n                    child: Container(\n                      color: Colors.white,\n                    ),\n                  ),\n                  errorWidget: (context, url, error) => Container(\n                    color: Theme.of(context).colorScheme.errorContainer,\n                    child: Center(\n                      child: Icon(\n                        Icons.error_outline,\n                        size: 48,\n                        color: Theme.of(context).colorScheme.error,\n                      ),\n                    ),\n                  ),\n                )\n              : const Icon(Icons.music_note, size: 100),\n        ),\n      ),\n    );\n  }\n} "
  },
  {
    "path": "lib/widgets/player/player_progress.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:asmrapp/presentation/viewmodels/player_viewmodel.dart';\n\nclass PlayerProgress extends StatelessWidget {\n  const PlayerProgress({super.key});\n\n  String _formatDuration(Duration? duration) {\n    if (duration == null) return '--:--';\n    String twoDigits(int n) => n.toString().padLeft(2, '0');\n    String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));\n    String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));\n    return '$twoDigitMinutes:$twoDigitSeconds';\n  }\n\n  double _ensureValueInRange(double value, double min, double max) {\n    if (value < min) return min;\n    if (value > max) return max;\n    return value;\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final viewModel = GetIt.I<PlayerViewModel>();\n    \n    return ListenableBuilder(\n      listenable: viewModel,\n      builder: (context, _) {\n        return Padding(\n          padding: const EdgeInsets.symmetric(horizontal: 4),\n          child: Column(\n            children: [\n              SliderTheme(\n                data: SliderTheme.of(context).copyWith(\n                  trackHeight: 2,\n                  thumbShape: const RoundSliderThumbShape(\n                    enabledThumbRadius: 6,\n                  ),\n                ),\n                child: Slider(\n                  value: _ensureValueInRange(\n                    viewModel.position?.inMilliseconds.toDouble() ?? 0,\n                    0,\n                    viewModel.duration?.inMilliseconds.toDouble() ?? 1\n                  ),\n                  min: 0,\n                  max: viewModel.duration?.inMilliseconds.toDouble() ?? 1,\n                  onChanged: (value) {\n                    viewModel.seek(Duration(milliseconds: value.round()));\n                  },\n                ),\n              ),\n              Padding(\n                padding: const EdgeInsets.symmetric(horizontal: 8),\n                child: Row(\n                  mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                  children: [\n                    Text(\n                      _formatDuration(viewModel.position),\n                      style: Theme.of(context).textTheme.bodySmall?.copyWith(\n                        color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7),\n                      ),\n                    ),\n                    Text(\n                      _formatDuration(viewModel.duration),\n                      style: Theme.of(context).textTheme.bodySmall?.copyWith(\n                        color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7),\n                      ),\n                    ),\n                  ],\n                ),\n              ),\n            ],\n          ),\n        );\n      },\n    );\n  }\n} "
  },
  {
    "path": "lib/widgets/player/player_seek_controls.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:get_it/get_it.dart';\nimport 'package:asmrapp/presentation/viewmodels/player_viewmodel.dart';\n\nclass PlayerSeekControls extends StatelessWidget {\n  const PlayerSeekControls({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    final viewModel = GetIt.I<PlayerViewModel>();\n    \n    return Row(\n      mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n      children: [\n        // 后退30s\n        IconButton(\n          icon: const Icon(Icons.replay_30),\n          iconSize: 24,\n          onPressed: () {\n            final position = viewModel.position;\n            if (position != null) {\n              viewModel.seek(position - const Duration(seconds: 30));\n            }\n          },\n        ),\n        // 后退5s\n        IconButton(\n          icon: const Icon(Icons.replay_5),\n          iconSize: 24,\n          onPressed: () {\n            final position = viewModel.position;\n            if (position != null) {\n              viewModel.seek(position - const Duration(seconds: 5));\n            }\n          },\n        ),\n        // 上一句歌词\n        IconButton(\n          icon: const Icon(Icons.skip_previous),\n          iconSize: 24,\n          onPressed: () => viewModel.seekToPreviousLyric(),\n        ),\n        // 下一句歌词\n        IconButton(\n          icon: const Icon(Icons.skip_next),\n          iconSize: 24,\n          onPressed: () => viewModel.seekToNextLyric(),\n        ),\n        // 快进5s\n        IconButton(\n          icon: const Icon(Icons.forward_5),\n          iconSize: 24,\n          onPressed: () {\n            final position = viewModel.position;\n            if (position != null) {\n              viewModel.seek(position + const Duration(seconds: 5));\n            }\n          },\n        ),\n        // 快进30s\n        IconButton(\n          icon: const Icon(Icons.forward_30),\n          iconSize: 24,\n          onPressed: () {\n            final position = viewModel.position;\n            if (position != null) {\n              viewModel.seek(position + const Duration(seconds: 30));\n            }\n          },\n        ),\n      ],\n    );\n  }\n} "
  },
  {
    "path": "lib/widgets/player/player_work_info.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:marquee/marquee.dart';\nimport 'package:asmrapp/core/audio/models/playback_context.dart';\n\nclass PlayerWorkInfo extends StatelessWidget {\n  final PlaybackContext? context;\n\n  const PlayerWorkInfo({\n    super.key,\n    required this.context,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      width: double.infinity,\n      padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: [\n          SizedBox(\n            height: Theme.of(context).textTheme.titleMedium!.fontSize! * 1.5,\n            child: Marquee(\n              text: this.context?.work.title ?? '未知作品',\n              style: Theme.of(context).textTheme.titleMedium?.copyWith(\n                    fontWeight: FontWeight.w600,\n                  ),\n              scrollAxis: Axis.horizontal,\n              crossAxisAlignment: CrossAxisAlignment.start,\n              blankSpace: 50.0,\n              velocity: 30.0,\n              pauseAfterRound: const Duration(seconds: 2),\n              startPadding: 10.0,\n              accelerationDuration: const Duration(seconds: 1),\n              accelerationCurve: Curves.linear,\n              decelerationDuration: const Duration(milliseconds: 500),\n              decelerationCurve: Curves.easeOut,\n            ),\n          ),\n          const SizedBox(height: 2),\n          Text(\n            this.context?.work.vas\n                    ?.map((va) => va['name'] as String?)\n                    .where((name) => name != null)\n                    .join('、') ?? \n                '未知演员',\n            style: Theme.of(context).textTheme.bodyMedium?.copyWith(\n                  color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7),\n                ),\n            maxLines: 1,\n            overflow: TextOverflow.ellipsis,\n          ),\n        ],\n      ),\n    );\n  }\n} "
  },
  {
    "path": "lib/widgets/work_card/components/work_cover_image.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:cached_network_image/cached_network_image.dart';\nimport 'package:shimmer/shimmer.dart';\n\nclass WorkCoverImage extends StatelessWidget {\n  final String imageUrl;\n  final int workId;\n  final String sourceId;\n  // 195/146 ≈ 1.336\n  static const double _aspectRatio = 195 / 146;\n\n  const WorkCoverImage({\n    super.key,\n    required this.imageUrl,\n    required this.workId,\n    required this.sourceId,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return AspectRatio(\n      aspectRatio: _aspectRatio,\n      child: Stack(\n        children: [\n          Hero(\n            tag: 'work-cover-$workId',\n            child: CachedNetworkImage(\n              imageUrl: imageUrl,\n              fit: BoxFit.cover,\n              width: double.infinity,\n              height: double.infinity,\n              placeholder: (context, url) => Shimmer.fromColors(\n                baseColor:\n                    Theme.of(context).colorScheme.surfaceContainerHighest,\n                highlightColor: Theme.of(context).colorScheme.surface,\n                child: Container(\n                  color: Colors.white,\n                ),\n              ),\n              errorWidget: (context, url, error) => Container(\n                color: Theme.of(context).colorScheme.errorContainer,\n                child: Center(\n                  child: Icon(\n                    Icons.error_outline,\n                    color: Theme.of(context).colorScheme.error,\n                  ),\n                ),\n              ),\n            ),\n          ),\n          Positioned(\n            left: 8,\n            top: 8,\n            child: Container(\n              padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),\n              decoration: BoxDecoration(\n                color: Colors.black.withOpacity(0.7),\n                borderRadius: BorderRadius.circular(4),\n              ),\n              child: Text(\n                sourceId,\n                style: Theme.of(context).textTheme.bodySmall?.copyWith(\n                      color: Colors.white,\n                      fontSize: 12,\n                    ),\n              ),\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/work_card/components/work_footer.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\n\nclass WorkFooter extends StatelessWidget {\n  final Work work;\n\n  const WorkFooter({\n    super.key,\n    required this.work,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return Row(\n      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n      children: [\n        Text(\n          work.release ?? '',\n          style: Theme.of(context).textTheme.bodySmall?.copyWith(\n                fontSize: 10,\n              ),\n        ),\n        Text(\n          '销量 ${work.dlCount ?? 0}',\n          style: Theme.of(context).textTheme.bodySmall?.copyWith(\n                fontSize: 10,\n              ),\n        ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/work_card/components/work_info_section.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'work_title.dart';\nimport 'work_tags_panel.dart';\nimport 'work_footer.dart';\n\nclass WorkInfoSection extends StatelessWidget {\n  final Work work;\n\n  const WorkInfoSection({\n    super.key,\n    required this.work,\n  });\n\n  String _formatDuration(int? seconds) {\n    if (seconds == null) return '';\n    final duration = Duration(seconds: seconds);\n    final hours = duration.inHours;\n    final minutes = duration.inMinutes.remainder(60);\n\n    if (hours > 0) {\n      return '${hours}h ${minutes}m';\n    } else {\n      return '${minutes}m';\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Padding(\n      padding: const EdgeInsets.all(8.0),\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: [\n          WorkTitle(work: work),\n          const SizedBox(height: 4),\n          Row(\n            children: [\n              if (work.duration != null) ...[\n                Icon(\n                  Icons.access_time,\n                  size: 14,\n                  color: Theme.of(context).colorScheme.onSurfaceVariant,\n                ),\n                const SizedBox(width: 4),\n                Text(\n                  _formatDuration(work.duration),\n                  style: Theme.of(context).textTheme.bodyMedium?.copyWith(\n                        fontSize: 12,\n                        color: Theme.of(context).colorScheme.onSurfaceVariant,\n                      ),\n                ),\n              ],\n            ],\n          ),\n          const SizedBox(height: 8),\n          WorkTagsPanel(work: work),\n          const SizedBox(height: 4),\n          const Spacer(),\n          WorkFooter(work: work),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/work_card/components/work_tags_panel.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/data/models/works/tag.dart';\n\nclass WorkTagsPanel extends StatelessWidget {\n  final Work work;\n\n  const WorkTagsPanel({\n    super.key,\n    required this.work,\n  });\n\n  String _getLocalizedTagName(Tag tag) {\n    final zhName = tag.i18n?.zhCn?.name;\n    if (zhName != null) return zhName;\n    final jaName = tag.i18n?.jaJp?.name;\n    if (jaName != null) return jaName;\n    return tag.name ?? '';\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Wrap(\n      spacing: 4,\n      runSpacing: 2,\n      children: [\n        if (work.circle?.name != null)\n          Container(\n            padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),\n            decoration: BoxDecoration(\n              color: Colors.orange.withOpacity(0.2),\n              borderRadius: BorderRadius.circular(4),\n            ),\n            child: Text(\n              work.circle?.name ?? '',\n              style: TextStyle(\n                fontSize: 10,\n                color: Colors.orange[700],\n              ),\n            ),\n          ),\n        ...?work.vas?.map((va) => Container(\n              padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),\n              decoration: BoxDecoration(\n                color: Colors.green.withOpacity(0.2),\n                borderRadius: BorderRadius.circular(4),\n              ),\n              child: Text(\n                va['name'] ?? '',\n                style: TextStyle(\n                  fontSize: 10,\n                  color: Colors.green[700],\n                ),\n              ),\n            )),\n        if (work.hasSubtitle == true)\n          Container(\n            padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),\n            decoration: BoxDecoration(\n              color: Colors.blue.withOpacity(0.2),\n              borderRadius: BorderRadius.circular(4),\n            ),\n            child: Text(\n              '字幕',\n              style: TextStyle(\n                fontSize: 10,\n                color: Colors.blue[700],\n              ),\n            ),\n          ),\n        ...work.tags\n                ?.map((tag) => Container(\n                      padding: const EdgeInsets.symmetric(\n                          horizontal: 6, vertical: 2),\n                      decoration: BoxDecoration(\n                        color: Theme.of(context)\n                            .colorScheme\n                            .surfaceContainerHighest,\n                        borderRadius: BorderRadius.circular(4),\n                      ),\n                      child: Text(\n                        _getLocalizedTagName(tag),\n                        style: TextStyle(\n                          fontSize: 10,\n                          color: Theme.of(context).colorScheme.onSurfaceVariant,\n                        ),\n                      ),\n                    ))\n                .toList() ??\n            [],\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/work_card/components/work_title.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\n\nclass WorkTitle extends StatelessWidget {\n  final Work work;\n\n  const WorkTitle({\n    super.key,\n    required this.work,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return Text(\n      work.title ?? '',\n      style: Theme.of(context).textTheme.titleMedium?.copyWith(\n            fontSize: 14,\n          ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/work_card/work_card.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'components/work_cover_image.dart';\nimport 'components/work_info_section.dart';\n\nclass WorkCard extends StatelessWidget {\n  final Work work;\n  final VoidCallback? onTap;\n\n  const WorkCard({\n    super.key,\n    required this.work,\n    this.onTap,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final isDark = Theme.of(context).brightness == Brightness.dark;\n    \n    return Card(\n      clipBehavior: Clip.antiAlias,\n      elevation: isDark ? 0 : 1,\n      color: isDark \n          ? Theme.of(context).colorScheme.surfaceVariant\n          : Theme.of(context).colorScheme.surface,\n      child: InkWell(\n        onTap: onTap,\n        child: Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            WorkCoverImage(\n              imageUrl: work.mainCoverUrl ?? '',\n              workId: work.id ?? 0,\n              sourceId: work.sourceId ?? '',\n            ),\n            Expanded(\n              child: WorkInfoSection(work: work),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/work_grid/components/grid_content.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/presentation/layouts/work_layout_strategy.dart';\nimport 'package:asmrapp/widgets/work_grid.dart';\nimport 'package:asmrapp/widgets/pagination_controls.dart';\nimport 'package:asmrapp/widgets/work_grid/models/grid_config.dart';\nimport 'package:asmrapp/screens/detail_screen.dart';\n\nclass GridContent extends StatelessWidget {\n  final List<Work> works;\n  final bool isLoading;\n  final WorkLayoutStrategy layoutStrategy;\n  final int? currentPage;\n  final int? totalPages;\n  final Future<void> Function(int page)? onPageChanged;\n  final ScrollController? scrollController;\n  final GridConfig? config;\n\n  const GridContent({\n    super.key,\n    required this.works,\n    required this.isLoading,\n    required this.layoutStrategy,\n    this.currentPage,\n    this.totalPages,\n    this.onPageChanged,\n    this.scrollController,\n    this.config,\n  });\n\n  void _scrollToTop() {\n    if (scrollController?.hasClients ?? false) {\n      scrollController!.animateTo(\n        0,\n        duration: config?.scrollDuration ?? const Duration(milliseconds: 300),\n        curve: config?.scrollCurve ?? Curves.easeOut,\n      );\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return CustomScrollView(\n      controller: scrollController,\n      physics: config?.physics,\n      slivers: [\n        SliverPadding(\n          padding: config?.padding ?? layoutStrategy.getPadding(context),\n          sliver: WorkGrid(\n            works: works,\n            layoutStrategy: layoutStrategy,\n            onWorkTap: (work) {\n              Navigator.push(\n                context,\n                MaterialPageRoute(\n                  builder: (context) => DetailScreen(work: work),\n                ),\n              );\n            },\n          ),\n        ),\n        if (config?.enablePagination != false && \n            currentPage != null && \n            totalPages != null)\n          SliverToBoxAdapter(\n            child: PaginationControls(\n              currentPage: currentPage!,\n              totalPages: totalPages!,\n              isLoading: isLoading,\n              onPageChanged: (page) async {\n                await onPageChanged?.call(page);\n                if (!isLoading) {\n                  _scrollToTop();\n                }\n              },\n            ),\n          ),\n      ],\n    );\n  }\n} "
  },
  {
    "path": "lib/widgets/work_grid/components/grid_empty.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass GridEmpty extends StatelessWidget {\n  final String? message;\n  final Widget? customWidget;\n\n  const GridEmpty({\n    super.key,\n    this.message,\n    this.customWidget,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    if (customWidget != null) {\n      return customWidget!;\n    }\n\n    return Center(\n      child: Column(\n        mainAxisAlignment: MainAxisAlignment.center,\n        children: [\n          Icon(\n            Icons.inbox_outlined,\n            size: 48,\n            color: Theme.of(context).colorScheme.outline,\n          ),\n          const SizedBox(height: 16),\n          Text(\n            message ?? '暂无内容',\n            style: Theme.of(context).textTheme.bodyLarge?.copyWith(\n                  color: Theme.of(context).colorScheme.outline,\n                ),\n          ),\n        ],\n      ),\n    );\n  }\n} "
  },
  {
    "path": "lib/widgets/work_grid/components/grid_error.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass GridError extends StatelessWidget {\n  final String error;\n  final VoidCallback? onRetry;\n\n  const GridError({\n    super.key,\n    required this.error,\n    this.onRetry,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return Center(\n      child: Column(\n        mainAxisAlignment: MainAxisAlignment.center,\n        children: [\n          Icon(\n            Icons.error_outline,\n            size: 48,\n            color: Theme.of(context).colorScheme.error,\n          ),\n          const SizedBox(height: 16),\n          Text(\n            error,\n            style: Theme.of(context).textTheme.bodyLarge,\n            textAlign: TextAlign.center,\n          ),\n          if (onRetry != null) ...[\n            const SizedBox(height: 16),\n            FilledButton.icon(\n              onPressed: onRetry,\n              icon: const Icon(Icons.refresh),\n              label: const Text('重试'),\n            ),\n          ],\n        ],\n      ),\n    );\n  }\n} "
  },
  {
    "path": "lib/widgets/work_grid/components/grid_loading.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:shimmer/shimmer.dart';\n\nclass GridLoading extends StatelessWidget {\n  const GridLoading({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Shimmer.fromColors(\n      baseColor: Theme.of(context).colorScheme.surfaceContainerHighest,\n      highlightColor: Theme.of(context).colorScheme.surface,\n      child: GridView.builder(\n        padding: const EdgeInsets.all(16),\n        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(\n          crossAxisCount: 2,\n          childAspectRatio: 0.75,\n          crossAxisSpacing: 16,\n          mainAxisSpacing: 16,\n        ),\n        itemCount: 6,\n        itemBuilder: (context, index) {\n          return Container(\n            decoration: BoxDecoration(\n              color: Colors.white,\n              borderRadius: BorderRadius.circular(8),\n            ),\n          );\n        },\n      ),\n    );\n  }\n} "
  },
  {
    "path": "lib/widgets/work_grid/enhanced_work_grid_view.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/presentation/layouts/work_layout_strategy.dart';\nimport 'package:asmrapp/widgets/work_grid/components/grid_content.dart';\nimport 'package:asmrapp/widgets/work_grid/components/grid_error.dart';\nimport 'package:asmrapp/widgets/work_grid/components/grid_empty.dart';\nimport 'package:asmrapp/widgets/work_grid/components/grid_loading.dart';\nimport 'package:asmrapp/widgets/work_grid/models/grid_config.dart';\n\nclass EnhancedWorkGridView extends StatelessWidget {\n  final List<Work> works;\n  final bool isLoading;\n  final String? error;\n  final VoidCallback? onRetry;\n  final Future<void> Function()? onRefresh;\n  final Future<void> Function(int page)? onPageChanged;\n  final int? currentPage;\n  final int? totalPages;\n  final String? emptyMessage;\n  final Widget? customEmptyWidget;\n  final WorkLayoutStrategy layoutStrategy;\n  final ScrollController? scrollController;\n  final GridConfig? config;\n\n  const EnhancedWorkGridView({\n    super.key,\n    required this.works,\n    required this.isLoading,\n    this.error,\n    this.onRetry,\n    this.onRefresh,\n    this.onPageChanged,\n    this.currentPage,\n    this.totalPages,\n    this.emptyMessage,\n    this.customEmptyWidget,\n    this.layoutStrategy = const WorkLayoutStrategy(),\n    this.scrollController,\n    this.config,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    if (isLoading && works.isEmpty) {\n      return const GridLoading();\n    }\n\n    if (error != null) {\n      return GridError(\n        error: error!,\n        onRetry: onRetry,\n      );\n    }\n\n    if (works.isEmpty) {\n      return GridEmpty(\n        message: emptyMessage,\n        customWidget: customEmptyWidget,\n      );\n    }\n\n    Widget content = GridContent(\n      works: works,\n      isLoading: isLoading,\n      layoutStrategy: layoutStrategy,\n      currentPage: currentPage,\n      totalPages: totalPages,\n      onPageChanged: onPageChanged,\n      scrollController: scrollController,\n      config: config,\n    );\n\n    if (onRefresh != null) {\n      content = RefreshIndicator(\n        onRefresh: onRefresh!,\n        child: content,\n      );\n    }\n\n    return content;\n  }\n} "
  },
  {
    "path": "lib/widgets/work_grid/models/grid_config.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass GridConfig {\n  final ScrollPhysics? physics;\n  final bool enablePagination;\n  final bool showLoadingOnEmpty;\n  final Duration scrollDuration;\n  final Curve scrollCurve;\n  final EdgeInsets? padding;\n\n  const GridConfig({\n    this.physics,\n    this.enablePagination = true,\n    this.showLoadingOnEmpty = true,\n    this.scrollDuration = const Duration(milliseconds: 300),\n    this.scrollCurve = Curves.easeOut,\n    this.padding,\n  });\n\n  static const GridConfig defaultConfig = GridConfig();\n} "
  },
  {
    "path": "lib/widgets/work_grid.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/widgets/work_row.dart';\nimport 'package:asmrapp/presentation/layouts/work_layout_strategy.dart';\n\nclass WorkGrid extends StatelessWidget {\n  final List<Work> works;\n  final void Function(Work work)? onWorkTap;\n  final WorkLayoutStrategy layoutStrategy;\n\n  const WorkGrid({\n    super.key,\n    required this.works,\n    this.onWorkTap,\n    this.layoutStrategy = const WorkLayoutStrategy(),\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final columnsCount = layoutStrategy.getColumnsCount(context);\n    final rows = layoutStrategy.groupWorksIntoRows(works, columnsCount);\n    final rowSpacing = layoutStrategy.getRowSpacing(context);\n    final columnSpacing = layoutStrategy.getColumnSpacing(context);\n\n    return SliverList(\n      delegate: SliverChildBuilderDelegate(\n        (context, index) {\n          if (index >= rows.length) return null;\n          return Padding(\n            padding: EdgeInsets.only(\n                bottom: index < rows.length - 1 ? rowSpacing : 0),\n            child: WorkRow(\n              works: rows[index],\n              onWorkTap: onWorkTap,\n              spacing: columnSpacing,\n            ),\n          );\n        },\n        childCount: rows.length,\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/work_grid_view.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/widgets/work_grid.dart';\nimport 'package:asmrapp/presentation/layouts/work_layout_strategy.dart';\nimport 'package:asmrapp/screens/detail_screen.dart';\n\nclass WorkGridView extends StatelessWidget {\n  final List<Work> works;\n  final bool isLoading;\n  final String? error;\n  final VoidCallback? onRetry;\n  final String? emptyMessage;\n  final Widget? customEmptyWidget;\n  final WorkLayoutStrategy layoutStrategy;\n  final ScrollController? scrollController;\n  final Widget? bottomWidget;\n\n  const WorkGridView({\n    super.key,\n    required this.works,\n    required this.isLoading,\n    this.error,\n    this.onRetry,\n    this.emptyMessage,\n    this.customEmptyWidget,\n    this.layoutStrategy = const WorkLayoutStrategy(),\n    this.scrollController,\n    this.bottomWidget,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    if (isLoading) {\n      return const Center(\n        child: CircularProgressIndicator(),\n      );\n    }\n\n    if (error != null) {\n      return Center(\n        child: Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: [\n            Text(error!),\n            if (onRetry != null) ...[\n              const SizedBox(height: 16),\n              ElevatedButton(\n                onPressed: onRetry,\n                child: const Text('重试'),\n              ),\n            ],\n          ],\n        ),\n      );\n    }\n\n    if (works.isEmpty) {\n      if (customEmptyWidget != null) {\n        return customEmptyWidget!;\n      }\n      if (emptyMessage != null) {\n        return Center(\n          child: Text(\n            emptyMessage!,\n            style: Theme.of(context).textTheme.bodyLarge,\n          ),\n        );\n      }\n      return const SizedBox.shrink();\n    }\n\n    return CustomScrollView(\n      controller: scrollController,\n      slivers: [\n        SliverPadding(\n          padding: layoutStrategy.getPadding(context),\n          sliver: WorkGrid(\n            works: works,\n            layoutStrategy: layoutStrategy,\n            onWorkTap: (work) {\n              Navigator.push(\n                context,\n                MaterialPageRoute(\n                  builder: (context) => DetailScreen(work: work),\n                ),\n              );\n            },\n          ),\n        ),\n        if (bottomWidget != null)\n          SliverToBoxAdapter(\n            child: bottomWidget!,\n          ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/work_row.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:asmrapp/data/models/works/work.dart';\nimport 'package:asmrapp/widgets/work_card/work_card.dart';\n\nclass WorkRow extends StatelessWidget {\n  final List<Work> works;\n  final void Function(Work work)? onWorkTap;\n  final double spacing;\n\n  const WorkRow({\n    super.key,\n    required this.works,\n    this.onWorkTap,\n    this.spacing = 8.0,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return IntrinsicHeight(\n      child: Row(\n        crossAxisAlignment: CrossAxisAlignment.stretch,\n        children: [\n          // 第一个卡片\n          Expanded(\n            child: works.isNotEmpty \n                ? WorkCard(\n                    work: works[0],\n                    onTap: onWorkTap != null ? () => onWorkTap!(works[0]) : null,\n                  )\n                : const SizedBox.shrink(),\n          ),\n          SizedBox(width: spacing),\n          // 第二个卡片或占位符\n          Expanded(\n            child: works.length > 1\n                ? WorkCard(\n                    work: works[1],\n                    onTap: onWorkTap != null ? () => onWorkTap!(works[1]) : null,\n                  )\n                : const SizedBox.shrink(), // 空占位符，保持两列布局\n          ),\n        ],\n      ),\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 \"asmrapp\")\n# The unique GTK application identifier for this application. See:\n# https://wiki.gnome.org/HowDoI/ChooseApplicationID\nset(APPLICATION_ID \"com.example.asmrapp\")\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\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# Copy the native assets provided by the build.dart from all packages.\nset(NATIVE_ASSETS_DIR \"${PROJECT_BUILD_DIR}native_assets/linux/\")\ninstall(DIRECTORY \"${NATIVE_ASSETS_DIR}\"\n   DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n   COMPONENT Runtime)\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\nvoid fl_register_plugins(FlPluginRegistry* registry) {\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)\n\nlist(APPEND FLUTTER_FFI_PLUGIN_LIST\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, \"asmrapp\");\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, \"asmrapp\");\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 GApplication::startup.\nstatic void my_application_startup(GApplication* application) {\n  //MyApplication* self = MY_APPLICATION(object);\n\n  // Perform any actions required at application startup.\n\n  G_APPLICATION_CLASS(my_application_parent_class)->startup(application);\n}\n\n// Implements GApplication::shutdown.\nstatic void my_application_shutdown(GApplication* application) {\n  //MyApplication* self = MY_APPLICATION(object);\n\n  // Perform any actions required at application shutdown.\n\n  G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application);\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_APPLICATION_CLASS(klass)->startup = my_application_startup;\n  G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown;\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 audio_service\nimport audio_session\nimport just_audio\nimport package_info_plus\nimport path_provider_foundation\nimport shared_preferences_foundation\nimport sqflite_darwin\nimport wakelock_plus\n\nfunc RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {\n  AudioServicePlugin.register(with: registry.registrar(forPlugin: \"AudioServicePlugin\"))\n  AudioSessionPlugin.register(with: registry.registrar(forPlugin: \"AudioSessionPlugin\"))\n  JustAudioPlugin.register(with: registry.registrar(forPlugin: \"JustAudioPlugin\"))\n  FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: \"FPPPackageInfoPlusPlugin\"))\n  PathProviderPlugin.register(with: registry.registrar(forPlugin: \"PathProviderPlugin\"))\n  SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: \"SharedPreferencesPlugin\"))\n  SqflitePlugin.register(with: registry.registrar(forPlugin: \"SqflitePlugin\"))\n  WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: \"WakelockPlusMacosPlugin\"))\n}\n"
  },
  {
    "path": "macos/Podfile",
    "content": "platform :osx, '10.14'\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__))\n  target 'RunnerTests' do\n    inherit! :search_paths\n  end\nend\n\npost_install do |installer|\n  installer.pods_project.targets.each do |target|\n    flutter_additional_macos_build_settings(target)\n  end\nend\n"
  },
  {
    "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  \"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  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\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=\"14490.70\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\" customObjectInstantitationMethod=\"direct\">\n    <dependencies>\n        <deployment identifier=\"macosx\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"14490.70\"/>\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=\"Runner\" 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\" id=\"QvC-M9-y7g\" customClass=\"MainFlutterWindow\" customModule=\"Runner\" customModuleProvider=\"target\">\n            <windowStyleMask key=\"styleMask\" titled=\"YES\" closable=\"YES\" miniaturizable=\"YES\" resizable=\"YES\"/>\n            <rect key=\"contentRect\" x=\"335\" y=\"390\" width=\"800\" height=\"600\"/>\n            <rect key=\"screenRect\" x=\"0.0\" y=\"0.0\" width=\"2560\" height=\"1577\"/>\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        </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 = asmrapp\n\n// The application's bundle identifier\nPRODUCT_BUNDLE_IDENTIFIER = com.example.asmrapp\n\n// The copyright displayed in application information\nPRODUCT_COPYRIGHT = Copyright © 2024 com.example. 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.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.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</dict>\n</plist>\n"
  },
  {
    "path": "macos/Runner/MainFlutterWindow.swift",
    "content": "import Cocoa\nimport FlutterMacOS\n\nclass MainFlutterWindow: NSWindow {\n  override func awakeFromNib() {\n    let flutterViewController = FlutterViewController()\n    let windowFrame = self.frame\n    self.contentViewController = flutterViewController\n    self.setFrame(windowFrame, display: true)\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.security.app-sandbox</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\t331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };\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/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\t331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 33CC10E52044A3C60003C045 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 33CC10EC2044A3C60003C045;\n\t\t\tremoteInfo = Runner;\n\t\t};\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\t331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; 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 /* asmrapp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = \"asmrapp.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\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/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t331C80D2294CF70F00263BE5 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t33CC10EA2044A3C60003C045 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t331C80D6294CF71000263BE5 /* RunnerTests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t331C80D7294CF71000263BE5 /* RunnerTests.swift */,\n\t\t\t);\n\t\t\tpath = RunnerTests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\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\t331C80D6294CF71000263BE5 /* RunnerTests */,\n\t\t\t\t33CC10EE2044A3C60003C045 /* Products */,\n\t\t\t\tD73912EC22F37F3D000D13A0 /* Frameworks */,\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 /* asmrapp.app */,\n\t\t\t\t331C80D5294CF71000263BE5 /* RunnerTests.xctest */,\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\tD73912EC22F37F3D000D13A0 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\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\t331C80D4294CF70F00263BE5 /* RunnerTests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget \"RunnerTests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t331C80D1294CF70F00263BE5 /* Sources */,\n\t\t\t\t331C80D2294CF70F00263BE5 /* Frameworks */,\n\t\t\t\t331C80D3294CF70F00263BE5 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t331C80DA294CF71000263BE5 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = RunnerTests;\n\t\t\tproductName = RunnerTests;\n\t\t\tproductReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\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\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);\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 /* asmrapp.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\tBuildIndependentTargetsInParallel = YES;\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\t331C80D4294CF70F00263BE5 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 14.0;\n\t\t\t\t\t\tTestTargetID = 33CC10EC2044A3C60003C045;\n\t\t\t\t\t};\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\t331C80D4294CF70F00263BE5 /* RunnerTests */,\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\t331C80D3294CF70F00263BE5 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\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/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t331C80D1294CF70F00263BE5 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\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\t331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 33CC10EC2044A3C60003C045 /* Runner */;\n\t\t\ttargetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;\n\t\t};\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\t331C80DB294CF71000263BE5 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.example.asmrapp.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/asmrapp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/asmrapp\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t331C80DC294CF71000263BE5 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.example.asmrapp.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/asmrapp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/asmrapp\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t331C80DD294CF71000263BE5 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.example.asmrapp.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/asmrapp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/asmrapp\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\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\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\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\tDEAD_CODE_STRIPPING = YES;\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\tENABLE_USER_SCRIPT_SANDBOXING = NO;\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.14;\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\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\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\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\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\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\tDEAD_CODE_STRIPPING = YES;\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\tENABLE_USER_SCRIPT_SANDBOXING = NO;\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.14;\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\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\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\tDEAD_CODE_STRIPPING = YES;\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\tENABLE_USER_SCRIPT_SANDBOXING = NO;\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.14;\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\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\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\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\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\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\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\t331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget \"RunnerTests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t331C80DB294CF71000263BE5 /* Debug */,\n\t\t\t\t331C80DC294CF71000263BE5 /* Release */,\n\t\t\t\t331C80DD294CF71000263BE5 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\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 = \"asmrapp.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 = \"asmrapp.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <Testables>\n         <TestableReference\n            skipped = \"NO\"\n            parallelizable = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"331C80D4294CF70F00263BE5\"\n               BuildableName = \"RunnerTests.xctest\"\n               BlueprintName = \"RunnerTests\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\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 = \"asmrapp.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 = \"asmrapp.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</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": "macos/RunnerTests/RunnerTests.swift",
    "content": "import Cocoa\nimport FlutterMacOS\nimport XCTest\n\nclass RunnerTests: XCTestCase {\n\n  func testExample() {\n    // If you add code to the Runner application, consider adding tests here.\n    // See https://developer.apple.com/documentation/xctest for more information about using XCTest.\n  }\n\n}\n"
  },
  {
    "path": "pubspec.yaml",
    "content": "name: asmrapp\ndescription: \"asmr one third party 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.\nversion: 1.1.11\n\nenvironment:\n  sdk: '>=3.2.3 <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  freezed_annotation: ^2.4.1\n  json_annotation: ^4.9.0\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  provider: ^6.1.1\n  dio: ^5.4.0\n  cached_network_image: ^3.3.0\n  logger: ^2.5.0\n  shimmer: ^3.0.0\n  just_audio: ^0.9.36\n  audio_session: ^0.1.18\n  get_it: ^8.0.2\n  audio_service: ^0.18.12\n  rxdart: ^0.28.0\n  path_provider: ^2.1.5\n  crypto: ^3.0.6\n  shared_preferences: ^2.2.2\n  flutter_cache_manager: ^3.4.1\n  permission_handler: ^11.3.1\n  scrollable_positioned_list: ^0.3.8\n  marquee: ^2.3.0\n  wakelock_plus: ^1.2.8\n\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n  build_runner: ^2.4.7\n  freezed: ^2.4.6\n  json_serializable: ^6.7.1\n  flutter_launcher_icons: ^0.13.1\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\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  #   - images/a_dot_burr.jpeg\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/to/resolution-aware-images\n\n  # For details regarding adding assets from package dependencies, see\n  # https://flutter.dev/to/asset-from-package\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: Schyler\n  #     fonts:\n  #       - asset: fonts/Schyler-Regular.ttf\n  #       - asset: fonts/Schyler-Italic.ttf\n  #         style: italic\n  #   - family: Trajan Pro\n  #     fonts:\n  #       - asset: fonts/TrajanPro.ttf\n  #       - asset: fonts/TrajanPro_Bold.ttf\n  #         weight: 700\n  #\n  # For details regarding fonts from package dependencies,\n  # see https://flutter.dev/to/font-from-package\n\nflutter_launcher_icons:\n  android: \"ic_launcher\"\n  ios: true\n  image_path: \"assets/icon/icon.png\"\n  min_sdk_android: 21\n\ndependency_overrides:\n  path: ^1.9.0  # 强制使用更高版本的 path 包\n"
  },
  {
    "path": "test/widget_test.dart",
    "content": "// This is a basic Flutter widget test.\n//\n// To perform an interaction with a widget in your test, use the WidgetTester\n// utility in the flutter_test package. For example, you can send tap and scroll\n// gestures. You can also use WidgetTester to find child widgets in the widget\n// tree, read text, and verify that the values of widget properties are correct.\n\nimport 'package:flutter/material.dart';\nimport 'package:flutter_test/flutter_test.dart';\n\nimport 'package:asmrapp/main.dart';\n\nvoid main() {\n  testWidgets('Counter increments smoke test', (WidgetTester tester) async {\n    // Build our app and trigger a frame.\n    await tester.pumpWidget(const MyApp());\n\n    // Verify that our counter starts at 0.\n    expect(find.text('0'), findsOneWidget);\n    expect(find.text('1'), findsNothing);\n\n    // Tap the '+' icon and trigger a frame.\n    await tester.tap(find.byIcon(Icons.add));\n    await tester.pump();\n\n    // Verify that our counter has incremented.\n    expect(find.text('0'), findsNothing);\n    expect(find.text('1'), findsOneWidget);\n  });\n}\n"
  },
  {
    "path": "web/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<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=\"A new Flutter project.\">\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=\"asmrapp\">\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>asmrapp</title>\n  <link rel=\"manifest\" href=\"manifest.json\">\n</head>\n<body>\n  <script src=\"flutter_bootstrap.js\" async></script>\n</body>\n</html>\n"
  },
  {
    "path": "web/manifest.json",
    "content": "{\n    \"name\": \"Yuro\",\n    \"short_name\": \"Yuro\",\n    \"start_url\": \".\",\n    \"display\": \"standalone\",\n    \"background_color\": \"#0175C2\",\n    \"theme_color\": \"#0175C2\",\n    \"description\": \"A new Flutter project.\",\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}\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"
  },
  {
    "path": "windows/CMakeLists.txt",
    "content": "# Project-level configuration.\ncmake_minimum_required(VERSION 3.14)\nproject(asmrapp 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 \"asmrapp\")\n\n# Explicitly opt in to modern CMake behaviors to avoid warnings with recent\n# versions of CMake.\ncmake_policy(VERSION 3.14...3.25)\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\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# Copy the native assets provided by the build.dart from all packages.\nset(NATIVE_ASSETS_DIR \"${PROJECT_BUILD_DIR}native_assets/windows/\")\ninstall(DIRECTORY \"${NATIVE_ASSETS_DIR}\"\n   DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n   COMPONENT Runtime)\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"
  },
  {
    "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 <permission_handler_windows/permission_handler_windows_plugin.h>\n\nvoid RegisterPlugins(flutter::PluginRegistry* registry) {\n  PermissionHandlerWindowsPluginRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"PermissionHandlerWindowsPlugin\"));\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  permission_handler_windows\n)\n\nlist(APPEND FLUTTER_FFI_PLUGIN_LIST\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\", \"com.example\" \"\\0\"\n            VALUE \"FileDescription\", \"asmrapp\" \"\\0\"\n            VALUE \"FileVersion\", VERSION_AS_STRING \"\\0\"\n            VALUE \"InternalName\", \"asmrapp\" \"\\0\"\n            VALUE \"LegalCopyright\", \"Copyright (C) 2024 com.example. All rights reserved.\" \"\\0\"\n            VALUE \"OriginalFilename\", \"asmrapp.exe\" \"\\0\"\n            VALUE \"ProductName\", \"asmrapp\" \"\\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\n  // Flutter can complete the first frame before the \"show window\" callback is\n  // registered. The following call ensures a frame is pending to ensure the\n  // window is shown. It is a no-op if the first frame hasn't completed yet.\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\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(1280, 720);\n  if (!window.Create(L\"asmrapp\", 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    </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  unsigned int target_length = ::WideCharToMultiByte(\n      CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,\n      -1, nullptr, 0, nullptr, nullptr)\n    -1; // remove the trailing null character\n  int input_length = (int)wcslen(utf16_string);\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      input_length, utf8_string.data(), 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 registrar 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  // responds 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"
  }
]