Repository: mylxsw/aidea
Branch: main
Commit: cea454f7b48a
Files: 400
Total size: 2.4 MB
Directory structure:
gitextract_69aiun7l/
├── .github/
│ └── workflows/
│ └── build_windows_app.yml
├── .gitignore
├── .metadata
├── AppRun
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── README.zh-CN.md
├── analysis_options.yaml
├── android/
│ ├── .gitignore
│ ├── app/
│ │ ├── build.gradle
│ │ ├── proguard-rules.pro
│ │ └── src/
│ │ ├── debug/
│ │ │ └── AndroidManifest.xml
│ │ ├── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── kotlin/
│ │ │ │ └── cc/
│ │ │ │ └── aicode/
│ │ │ │ └── flutter/
│ │ │ │ └── askaide/
│ │ │ │ └── askaide/
│ │ │ │ └── MainActivity.kt
│ │ │ └── res/
│ │ │ ├── drawable/
│ │ │ │ └── launch_background.xml
│ │ │ ├── drawable-v21/
│ │ │ │ └── launch_background.xml
│ │ │ ├── values/
│ │ │ │ └── styles.xml
│ │ │ ├── values-night/
│ │ │ │ └── styles.xml
│ │ │ ├── values-night-v31/
│ │ │ │ └── styles.xml
│ │ │ └── values-v31/
│ │ │ └── styles.xml
│ │ └── profile/
│ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle/
│ │ └── wrapper/
│ │ └── gradle-wrapper.properties
│ ├── gradle.properties
│ └── settings.gradle
├── askaide.desktop
├── assets/
│ └── lottie/
│ └── empty_status.json
├── build-win-msix.bat
├── build-win.bat
├── devtools_options.yaml
├── docker-build.sh
├── flutter_launcher_icons.yaml
├── install.icns
├── install.iss
├── ios/
│ ├── .gitignore
│ ├── Flutter/
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Podfile
│ ├── Runner/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ ├── LaunchBackground.imageset/
│ │ │ │ └── Contents.json
│ │ │ └── LaunchImage.imageset/
│ │ │ ├── Contents.json
│ │ │ └── README.md
│ │ ├── Base.lproj/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ ├── Runner-Bridging-Header.h
│ │ └── Runner.entitlements
│ ├── Runner.xcodeproj/
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace/
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata/
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── Runner.xcscheme
│ └── Runner.xcworkspace/
│ ├── contents.xcworkspacedata
│ └── xcshareddata/
│ ├── IDEWorkspaceChecks.plist
│ └── WorkspaceSettings.xcsettings
├── lib/
│ ├── bloc/
│ │ ├── account_bloc.dart
│ │ ├── account_event.dart
│ │ ├── account_state.dart
│ │ ├── admin_payment_bloc.dart
│ │ ├── admin_payment_event.dart
│ │ ├── admin_payment_state.dart
│ │ ├── admin_room_bloc.dart
│ │ ├── admin_room_event.dart
│ │ ├── admin_room_state.dart
│ │ ├── background_image_bloc.dart
│ │ ├── background_image_event.dart
│ │ ├── background_image_state.dart
│ │ ├── bloc_manager.dart
│ │ ├── channel_bloc.dart
│ │ ├── channel_event.dart
│ │ ├── channel_state.dart
│ │ ├── chat_chat_bloc.dart
│ │ ├── chat_chat_event.dart
│ │ ├── chat_chat_state.dart
│ │ ├── chat_event.dart
│ │ ├── chat_message_bloc.dart
│ │ ├── chat_state.dart
│ │ ├── creative_island_bloc.dart
│ │ ├── creative_island_event.dart
│ │ ├── creative_island_state.dart
│ │ ├── free_count_bloc.dart
│ │ ├── free_count_event.dart
│ │ ├── free_count_state.dart
│ │ ├── gallery_bloc.dart
│ │ ├── gallery_event.dart
│ │ ├── gallery_state.dart
│ │ ├── group_chat_bloc.dart
│ │ ├── group_chat_event.dart
│ │ ├── group_chat_state.dart
│ │ ├── model_bloc.dart
│ │ ├── model_event.dart
│ │ ├── model_state.dart
│ │ ├── notify_bloc.dart
│ │ ├── notify_event.dart
│ │ ├── notify_state.dart
│ │ ├── payment_bloc.dart
│ │ ├── payment_event.dart
│ │ ├── payment_state.dart
│ │ ├── room_bloc.dart
│ │ ├── room_event.dart
│ │ ├── room_state.dart
│ │ ├── user_api_keys_bloc.dart
│ │ ├── user_api_keys_event.dart
│ │ ├── user_api_keys_state.dart
│ │ ├── user_bloc.dart
│ │ ├── user_event.dart
│ │ ├── user_state.dart
│ │ ├── version_bloc.dart
│ │ ├── version_event.dart
│ │ └── version_state.dart
│ ├── data/
│ │ └── migrate.dart
│ ├── helper/
│ │ ├── ability.dart
│ │ ├── cache.dart
│ │ ├── chat_token.dart
│ │ ├── color.dart
│ │ ├── constant.dart
│ │ ├── env.dart
│ │ ├── error.dart
│ │ ├── event.dart
│ │ ├── global_store.dart
│ │ ├── haptic_feedback.dart
│ │ ├── helper.dart
│ │ ├── http.dart
│ │ ├── image.dart
│ │ ├── logger.dart
│ │ ├── lru.dart
│ │ ├── model.dart
│ │ ├── model_resolver.dart
│ │ ├── path.dart
│ │ ├── platform.dart
│ │ ├── queue.dart
│ │ ├── tips.dart
│ │ └── upload.dart
│ ├── lang/
│ │ └── lang.dart
│ ├── main.dart
│ ├── page/
│ │ ├── admin/
│ │ │ ├── channels.dart
│ │ │ ├── channels_add.dart
│ │ │ ├── channels_edit.dart
│ │ │ ├── dashboard.dart
│ │ │ ├── messages.dart
│ │ │ ├── models.dart
│ │ │ ├── models_add.dart
│ │ │ ├── models_edit.dart
│ │ │ ├── payments.dart
│ │ │ ├── recently_messages.dart
│ │ │ ├── rooms.dart
│ │ │ ├── user.dart
│ │ │ └── users.dart
│ │ ├── app_scaffold.dart
│ │ ├── auth/
│ │ │ ├── signin_or_signup.dart
│ │ │ ├── signin_screen.dart
│ │ │ └── signup_screen.dart
│ │ ├── balance/
│ │ │ ├── free_statistics.dart
│ │ │ ├── payment.dart
│ │ │ ├── payment_history.dart
│ │ │ ├── price_block.dart
│ │ │ ├── quota_usage_details.dart
│ │ │ ├── quota_usage_statistics.dart
│ │ │ ├── web/
│ │ │ │ ├── payment_element.dart
│ │ │ │ └── payment_element_web.dart
│ │ │ ├── web_payment_proxy.dart
│ │ │ └── web_payment_result.dart
│ │ ├── chat/
│ │ │ ├── character_chat.dart
│ │ │ ├── character_create.dart
│ │ │ ├── character_edit.dart
│ │ │ ├── characters.dart
│ │ │ ├── component/
│ │ │ │ ├── character_box.dart
│ │ │ │ ├── group_avatar.dart
│ │ │ │ ├── group_empty.dart
│ │ │ │ ├── model_switcher.dart
│ │ │ │ └── stop_button.dart
│ │ │ ├── group/
│ │ │ │ ├── chat.dart
│ │ │ │ ├── create.dart
│ │ │ │ └── edit.dart
│ │ │ ├── home.dart
│ │ │ ├── home_chat.dart
│ │ │ └── home_chat_history.dart
│ │ ├── component/
│ │ │ ├── account_quota_card.dart
│ │ │ ├── advanced_button.dart
│ │ │ ├── animated_cursor.dart
│ │ │ ├── attached_button_panel.dart
│ │ │ ├── audio_player.dart
│ │ │ ├── avatar_selector.dart
│ │ │ ├── background_container.dart
│ │ │ ├── bottom_sheet_box.dart
│ │ │ ├── button.dart
│ │ │ ├── chat/
│ │ │ │ ├── chat_bubble.dart
│ │ │ │ ├── chat_input.dart
│ │ │ │ ├── chat_input_button.dart
│ │ │ │ ├── chat_preview.dart
│ │ │ │ ├── chat_share.dart
│ │ │ │ ├── empty.dart
│ │ │ │ ├── enhanced_selectable_text.dart
│ │ │ │ ├── file_upload.dart
│ │ │ │ ├── help_tips.dart
│ │ │ │ ├── markdown/
│ │ │ │ │ ├── citation.dart
│ │ │ │ │ ├── code.dart
│ │ │ │ │ ├── latex/
│ │ │ │ │ │ ├── latex_block_syntax.dart
│ │ │ │ │ │ ├── latex_element_builder.dart
│ │ │ │ │ │ └── latex_inline_syntax.dart
│ │ │ │ │ └── latex.dart
│ │ │ │ ├── markdown.dart
│ │ │ │ ├── message_state_manager.dart
│ │ │ │ ├── role_avatar.dart
│ │ │ │ ├── search_result.dart
│ │ │ │ ├── thinking_card.dart
│ │ │ │ └── voice_record.dart
│ │ │ ├── chat_tools_button.dart
│ │ │ ├── column_block.dart
│ │ │ ├── credit.dart
│ │ │ ├── dialog.dart
│ │ │ ├── effect/
│ │ │ │ └── glass.dart
│ │ │ ├── enhanced_button.dart
│ │ │ ├── enhanced_error.dart
│ │ │ ├── enhanced_input.dart
│ │ │ ├── enhanced_popup_menu.dart
│ │ │ ├── enhanced_textfield.dart
│ │ │ ├── file_preview.dart
│ │ │ ├── gallery_item_share.dart
│ │ │ ├── global_alert.dart
│ │ │ ├── gradient_style.dart
│ │ │ ├── group_list_widget.dart
│ │ │ ├── icon_box.dart
│ │ │ ├── icon_box_button.dart
│ │ │ ├── image.dart
│ │ │ ├── image_action.dart
│ │ │ ├── image_preview.dart
│ │ │ ├── invite_card.dart
│ │ │ ├── item_selector.dart
│ │ │ ├── item_selector_search.dart
│ │ │ ├── loading.dart
│ │ │ ├── message_box.dart
│ │ │ ├── model_indicator.dart
│ │ │ ├── model_item.dart
│ │ │ ├── multi_item_selector.dart
│ │ │ ├── notify_message.dart
│ │ │ ├── pagination.dart
│ │ │ ├── password_field.dart
│ │ │ ├── prompt_tags_selector.dart
│ │ │ ├── random_avatar.dart
│ │ │ ├── room_card.dart
│ │ │ ├── rotating_widget.dart
│ │ │ ├── select_mode_toolbar.dart
│ │ │ ├── share.dart
│ │ │ ├── sliver_component.dart
│ │ │ ├── social_icon.dart
│ │ │ ├── take_photo.dart
│ │ │ ├── theme/
│ │ │ │ ├── custom_size.dart
│ │ │ │ ├── custom_theme.dart
│ │ │ │ └── theme.dart
│ │ │ ├── transition_resolver.dart
│ │ │ ├── verify_code_input.dart
│ │ │ ├── video_player.dart
│ │ │ ├── weak_text_button.dart
│ │ │ └── windows.dart
│ │ ├── creative_island/
│ │ │ ├── draw/
│ │ │ │ ├── artistic_qr.dart
│ │ │ │ ├── artistic_wordart.dart
│ │ │ │ ├── components/
│ │ │ │ │ ├── artistic_style_selector.dart
│ │ │ │ │ ├── box.dart
│ │ │ │ │ ├── content_preview.dart
│ │ │ │ │ ├── creative_item.dart
│ │ │ │ │ ├── image_selector.dart
│ │ │ │ │ ├── image_size.dart
│ │ │ │ │ └── image_style_selector.dart
│ │ │ │ ├── data/
│ │ │ │ │ └── draw_history_datasource.dart
│ │ │ │ ├── draw_create.dart
│ │ │ │ ├── draw_list.dart
│ │ │ │ ├── draw_result.dart
│ │ │ │ └── image_edit_direct.dart
│ │ │ ├── gallery/
│ │ │ │ ├── components/
│ │ │ │ │ └── image_card.dart
│ │ │ │ ├── data/
│ │ │ │ │ └── gallery_datasource.dart
│ │ │ │ ├── gallery.dart
│ │ │ │ └── gallery_item.dart
│ │ │ ├── my_creation.dart
│ │ │ └── my_creation_item.dart
│ │ ├── custom_scaffold.dart
│ │ ├── data/
│ │ │ ├── chat_history_datasource.dart
│ │ │ ├── group_message_datasource.dart
│ │ │ └── notification_datasource.dart
│ │ ├── drawer.dart
│ │ ├── home.dart
│ │ ├── lab/
│ │ │ ├── avatar_selector.dart
│ │ │ ├── creative_models.dart
│ │ │ ├── draw_board.dart
│ │ │ ├── prompt.dart
│ │ │ └── user_center.dart
│ │ └── setting/
│ │ ├── account_security.dart
│ │ ├── article.dart
│ │ ├── background_selector.dart
│ │ ├── bind_phone_page.dart
│ │ ├── change_password.dart
│ │ ├── custom_home_models.dart
│ │ ├── destroy_account.dart
│ │ ├── diagnosis.dart
│ │ ├── notification.dart
│ │ ├── openai_setting.dart
│ │ ├── retrieve_password_screen.dart
│ │ ├── setting_screen.dart
│ │ └── user_api_keys.dart
│ └── repo/
│ ├── api/
│ │ ├── admin/
│ │ │ ├── channels.dart
│ │ │ ├── models.dart
│ │ │ ├── payment.dart
│ │ │ └── users.dart
│ │ ├── article.dart
│ │ ├── creative.dart
│ │ ├── image_model.dart
│ │ ├── info.dart
│ │ ├── keys.dart
│ │ ├── model.dart
│ │ ├── notification.dart
│ │ ├── page.dart
│ │ ├── payment.dart
│ │ ├── quota.dart
│ │ ├── room_gallery.dart
│ │ └── user.dart
│ ├── api_server.dart
│ ├── cache_repo.dart
│ ├── chat_message_repo.dart
│ ├── creative_island_repo.dart
│ ├── data/
│ │ ├── cache_data.dart
│ │ ├── chat_history.dart
│ │ ├── chat_message_data.dart
│ │ ├── creative_island_data.dart
│ │ ├── room_data.dart
│ │ └── settings_data.dart
│ ├── deepai_repo.dart
│ ├── model/
│ │ ├── chat_history.dart
│ │ ├── chat_message.dart
│ │ ├── creative_island_history.dart
│ │ ├── group.dart
│ │ ├── message.dart
│ │ ├── misc.dart
│ │ ├── model.dart
│ │ └── room.dart
│ ├── openai_repo.dart
│ ├── settings_repo.dart
│ └── stabilityai_repo.dart
├── linux/
│ ├── .gitignore
│ ├── CMakeLists.txt
│ ├── flutter/
│ │ ├── CMakeLists.txt
│ │ ├── generated_plugin_registrant.cc
│ │ ├── generated_plugin_registrant.h
│ │ └── generated_plugins.cmake
│ ├── main.cc
│ ├── my_application.cc
│ └── my_application.h
├── macos/
│ ├── .gitignore
│ ├── Flutter/
│ │ ├── Flutter-Debug.xcconfig
│ │ ├── Flutter-Release.xcconfig
│ │ └── GeneratedPluginRegistrant.swift
│ ├── Podfile
│ ├── Runner/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ └── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ ├── Base.lproj/
│ │ │ └── MainMenu.xib
│ │ ├── Configs/
│ │ │ ├── AppInfo.xcconfig
│ │ │ ├── Debug.xcconfig
│ │ │ ├── Release.xcconfig
│ │ │ └── Warnings.xcconfig
│ │ ├── DebugProfile.entitlements
│ │ ├── Info.plist
│ │ ├── MainFlutterWindow.swift
│ │ └── Release.entitlements
│ ├── Runner.xcodeproj/
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace/
│ │ │ └── xcshareddata/
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── Runner.xcscheme
│ └── Runner.xcworkspace/
│ ├── contents.xcworkspacedata
│ └── xcshareddata/
│ └── IDEWorkspaceChecks.plist
├── nginx.conf
├── pubspec.yaml
├── scripts/
│ ├── go.mod
│ ├── go.sum
│ ├── macos-icon-replace.sh
│ └── main.go
├── web/
│ ├── index.html
│ ├── manifest.json
│ ├── splash/
│ │ ├── splash.js
│ │ └── style.css
│ ├── sqflite_sw.js
│ └── sqlite3.wasm
└── windows/
├── .gitignore
├── CMakeLists.txt
├── flutter/
│ ├── CMakeLists.txt
│ ├── generated_plugin_registrant.cc
│ ├── generated_plugin_registrant.h
│ └── generated_plugins.cmake
└── runner/
├── CMakeLists.txt
├── Runner.rc
├── flutter_window.cpp
├── flutter_window.h
├── main.cpp
├── resource.h
├── runner.exe.manifest
├── utils.cpp
├── utils.h
├── win32_window.cpp
└── win32_window.h
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/build_windows_app.yml
================================================
name: Build Windows app
on:
push:
branches:
- main
- develop
- main-pc
jobs:
build:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Flutter
uses: subosito/flutter-action@v2.10.0
with:
flutter-version: "3.19.2" # Set flutter version here
- name: Build Windows app
#run: flutter build windows --release
run: dart run msix:create --release -v --output-path build/windows/runner --output-name AIdea --install-certificate false
# - name: Copy dependencies
# run: copy .\windows\sqlite3.dll .\build\windows\runner\Release\
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: aidea
path: build/windows/runner/AIdea.msix
================================================
FILE: .gitignore
================================================
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
Makefile.local
================================================
FILE: .metadata
================================================
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled.
version:
revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
channel: stable
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
- platform: android
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
- platform: ios
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
- platform: linux
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
- platform: macos
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
- platform: web
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
- platform: windows
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
================================================
FILE: AppRun
================================================
#!/bin/sh
cd "$(dirname "$0")"
exec ./askaide
================================================
FILE: Dockerfile
================================================
FROM nginx:1.25
COPY build/web/ /data/webroot
COPY nginx.conf /etc/nginx/conf.d/default.conf
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2023 管宜尧
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Makefile
================================================
ipa:
flutter build ipa --no-tree-shake-icons --release
open build/ios/ipa
run:
flutter run --release
build-all: build-android ipa build-dmg
rm -fr build/release
mkdir -p build/release
mv build/app/outputs/flutter-apk/app-release.apk build/release/aidea-android.apk
mv build/ios/ipa/askaide.ipa build/release/aidea-ios.ipa
mv build/macos/Build/Products/Package/AIdea-Installer.dmg build/release/aidea-macos.dmg
open build/release
build-android:
flutter build apk --release --no-tree-shake-icons
build-and-sync-android: build-android
mv build/app/outputs/flutter-apk/app-release.apk /Users/mylxsw/ResilioSync/ResilioSync/临时文件/aidea-release.apk
build-macos:
flutter build macos --no-tree-shake-icons --release
codesign -f -s "Developer ID Application: YIYAO GUAN (N95437SZ2A)" build/macos/Build/Products/Release/AIdea.app
build-appimage:
flutter build linux --no-tree-shake-icons --release
mkdir -p aidea_app.AppDir
cp -r build/linux/x64/release/bundle/* aidea_app.AppDir
cp assets/app.png aidea_app.AppDir/
cp AppRun aidea_app.AppDir/
cp askaide.desktop aidea_app.AppDir/
appimagetool aidea_app.AppDir/
build-dmg: build-macos
rm -fr build/macos/Build/Products/Package
mkdir -p build/macos/Build/Products/Package && cp -r build/macos/Build/Products/Release/AIdea.app build/macos/Build/Products/Package
create-dmg --volname "AIdea Installer" \
--volicon "install.icns" \
--background "background.jpg" \
--window-pos 200 120 \
--window-size 600 320 \
--icon-size 100 \
--icon "AIdea.app" 170 130 \
--hide-extension "AIdea.app" \
--app-drop-link 430 130 \
--sandbox-safe \
--no-internet-enable \
"build/macos/Build/Products/Package/AIdea-Installer.dmg" \
"build/macos/Build/Products/Package"
open build/macos/Build/Products/Package/
build-web:
#flutter build web --web-renderer canvaskit --release --dart-define=FLUTTER_WEB_CANVASKIT_URL=https://resources.aicode.cc/canvaskit/
flutter build web --web-renderer canvaskit --release
cd scripts && go run main.go ../build/web/main.dart.js && cd ..
build-web-samehost:
flutter build web --web-renderer canvaskit --release --dart-define=API_SERVER_URL=/
cd scripts && go run main.go ../build/web/main.dart.js && cd ..
deploy-web: build-web
cd build && tar -zcvf web.tar.gz web
scp build/web.tar.gz huawei-1:/data/webroot
ssh huawei-1 "cd /data/webroot && tar -zxvf web.tar.gz && rm -rf web.tar.gz app && mv web app"
rm -fr build/web.tar.gz
.PHONY: run build-android build-macos ipa build-web-samehost build-web deploy-web build-dmg build-all build-and-sync-android
================================================
FILE: README.md
================================================
# AIdea - AI Chat, Collaboration & Image Generation
English | [中文](./README.zh-CN.md)
[](https://app.fossa.com/projects/custom%2B39727%2Fgithub.com%2Fmylxsw%2Faidea?ref=badge_shield)


An APP that integrates mainstream large language models and image generation models, built with Flutter, with fully open-source code.
Download & Try:
https://aidea.aicode.cc
Open Source Repositories:
- Client: https://github.com/mylxsw/aidea
- Server: https://github.com/mylxsw/aidea-server
- Docker Deployment: https://github.com/mylxsw/aidea-docker
## Development & Build Environment
The default branch `main` is the v2 version, currently under active development. If you need to self-host, please switch to the [v1.x](https://github.com/mylxsw/aidea/tree/v1.x) branch.
```bash
git checkout v1.x
```
To set up a development environment for compiling and packaging the APP, you can refer to the following articles (more articles will be added over time):
- [AIdea Development Environment Tutorial (1): Flutter Frontend Setup](https://mp.weixin.qq.com/s/bgAIH6s7t5IREusK_WtpRg)
- [AIdea Development Environment Tutorial (2): Golang Server Setup](https://mp.weixin.qq.com/s?__biz=MzA3NTU1NDk4Mg==&mid=2454663711&idx=1&sn=c2c66abc20f8e0900afe645ff1f552ac&chksm=88d55bd6bfa2d2c063ea15a4e8864c197009b49233c710b85725f1aa946836e15a26439c69a7&scene=178&cur_album_id=3204997940193296389#rd)
- [AIdea Development Environment Tutorial (3): Windows Build Environment Setup](https://mp.weixin.qq.com/s?__biz=MzA3NTU1NDk4Mg==&mid=2454663731&idx=1&sn=2aa4841daeb8dc4132e8abe63f585996&chksm=88d55bfabfa2d2ecce8224dcf23da6f911d3d8324121d141fd5c0324197c6f4845dd63639ac2&scene=178&cur_album_id=3204997940193296389#rd)
- [Flutter App Windows Installer Creation Tutorial](https://mp.weixin.qq.com/s?__biz=MzA3NTU1NDk4Mg==&mid=2454663689&idx=1&sn=73c93edd9ddacb2d4c36061cc79be059&chksm=88d55bc0bfa2d2d6ecaa7979835431467105d9572953f1e96c0f735df3fe60d4f6d6137f041d&scene=178&cur_album_id=3204997940193296389#rd)
> Some users encounter build failures, which can be very frustrating. This is not a bug -- it is a known characteristic of Flutter. Build failures are common as the Flutter version changes.
> To be safe, you can refer to my local environment configuration:
>
> 
## Self-Hosting / Private Deployment
If you do not want to use the managed cloud service, you can deploy the server yourself. [See deployment instructions here](https://github.com/mylxsw/aidea-server/blob/main/docs/deploy.md).
If you prefer not to set it up yourself, you can contact me for assisted deployment. See [VIP Deployment Service](https://github.com/mylxsw/aidea-server/blob/main/docs/deploy-vip.md) for details.
## Community & Support
- WeChat Tech Group: Add WeChat ID `x-prometheus` as a friend for an invitation to the group.
- WeChat Official Account:
## Screenshots
Mobile

MacOS

Windows

## License
MIT
Copyright (c) 2025, mylxsw
================================================
FILE: README.zh-CN.md
================================================
# AIdea - AI 聊天、协作、图像生成
[English](./README.md) | 中文
[](https://app.fossa.com/projects/custom%2B39727%2Fgithub.com%2Fmylxsw%2Faidea?ref=badge_shield)


一款集成了主流大语言模型以及绘图模型的 APP, 采用 Flutter 开发,代码完全开源。
下载体验地址:
https://aidea.aicode.cc
开源代码:
- 客户端:https://github.com/mylxsw/aidea
- 服务端:https://github.com/mylxsw/aidea-server
- Docker 部署:https://github.com/mylxsw/aidea-docker
## 开发、编译运行环境
默认分支 `main` 是 v2 版本,当前正在开发中,如需自己部署,请切换到 [v1.x](https://github.com/mylxsw/aidea/tree/v1.x) 分支。
```bash
git checkout v1.x
```
搭建开发环境,用来编译和打包 APP,可以参考下面的文章,更多文章后面有时间了会持续更新。
- [AIdea 项目开发环境部署教程(一)前端 Flutter 环境搭建](https://mp.weixin.qq.com/s/bgAIH6s7t5IREusK_WtpRg)
- [AIdea 项目开发环境部署教程(二)服务端 Golang 环境搭建](https://mp.weixin.qq.com/s?__biz=MzA3NTU1NDk4Mg==&mid=2454663711&idx=1&sn=c2c66abc20f8e0900afe645ff1f552ac&chksm=88d55bd6bfa2d2c063ea15a4e8864c197009b49233c710b85725f1aa946836e15a26439c69a7&scene=178&cur_album_id=3204997940193296389#rd)
- [AIdea 项目开发环境部署教程(三)Windows 编译环境搭建](https://mp.weixin.qq.com/s?__biz=MzA3NTU1NDk4Mg==&mid=2454663731&idx=1&sn=2aa4841daeb8dc4132e8abe63f585996&chksm=88d55bfabfa2d2ecce8224dcf23da6f911d3d8324121d141fd5c0324197c6f4845dd63639ac2&scene=178&cur_album_id=3204997940193296389#rd)
- [Flutter 应用 Windows 安装包创建教程](https://mp.weixin.qq.com/s?__biz=MzA3NTU1NDk4Mg==&mid=2454663689&idx=1&sn=73c93edd9ddacb2d4c36061cc79be059&chksm=88d55bc0bfa2d2d6ecaa7979835431467105d9572953f1e96c0f735df3fe60d4f6d6137f041d&scene=178&cur_album_id=3204997940193296389#rd)
> 有些小伙伴在编译的时候总是失败,非常令人抓狂,这并不是什么问题,而是 Flutter 特有的特性,随着 Flutter 版本的变化,编译失败是常态。
> 保险起见,你可以参考我的本地环境配置:
>
> 
## 私有化部署
如果你不想使用托管的云服务,可以自己部署服务端,[部署请看这里](https://github.com/mylxsw/aidea-server/blob/main/docs/deploy.md)。
不想自己折腾,可以找我来帮你部署,详情参考 [服务器代部署说明](https://github.com/mylxsw/aidea-server/blob/main/docs/deploy-vip.md)。
## 技术交流
- 微信技术交流群:请添加微信号 `x-prometheus` 为好友,拉你进群。
- 微信公众号
## 产品截图
移动端

MacOS 端

Windows 端

## License
MIT
Copyright (c) 2025, mylxsw
================================================
FILE: analysis_options.yaml
================================================
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
================================================
FILE: android/.gitignore
================================================
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
**/*.keystore
**/*.jks
================================================
FILE: android/app/build.gradle
================================================
plugins {
id "com.android.application"
id "kotlin-android"
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
namespace 'cc.aicode.flutter.askaide.askaide'
compileSdkVersion flutter.compileSdkVersion
// ndkVersion flutter.ndkVersion
ndkVersion "27.0.12077973"
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "cc.aicode.flutter.askaide.askaide"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
// minSdkVersion flutter.minSdkVersion
minSdkVersion 23
targetSdkVersion flutter.targetSdkVersion
versionCode flutter.versionCode
versionName flutterVersionName
}
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
flutter {
source '../..'
}
================================================
FILE: android/app/proguard-rules.pro
================================================
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivity$g
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter$Args
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter$Error
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningEphemeralKeyProvider
================================================
FILE: android/app/src/debug/AndroidManifest.xml
================================================
================================================
FILE: android/app/src/main/AndroidManifest.xml
================================================
================================================
FILE: android/app/src/main/kotlin/cc/aicode/flutter/askaide/askaide/MainActivity.kt
================================================
package cc.aicode.flutter.askaide.askaide
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.android.FlutterFragmentActivity
class MainActivity: FlutterFragmentActivity() {
}
================================================
FILE: android/app/src/main/res/drawable/launch_background.xml
================================================
-
-
================================================
FILE: android/app/src/main/res/drawable-v21/launch_background.xml
================================================
-
-
================================================
FILE: android/app/src/main/res/values/styles.xml
================================================
================================================
FILE: android/app/src/main/res/values-night/styles.xml
================================================
================================================
FILE: android/app/src/main/res/values-night-v31/styles.xml
================================================
================================================
FILE: android/app/src/main/res/values-v31/styles.xml
================================================
================================================
FILE: android/app/src/profile/AndroidManifest.xml
================================================
================================================
FILE: android/build.gradle
================================================
allprojects {
repositories {
google()
mavenCentral()
}
subprojects {
afterEvaluate { project ->
if (project.hasProperty('android')) {
project.android {
if (namespace == null) {
namespace project.group
}
}
}
if (project.hasProperty("android")) {
project.android {
compileSdkVersion = 34
}
}
}
}
}
rootProject.buildDir = "../build"
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
================================================
FILE: android/gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
================================================
FILE: android/gradle.properties
================================================
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
kotlin.jvm.target.validation.mode = IGNORE
================================================
FILE: android/settings.gradle
================================================
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}()
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version '8.7.2' apply false
id "org.jetbrains.kotlin.android" version "1.9.20" apply false
}
include ":app"
================================================
FILE: askaide.desktop
================================================
[Desktop Entry]
Version=1.0
Name=Ask aide
Comment=Ask aide!
Icon=app
Exec=askaide %U
Terminal=false
Type=Application
Categories=Utility;
Keywords=Internet;
StartupNotify=false
================================================
FILE: assets/lottie/empty_status.json
================================================
{"v":"4.5.9","fr":25,"ip":0,"op":100,"w":248,"h":187,"ddd":0,"assets":[],"layers":[{"ddd":0,"ind":0,"ty":4,"nm":"\rtail","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[21],"e":[-60]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":12,"s":[-60],"e":[21]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":25,"s":[21],"e":[-60]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":37,"s":[-60],"e":[21]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":50,"s":[21],"e":[-60]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":62,"s":[-60],"e":[21]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":74,"s":[21],"e":[-60]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":87,"s":[-60],"e":[21]},{"t":99}]},"p":{"a":0,"k":[80,44.429,0]},"a":{"a":0,"k":[8.473,9.453,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[-1.739,1.217],[0,0],[2.934,-2.055],[0.434,-0.631],[0.467,0.666],[0,0],[-2.054,-2.934],[-1.467,-0.418],[0,0]],"o":[[0,0],[1.521,0.11],[2.933,-2.055],[0,0],[-0.666,0.466],[-0.192,-0.741],[-2.054,-2.934],[0,0],[1.218,1.738],[0,0],[0,0]],"v":[[-0.161,5.232],[-0.161,5.232],[5.32,4.011],[8.702,-2.465],[1.462,-1.499],[-0.177,0.187],[-1.14,-1.958],[-7.614,-5.342],[-6.649,1.901],[-1.916,4.923],[-1.916,4.923]],"c":true}},"nm":"路径 1","mn":"ADBE Vector Shape - Group"},{"ty":"fl","c":{"a":0,"k":[0.769,0.769,0.851,1]},"o":{"a":0,"k":100},"r":1,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[8.952,5.591],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"mn":"ADBE Vector Group"}],"ip":0,"op":750,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":1,"ty":4,"nm":"\reye","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[145.857,82.715,0]},"a":{"a":0,"k":[5.964,5.965,0]},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":42,"s":[100,100,100],"e":[100,1,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":45,"s":[100,1,100],"e":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":48,"s":[100,100,100],"e":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":93,"s":[100,100,100],"e":[100,1,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":96,"s":[100,1,100],"e":[100,100,100]},{"t":99}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-3.156,0],[0,-3.156],[3.156,0],[0,3.156]],"o":[[3.156,0],[0,3.156],[-3.156,0],[0,-3.156]],"v":[[0,-5.715],[5.714,-0.001],[0,5.715],[-5.714,-0.001]],"c":true}},"nm":"路径 1","mn":"ADBE Vector Shape - Group"},{"ty":"fl","c":{"a":0,"k":[0.671,0.671,0.761,1]},"o":{"a":0,"k":100},"r":1,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[5.964,5.965],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"mn":"ADBE Vector Group"}],"ip":0,"op":750,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":2,"ty":4,"nm":"\reyes white","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[143.714,82.714,0]},"a":{"a":0,"k":[11.679,11.678,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-6.312,0],[0,6.312],[6.312,0],[0,-6.311]],"o":[[6.312,0],[0,-6.311],[-6.312,0],[0,6.312]],"v":[[0,11.428],[11.429,-0.001],[0,-11.428],[-11.429,-0.001]],"c":true}},"nm":"路径 1","mn":"ADBE Vector Shape - Group"},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[11.679,11.678],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"mn":"ADBE Vector Group"}],"ip":0,"op":750,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":3,"ty":4,"nm":"\rbody","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[123.719,72.664,0]},"a":{"a":0,"k":[60.153,37.965,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[31.5,0],[10.189,-7.981],[1.68,1.422],[0,0],[0,0],[4.418,-6.529],[0,-12.88],[0,0],[-3.015,-6.029],[0,0],[-12.453,0],[-6.911,9.934]],"o":[[-13.946,0],[-1.536,1.204],[0,0],[0,0],[-0.153,2.463],[-6.908,9.775],[0,0],[10.063,-5.593],[0,0],[9.428,-2.285],[20.38,0],[-2.476,-30.863]],"v":[[0.097,-37.715],[-36.872,-24.975],[-42.158,-26.982],[-43.403,-27.495],[-44.57,-27.098],[-48.937,-12.301],[-59.903,22.286],[-59.499,29.285],[-33.617,30.857],[-1.617,30.857],[23.526,37.715],[59.903,17.423]],"c":true}},"nm":"路径 1","mn":"ADBE Vector Shape - Group"},{"ty":"fl","c":{"a":0,"k":[0.769,0.769,0.851,1]},"o":{"a":0,"k":100},"r":1,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[60.153,37.965],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"mn":"ADBE Vector Group"}],"ip":0,"op":750,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":4,"ty":4,"nm":"\rfin","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[104.536,103.099,0]},"a":{"a":0,"k":[24.733,0.25,0]},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":0,"s":[100,100,100],"e":[100,70,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":6,"s":[100,70,100],"e":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":12,"s":[100,100,100],"e":[100,56,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":18,"s":[100,56,100],"e":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":24,"s":[100,100,100],"e":[100,70,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":30,"s":[100,70,100],"e":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":36,"s":[100,100,100],"e":[100,56,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":43,"s":[100,56,100],"e":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":49,"s":[100,100,100],"e":[100,70,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":55,"s":[100,70,100],"e":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":61,"s":[100,100,100],"e":[100,56,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":67,"s":[100,56,100],"e":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":74,"s":[100,100,100],"e":[100,70,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":80,"s":[100,70,100],"e":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":87,"s":[100,100,100],"e":[100,56,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":94,"s":[100,56,100],"e":[100,100,100]},{"t":100}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[15.429,0],[3.714,7.429]],"o":[[-9.429,2.286],[-12,0],[0,0]],"v":[[21,-8.572],[-9,8.572],[-11,-8.572]],"c":true}},"nm":"路径 1","mn":"ADBE Vector Shape - Group"},{"ty":"fl","c":{"a":0,"k":[0.769,0.769,0.851,1]},"o":{"a":0,"k":100},"r":1,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[21.25,8.822],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"mn":"ADBE Vector Group"}],"ip":0,"op":750,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":5,"ty":4,"nm":"\rbelly5","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[88.286,85.293,0]},"a":{"a":0,"k":[24.536,34.528,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[18.68,0.296],[2.348,-3.47],[0,-12.88],[-2.993,-7.135],[-3.347,0],[0,18.935]],"o":[[-0.485,2.668],[-6.908,9.775],[0,8.221],[3.058,0.894],[18.935,0],[0,-18.751]],"v":[[-9.448,-34.279],[-13.32,-24.878],[-24.286,9.708],[-19.636,32.896],[-10,34.279],[24.286,-0.007]],"c":true}},"nm":"路径 1","mn":"ADBE Vector Shape - Group"},{"ty":"fl","c":{"a":0,"k":[0.973,0.973,0.988,1]},"o":{"a":0,"k":100},"r":1,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[24.536,34.528],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"mn":"ADBE Vector Group"}],"ip":0,"op":750,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":6,"ty":4,"nm":"\rbelly4","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[98.286,85.659,0]},"a":{"a":0,"k":[34.536,44.162,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[20.602,3.796],[2.903,-2.274],[1.714,0.858],[6.177,-9.127],[0,-12.88],[-5.829,-9.25],[-5.292,0],[0,24.458]],"o":[[-3.298,1.715],[-2.142,1.678],[-1.714,-0.857],[-6.908,9.775],[0,11.742],[4.705,1.702],[24.458,0],[0,-21.701]],"v":[[-1.929,-43.912],[-11.254,-37.921],[-18.286,-41.231],[-23.32,-25.246],[-34.286,9.341],[-25.071,41.278],[-10,43.912],[34.286,-0.373]],"c":true}},"nm":"路径 1","mn":"ADBE Vector Shape - Group"},{"ty":"fl","c":{"a":0,"k":[0.945,0.945,0.973,1]},"o":{"a":0,"k":100},"r":1,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[34.536,44.162],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"mn":"ADBE Vector Group"}],"ip":0,"op":750,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":7,"ty":4,"nm":"\rbelly3","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[108.286,87.383,0]},"a":{"a":0,"k":[44.535,52.439,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[19.58,8.198],[9.016,-7.063],[1.714,0.857],[6.178,-9.128],[0,-12.88],[-10.446,-10.797],[-6.088,0],[0,29.981]],"o":[[-12.096,0.951],[-2.141,1.677],[-1.715,-0.857],[-6.907,9.775],[0,16.21],[5.464,1.848],[29.982,0],[0,-22.556]],"v":[[10.948,-52.189],[-21.255,-39.644],[-28.286,-42.954],[-33.321,-26.969],[-44.286,7.617],[-27.4,49.323],[-10.001,52.189],[44.286,-2.097]],"c":true}},"nm":"路径 1","mn":"ADBE Vector Shape - Group"},{"ty":"fl","c":{"a":0,"k":[0.91,0.914,0.961,1]},"o":{"a":0,"k":100},"r":1,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[44.535,52.439],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"mn":"ADBE Vector Group"}],"ip":0,"op":750,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":8,"ty":4,"nm":"\rbelly2","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[118.286,92.286,0]},"a":{"a":0,"k":[54.535,57.536,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,35.504],[7.996,10.717],[13.435,0],[10.189,-7.981],[1.714,0.857],[6.177,-9.128],[0,-12.879],[-19.548,-9.89],[-3.867,0]],"o":[[0,-14.399],[-10.001,-7.459],[-13.946,0],[-2.141,1.677],[-1.715,-0.857],[-6.907,9.775],[0,23.406],[3.679,0.655],[35.504,0]],"v":[[54.286,-7],[41.553,-45.409],[5.715,-57.286],[-31.255,-44.547],[-38.286,-47.857],[-43.321,-31.872],[-54.286,2.714],[-21.327,56.27],[-10.001,57.286]],"c":true}},"nm":"路径 1","mn":"ADBE Vector Shape - Group"},{"ty":"fl","c":{"a":0,"k":[0.886,0.886,0.937,1]},"o":{"a":0,"k":100},"r":1,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[54.535,57.536],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"mn":"ADBE Vector Group"}],"ip":0,"op":750,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":9,"ty":4,"nm":"\rbelly1","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[124,95,0]},"a":{"a":0,"k":[60.25,60.25,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-33.137,0],[0,33.137],[33.137,0],[10.189,-7.981],[1.714,0.857],[6.177,-9.127],[0,-12.88]],"o":[[33.137,0],[0,-33.137],[-13.946,0],[-2.141,1.677],[-1.714,-0.858],[-6.908,9.776],[0,33.137]],"v":[[0,60],[60,0],[0,-60],[-36.969,-47.261],[-44,-50.571],[-49.034,-34.587],[-60,0]],"c":true}},"nm":"路径 1","mn":"ADBE Vector Shape - Group"},{"ty":"fl","c":{"a":0,"k":[0.839,0.843,0.906,1]},"o":{"a":0,"k":100},"r":1,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[60.25,60.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"mn":"ADBE Vector Group"}],"ip":0,"op":750,"st":0,"bm":0,"sr":1}]}
================================================
FILE: build-win-msix.bat
================================================
dart run msix:create --release -v --output-path build/windows/runner --output-name AIdea
================================================
FILE: build-win.bat
================================================
flutter build windows --release
================================================
FILE: devtools_options.yaml
================================================
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
================================================
FILE: docker-build.sh
================================================
#!/usr/bin/env bash
VERSION=1.0.14
rm -fr build/web
flutter build web --web-renderer canvaskit --release --dart-define=API_SERVER_URL=/
docker buildx build --platform=linux/amd64,linux/arm64 -t mylxsw/aidea-web:$VERSION . --push
================================================
FILE: flutter_launcher_icons.yaml
================================================
# flutter pub run flutter_launcher_icons
flutter_launcher_icons:
image_path: "assets/app.png"
android: "launcher_icon"
# image_path_android: "assets/icon/icon.png"
min_sdk_android: 21 # android min sdk min:16, default 21
# adaptive_icon_background: "assets/icon/background.png"
# adaptive_icon_foreground: "assets/icon/foreground.png"
# adaptive_icon_monochrome: "assets/icon/monochrome.png"
ios: true
# image_path_ios: "assets/icon/icon.png"
remove_alpha_channel_ios: true
# image_path_ios_dark_transparent: "assets/icon/icon_dark.png"
# image_path_ios_tinted_grayscale: "assets/icon/icon_tinted.png"
# desaturate_tinted_to_grayscale_ios: true
web:
generate: true
# image_path: "path/to/image.png"
background_color: "#hexcode"
theme_color: "#hexcode"
windows:
generate: true
image_path: "assets/app-macos.png"
icon_size: 256 # min:48, max:256, default: 48
macos:
generate: true
image_path: "assets/app-macos.png"
================================================
FILE: install.iss
================================================
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppName "AIdea"
#define MyAppVersion "2.0.0"
#define MyAppPublisher "Shenzhen Gulu Artificial Intelligence Technology Co., Ltd."
#define MyAppURL "https://ai.aicode.cc/"
#define MyAppExeName "AIdea.exe"
[Setup]
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{F9E7E323-8BD4-46B3-ABEB-20C5CE03F5C7}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={autopf}\{#MyAppName}
DisableProgramGroupPage=yes
; Uncomment the following line to run in non administrative install mode (install for current user only.)
;PrivilegesRequired=lowest
OutputDir=C:\Users\mylxsw\Desktop
OutputBaseFilename={#MyAppName}-{#MyAppVersion}-Installer
SetupIconFile=D:\Workstation\codes\aidea\app.ico
Compression=lzma
SolidCompression=yes
WizardStyle=modern
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: checkablealone
[Files]
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-console-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-console-l1-2-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-datetime-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-debug-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-errorhandling-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-fibers-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-file-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-file-l1-2-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-file-l2-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-handle-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-heap-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-interlocked-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-libraryloader-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-localization-l1-2-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-memory-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-namedpipe-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-processenvironment-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-processthreads-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-processthreads-l1-1-1.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-profile-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-rtlsupport-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-string-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-synch-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-synch-l1-2-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-sysinfo-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-timezone-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-core-util-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-crt-conio-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-crt-convert-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-crt-environment-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-crt-filesystem-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-crt-heap-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-crt-locale-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-crt-math-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-crt-multibyte-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-crt-private-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-crt-process-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-crt-runtime-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-crt-stdio-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-crt-string-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-crt-time-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-crt-utility-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-downlevel-kernel32-l2-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\api-ms-win-eventing-provider-l1-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\audioplayers_windows_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\concrt140.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\d3dcompiler_47.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\file_saver_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\flutter_localization_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\flutter_tts_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\flutter_windows.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\libc++.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\libEGL.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\libGLESv2.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\libmpv-2.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\media_kit_libs_windows_video_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\media_kit_native_event_loop.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\media_kit_video_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\msvcp140.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\msvcp140_1.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\msvcp140_2.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\msvcp140_atomic_wait.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\msvcp140_codecvt_ids.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\record_windows_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\screen_brightness_windows_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\share_plus_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\sqlite3.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\ucrtbase.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\ucrtbased.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\url_launcher_windows_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\vccorlib140.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\vccorlib140d.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\vcruntime140.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\vcruntime140_1.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\vcruntime140_1d.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\vcruntime140d.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\vk_swiftshader.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\vulkan-1.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\zlib.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\Workstation\codes\aidea\build\windows\x64\runner\Release\data\*"; DestDir: "{app}\data"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
================================================
FILE: ios/.gitignore
================================================
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3
================================================
FILE: ios/Flutter/AppFrameworkInfo.plist
================================================
CFBundleDevelopmentRegion
en
CFBundleExecutable
App
CFBundleIdentifier
io.flutter.flutter.app
CFBundleInfoDictionaryVersion
6.0
CFBundleName
App
CFBundlePackageType
FMWK
CFBundleShortVersionString
1.0
CFBundleSignature
????
CFBundleVersion
1.0
MinimumOSVersion
12.0
================================================
FILE: ios/Flutter/Debug.xcconfig
================================================
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
================================================
FILE: ios/Flutter/Release.xcconfig
================================================
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
================================================
FILE: ios/Podfile
================================================
# Uncomment this line to define a global platform for your project
platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end
================================================
FILE: ios/Runner/AppDelegate.swift
================================================
import UIKit
import Flutter
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
================================================
FILE: ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{"images":[{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"Icon-App-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"Icon-App-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
================================================
FILE: ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json
================================================
{
"images" : [
{
"filename" : "background.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
================================================
FILE: ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
================================================
{
"images" : [
{
"filename" : "LaunchImage.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "LaunchImage@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "LaunchImage@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
================================================
FILE: ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
================================================
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You 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.
================================================
FILE: ios/Runner/Base.lproj/LaunchScreen.storyboard
================================================
================================================
FILE: ios/Runner/Base.lproj/Main.storyboard
================================================
================================================
FILE: ios/Runner/Info.plist
================================================
CADisableMinimumFrameDurationOnPhone
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleDisplayName
AIdea
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
$(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleInfoDictionaryVersion
6.0
CFBundleName
askaide
CFBundlePackageType
APPL
CFBundleShortVersionString
$(FLUTTER_BUILD_NAME)
CFBundleSignature
????
CFBundleVersion
$(FLUTTER_BUILD_NUMBER)
ITSAppUsesNonExemptEncryption
LSApplicationQueriesSchemes
weixin
weixinURLParamsAPI
weixinULAPI
LSRequiresIPhoneOS
LSSupportsOpeningDocumentsInPlace
NSAppTransportSecurity
NSAllowsArbitraryLoads
NSAllowsArbitraryLoadsForMedia
NSCameraUsageDescription
Used to demonstrate image picker plugin
NSDocumentsFolderUsageDescription
$(PRODUCT_NAME) needs access to your documents folder to save your files.
NSMicrophoneUsageDescription
We need to access to the microphone to record audio file
NSPhotoLibraryUsageDescription
Used to demonstrate image picker plugin
NSLocationWhenInUseUsageDescription
To enable GPS location access for Exif data
UIApplicationSupportsIndirectInputEvents
UIFileSharingEnabled
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
Main
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
UISupportedInterfaceOrientations~ipad
UIInterfaceOrientationPortrait
UIInterfaceOrientationPortraitUpsideDown
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
UIViewControllerBasedStatusBarAppearance
UIStatusBarHidden
================================================
FILE: ios/Runner/Runner-Bridging-Header.h
================================================
#import "GeneratedPluginRegistrant.h"
================================================
FILE: ios/Runner/Runner.entitlements
================================================
aps-environment
development
com.apple.developer.applesignin
Default
com.apple.developer.associated-domains
applinks:ai.aicode.cc
================================================
FILE: ios/Runner.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
4AB86C8F0973565BB3C184F4 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 16B3CC3FDED8D8CC86722DEC /* Pods_Runner.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
A5ECBA6F2A2B177200E3A820 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5ECBA6E2A2B177200E3A820 /* StoreKit.framework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
16B3CC3FDED8D8CC86722DEC /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
A5D102132A0AA8E200331391 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; };
A5ECBA6E2A2B177200E3A820 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
A6F16BE481E65688799479DB /* 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 = ""; };
D5F820BBFB52D6BE21AD358D /* 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 = ""; };
EA2292F51AC4CF434D7A04AF /* 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 = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
4AB86C8F0973565BB3C184F4 /* Pods_Runner.framework in Frameworks */,
A5ECBA6F2A2B177200E3A820 /* StoreKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
652A687C2637E7BC60520D2C /* Frameworks */ = {
isa = PBXGroup;
children = (
A5ECBA6E2A2B177200E3A820 /* StoreKit.framework */,
16B3CC3FDED8D8CC86722DEC /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
F0FF12F5C5F28587D2089C68 /* Pods */,
652A687C2637E7BC60520D2C /* Frameworks */,
);
sourceTree = "";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
A5D102132A0AA8E200331391 /* Runner.entitlements */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "";
};
F0FF12F5C5F28587D2089C68 /* Pods */ = {
isa = PBXGroup;
children = (
D5F820BBFB52D6BE21AD358D /* Pods-Runner.debug.xcconfig */,
A6F16BE481E65688799479DB /* Pods-Runner.release.xcconfig */,
EA2292F51AC4CF434D7A04AF /* Pods-Runner.profile.xcconfig */,
);
path = Pods;
sourceTree = "";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
5E113495E8126976AA1B9B6D /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
13DFE0107180EC2050B1ABEA /* [CP] Embed Pods Frameworks */,
8C0A97AF55BE760FDB31E756 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
13DFE0107180EC2050B1ABEA /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
5E113495E8126976AA1B9B6D /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "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";
showEnvVarsInLog = 0;
};
8C0A97AF55BE760FDB31E756 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = N95437SZ2A;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64";
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
"-l\"WechatOpenSDK\"",
"-l\"c++\"",
"-l\"sqlite3\"",
"-l\"sqlite3.0\"",
"-l\"z\"",
"-framework",
"\"AVFoundation\"",
"-framework",
"\"AVKit\"",
"-framework",
"\"AlipaySDK\"",
"-framework",
"\"CFNetwork\"",
"-framework",
"\"CoreGraphics\"",
"-framework",
"\"CoreMotion\"",
"-framework",
"\"CoreTelephony\"",
"-framework",
"\"CoreText\"",
"-framework",
"\"DKImagePickerController\"",
"-framework",
"\"DKPhotoGallery\"",
"-framework",
"\"Foundation\"",
"-framework",
"\"ImageIO\"",
"-framework",
"\"Mantle\"",
"-framework",
"\"Photos\"",
"-framework",
"\"QuartzCore\"",
"-framework",
"\"SDWebImage\"",
"-framework",
"\"SDWebImageWebPCoder\"",
"-framework",
"\"Security\"",
"-framework",
"\"SwiftyGif\"",
"-framework",
"\"SystemConfiguration\"",
"-framework",
"\"UIKit\"",
"-framework",
"\"WebKit\"",
"-framework",
"\"audioplayers_darwin\"",
"-framework",
"\"file_picker\"",
"-framework",
"\"file_saver\"",
"-framework",
"\"flutter_localization\"",
"-framework",
"\"flutter_native_splash\"",
"-framework",
"\"flutter_tts\"",
"-framework",
"\"fluwx\"",
"-framework",
"\"image_gallery_saver\"",
"-framework",
"\"in_app_purchase_storekit\"",
"-framework",
"\"libwebp\"",
"-framework",
"\"path_provider_foundation\"",
"-framework",
"\"share_plus\"",
"-framework",
"\"shared_preferences_foundation\"",
"-framework",
"\"sign_in_with_apple\"",
"-framework",
"\"sqflite\"",
"-framework",
"\"tobias\"",
"-framework",
"\"url_launcher_ios\"",
"-weak_framework",
"\"LinkPresentation\"",
"-ld64",
);
PRODUCT_BUNDLE_IDENTIFIER = cc.aicode.flutter.askaide.askaide;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = N95437SZ2A;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64";
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
"-l\"WechatOpenSDK\"",
"-l\"c++\"",
"-l\"sqlite3\"",
"-l\"sqlite3.0\"",
"-l\"z\"",
"-framework",
"\"AVFoundation\"",
"-framework",
"\"AVKit\"",
"-framework",
"\"AlipaySDK\"",
"-framework",
"\"CFNetwork\"",
"-framework",
"\"CoreGraphics\"",
"-framework",
"\"CoreMotion\"",
"-framework",
"\"CoreTelephony\"",
"-framework",
"\"CoreText\"",
"-framework",
"\"DKImagePickerController\"",
"-framework",
"\"DKPhotoGallery\"",
"-framework",
"\"Foundation\"",
"-framework",
"\"ImageIO\"",
"-framework",
"\"Mantle\"",
"-framework",
"\"Photos\"",
"-framework",
"\"QuartzCore\"",
"-framework",
"\"SDWebImage\"",
"-framework",
"\"SDWebImageWebPCoder\"",
"-framework",
"\"Security\"",
"-framework",
"\"SwiftyGif\"",
"-framework",
"\"SystemConfiguration\"",
"-framework",
"\"UIKit\"",
"-framework",
"\"WebKit\"",
"-framework",
"\"audioplayers_darwin\"",
"-framework",
"\"file_picker\"",
"-framework",
"\"file_saver\"",
"-framework",
"\"flutter_localization\"",
"-framework",
"\"flutter_native_splash\"",
"-framework",
"\"flutter_tts\"",
"-framework",
"\"fluwx\"",
"-framework",
"\"image_gallery_saver\"",
"-framework",
"\"in_app_purchase_storekit\"",
"-framework",
"\"libwebp\"",
"-framework",
"\"path_provider_foundation\"",
"-framework",
"\"share_plus\"",
"-framework",
"\"shared_preferences_foundation\"",
"-framework",
"\"sign_in_with_apple\"",
"-framework",
"\"sqflite\"",
"-framework",
"\"tobias\"",
"-framework",
"\"url_launcher_ios\"",
"-weak_framework",
"\"LinkPresentation\"",
"-ld64",
);
PRODUCT_BUNDLE_IDENTIFIER = cc.aicode.flutter.askaide.askaide;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = N95437SZ2A;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64";
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
"-l\"WechatOpenSDK\"",
"-l\"c++\"",
"-l\"sqlite3\"",
"-l\"sqlite3.0\"",
"-l\"z\"",
"-framework",
"\"AVFoundation\"",
"-framework",
"\"AVKit\"",
"-framework",
"\"AlipaySDK\"",
"-framework",
"\"CFNetwork\"",
"-framework",
"\"CoreGraphics\"",
"-framework",
"\"CoreMotion\"",
"-framework",
"\"CoreTelephony\"",
"-framework",
"\"CoreText\"",
"-framework",
"\"DKImagePickerController\"",
"-framework",
"\"DKPhotoGallery\"",
"-framework",
"\"Foundation\"",
"-framework",
"\"ImageIO\"",
"-framework",
"\"Mantle\"",
"-framework",
"\"Photos\"",
"-framework",
"\"QuartzCore\"",
"-framework",
"\"SDWebImage\"",
"-framework",
"\"SDWebImageWebPCoder\"",
"-framework",
"\"Security\"",
"-framework",
"\"SwiftyGif\"",
"-framework",
"\"SystemConfiguration\"",
"-framework",
"\"UIKit\"",
"-framework",
"\"WebKit\"",
"-framework",
"\"audioplayers_darwin\"",
"-framework",
"\"file_picker\"",
"-framework",
"\"file_saver\"",
"-framework",
"\"flutter_localization\"",
"-framework",
"\"flutter_native_splash\"",
"-framework",
"\"flutter_tts\"",
"-framework",
"\"fluwx\"",
"-framework",
"\"image_gallery_saver\"",
"-framework",
"\"in_app_purchase_storekit\"",
"-framework",
"\"libwebp\"",
"-framework",
"\"path_provider_foundation\"",
"-framework",
"\"share_plus\"",
"-framework",
"\"shared_preferences_foundation\"",
"-framework",
"\"sign_in_with_apple\"",
"-framework",
"\"sqflite\"",
"-framework",
"\"tobias\"",
"-framework",
"\"url_launcher_ios\"",
"-weak_framework",
"\"LinkPresentation\"",
"-ld64",
);
PRODUCT_BUNDLE_IDENTIFIER = cc.aicode.flutter.askaide.askaide;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
================================================
FILE: ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
================================================
FILE: ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
IDEDidComputeMac32BitWarning
================================================
FILE: ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
================================================
PreviewsEnabled
================================================
FILE: ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
================================================
================================================
FILE: ios/Runner.xcworkspace/contents.xcworkspacedata
================================================
================================================
FILE: ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
IDEDidComputeMac32BitWarning
================================================
FILE: ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
================================================
PreviewsEnabled
================================================
FILE: lib/bloc/account_bloc.dart
================================================
import 'package:askaide/helper/constant.dart';
import 'package:askaide/helper/http.dart';
import 'package:askaide/repo/api/user.dart';
import 'package:askaide/repo/api_server.dart';
import 'package:askaide/repo/settings_repo.dart';
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
part 'account_event.dart';
part 'account_state.dart';
class AccountBloc extends Bloc {
final SettingRepository settings;
AccountBloc(this.settings) : super(AccountInitial()) {
// 加载用户信息
on((event, emit) async {
emit(AccountLoading());
final token = settings.get(settingAPIServerToken);
if (token != null && token != '') {
try {
final user = await APIServer().userInfo(cache: event.cache);
if (user != null) {
emit(AccountLoaded(user));
} else {
emit(AccountNeedSignIn());
}
} catch (e) {
emit(AccountLoaded(null, error: e));
}
} else {
emit(AccountNeedSignIn());
}
});
on((event, emit) async {
await settings.set(settingAPIServerToken, '');
await settings.set(settingUserInfo, '');
await HttpClient.cleanCache();
emit(AccountNeedSignIn());
});
on((event, emit) async {
try {
if (event.realname != null) {
await APIServer().updateUserRealname(realname: event.realname!);
}
if (event.avatarURL != null) {
await APIServer().updateUserAvatar(avatarURL: event.avatarURL!);
}
emit(AccountLoaded(await APIServer().userInfo(cache: false)));
} catch (e) {
emit(AccountLoaded(await APIServer().userInfo(cache: false), error: e));
}
});
}
}
================================================
FILE: lib/bloc/account_event.dart
================================================
part of 'account_bloc.dart';
@immutable
abstract class AccountEvent {}
class AccountLoadEvent extends AccountEvent {
final bool cache;
AccountLoadEvent({this.cache = true});
}
class AccountSignOutEvent extends AccountEvent {}
class AccountUpdateEvent extends AccountEvent {
final String? realname;
final String? avatarURL;
AccountUpdateEvent({this.realname, this.avatarURL});
}
================================================
FILE: lib/bloc/account_state.dart
================================================
part of 'account_bloc.dart';
@immutable
abstract class AccountState {}
class AccountInitial extends AccountState {}
class AccountLoading extends AccountState {}
class AccountLoaded extends AccountState {
final UserInfo? user;
final Object? error;
AccountLoaded(this.user, {this.error});
}
class AccountNeedSignIn extends AccountState {}
================================================
FILE: lib/bloc/admin_payment_bloc.dart
================================================
import 'package:askaide/repo/api/admin/payment.dart';
import 'package:askaide/repo/api/page.dart';
import 'package:askaide/repo/api_server.dart';
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
part 'admin_payment_event.dart';
part 'admin_payment_state.dart';
class AdminPaymentBloc extends Bloc {
AdminPaymentBloc() : super(AdminPaymentInitial()) {
on((event, emit) async {
final histories = await APIServer().adminPaymentHistories(
page: event.page,
perPage: event.perPage,
keyword: event.keyword,
);
emit(AdminPaymentHistoriesLoaded(histories));
});
}
}
================================================
FILE: lib/bloc/admin_payment_event.dart
================================================
part of 'admin_payment_bloc.dart';
@immutable
sealed class AdminPaymentEvent {}
class AdminPaymentHistoriesLoadEvent extends AdminPaymentEvent {
final int page;
final int perPage;
final String? keyword;
AdminPaymentHistoriesLoadEvent({
this.page = 1,
this.perPage = 20,
this.keyword,
});
}
================================================
FILE: lib/bloc/admin_payment_state.dart
================================================
part of 'admin_payment_bloc.dart';
@immutable
sealed class AdminPaymentState {}
final class AdminPaymentInitial extends AdminPaymentState {}
class AdminPaymentOperationResult extends AdminPaymentState {
final bool success;
final String message;
AdminPaymentOperationResult(this.success, this.message);
}
class AdminPaymentHistoriesLoaded extends AdminPaymentState {
final PagedData histories;
AdminPaymentHistoriesLoaded(this.histories);
}
================================================
FILE: lib/bloc/admin_room_bloc.dart
================================================
import 'package:askaide/repo/api/page.dart';
import 'package:askaide/repo/api_server.dart';
import 'package:askaide/repo/model/message.dart';
import 'package:askaide/repo/model/misc.dart';
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
part 'admin_room_event.dart';
part 'admin_room_state.dart';
class AdminRoomBloc extends Bloc {
AdminRoomBloc() : super(AdminRoomInitial()) {
on((event, emit) async {
final rooms = await APIServer().adminUserRooms(userId: event.userId);
emit(AdminRoomsLoaded(rooms: rooms));
});
on((event, emit) async {
final room = await APIServer().adminUserRoom(
userId: event.userId,
roomId: event.roomId,
);
emit(AdminRoomLoaded(room: room));
});
on((event, emit) async {
if (event.roomType == 4) {
final messages = await APIServer().adminUserRoomGroupMessages(
userId: event.userId, roomId: event.roomId);
emit(AdminRoomRecentlyMessagesLoaded(
messages: messages
.map((e) => Message(
e.role == 'user' ? Role.sender : Role.receiver,
e.message,
type: MessageType.text,
ts: e.createdAt,
model: e.model,
quotaConsumed: e.quotaConsumed,
tokenConsumed: e.tokenConsumed,
refId: e.pid,
id: e.id,
serverId: e.id,
))
.toList()));
} else {
final messages = await APIServer().adminUserRoomMessages(
userId: event.userId,
roomId: event.roomId,
);
emit(AdminRoomRecentlyMessagesLoaded(
messages: messages
.map((e) => Message(
e.role == 1 ? Role.sender : Role.receiver,
e.message,
type: MessageType.text,
ts: e.createdAt,
model: e.model,
quotaConsumed: e.quotaConsumed,
tokenConsumed: e.tokenConsumed,
refId: e.pid,
id: e.id,
serverId: e.id,
userId: e.userId,
roomId: e.roomId,
))
.toList()));
}
});
on((event, emit) async {
final messages = await APIServer().adminRecentlyMessages(
page: event.page,
perPage: event.perPage,
keyword: event.keyword,
);
emit(AdminRecentlyMessagesLoaded(PagedData(
data: messages.data
.map((e) => Message(
e.role == 1 ? Role.sender : Role.receiver,
e.message,
type: MessageType.text,
ts: e.createdAt,
model: e.model,
quotaConsumed: e.quotaConsumed,
tokenConsumed: e.tokenConsumed,
refId: e.pid,
id: e.id,
serverId: e.id,
userId: e.userId,
roomId: e.roomId,
))
.toList(),
page: messages.page,
perPage: messages.perPage,
total: messages.total,
lastPage: messages.lastPage,
)));
});
}
}
================================================
FILE: lib/bloc/admin_room_event.dart
================================================
part of 'admin_room_bloc.dart';
@immutable
sealed class AdminRoomEvent {}
class AdminRoomsLoadEvent extends AdminRoomEvent {
final int userId;
AdminRoomsLoadEvent({required this.userId});
}
class AdminRoomLoadEvent extends AdminRoomEvent {
final int userId;
final int roomId;
AdminRoomLoadEvent({required this.roomId, required this.userId});
}
class AdminRoomRecentlyMessagesLoadEvent extends AdminRoomEvent {
final int userId;
final int roomId;
final int roomType;
AdminRoomRecentlyMessagesLoadEvent({
required this.roomId,
required this.userId,
required this.roomType,
});
}
class AdminRecentlyMessagesLoadEvent extends AdminRoomEvent {
final int page;
final int perPage;
final String? keyword;
AdminRecentlyMessagesLoadEvent({
required this.page,
required this.perPage,
this.keyword,
});
}
================================================
FILE: lib/bloc/admin_room_state.dart
================================================
part of 'admin_room_bloc.dart';
@immutable
sealed class AdminRoomState {}
final class AdminRoomInitial extends AdminRoomState {}
final class AdminRoomsLoaded extends AdminRoomState {
final List rooms;
AdminRoomsLoaded({required this.rooms});
}
final class AdminRoomLoaded extends AdminRoomState {
final RoomInServer room;
AdminRoomLoaded({required this.room});
}
final class AdminRoomRecentlyMessagesLoaded extends AdminRoomState {
final List messages;
AdminRoomRecentlyMessagesLoaded({required this.messages});
}
class AdminRoomOperationResult extends AdminRoomState {
final bool success;
final String message;
AdminRoomOperationResult(this.success, this.message);
}
class AdminRecentlyMessagesLoaded extends AdminRoomState {
final PagedData messages;
AdminRecentlyMessagesLoaded(this.messages);
}
================================================
FILE: lib/bloc/background_image_bloc.dart
================================================
import 'package:askaide/repo/api_server.dart';
import 'package:askaide/repo/model/misc.dart';
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
part 'background_image_event.dart';
part 'background_image_state.dart';
class BackgroundImageBloc
extends Bloc {
BackgroundImageBloc() : super(BackgroundImageInitial()) {
on((event, emit) async {
final images = await APIServer().backgrounds();
emit(BackgroundImageLoaded(images));
});
}
}
================================================
FILE: lib/bloc/background_image_event.dart
================================================
part of 'background_image_bloc.dart';
@immutable
abstract class BackgroundImageEvent {}
class BackgroundImageLoadEvent extends BackgroundImageEvent {}
================================================
FILE: lib/bloc/background_image_state.dart
================================================
part of 'background_image_bloc.dart';
@immutable
abstract class BackgroundImageState {}
class BackgroundImageInitial extends BackgroundImageState {}
class BackgroundImageLoaded extends BackgroundImageState {
final List images;
BackgroundImageLoaded(this.images);
}
================================================
FILE: lib/bloc/bloc_manager.dart
================================================
// ignore_for_file: must_call_super
import 'package:askaide/bloc/chat_message_bloc.dart';
import 'package:askaide/helper/lru.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class ChatBlocManager {
static final ChatBlocManager _singleton = ChatBlocManager._internal();
factory ChatBlocManager() {
return _singleton;
}
ChatBlocManager._internal();
late final ChatMessageBloc Function(int roomId, {int? chatHistoryId})
blocBuilder;
init(ChatMessageBloc Function(int roomId, {int? chatHistoryId}) blocBuilder) {
this.blocBuilder = blocBuilder;
}
final LRUCache _blocs = LRUCache(10);
ChatMessageBloc getBloc(int roomId, {int? chatHistoryId}) {
final key = '$roomId-$chatHistoryId';
if (_blocs.containsKey(key)) {
return _blocs.get(key)!;
} else {
final bloc = blocBuilder(roomId, chatHistoryId: chatHistoryId);
_blocs.put(key, bloc);
return bloc;
}
}
void dispose() {
_blocs.clear();
}
}
abstract class BlocExt extends Bloc implements Disposable {
BlocExt(super.initialState);
@override
void dispose() {
super.close();
}
@override
Future close() async {
return;
}
}
================================================
FILE: lib/bloc/channel_bloc.dart
================================================
import 'package:askaide/repo/api/admin/channels.dart';
import 'package:askaide/repo/api_server.dart';
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
part 'channel_event.dart';
part 'channel_state.dart';
class ChannelBloc extends Bloc {
ChannelBloc() : super(ChannelInitial()) {
/// 加载所有渠道
on((event, emit) async {
final channels = await APIServer().adminChannels();
emit(ChannelsLoaded(channels));
});
/// 加载单个渠道
on((event, emit) async {
final channel = await APIServer().adminChannel(id: event.channelId);
emit(ChannelLoaded(channel));
});
/// 创建渠道
on((event, emit) async {
try {
await APIServer().adminCreateChannel(event.req);
emit(ChannelOperationResult(true, '创建成功'));
} catch (e) {
emit(ChannelOperationResult(false, e.toString()));
}
});
/// 更新渠道
on((event, emit) async {
try {
await APIServer().adminUpdateChannel(
id: event.channelId,
req: event.req,
);
emit(ChannelOperationResult(true, '更新成功'));
} catch (e) {
emit(ChannelOperationResult(false, e.toString()));
}
});
/// 删除渠道
on((event, emit) async {
try {
await APIServer().adminDeleteChannel(id: event.channelId);
emit(ChannelOperationResult(true, '删除成功'));
} catch (e) {
emit(ChannelOperationResult(false, e.toString()));
}
});
}
}
================================================
FILE: lib/bloc/channel_event.dart
================================================
part of 'channel_bloc.dart';
@immutable
sealed class ChannelEvent {}
class ChannelsLoadEvent extends ChannelEvent {}
class ChannelLoadEvent extends ChannelEvent {
final int channelId;
ChannelLoadEvent(this.channelId);
}
class ChannelCreateEvent extends ChannelEvent {
final AdminChannelAddReq req;
ChannelCreateEvent(this.req);
}
class ChannelUpdateEvent extends ChannelEvent {
final int channelId;
final AdminChannelUpdateReq req;
ChannelUpdateEvent(this.channelId, this.req);
}
class ChannelDeleteEvent extends ChannelEvent {
final int channelId;
ChannelDeleteEvent(this.channelId);
}
================================================
FILE: lib/bloc/channel_state.dart
================================================
part of 'channel_bloc.dart';
@immutable
sealed class ChannelState {}
final class ChannelInitial extends ChannelState {}
class ChannelsLoaded extends ChannelState {
final List channels;
ChannelsLoaded(this.channels);
}
class ChannelLoaded extends ChannelState {
final AdminChannel channel;
ChannelLoaded(this.channel);
}
class ChannelOperationResult extends ChannelState {
final bool success;
final String message;
ChannelOperationResult(this.success, this.message);
}
================================================
FILE: lib/bloc/chat_chat_bloc.dart
================================================
import 'package:askaide/helper/constant.dart';
import 'package:askaide/repo/api_server.dart';
import 'package:askaide/repo/chat_message_repo.dart';
import 'package:askaide/repo/model/chat_history.dart';
import 'package:askaide/repo/model/misc.dart';
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
part 'chat_chat_event.dart';
part 'chat_chat_state.dart';
class ChatChatBloc extends Bloc {
final ChatMessageRepository _chatMessageRepository;
ChatChatBloc(this._chatMessageRepository) : super(ChatChatInitial()) {
// 加载最近的历史记录
on((event, emit) async {
final histories = await _chatMessageRepository.recentChatHistories(
event.count,
userId: APIServer().localUserID(),
);
emit(ChatChatRecentHistoriesLoaded(
histories: histories,
examples: const [],
));
});
// 删除历史记录
on((event, emit) async {
await _chatMessageRepository.deleteChatHistory(event.chatId);
add(ChatChatLoadRecentHistories());
});
}
}
================================================
FILE: lib/bloc/chat_chat_event.dart
================================================
part of 'chat_chat_bloc.dart';
@immutable
abstract class ChatChatEvent {}
class ChatChatLoadRecentHistories extends ChatChatEvent {
final int count;
ChatChatLoadRecentHistories({this.count = defaultChatHistoryCount});
}
class ChatChatNewChat extends ChatChatEvent {
final String text;
ChatChatNewChat(this.text);
}
class ChatChatDeleteHistory extends ChatChatEvent {
final int chatId;
ChatChatDeleteHistory(this.chatId);
}
================================================
FILE: lib/bloc/chat_chat_state.dart
================================================
part of 'chat_chat_bloc.dart';
@immutable
abstract class ChatChatState {}
class ChatChatInitial extends ChatChatState {}
class ChatChatRecentHistoriesLoaded extends ChatChatState {
final List histories;
final List? examples;
ChatChatRecentHistoriesLoaded({this.histories = const [], this.examples});
}
================================================
FILE: lib/bloc/chat_event.dart
================================================
part of 'chat_message_bloc.dart';
@immutable
abstract class ChatMessageEvent {}
class ChatMessageReceivedEvent extends ChatMessageEvent {
final Message message;
ChatMessageReceivedEvent(this.message);
}
class ChatMessageSendEvent extends ChatMessageEvent {
final Message message;
final int? index;
final bool isResent;
final String? tempModel;
ChatMessageSendEvent(this.message,
{this.index, this.isResent = false, this.tempModel});
}
class ChatMessageGetRecentEvent extends ChatMessageEvent {
final int? chatHistoryId;
ChatMessageGetRecentEvent({this.chatHistoryId});
}
class ChatMessageClearAllEvent extends ChatMessageEvent {}
class ChatMessageBreakContextEvent extends ChatMessageEvent {}
class ChatMessageDeleteEvent extends ChatMessageEvent {
final List ids;
final int? chatHistoryId;
ChatMessageDeleteEvent(this.ids, {this.chatHistoryId});
}
class ChatMessageStopEvent extends ChatMessageEvent {}
================================================
FILE: lib/bloc/chat_message_bloc.dart
================================================
import 'dart:convert';
import 'package:askaide/bloc/bloc_manager.dart';
import 'package:askaide/helper/ability.dart';
import 'package:askaide/helper/error.dart';
import 'package:askaide/helper/logger.dart';
import 'package:askaide/helper/model_resolver.dart';
import 'package:askaide/helper/queue.dart';
import 'package:askaide/lang/lang.dart';
import 'package:askaide/repo/api_server.dart';
import 'package:askaide/repo/chat_message_repo.dart';
import 'package:askaide/repo/data/chat_message_data.dart';
import 'package:askaide/repo/model/chat_history.dart';
import 'package:askaide/repo/model/message.dart';
import 'package:askaide/repo/model/room.dart';
import 'package:askaide/repo/openai_repo.dart';
import 'package:askaide/repo/settings_repo.dart';
import 'package:dart_openai/openai.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
part 'chat_event.dart';
part 'chat_state.dart';
class ChatMessageBloc extends BlocExt {
final ChatMessageRepository chatMsgRepo;
final SettingRepository settingRepo;
final int roomId;
final int? chatHistoryId;
GracefulQueue? currentQueue;
ChatMessageBloc(
this.roomId, {
required this.chatMsgRepo,
required this.settingRepo,
this.chatHistoryId,
}) : super(ChatMessageInitial()) {
on(_messageSendEventHandler);
on(_getRecentEventHandler);
on(_clearAllEventHandler);
on(_breakContextEventHandler);
on(_deleteMessageEventHandler);
on(_stopEventHandler);
}
Future fixRoomId(int? chatHistoryId) async {
if (chatHistoryId != null && chatHistoryId > 0) {
final his = await chatMsgRepo.getChatHistory(chatHistoryId);
if (his != null) {
return his.roomId ?? roomId;
}
}
return roomId;
}
Future _deleteMessageEventHandler(event, emit) async {
final roomId = await fixRoomId(event.chatHistoryId);
await chatMsgRepo.removeMessage(roomId, event.ids);
ChatHistory? his;
if (event.chatHistoryId != null && event.chatHistoryId! > 0) {
his = await chatMsgRepo.getChatHistory(event.chatHistoryId!);
}
emit(ChatMessagesLoaded(
await chatMsgRepo.getRecentMessages(
roomId: roomId,
userId: APIServer().localUserID(),
chatHistoryId: event.chatHistoryId,
),
chatHistory: his,
));
}
/// 设置上下文清理标识
Future _breakContextEventHandler(event, emit) async {
final roomId = await fixRoomId(event.chatHistoryId);
// 查询当前 Room 信息
final room = await queryRoomById(chatMsgRepo, roomId);
if (room == null) {
emit(ChatMessagesLoaded(
await chatMsgRepo.getRecentMessages(
roomId: roomId,
userId: APIServer().localUserID(),
),
error: 'The selected item does not exist',
));
return;
}
final lastMessage = await chatMsgRepo.getLastMessage(
roomId,
userId: APIServer().localUserID(),
);
if (lastMessage != null && (lastMessage.type == MessageType.contextBreak || lastMessage.isInitMessage())) {
return;
}
await chatMsgRepo.sendMessage(
roomId,
Message(
Role.receiver,
AppLocale.contextBreakMessage,
ts: DateTime.now(),
type: MessageType.contextBreak,
roomId: roomId,
userId: APIServer().localUserID(),
),
);
if (room.initMessage != null && room.initMessage != '') {
await chatMsgRepo.sendMessage(
roomId,
Message(
Role.receiver,
room.initMessage!,
ts: DateTime.now(),
type: MessageType.initMessage,
roomId: roomId,
userId: APIServer().localUserID(),
),
);
}
final messages = await chatMsgRepo.getRecentMessages(
roomId: roomId,
userId: APIServer().localUserID(),
);
emit(ChatMessagesLoaded(messages));
emit(ChatMessageUpdated(messages.last));
}
/// 清空消息事件处理
Future _clearAllEventHandler(event, emit) async {
final roomId = await fixRoomId(event.chatHistoryId);
// 查询当前 Room 信息
final room = await queryRoomById(chatMsgRepo, roomId);
if (room == null) {
emit(ChatMessagesLoaded(
await chatMsgRepo.getRecentMessages(
roomId: roomId,
userId: APIServer().localUserID(),
),
error: 'The selected item does not exist',
));
return;
}
await chatMsgRepo.clearMessages(
roomId,
userId: APIServer().localUserID(),
);
if (room.initMessage != null && room.initMessage != '') {
await chatMsgRepo.sendMessage(
roomId,
Message(
Role.receiver,
room.initMessage!,
ts: DateTime.now(),
type: MessageType.initMessage,
roomId: roomId,
userId: APIServer().localUserID(),
),
);
}
emit(ChatMessagesLoaded(await chatMsgRepo.getRecentMessages(
roomId: roomId,
userId: APIServer().localUserID(),
)));
}
/// 页面加载事件处理
Future _getRecentEventHandler(event, emit) async {
final roomId = await fixRoomId(event.chatHistoryId);
ChatHistory? his;
if (event.chatHistoryId != null && event.chatHistoryId! > 0) {
his = await chatMsgRepo.getChatHistory(event.chatHistoryId!);
}
if (his == null) {
emit(ChatMessagesLoaded(const []));
} else {
emit(ChatMessagesLoaded(
await chatMsgRepo.getRecentMessages(
roomId: roomId,
userId: APIServer().localUserID(),
chatHistoryId: event.chatHistoryId,
),
chatHistory: his,
));
}
}
/// 停止输出事件处理
Future _stopEventHandler(event, emit) async {
if (currentQueue != null) {
currentQueue!.finish();
}
}
Future resolveChatHistory(Message message, int roomId) async {
// 如果是聊一聊,自动创建聊天记录历史
if (message.chatHistoryId == null || message.chatHistoryId! <= 0) {
final chatHistory = await chatMsgRepo.createChatHistory(
title: message.text,
userId: APIServer().localUserID(),
roomId: roomId,
model: message.model,
lastMessage: message.text,
);
return chatHistory;
}
return await chatMsgRepo.getChatHistory(message.chatHistoryId!);
}
/// Message sending event processing
Future _messageSendEventHandler(event, emit) async {
if (event.message is! Message) {
return;
}
Message message = event.message as Message;
final roomId = await fixRoomId(message.chatHistoryId);
ChatHistory localChatHistory = (await resolveChatHistory(message, roomId))!;
message.chatHistoryId = localChatHistory.id;
emit(ChatHistoryInited(localChatHistory.id!));
// 查询当前 Room 信息
final room = await queryRoomById(chatMsgRepo, roomId);
if (room == null) {
emit(ChatMessagesLoaded(
await chatMsgRepo.getRecentMessages(
roomId: roomId,
userId: APIServer().localUserID(),
chatHistoryId: localChatHistory.id,
),
error: 'The selected item does not exist',
chatHistory: localChatHistory,
));
return;
}
if (localChatHistory.model != null) {
room.model = localChatHistory.model!;
}
// 查询最后一条消息
// 如果最后一条消息符合以下情况,则创建时间线
// 1. 最后一条消息不存在
// 2. 最后一条消息的时间距离当前时间超过 3 小时
var last = await chatMsgRepo.getLastMessage(
roomId,
chatHistoryId: localChatHistory.id,
userId: APIServer().localUserID(),
);
if (last == null || last.ts == null || DateTime.now().difference(last.ts!).inMinutes > 60 * 3) {
// 发送时间线消息
await chatMsgRepo.sendMessage(
roomId,
Message(
Role.receiver,
DateFormat('y-MM-dd HH:mm').format(DateTime.now().toLocal()),
type: MessageType.timeline,
ts: DateTime.now(),
roomId: roomId,
userId: APIServer().localUserID(),
chatHistoryId: localChatHistory.id,
),
);
}
// 发送当前用户消息
message.model ??= room.model;
message.userId = APIServer().localUserID();
message.status = 0;
// 模型切换
String? tempModel = event.tempModel;
String? originalModel = message.model;
room.model = tempModel ?? originalModel ?? room.model;
// Logger.instance
// .d('发送消息, originalModel: $originalModel, tempModel: $tempModel');
// 聊天历史记录中,所有发送状态为 pending 状态的消息,全部设置为失败
await chatMsgRepo.fixMessageStatus(roomId);
// 记录当前消息
var sentMessageId = 0;
if (event.isResent && event.index == 0 && last != null && last.type == MessageType.text) {
// 如果当前是消息重发,同时重发的是最后一条消息,则不会重新生成该消息,直接生成答案即可
sentMessageId = last.id!;
if (last.statusIsFailed()) {
// 如果最后一条消息发送失败,则重新发送
await chatMsgRepo.updateMessagePart(roomId, last.id!, [
MessagePart('status', 0),
]);
}
} else {
message.model = tempModel ?? message.model;
sentMessageId = await chatMsgRepo.sendMessage(roomId, message);
message.model = originalModel;
}
// 更新 Room 最后活跃时间
// 这里没有使用 await,因为不需要等待更新完成,让 room 的更新异步的去处理吧
if (!Ability().isUserLogon()) {
chatMsgRepo.updateRoomLastActiveTime(roomId);
}
// 重新查询消息列表,此时包含了刚刚发送的消息+机器人思考中消息
final messages = await chatMsgRepo.getRecentMessages(
roomId: roomId,
userId: APIServer().localUserID(),
chatHistoryId: localChatHistory.id,
);
// 创建机器人思考中系统消息
Message waitMessage = Message(
Role.receiver,
'',
ts: DateTime.now(),
type: MessageType.text,
model: tempModel ?? originalModel,
roomId: roomId,
userId: APIServer().localUserID(),
refId: sentMessageId,
chatHistoryId: localChatHistory.id,
extra: '{}',
);
// 回写消息 ID
waitMessage.id = await chatMsgRepo.sendMessage(roomId, waitMessage);
waitMessage.isReady = false;
messages.add(waitMessage);
emit(ChatMessagesLoaded(
messages,
processing: true,
chatHistory: localChatHistory,
));
emit(ChatMessageUpdated(waitMessage, processing: true));
// 等待监听机器人应答消息
final queue = GracefulQueue();
currentQueue = queue;
try {
RequestFailedException? error;
try {
var isThinking = false;
var reasoningContent = '';
var listener = queue.listen(const Duration(milliseconds: 10), (items) {
for (var element in items) {
if (element.role == 'system') {
try {
// SYSTEM 命令
// - type: 命令类型
//
// type=summary (默认值)
// - question_id: 问题 ID
// - answer_id: 答案 ID
// - quota_consumed: 消耗的配额
// - token: 消耗的 token
// - info: 提示信息
//
// type=thinking
// type=thinking-done: [time_consumed]
final cmd = jsonDecode(element.content);
switch (cmd['type']) {
case 'summary':
message.serverId = cmd['question_id'];
waitMessage.serverId = cmd['answer_id'];
final quotaConsumed = cmd['quota_consumed'] ?? 0;
final tokenConsumed = cmd['token'] ?? 0;
final info = cmd['info'] ?? '';
if (info != '') {
waitMessage.updateExtra({'info': info});
}
if (quotaConsumed == 0 && tokenConsumed == 0) {
continue;
}
waitMessage.quotaConsumed = quotaConsumed;
waitMessage.tokenConsumed = tokenConsumed;
break;
case 'thinking':
waitMessage.pushExtra('states', 'thinking');
isThinking = true;
break;
case 'thinking-done':
waitMessage.pushExtra('states', 'thinking-done');
waitMessage.updateExtra({'thinking_time_consumed': cmd['time_consumed'] ?? 0});
isThinking = false;
break;
case 'reference-documents':
waitMessage.updateExtra({'reference-documents': cmd['data']});
break;
case 'searching':
waitMessage.pushExtra('states', 'searching');
break;
case 'search-results':
waitMessage.popExtra('states');
waitMessage.updateExtra({'search-results': cmd['data']});
break;
default:
}
} catch (e) {
// ignore: avoid_print
}
} else {
if (isThinking) {
reasoningContent = (reasoningContent + element.content).trim();
if (reasoningContent.contains('')) {
final allParts = reasoningContent.split('');
final parts = [allParts[0], allParts.skip(1).join('')];
reasoningContent = parts[0].trim();
waitMessage.text += parts[1].trim();
}
waitMessage.updateExtra({'reasoning': reasoningContent.replaceAll(RegExp('?think>'), '')});
} else {
waitMessage.text += element.content;
}
}
}
emit(ChatMessageUpdated(waitMessage, processing: true));
// 失败处理
for (var e in items) {
if (e.code != null && e.code! > 0) {
error = RequestFailedException(e.error ?? 'Request processing failure', e.code!);
}
}
});
await ModelResolver.instance
.request(
room: room,
tempModel: tempModel,
contextMessages: messages.sublist(0, messages.length - 1),
onMessage: queue.add,
maxTokens: room.maxTokens,
historyId: localChatHistory.id,
flags: message.flags,
)
.whenComplete(queue.finish);
await listener;
waitMessage.text = waitMessage.text.trim();
if (error == null && waitMessage.text.isEmpty) {
error = RequestFailedException('The answer is empty', 500);
}
if (error != null) {
throw error!;
}
} catch (e) {
if (waitMessage.text.isEmpty) {
Logger.instance.e('An error occurred during the response process: $e');
rethrow;
}
}
// 机器人应答完成,将最后一条机器人应答消息更新到数据库,替换掉思考中消息
waitMessage.isReady = true;
await chatMsgRepo.updateMessage(roomId, waitMessage.id!, waitMessage);
// 更新聊天问题的服务端 ID 和消息状态
var sentMessageParts = [];
sentMessageParts.add(MessagePart('status', 1));
if (message.serverId != null && message.serverId! > 0) {
sentMessageParts.add(MessagePart('server_id', message.serverId));
}
await chatMsgRepo.updateMessagePart(
roomId,
sentMessageId,
sentMessageParts,
);
// 更新聊天历史纪录最后一条消息
final chatHistory = await chatMsgRepo.getChatHistory(localChatHistory.id!);
if (chatHistory != null) {
chatHistory.lastMessage = waitMessage.text;
// 异步处理就好,不需要等待
chatMsgRepo.updateChatHistory(localChatHistory.id!, chatHistory);
}
// 重新查询消息列表,此时包含了刚刚发送的消息+机器人应答消息
emit(ChatMessagesLoaded(
await chatMsgRepo.getRecentMessages(
roomId: roomId,
userId: APIServer().localUserID(),
chatHistoryId: localChatHistory.id,
),
chatHistory: localChatHistory,
));
} catch (e) {
final error = resolveErrorMessage(e, isChat: true);
await chatMsgRepo.updateMessagePart(
roomId,
sentMessageId,
[
MessagePart('status', 2),
MessagePart('extra', jsonEncode({'error': error.toString()})),
],
);
if (waitMessage.id != null) {
if (waitMessage.isReady) {
await chatMsgRepo.updateMessage(
roomId,
waitMessage.id!,
Message(
Role.receiver,
error.toString(),
id: waitMessage.id,
ts: DateTime.now(),
type: MessageType.system,
roomId: roomId,
userId: APIServer().localUserID(),
chatHistoryId: localChatHistory.id,
model: tempModel ?? originalModel,
),
);
} else {
await chatMsgRepo.removeMessage(roomId, [waitMessage.id!]);
}
}
emit(ChatMessagesLoaded(
await chatMsgRepo.getRecentMessages(
roomId: roomId,
userId: APIServer().localUserID(),
chatHistoryId: localChatHistory.id,
),
error: error,
chatHistory: localChatHistory,
));
queue.finish();
} finally {
queue.dispose();
currentQueue = null;
}
emit(ChatMessageUpdated(waitMessage));
}
}
Future queryRoomById(ChatMessageRepository chatMsgRepo, int roomId) async {
Room? room;
if (Ability().isUserLogon()) {
final roomInServer = await APIServer().room(roomId: roomId);
room = Room(
roomInServer.name,
'chat',
description: roomInServer.description,
id: roomInServer.id,
userId: roomInServer.userId,
createdAt: roomInServer.createdAt,
lastActiveTime: roomInServer.lastActiveTime,
systemPrompt: roomInServer.systemPrompt,
priority: roomInServer.priority ?? 0,
model: '${roomInServer.vendor}:${roomInServer.model}',
initMessage: roomInServer.initMessage,
maxContext: roomInServer.maxContext,
maxTokens: roomInServer.maxTokens,
localRoom: false,
);
} else {
room = await chatMsgRepo.room(roomId);
}
return room;
}
================================================
FILE: lib/bloc/chat_state.dart
================================================
part of 'chat_message_bloc.dart';
@immutable
abstract class ChatMessageState {}
class ChatMessageInitial extends ChatMessageState {}
// 加载全量聊天记录
class ChatMessagesLoaded extends ChatMessageState {
final List _messages;
final bool processing;
final Object? _error;
final ChatHistory? chatHistory;
ChatMessagesLoaded(
this._messages, {
Object? error,
this.processing = false,
this.chatHistory,
}) : _error = error;
get messages => _messages;
get error => _error;
}
class ChatMessageError extends ChatMessageState {
final String message;
ChatMessageError(this.message);
}
class ChatMessageUpdated extends ChatMessageState {
final Message message;
/// 是否新消息正在处理中
final bool processing;
ChatMessageUpdated(this.message, {this.processing = false});
}
class ChatHistoryInited extends ChatMessageState {
final int chatId;
ChatHistoryInited(this.chatId);
}
================================================
FILE: lib/bloc/creative_island_bloc.dart
================================================
import 'package:askaide/bloc/bloc_manager.dart';
import 'package:askaide/repo/api/creative.dart';
import 'package:askaide/repo/api_server.dart';
import 'package:askaide/repo/creative_island_repo.dart';
import 'package:flutter/material.dart';
part 'creative_island_event.dart';
part 'creative_island_state.dart';
class CreativeIslandBloc
extends BlocExt {
final CreativeIslandRepository creativeIslandRepo;
CreativeIslandBloc(this.creativeIslandRepo) : super(CreativeIslandInitial()) {
// on((event, emit) async {
// await creativeIslandRepo.create(
// event.itemId,
// arguments: jsonEncode(event.arguments),
// prompt: event.prompt,
// answer: event.answer,
// userId: APIServer().localUserID(),
// );
// emit(CreativeIslandSaved());
// });
on((event, emit) async {
final items =
await APIServer().creativeIslandItemsV2(cache: !event.forceRefresh);
emit(CreativeIslandItemsV2Loaded(
items: items,
));
});
on((event, emit) async {
final resp = await APIServer().creativeIslandItem(event.itemId);
emit(CreativeIslandItemLoaded(resp));
});
on((event, emit) async {
emit(CreativeIslandHistoriesLoading());
final items = await APIServer()
.creativeHistories(cache: !event.forceRefresh, mode: event.mode);
emit(CreativeIslandHistoriesAllLoaded(items.data));
});
on((event, emit) async {
emit(CreativeIslandHistoriesLoading());
final items = await APIServer().creativeUserGallery(
cache: false, mode: event.mode, model: event.model);
emit(CreativeIslandGalleryLoaded(items));
});
on((event, emit) async {
emit(CreativeIslandHistoriesLoading());
final island = await APIServer().creativeIslandItem(event.itemId);
emit(CreativeIslandHistoriesLoaded(
island,
await APIServer().creativeItemHistories(
island.id,
cache: !event.forceRefresh,
),
));
});
on((event, emit) async {
emit(CreativeIslandHistoriesLoading());
await APIServer()
.deleteCreativeHistoryItem(event.itemId, hisId: event.id);
if (event.source == 'all-histories') {
final res =
await APIServer().creativeHistories(cache: false, mode: event.mode);
emit(CreativeIslandHistoriesAllLoaded(res.data));
} else {
final island = await APIServer().creativeIslandItem(event.itemId);
emit(CreativeIslandHistoriesLoaded(
island,
await APIServer().creativeItemHistories(
island.id,
cache: false,
),
));
}
});
on((event, emit) async {
emit(CreativeIslandInitial());
try {
final items = await APIServer().creativeIslandItems(mode: event.mode);
emit(CreativeIslandListLoaded(
items.items,
categories: items.categories,
backgroundImage: items.backgroundImage,
));
} catch (e) {
emit(
CreativeIslandListLoaded(const [], error: e, categories: const []));
}
});
on((event, emit) async {
emit(CreativeIslandHistoryItemLoading());
emit(CreativeIslandHistoryItemLoaded(
item: await APIServer().creativeHistoryItem(
hisId: event.itemId,
cache: !event.forceRefresh,
),
));
});
}
}
================================================
FILE: lib/bloc/creative_island_event.dart
================================================
part of 'creative_island_bloc.dart';
@immutable
abstract class CreativeIslandEvent {}
// class CreativeIslandSaveEvent extends CreativeIslandEvent {
// final String itemId;
// final Map arguments;
// final String prompt;
// final String answer;
// CreativeIslandSaveEvent(
// this.itemId, {
// this.arguments = const {},
// this.prompt = '',
// this.answer = '',
// });
// }
class CreativeIslandItemLoadEvent extends CreativeIslandEvent {
final String itemId;
CreativeIslandItemLoadEvent(this.itemId);
}
class CreativeIslandHistoriesAllLoadEvent extends CreativeIslandEvent {
final bool forceRefresh;
final String mode;
CreativeIslandHistoriesAllLoadEvent(
{this.forceRefresh = false, required this.mode});
}
class CreativeIslandGalleryLoadEvent extends CreativeIslandEvent {
final bool forceRefresh;
final String mode;
final String? model;
CreativeIslandGalleryLoadEvent({
this.forceRefresh = false,
required this.mode,
this.model,
});
}
class CreativeIslandHistoriesLoadEvent extends CreativeIslandEvent {
final String itemId;
final bool forceRefresh;
CreativeIslandHistoriesLoadEvent(this.itemId, {this.forceRefresh = false});
}
class CreativeIslandDeleteEvent extends CreativeIslandEvent {
final String itemId;
final int id;
final String source;
final String mode;
CreativeIslandDeleteEvent(this.itemId, this.id,
{this.source = '', required this.mode});
}
class CreativeIslandListLoadEvent extends CreativeIslandEvent {
final String mode;
CreativeIslandListLoadEvent({required this.mode});
}
class CreativeIslandHistoryItemLoadEvent extends CreativeIslandEvent {
final int itemId;
final bool forceRefresh;
CreativeIslandHistoryItemLoadEvent(this.itemId, {this.forceRefresh = false});
}
class CreativeIslandItemsV2LoadEvent extends CreativeIslandEvent {
final bool forceRefresh;
CreativeIslandItemsV2LoadEvent({this.forceRefresh = false});
}
================================================
FILE: lib/bloc/creative_island_state.dart
================================================
part of 'creative_island_bloc.dart';
@immutable
abstract class CreativeIslandState {}
class CreativeIslandInitial extends CreativeIslandState {}
// class CreativeIslandSaved extends CreativeIslandState {
// final String? _error;
// CreativeIslandSaved({String? error}) : _error = error;
// get error => _error;
// }
class CreativeIslandItemLoaded extends CreativeIslandState {
final String? _error;
final CreativeIslandItem item;
CreativeIslandItemLoaded(this.item, {String? error}) : _error = error;
get error => _error;
}
class CreativeIslandHistoriesLoading extends CreativeIslandInitial {}
class CreativeIslandHistoriesLoaded extends CreativeIslandState {
final String? _error;
final CreativeIslandItem island;
final List histories;
CreativeIslandHistoriesLoaded(this.island, this.histories, {String? error})
: _error = error;
get error => _error;
}
class CreativeIslandGalleryLoaded extends CreativeIslandState {
final String? _error;
final List items;
CreativeIslandGalleryLoaded(this.items, {String? error}) : _error = error;
get error => _error;
}
class CreativeIslandHistoriesAllLoaded extends CreativeIslandState {
final String? _error;
final List histories;
CreativeIslandHistoriesAllLoaded(this.histories, {String? error})
: _error = error;
get error => _error;
}
class CreativeIslandListLoaded extends CreativeIslandState {
final Object? _error;
final List items;
final List categories;
final String? backgroundImage;
CreativeIslandListLoaded(
this.items, {
Object? error,
required this.categories,
this.backgroundImage,
}) : _error = error;
get error => _error;
}
class CreativeIslandHistoryItemLoading extends CreativeIslandState {}
class CreativeIslandHistoryItemLoaded extends CreativeIslandState {
final Object? error;
final CreativeItemInServer? item;
CreativeIslandHistoryItemLoaded({this.item, this.error});
}
class CreativeIslandItemsV2Loaded extends CreativeIslandState {
final Object? error;
final List items;
CreativeIslandItemsV2Loaded({required this.items, this.error});
}
================================================
FILE: lib/bloc/free_count_bloc.dart
================================================
import 'package:askaide/helper/ability.dart';
import 'package:askaide/repo/api_server.dart';
import 'package:askaide/repo/model/misc.dart';
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
part 'free_count_event.dart';
part 'free_count_state.dart';
class FreeCountBloc extends Bloc {
List counts = [];
FreeCountBloc() : super(FreeCountInitial()) {
// 重新加载所有的模型免费使用次数
on((event, emit) async {
if (!Ability().isUserLogon()) {
emit(FreeCountLoadedState(
counts: await APIServer().freeChatCounts(),
needSignin: event.checkSigninStatus,
));
return;
}
counts = await APIServer().userFreeStatistics();
emit(FreeCountLoadedState(counts: counts));
});
// 重新加载指定模型的免费使用次数
on((event, emit) async {
if (Ability().usingLocalOpenAIModel(event.model) ||
!Ability().isUserLogon()) {
emit(FreeCountLoadedState(counts: counts));
return;
}
final freeCount = await APIServer().userFreeStatisticsForModel(
model: event.model.startsWith('v2@')
? event.model
: event.model.split(':').last,
);
if (freeCount.maxCount > 0) {
var matched = false;
for (var i = 0; i < counts.length; i++) {
if (counts[i].model == freeCount.model) {
counts[i] = freeCount;
matched = true;
break;
}
}
if (!matched) {
counts.add(freeCount);
}
}
emit(FreeCountLoadedState(counts: counts));
});
}
}
================================================
FILE: lib/bloc/free_count_event.dart
================================================
part of 'free_count_bloc.dart';
@immutable
sealed class FreeCountEvent {}
class FreeCountReloadEvent extends FreeCountEvent {
final String model;
FreeCountReloadEvent({required this.model});
}
class FreeCountReloadAllEvent extends FreeCountEvent {
final bool checkSigninStatus;
FreeCountReloadAllEvent({this.checkSigninStatus = false});
}
================================================
FILE: lib/bloc/free_count_state.dart
================================================
part of 'free_count_bloc.dart';
@immutable
sealed class FreeCountState {}
final class FreeCountInitial extends FreeCountState {}
class FreeCountLoadedState extends FreeCountState {
final List counts;
final bool needSignin;
FreeModelCount? model(String model) {
model = model.split(':').last;
for (var i = 0; i < counts.length; i++) {
if (counts[i].model == model) {
return counts[i];
}
}
return null;
}
FreeCountLoadedState({required this.counts, this.needSignin = false});
}
================================================
FILE: lib/bloc/gallery_bloc.dart
================================================
import 'package:askaide/repo/api/creative.dart';
import 'package:askaide/repo/api/page.dart';
import 'package:askaide/repo/api_server.dart';
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
part 'gallery_event.dart';
part 'gallery_state.dart';
class GalleryBloc extends Bloc {
GalleryBloc() : super(GalleryInitial()) {
on((event, emit) async {
emit(GalleryInitial());
final res = await APIServer().creativeGallery(
cache: !event.forceRefresh,
page: event.page,
perPage: 20,
);
emit(GalleryLoaded(data: res));
});
on((event, emit) async {
emit(GalleryInitial());
final res = await APIServer().creativeGalleryItem(
cache: !event.forceRefresh,
id: event.id,
);
emit(GalleryItemLoaded(
item: res.item,
isInternalUser: res.isInternalUser,
));
});
}
}
================================================
FILE: lib/bloc/gallery_event.dart
================================================
part of 'gallery_bloc.dart';
@immutable
abstract class GalleryEvent {}
class GalleryLoadEvent extends GalleryEvent {
final bool forceRefresh;
final int page;
GalleryLoadEvent({this.forceRefresh = false, this.page = 1});
}
class GalleryItemLoadEvent extends GalleryEvent {
final int id;
final bool forceRefresh;
GalleryItemLoadEvent({required this.id, this.forceRefresh = false});
}
================================================
FILE: lib/bloc/gallery_state.dart
================================================
part of 'gallery_bloc.dart';
@immutable
abstract class GalleryState {}
class GalleryInitial extends GalleryState {}
class GalleryLoaded extends GalleryState {
final PagedData data;
GalleryLoaded({required this.data});
}
class GalleryItemLoaded extends GalleryState {
final CreativeGallery item;
final bool isInternalUser;
GalleryItemLoaded({required this.item, this.isInternalUser = false});
}
================================================
FILE: lib/bloc/group_chat_bloc.dart
================================================
import 'dart:convert';
import 'package:askaide/helper/cache.dart';
import 'package:askaide/helper/logger.dart';
import 'package:askaide/page/component/chat/message_state_manager.dart';
import 'package:askaide/repo/api_server.dart';
import 'package:askaide/repo/model/group.dart';
import 'package:askaide/repo/model/message.dart';
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
part 'group_chat_event.dart';
part 'group_chat_state.dart';
class GroupChatBloc extends Bloc {
var messages = [];
final MessageStateManager stateManager;
GroupChatBloc({required this.stateManager}) : super(GroupChatInitial()) {
// 加载聊天组
on((event, emit) async {
final group =
await APIServer().chatGroup(event.groupId, cache: !event.forceUpdate);
final states = await stateManager.loadRoomStates(event.groupId);
final defaultChatMembers = await loadDefaultChatMembers(event.groupId);
emit(GroupChatLoaded(
group: group,
states: states,
defaultChatMembers: defaultChatMembers.isEmpty
? group.members.map((e) => e.id!).toList()
: defaultChatMembers,
));
});
// 加载聊天组聊天记录
on((event, emit) async {
if (event.isInitRequest) {
try {
final cached =
await Cache().stringGet(key: 'group:speed:${event.groupId}');
if (cached != null) {
final messages = (jsonDecode(cached) as List)
.map((e) => GroupMessage.fromJson(e))
.toList();
emit(GroupChatMessagesLoaded(messages: messages));
}
} catch (e) {
Logger.instance.e(e);
}
}
await refreshGroupMessages(
event.groupId,
startId: event.startId,
forceRefresh: true,
);
emit(GroupChatMessagesLoaded(messages: messages));
});
// 发送聊天组消息
on((event, emit) async {
try {
final resp = await APIServer().chatGroupSendMessage(
event.groupId,
GroupChatSendRequest(
message: event.message,
memberIds: event.members,
),
);
// 记录默认聊天成员
updateDefaultChatMembers(
event.groupId,
resp.tasks.map((e) => e.memberId).toList(),
).then((members) {
emit(GroupDefaultMemberSelected(members));
});
await refreshGroupMessages(
event.groupId,
startId: 0,
forceRefresh: true,
);
emit(GroupChatMessagesLoaded(messages: messages));
} catch (e) {
await refreshGroupMessages(
event.groupId,
startId: 0,
forceRefresh: true,
);
emit(GroupChatMessagesLoaded(messages: messages, error: e));
}
});
// 发送系统消息
on((event, emit) async {
try {
final resp = await APIServer().chatGroupSendSystemMessage(
event.groupId,
messageType: event.type.getTypeText(),
message: event.message,
);
Logger.instance.d(resp.toJson());
} finally {
await refreshGroupMessages(
event.groupId,
startId: 0,
forceRefresh: true,
);
emit(GroupChatMessagesLoaded(messages: messages));
}
});
// 更新聊天组消息状态
on((event, emit) async {
final waitMessageIds = messages
.where((msg) => msg.status == groupMessageStatusWaiting)
.map((msg) => msg.id)
.toList();
if (waitMessageIds.isEmpty) {
return;
}
final resp = await APIServer()
.chatGroupMessageStatus(event.groupId, waitMessageIds);
final newMessageStatusMap = {};
for (var msg in resp) {
newMessageStatusMap[msg.id] = msg;
}
for (var i = 0; i < messages.length; i++) {
final msg = messages[i];
if (newMessageStatusMap.containsKey(msg.id)) {
messages[i] = newMessageStatusMap[msg.id]!;
}
}
emit(GroupChatMessagesLoaded(messages: messages));
});
// 清空聊天组消息
on((event, emit) async {
await APIServer().chatGroupDeleteAllMessages(event.groupId);
messages.clear();
emit(GroupChatMessagesLoaded(messages: messages));
});
// 删除聊天组消息
on((event, emit) async {
await APIServer().chatGroupDeleteMessage(event.groupId, event.messageId);
messages.removeWhere((msg) => msg.id == event.messageId);
emit(GroupChatMessagesLoaded(messages: messages));
});
}
refreshGroupMessages(
int groupId, {
int startId = 0,
bool forceRefresh = false,
}) async {
final data = await APIServer()
.chatGroupMessages(groupId, startId: startId, cache: !forceRefresh);
messages = data.data.reversed.toList();
if (startId == 0) {
Cache()
.setString(key: 'group:speed:$groupId', value: jsonEncode(messages));
}
}
Future> loadDefaultChatMembers(int groupId) async {
final defaultMembers =
await Cache().stringGet(key: 'group:$groupId:default-members');
return (defaultMembers ?? '')
.split(',')
.map((e) => int.tryParse(e) ?? 0)
.where((e) => e > 0)
.toList();
}
Future> updateDefaultChatMembers(
int groupId, List members) async {
// 记录默认聊天成员
await Cache().setString(
key: 'group:$groupId:default-members',
value: members.join(','),
duration: const Duration(days: 365),
);
return members;
}
}
================================================
FILE: lib/bloc/group_chat_event.dart
================================================
part of 'group_chat_bloc.dart';
@immutable
sealed class GroupChatEvent {}
class GroupChatLoadEvent extends GroupChatEvent {
final int groupId;
final bool forceUpdate;
GroupChatLoadEvent(this.groupId, {this.forceUpdate = false});
}
class GroupChatMessagesLoadEvent extends GroupChatEvent {
final int groupId;
final int startId;
final bool isInitRequest;
GroupChatMessagesLoadEvent(
this.groupId, {
this.startId = 0,
this.isInitRequest = false,
});
}
class GroupChatSendEvent extends GroupChatEvent {
final int groupId;
final String message;
final List members;
final int? index;
final bool isResent;
GroupChatSendEvent(this.groupId, this.message, this.members,
{this.index, this.isResent = false});
}
class GroupChatUpdateMessageStatusEvent extends GroupChatEvent {
final int groupId;
GroupChatUpdateMessageStatusEvent(this.groupId);
}
class GroupChatSendSystemEvent extends GroupChatEvent {
final int groupId;
final String? message;
final MessageType type;
GroupChatSendSystemEvent(this.groupId, this.type, {this.message});
}
class GroupChatDeleteAllEvent extends GroupChatEvent {
final int groupId;
GroupChatDeleteAllEvent(this.groupId);
}
class GroupChatDeleteEvent extends GroupChatEvent {
final int groupId;
final int messageId;
GroupChatDeleteEvent(this.groupId, this.messageId);
}
================================================
FILE: lib/bloc/group_chat_state.dart
================================================
part of 'group_chat_bloc.dart';
@immutable
sealed class GroupChatState {}
final class GroupChatInitial extends GroupChatState {}
class GroupChatLoaded extends GroupChatState {
final ChatGroup group;
final Map states;
final List? defaultChatMembers;
GroupChatLoaded({
required this.group,
required this.states,
this.defaultChatMembers,
});
}
class GroupDefaultMemberSelected extends GroupChatState {
final List members;
GroupDefaultMemberSelected(this.members);
}
class GroupChatMessagesLoaded extends GroupChatState {
final List messages;
final Object? _error;
get error => _error;
bool get hasWaitTasks =>
messages.any((element) => element.status == groupMessageStatusWaiting);
GroupChatMessagesLoaded({
required this.messages,
Object? error,
}) : _error = error;
}
================================================
FILE: lib/bloc/model_bloc.dart
================================================
import 'package:askaide/repo/api/admin/models.dart';
import 'package:askaide/repo/api_server.dart';
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
part 'model_event.dart';
part 'model_state.dart';
class ModelBloc extends Bloc {
ModelBloc() : super(ModelInitial()) {
/// 加载所有模型
on((event, emit) async {
final channels = await APIServer().adminModels();
emit(ModelsLoaded(channels));
});
/// 加载单个模型
on((event, emit) async {
final channel = await APIServer().adminModel(modelId: event.modelId);
emit(ModelLoaded(channel));
});
/// 创建模型
on((event, emit) async {
try {
await APIServer().adminCreateModel(event.req);
emit(ModelOperationResult(true, 'Creation successful'));
} catch (e) {
emit(ModelOperationResult(false, e.toString()));
}
});
/// 更新模型
on((event, emit) async {
try {
await APIServer().adminUpdateModel(
modelId: event.modelId,
req: event.req,
);
emit(ModelOperationResult(true, 'Update successful'));
} catch (e) {
emit(ModelOperationResult(false, e.toString()));
}
});
/// 删除模型
on((event, emit) async {
try {
await APIServer().adminDeleteModel(modelId: event.modelId);
emit(ModelOperationResult(true, 'Delete successful'));
} catch (e) {
emit(ModelOperationResult(false, e.toString()));
}
});
}
}
================================================
FILE: lib/bloc/model_event.dart
================================================
part of 'model_bloc.dart';
@immutable
sealed class ModelEvent {}
class ModelsLoadEvent extends ModelEvent {}
class ModelLoadEvent extends ModelEvent {
final String modelId;
ModelLoadEvent(this.modelId);
}
class ModelCreateEvent extends ModelEvent {
final AdminModelAddReq req;
ModelCreateEvent(this.req);
}
class ModelUpdateEvent extends ModelEvent {
final String modelId;
final AdminModelUpdateReq req;
ModelUpdateEvent(this.modelId, this.req);
}
class ModelDeleteEvent extends ModelEvent {
final String modelId;
ModelDeleteEvent(this.modelId);
}
================================================
FILE: lib/bloc/model_state.dart
================================================
part of 'model_bloc.dart';
@immutable
sealed class ModelState {}
final class ModelInitial extends ModelState {}
class ModelsLoaded extends ModelState {
final List models;
ModelsLoaded(this.models);
}
class ModelLoaded extends ModelState {
final AdminModel model;
ModelLoaded(this.model);
}
class ModelOperationResult extends ModelState {
final bool success;
final String message;
ModelOperationResult(this.success, this.message);
}
================================================
FILE: lib/bloc/notify_bloc.dart
================================================
import 'package:askaide/bloc/bloc_manager.dart';
import 'package:flutter/material.dart';
part 'notify_event.dart';
part 'notify_state.dart';
class NotifyBloc extends BlocExt {
NotifyBloc() : super(NotifyInitial()) {
on((event, emit) {
emit(NotifyFired(event.title, event.body, event.type));
});
on((event, emit) {
emit(NotifyInitial());
});
}
}
================================================
FILE: lib/bloc/notify_event.dart
================================================
part of 'notify_bloc.dart';
@immutable
abstract class NotifyEvent {}
class NotifyFiredEvent extends NotifyEvent {
final String title;
final String body;
final String type;
NotifyFiredEvent(this.title, this.body, this.type);
}
class NotifyResetEvent extends NotifyEvent {}
================================================
FILE: lib/bloc/notify_state.dart
================================================
part of 'notify_bloc.dart';
@immutable
abstract class NotifyState {}
class NotifyInitial extends NotifyState {}
class NotifyFired extends NotifyState {
final String title;
final String body;
final String type;
NotifyFired(this.title, this.body, this.type);
}
================================================
FILE: lib/bloc/payment_bloc.dart
================================================
import 'package:askaide/helper/platform.dart';
import 'package:askaide/repo/api/payment.dart';
import 'package:askaide/repo/api_server.dart';
import 'package:bloc/bloc.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:meta/meta.dart';
part 'payment_event.dart';
part 'payment_state.dart';
class PaymentBloc extends Bloc {
PaymentBloc() : super(PaymentInitial()) {
on((event, emit) async {
if (PlatformTool.isIOS()) {
final products = await APIServer().paymentProducts();
if (products.consume.isEmpty) {
emit(PaymentAppleProductsLoaded(
const [],
note: products.note,
error: '没有任何可购买的项目',
localProducts: const [],
loading: false,
));
return;
}
emit(PaymentAppleProductsLoaded(
products.consume
.map(
(e) => ProductDetails(
id: e.id,
title: e.name,
description: '',
price: '-',
rawPrice: 0,
currencyCode: '',
),
)
.toList(),
note: products.note,
localProducts: products.consume,
loading: true));
final productIds = products.consume.map((e) => e.id).toSet();
final response =
await InAppPurchase.instance.queryProductDetails(productIds);
if (response.notFoundIDs.isNotEmpty) {
emit(PaymentAppleProductsLoaded(
const [],
note: products.note,
localProducts: products.consume,
error: '没有任何可购买的项目',
loading: false,
));
return;
}
final remoteProducts = [];
for (var id in productIds) {
remoteProducts.add(
response.productDetails.firstWhere((element) => element.id == id),
);
}
emit(PaymentAppleProductsLoaded(
remoteProducts,
note: products.note,
localProducts: products.consume,
loading: false,
));
} else {
final products = await APIServer().paymentProducts();
if (products.consume.isEmpty) {
emit(PaymentAppleProductsLoaded(
const [],
note: products.note,
error: '没有任何可购买的项目',
localProducts: const [],
loading: false,
));
return;
}
emit(
PaymentAppleProductsLoaded(
products.consume
.map(
(e) => ProductDetails(
id: e.id,
title: e.name,
description: '',
price: products.preferUSD
? e.retailPriceUSDText
: e.retailPriceText,
rawPrice: e.retailPrice.toDouble(),
currencyCode: '',
),
)
.toList(),
note: products.note,
localProducts: products.consume,
loading: false,
preferUSD: products.preferUSD,
),
);
}
});
}
}
================================================
FILE: lib/bloc/payment_event.dart
================================================
part of 'payment_bloc.dart';
@immutable
abstract class PaymentEvent {}
class PaymentLoadAppleProducts extends PaymentEvent {}
================================================
FILE: lib/bloc/payment_state.dart
================================================
part of 'payment_bloc.dart';
@immutable
abstract class PaymentState {}
class PaymentInitial extends PaymentState {}
class PaymentAppleProductsLoaded extends PaymentState {
final List products;
final List localProducts;
final Object? error;
final bool loading;
final String? note;
final bool preferUSD;
PaymentAppleProductsLoaded(
this.products, {
this.note,
required this.localProducts,
this.error,
required this.loading,
this.preferUSD = false,
});
}
================================================
FILE: lib/bloc/room_bloc.dart
================================================
import 'package:askaide/helper/ability.dart';
import 'package:askaide/helper/constant.dart';
import 'package:askaide/page/component/chat/message_state_manager.dart';
import 'package:askaide/repo/api/room_gallery.dart';
import 'package:askaide/repo/api_server.dart';
import 'package:askaide/repo/model/group.dart';
import 'package:askaide/repo/model/misc.dart';
import 'package:askaide/repo/model/room.dart';
import 'package:askaide/bloc/bloc_manager.dart';
import 'package:askaide/repo/chat_message_repo.dart';
import 'package:flutter/material.dart';
part 'room_event.dart';
part 'room_state.dart';
class RoomBloc extends BlocExt {
final ChatMessageRepository chatMsgRepo;
final MessageStateManager stateManager;
Future fixRoomId(int? chatHistoryId) async {
if (chatHistoryId != null && chatHistoryId > 0) {
final his = await chatMsgRepo.getChatHistory(chatHistoryId);
if (his != null) {
return his.roomId;
}
}
return null;
}
/// 加载房间信息,如果房间不存在,则加载默认房间
Future loadRoom(int roomId) async {
try {
final room = await APIServer().room(roomId: roomId);
return room;
} catch (e) {
return await APIServer().room(roomId: chatAnywhereRoomId);
}
}
RoomBloc({
required this.chatMsgRepo,
required this.stateManager,
}) : super(RoomInitial()) {
// 加载指定聊天室信息
on((event, emit) async {
try {
// 加快首屏加载速度,避免加载中状态
emit(RoomLoaded(
Room(
'',
'chat',
id: event.roomId,
),
const {},
cascading: false,
));
final roomId = await fixRoomId(event.chatHistoryId) ?? event.roomId;
if (Ability().isUserLogon()) {
final room = await loadRoom(roomId);
if (event.chatHistoryId != null && event.chatHistoryId! > 0) {
final chatHistory = await chatMsgRepo.getChatHistory(event.chatHistoryId!);
if (chatHistory != null && chatHistory.model != null) {
room.model = chatHistory.model!;
}
}
emit(RoomLoaded(
Room(
room.name,
'chat',
description: room.description,
id: room.id,
userId: room.userId,
createdAt: room.createdAt,
lastActiveTime: room.lastActiveTime,
systemPrompt: room.systemPrompt,
priority: room.priority ?? 0,
model: room.model.startsWith('v2@') ? room.model : '${room.vendor}:${room.model}',
initMessage: room.initMessage,
maxContext: room.maxContext,
avatarId: room.avatarId,
avatarUrl: room.avatarUrl,
roomType: room.roomType,
),
const {},
cascading: false,
));
final states = await stateManager.loadRoomStates(roomId);
emit(RoomLoaded(
Room(
room.name,
'chat',
description: room.description,
id: room.id,
userId: room.userId,
createdAt: room.createdAt,
lastActiveTime: room.lastActiveTime,
systemPrompt: room.systemPrompt,
priority: room.priority ?? 0,
model: room.model.startsWith('v2@') ? room.model : '${room.vendor}:${room.model}',
initMessage: room.initMessage,
maxContext: room.maxContext,
avatarId: room.avatarId,
avatarUrl: room.avatarUrl,
roomType: room.roomType,
),
states,
examples: await APIServer().example(
room.model.startsWith('v2@') ? room.model : '${room.vendor}:${room.model}',
),
cascading: event.cascading,
));
return;
}
final room = await chatMsgRepo.room(roomId);
if (room != null) {
final states = await stateManager.loadRoomStates(roomId);
emit(RoomLoaded(
room,
states,
examples: await APIServer().example(room.model),
cascading: event.cascading,
));
}
} catch (e) {
emit(RoomLoaded(
Room('-', '-'),
const {},
error: e,
cascading: event.cascading,
));
}
});
// 加载聊天室列表
on((event, emit) async {
if (!event.forceRefresh) {
emit(RoomsLoading());
}
emit(await createRoomsLoadedState(cache: !event.forceRefresh));
});
// 创建聊天室
on((event, emit) async {
emit(RoomsLoading());
try {
if (Ability().isUserLogon()) {
String? model;
String? vendor;
if (event.model != null) {
final segs = event.model!.split(':');
model = event.model!.startsWith('v2@') ? event.model! : (segs.length > 1 ? segs.last : event.model);
vendor = event.model!.startsWith('v2@') ? '' : (segs.length > 1 ? segs.first : '');
}
await APIServer().createRoom(
name: event.name,
vendor: vendor,
model: model,
systemPrompt: event.prompt,
avatarId: event.avatarId,
avatarUrl: event.avatarUrl,
maxContext: event.maxContext,
initMessage: event.initMessage,
);
} else {
await chatMsgRepo.createRoom(
name: event.name,
category: 'chat',
model: event.model ?? 'gpt-4o',
systemPrompt: event.prompt,
userId: APIServer().localUserID(),
maxContext: event.maxContext,
);
}
emit(RoomOperationResult(true));
emit(await createRoomsLoadedState(cache: false));
} catch (e) {
emit(RoomOperationResult(false, error: e.toString()));
// emit(RoomsLoaded(const [], error: e.toString()));
}
});
// 删除聊天室
on((event, emit) async {
emit(RoomsLoading());
try {
if (Ability().isUserLogon()) {
await APIServer().deleteRoom(roomId: event.roomId);
} else {
var room = await chatMsgRepo.room(event.roomId);
if (room == null || room.category == 'system') {
return;
}
await chatMsgRepo.deleteRoom(event.roomId);
}
emit(await createRoomsLoadedState(cache: false));
} catch (e) {
emit(RoomsLoaded(const [], error: e.toString()));
}
});
// 更新聊天室信息
on((event, emit) async {
try {
if (Ability().isUserLogon()) {
final room = await APIServer().updateRoom(
roomId: event.roomId,
name: event.name!,
model: event.model != null
? (event.model!.startsWith('v2@') ? event.model! : event.model!.split(':').last)
: null,
vendor: event.model != null ? (event.model!.startsWith('v2@') ? '' : event.model!.split(':').first) : null,
systemPrompt: event.prompt!,
avatarId: event.avatarId,
avatarUrl: event.avatarUrl,
maxContext: event.maxContext,
initMessage: event.initMessage,
);
final states = await stateManager.loadRoomStates(event.roomId);
emit(
RoomLoaded(
Room(
room.name,
'chat',
description: room.description,
id: room.id,
userId: room.userId,
createdAt: room.createdAt,
lastActiveTime: room.lastActiveTime,
systemPrompt: room.systemPrompt,
priority: room.priority ?? 0,
model: room.model.startsWith('v2@') ? room.model : '${room.vendor}:${room.model}',
avatarId: room.avatarId,
avatarUrl: room.avatarUrl,
initMessage: room.initMessage,
roomType: room.roomType,
),
states,
examples: await APIServer().example(room.model),
cascading: false,
),
);
} else {
final room = await chatMsgRepo.room(event.roomId);
if (room != null) {
if (event.name != null && event.name != '') {
room.name = event.name!;
}
if (event.model != null && event.model != '') {
room.model = event.model!;
}
if (event.prompt != null && event.prompt != '') {
room.systemPrompt = event.prompt!;
}
if (event.maxContext != null) {
room.maxContext = event.maxContext!;
}
await chatMsgRepo.updateRoom(room);
final states = await stateManager.loadRoomStates(event.roomId);
emit(RoomLoaded(
room,
states,
examples: await APIServer().examples(),
cascading: false,
));
}
}
} catch (e) {
emit(RoomOperationResult(false, error: e.toString()));
}
});
on((event, emit) async {
if (event.ids.isEmpty) {
return;
}
try {
final ids = await APIServer().copyRoomGallery(ids: event.ids);
emit(await createRoomsLoadedState(cache: false));
if (ids.isNotEmpty) {
emit(RoomOperationResult(true, redirect: '/room/${ids.first}/chat'));
}
} catch (e) {
emit(RoomCreateError(e));
}
});
on((event, emit) async {
try {
final resp = await APIServer().roomGalleries();
emit(RoomGalleriesLoaded(resp.galleries, tags: resp.tags));
} catch (e) {
emit(RoomGalleriesLoaded(const [], error: e));
}
});
// 创建群聊聊天室
on((event, emit) async {
emit(RoomsLoading());
try {
await APIServer().createGroupRoom(
name: event.name,
avatarUrl: event.avatarUrl,
members: event.members,
);
emit(GroupRoomUpdateResultState(true));
emit(await createRoomsLoadedState(cache: false));
} catch (e) {
emit(GroupRoomUpdateResultState(false, error: e));
emit(RoomsLoaded(const [], error: e.toString()));
}
});
// 群聊聊天室更新
on((event, emit) async {
emit(RoomsLoading());
try {
await APIServer().updateGroupRoom(
groupId: event.groupId,
name: event.name,
avatarUrl: event.avatarUrl,
members: event.members,
);
emit(GroupRoomUpdateResultState(true));
} catch (e) {
emit(GroupRoomUpdateResultState(false, error: e));
}
});
on((event, emit) async {
try {
final rooms = await APIServer().recentRooms();
emit(RoomsRecentLoaded(rooms));
} catch (e) {
emit(RoomsRecentLoaded(const [], error: e));
}
});
}
Future createRoomsLoadedState({bool cache = true}) async {
try {
if (Ability().isUserLogon()) {
final resp = await APIServer().rooms(cache: cache);
return RoomsLoaded(
resp.rooms
.map((room) => Room(
room.name,
'chat',
description: room.description,
id: room.id,
userId: room.userId,
createdAt: room.createdAt,
lastActiveTime: room.lastActiveTime,
systemPrompt: room.systemPrompt,
priority: room.priority ?? 0,
model: room.model.startsWith('v2@') ? room.model : '${room.vendor}:${room.model}',
avatarId: room.avatarId,
avatarUrl: room.avatarUrl,
roomType: room.roomType,
members: room.members,
))
.toList(),
suggests: resp.suggests ?? [],
);
} else {
final rooms = await chatMsgRepo.rooms(
userId: APIServer().localUserID(),
);
rooms.removeWhere((element) => element.id == chatAnywhereRoomId && element.category == 'system');
return RoomsLoaded(rooms);
}
} catch (e) {
return RoomsLoaded(const [], error: e);
}
}
}
================================================
FILE: lib/bloc/room_event.dart
================================================
part of 'room_bloc.dart';
@immutable
abstract class RoomEvent {}
class RoomsLoadEvent extends RoomEvent {
final bool forceRefresh;
RoomsLoadEvent({this.forceRefresh = false});
}
class RoomsRecentLoadEvent extends RoomEvent {
RoomsRecentLoadEvent();
}
class RoomCreateEvent extends RoomEvent {
final String name;
final String? model;
final String? prompt;
final int? avatarId;
final String? avatarUrl;
final int? maxContext;
final String? initMessage;
RoomCreateEvent(
this.name,
this.prompt, {
this.model,
this.avatarId,
this.avatarUrl,
this.maxContext,
this.initMessage,
});
}
class GroupRoomCreateEvent extends RoomEvent {
final String name;
final String? avatarUrl;
final List? members;
GroupRoomCreateEvent({
required this.name,
this.avatarUrl,
this.members,
});
}
class GroupRoomUpdateEvent extends RoomEvent {
final int groupId;
final String name;
final String? avatarUrl;
final List? members;
GroupRoomUpdateEvent({
required this.groupId,
required this.name,
this.avatarUrl,
this.members,
});
}
class RoomDeleteEvent extends RoomEvent {
final int roomId;
RoomDeleteEvent(this.roomId);
}
class RoomLoadEvent extends RoomEvent {
final int roomId;
final int? chatHistoryId;
final bool cascading;
RoomLoadEvent(this.roomId, {this.chatHistoryId, required this.cascading});
}
class RoomUpdateEvent extends RoomEvent {
final int roomId;
final String? name;
final String? model;
final String? prompt;
final int? avatarId;
final String? avatarUrl;
final int? maxContext;
final String? initMessage;
RoomUpdateEvent(
this.roomId, {
this.name,
this.model,
this.prompt,
this.avatarId,
this.avatarUrl,
this.maxContext,
this.initMessage,
});
}
class GalleryRoomCopyEvent extends RoomEvent {
final List ids;
GalleryRoomCopyEvent(this.ids);
}
class RoomGalleriesLoadEvent extends RoomEvent {
RoomGalleriesLoadEvent();
}
================================================
FILE: lib/bloc/room_state.dart
================================================
part of 'room_bloc.dart';
@immutable
abstract class RoomState {}
class RoomInitial extends RoomState {}
class RoomsLoading extends RoomState {}
class RoomsLoaded extends RoomState {
final List rooms;
final List suggests;
final Object? error;
RoomsLoaded(this.rooms, {this.error, this.suggests = const []});
}
class RoomsRecentLoaded extends RoomState {
final List rooms;
final Object? error;
RoomsRecentLoaded(this.rooms, {this.error});
}
class RoomLoaded extends RoomState {
final Room room;
final List? examples;
final Object? error;
final Map states;
final bool cascading;
RoomLoaded(
this.room,
this.states, {
this.error,
this.examples,
required this.cascading,
}) {
if (examples != null) {
examples!.shuffle();
}
}
}
class RoomCreateError extends RoomState {
final Object error;
RoomCreateError(this.error);
}
class RoomGalleriesLoaded extends RoomState {
final List galleries;
final List tags;
final Object? error;
RoomGalleriesLoaded(this.galleries, {this.error, this.tags = const []});
}
class GroupRoomUpdateResultState extends RoomState {
final bool success;
final Object? error;
GroupRoomUpdateResultState(this.success, {this.error});
}
class RoomOperationResult extends RoomState {
final bool success;
final Object? error;
final String? redirect;
RoomOperationResult(this.success, {this.error, this.redirect});
}
================================================
FILE: lib/bloc/user_api_keys_bloc.dart
================================================
import 'package:askaide/repo/api/keys.dart';
import 'package:askaide/repo/api_server.dart';
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
part 'user_api_keys_event.dart';
part 'user_api_keys_state.dart';
class UserApiKeysBloc extends Bloc {
UserApiKeysBloc() : super(UserApiKeysInitial()) {
// 加载用户 API Key 列表
on((event, emit) async {
final keys = await APIServer().userAPIKeys();
emit(UserApiKeysLoaded(keys: keys));
});
// 加载用户 API Key
on((event, emit) async {
final key = await APIServer().userAPIKeyDetail(id: event.id);
emit(UserApiKeyLoaded(key: key));
});
// 创建用户 API Key
on((event, emit) async {
final key = await APIServer().createAPIKey(name: event.name);
emit(UserApiKeyCreated(key: key));
final keys = await APIServer().userAPIKeys();
emit(UserApiKeysLoaded(keys: keys));
});
// 删除用户 API Key
on((event, emit) async {
await APIServer().deleteAPIKey(id: event.id);
final keys = await APIServer().userAPIKeys();
emit(UserApiKeysLoaded(keys: keys));
});
}
}
================================================
FILE: lib/bloc/user_api_keys_event.dart
================================================
part of 'user_api_keys_bloc.dart';
@immutable
sealed class UserApiKeysEvent {}
class UserApiKeysLoad extends UserApiKeysEvent {}
class UserApiKeyLoad extends UserApiKeysEvent {
final int id;
UserApiKeyLoad(this.id);
}
class UserApiKeyCreate extends UserApiKeysEvent {
final String name;
UserApiKeyCreate(this.name);
}
class UserApiKeyDelete extends UserApiKeysEvent {
final int id;
UserApiKeyDelete(this.id);
}
================================================
FILE: lib/bloc/user_api_keys_state.dart
================================================
part of 'user_api_keys_bloc.dart';
@immutable
sealed class UserApiKeysState {}
final class UserApiKeysInitial extends UserApiKeysState {}
class UserApiKeysLoaded extends UserApiKeysState {
final List keys;
UserApiKeysLoaded({required this.keys});
}
class UserApiKeyLoaded extends UserApiKeysState {
final UserAPIKey key;
UserApiKeyLoaded({required this.key});
}
class UserApiKeyCreated extends UserApiKeysState {
final String key;
UserApiKeyCreated({required this.key});
}
================================================
FILE: lib/bloc/user_bloc.dart
================================================
import 'package:askaide/repo/api/admin/users.dart';
import 'package:askaide/repo/api/page.dart';
import 'package:askaide/repo/api/quota.dart';
import 'package:askaide/repo/api_server.dart';
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
part 'user_event.dart';
part 'user_state.dart';
class UserBloc extends Bloc {
UserBloc() : super(UserInitial()) {
// 加载指定用户信息
on((event, emit) async {
final user = await APIServer().adminUser(id: event.userId);
emit(UserLoaded(user));
});
// 加载用户列表
on((event, emit) async {
final users = await APIServer().adminUsers(
page: event.page,
perPage: event.perPage,
keyword: event.keyword,
);
emit(UsersLoaded(users));
});
// 加载用户配额
on((event, emit) async {
final quota = await APIServer().adminUserQuota(userId: event.userId);
emit(UserQuotaLoaded(quota));
});
}
}
================================================
FILE: lib/bloc/user_event.dart
================================================
part of 'user_bloc.dart';
@immutable
sealed class UserEvent {}
class UserLoadEvent extends UserEvent {
final int userId;
UserLoadEvent(this.userId);
}
class UserListLoadEvent extends UserEvent {
final int page;
final int perPage;
final String? keyword;
UserListLoadEvent({
this.page = 1,
this.perPage = 20,
this.keyword,
});
}
class UserQuotaLoadEvent extends UserEvent {
final int userId;
UserQuotaLoadEvent(this.userId);
}
================================================
FILE: lib/bloc/user_state.dart
================================================
part of 'user_bloc.dart';
@immutable
sealed class UserState {}
final class UserInitial extends UserState {}
class UserLoaded extends UserState {
final AdminUser user;
UserLoaded(this.user);
}
class UserOperationResult extends UserState {
final bool success;
final String? message;
UserOperationResult(this.success, {this.message});
}
class UsersLoaded extends UserState {
final PagedData users;
UsersLoaded(this.users);
}
class UserQuotaLoaded extends UserState {
final QuotaResp quota;
UserQuotaLoaded(this.quota);
}
================================================
FILE: lib/bloc/version_bloc.dart
================================================
import 'package:askaide/repo/api_server.dart';
import 'package:askaide/repo/model/misc.dart';
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
part 'version_event.dart';
part 'version_state.dart';
class VersionBloc extends Bloc {
VersionBloc() : super(VersionInitial()) {
on((event, emit) async {
emit(VersionInitial());
final version = await APIServer().versionCheck();
emit(VersionCheckLoaded(version));
});
}
}
================================================
FILE: lib/bloc/version_event.dart
================================================
part of 'version_bloc.dart';
@immutable
abstract class VersionEvent {}
class VersionCheckEvent extends VersionEvent {}
================================================
FILE: lib/bloc/version_state.dart
================================================
part of 'version_bloc.dart';
@immutable
abstract class VersionState {}
class VersionInitial extends VersionState {}
class VersionCheckLoaded extends VersionState {
final VersionCheckResp version;
VersionCheckLoaded(this.version);
}
================================================
FILE: lib/data/migrate.dart
================================================
import 'package:askaide/helper/constant.dart';
import 'package:sqflite/sqflite.dart';
/// 执行数据库迁移
Future migrate(db, oldVersion, newVersion) async {
if (oldVersion <= 1) {
await db.execute('''
ALTER TABLE chat_room ADD COLUMN color TEXT;
UPDATE chat_room SET color = 'FF4CAF50' WHERE category = 'system';
''');
}
if (oldVersion <= 2) {
await db.execute('ALTER TABLE chat_message ADD COLUMN extra TEXT;');
await db.execute('ALTER TABLE chat_message ADD COLUMN model TEXT;');
}
if (oldVersion < 5) {
await db.execute('''
CREATE TABLE cache (
`key` TEXT NOT NULL PRIMARY KEY,
`value` TEXT NOT NULL,
`created_at` INTEGER,
`valid_before` INTEGER
)
''');
}
if (oldVersion < 6) {
await db.execute('''
CREATE TABLE creative_island_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
item_id TEXT NOT NULL,
arguments TEXT NULL,
prompt TEXT NULL,
answer TEXT NULL,
created_at INTEGER NOT NULL
)
''');
}
if (oldVersion < 7) {
await db.execute('ALTER TABLE creative_island_history ADD COLUMN task_id TEXT NULL;');
await db.execute('ALTER TABLE creative_island_history ADD COLUMN status TEXT NULL;');
}
if (oldVersion < 10) {
await db.execute('ALTER TABLE cache ADD COLUMN `group` TEXT NULL;');
}
if (oldVersion < 11) {
await db.execute('''
CREATE TABLE settings (
`key` TEXT NOT NULL PRIMARY KEY,
`value` TEXT NOT NULL
);
''');
}
if (oldVersion < 12) {
await db.execute('''ALTER TABLE chat_room ADD COLUMN user_id INTEGER NULL;''');
await db.execute('''ALTER TABLE creative_island_history ADD COLUMN user_id INTEGER NULL;''');
}
if (oldVersion < 13) {
await db.execute('''ALTER TABLE chat_message ADD COLUMN user_id INTEGER NULL;''');
}
if (oldVersion < 14) {
await db.execute('''ALTER TABLE chat_message ADD COLUMN ref_id INTEGER NULL;''');
await db.execute('''ALTER TABLE chat_message ADD COLUMN token_consumed INTEGER NULL;''');
await db.execute('''ALTER TABLE chat_message ADD COLUMN quota_consumed INTEGER NULL;''');
}
if (oldVersion < 15) {
await db.execute('''ALTER TABLE chat_room ADD COLUMN init_message TEXT;''');
await db.execute('''ALTER TABLE chat_room ADD COLUMN max_context INTEGER DEFAULT 10;''');
}
if (oldVersion < 20) {
await db.execute('''ALTER TABLE chat_message ADD COLUMN chat_history_id INTEGER NULL;''');
await db.execute('''
CREATE TABLE chat_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NULL,
room_id INTEGER NOT NULL,
title TEXT,
last_message TEXT,
created_at INTEGER,
updated_at INTEGER
)
''');
}
if (oldVersion < 23) {
await db.execute('ALTER TABLE chat_history ADD COLUMN model TEXT;');
}
if (oldVersion < 24) {
await db.execute('ALTER TABLE chat_message ADD COLUMN server_id INTEGER NULL;');
}
if (oldVersion < 25) {
await db.execute('ALTER TABLE chat_message ADD COLUMN status INTEGER DEFAULT 1;');
}
if (oldVersion < 26) {
await db.execute('ALTER TABLE chat_message ADD COLUMN images TEXT NULL;');
}
if (oldVersion < 27) {
await db.execute('ALTER TABLE chat_message ADD COLUMN file TEXT NULL;');
}
if (oldVersion < 28) {
await db.execute('ALTER TABLE chat_message ADD COLUMN flags TEXT NULL;');
}
}
/// 数据库初始化
void initDatabase(db, version) async {
await db.execute('''
CREATE TABLE chat_room (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NULL,
name TEXT NOT NULL,
category TEXT NOT NULL,
priority INTEGER DEFAULT 0,
model TEXT NOT NULL,
icon_data TEXT NOT NULL,
color TEXT,
description TEXT,
system_prompt TEXT,
init_message TEXT,
max_context INTEGER DEFAULT 10,
created_at INTEGER,
last_active_time INTEGER
)
''');
await db.execute('''
INSERT INTO chat_room (id, name, category, priority, model, icon_data, color, created_at, last_active_time)
VALUES (1, '随便聊聊', 'system', 99999, '$modelTypeOpenAI:$defaultChatModel', '57683,MaterialIcons', 'FF4CAF50', 1680969581486, ${DateTime.now().millisecondsSinceEpoch});
''');
await db.execute('''
CREATE TABLE chat_message (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NULL,
room_id INTEGER NOT NULL,
chat_history_id INTEGER NULL,
type TEXT NOT NULL,
role TEXT NOT NULL,
user TEXT,
text TEXT,
extra TEXT,
ref_id INTEGER NULL,
server_id INTEGER NULL,
status INTEGER DEFAULT 1,
token_consumed INTEGER NULL,
quota_consumed INTEGER NULL,
model TEXT,
images TEXT NULL,
file TEXT NULL,
flags TEXT NULL,
ts INTEGER NOT NULL
)
''');
await db.execute('''
CREATE TABLE chat_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NULL,
room_id INTEGER NOT NULL,
title TEXT,
last_message TEXT,
model TEXT,
created_at INTEGER,
updated_at INTEGER
)
''');
await db.execute('''
CREATE TABLE cache (
`key` TEXT NOT NULL PRIMARY KEY,
`value` TEXT NOT NULL,
`group` TEXT NULL,
`created_at` INTEGER,
`valid_before` INTEGER
)
''');
await db.execute('''
CREATE TABLE creative_island_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NULL,
item_id TEXT NOT NULL,
arguments TEXT NULL,
prompt TEXT NULL,
answer TEXT NULL,
task_id TEXT NULL,
status TEXT NULL,
created_at INTEGER NOT NULL
)
''');
await db.execute('''
CREATE TABLE settings (
`key` TEXT NOT NULL PRIMARY KEY,
`value` TEXT NOT NULL
);
''');
// await initUserDefaultRooms(db);
}
Future initUserDefaultRooms(Database db, {int? userId}) async {
await db.execute('''
INSERT INTO chat_room (name, category, priority, model, icon_data, description, system_prompt, created_at, last_active_time, color, user_id)
VALUES ('职业进阶导师', 'global', 0, 'openai:gpt-3.5-turbo', '57683,MaterialIcons', null, '我想让你担任我的职业进阶导师,你的任务是依据我的兴趣、技能和经验,为我提供职业发展建议,帮助我确定最适合的职业。注意,你需要对各种可行的职业类型进行深度研究,并在建议中包含各行业的市场趋势、就业趋势及进入该特定领域需要具备的资格', 1680969581486, ${DateTime.now().millisecondsSinceEpoch}, 'ff2196f3', ${userId ?? 'null'});
''');
await db.execute('''
INSERT INTO chat_room (name, category, priority, model, icon_data, description, system_prompt, created_at, last_active_time, color, user_id)
VALUES ('人生导师', 'global', 0, 'openai:gpt-3.5-turbo', '57683,MaterialIcons', null, '你是一名在个人和职业发展方面拥有丰富经验的人,我希望你成为帮助我定制并实现个人目标和愿景的人生导师。请根据我的需求,为我提供专业的建议和指导,并鼓励我以积极乐观地态度感恩生活,勇于面对困难和挑战,不断突破自我、持续成长,成为珍惜自我,尊重他人,信任可靠,散发积极正能量的人。接下来我会像你进行提问,请称呼我为朋友。', 1680969581486, ${DateTime.now().millisecondsSinceEpoch}, 'ff2196f3', ${userId ?? 'null'});
''');
await db.execute('''
INSERT INTO chat_room (name, category, priority, model, icon_data, description, system_prompt, created_at, last_active_time, color, user_id)
VALUES ('理财顾问', 'global', 0, 'openai:gpt-3.5-turbo', '57683,MaterialIcons', null, '我希望你成为我的理财顾问,为我提供创造性的理财方案并制定出理财计划。你需要考虑投资预算、投资策略和风险管理。在某些情况下,你可能还需要提供有关税收法律法规的建议,以帮助我实现最大化收益', 1680969581486, ${DateTime.now().millisecondsSinceEpoch}, 'ff2196f3', ${userId ?? 'null'});
''');
await db.execute('''
INSERT INTO chat_room (name, category, priority, model, icon_data, description, system_prompt, created_at, last_active_time, color, user_id)
VALUES ('玩乐指南', 'global', 0, 'openai:gpt-3.5-turbo', '57683,MaterialIcons', null, '我希望你是我的专属吃喝玩乐(旅行)达人,在美食、娱乐、旅游等领域拥有丰富的经验。请根据我要求的领域、位置及其他需求,为我推荐几个准确、实用、高质量的好去处,以帮助我拥有更丰富的人生体验', 1680969581486, ${DateTime.now().millisecondsSinceEpoch}, 'ff2196f3', ${userId ?? 'null'});
''');
await db.execute('''
INSERT INTO chat_room (name, category, priority, model, icon_data, description, system_prompt, created_at, last_active_time, color, user_id)
VALUES ('野蛮女友', 'global', 0, 'openai:gpt-3.5-turbo', '57683,MaterialIcons', null, '你是我的野蛮女友,能和我畅谈任何话题。你纯真无邪,性格里带着一丝精灵古怪和小小任性,偶尔会撒撒娇,或对我冷嘲热讽一番,非常可爱。和你聊天时,你常常会使用表情符号或Emoji回应我。另外请称呼我为大懒虫', 1680969581486, ${DateTime.now().millisecondsSinceEpoch}, 'ff2196f3', ${userId ?? 'null'});
''');
await db.execute('''
INSERT INTO chat_room (name, category, priority, model, icon_data, description, system_prompt, created_at, last_active_time, color, user_id)
VALUES ('鸡汤达人', 'global', 0, 'openai:gpt-3.5-turbo', '57683,MaterialIcons', null, '你是一名鸡汤达人。请在和我对话的过程中,使用你擅长的鸡汤式发言进行回答,要求回答饱经沧桑、引经据典、充满智慧和正能量。另外请称呼我为朋友', 1680969581486, ${DateTime.now().millisecondsSinceEpoch}, 'ff2196f3', ${userId ?? 'null'});
''');
await db.execute('''
INSERT INTO chat_room (name, category, priority, model, icon_data, description, system_prompt, created_at, last_active_time, color, user_id)
VALUES ('成语接龙', 'global', 0, 'openai:gpt-3.5-turbo', '57683,MaterialIcons', null, '陪我玩成语接龙游戏,我说一句话或一个成语,你将以最后一个字为作为起始,写一个成语出来', 1680969581486, ${DateTime.now().millisecondsSinceEpoch}, 'ff2196f3', ${userId ?? 'null'});
''');
await db.execute('''
INSERT INTO chat_room (name, category, priority, model, icon_data, description, system_prompt, created_at, last_active_time, color, user_id)
VALUES ('哲学大师', 'global', 0, 'openai:gpt-3.5-turbo', '57683,MaterialIcons', null, '你是一名哲学家。我会提出一些与哲学相关的话题或问题,你的工作就是深入探索这些概念并回答我。这可能涉及到对各种哲学理论的研究、提出新的想法或寻找创造性的解决方案以解决复杂的问题', 1680969581486, ${DateTime.now().millisecondsSinceEpoch}, 'ff2196f3', ${userId ?? 'null'});
''');
await db.execute('''
INSERT INTO chat_room (name, category, priority, model, icon_data, description, system_prompt, created_at, last_active_time, color, user_id)
VALUES ('开心果', 'global', 0, 'openai:gpt-3.5-turbo', '57683,MaterialIcons', null, '你是一个非常幽默的人,是大家心目中的开心果。作为开心果,你总是能用机智逗趣、诙谐幽默的方式回应我,妙语连珠,尽显活力,必要时还会通过讲笑话或调侃等方式来缓解气氛', 1680969581486, ${DateTime.now().millisecondsSinceEpoch}, 'ff2196f3', ${userId ?? 'null'});
''');
await db.execute('''
INSERT INTO chat_room (name, category, priority, model, icon_data, description, system_prompt, created_at, last_active_time, color, user_id)
VALUES ('健身教练', 'global', 0, 'openai:gpt-3.5-turbo', '57683,MaterialIcons', null, '我希望你成为我的私人教练。你的职责是运用运动学科知识、营养建议及其他相关因素,结同时考虑我的生活习惯、目标及当前健身水平,为我制定最适合我的健身计划', 1680969581486, ${DateTime.now().millisecondsSinceEpoch}, 'ff2196f3', ${userId ?? 'null'});
''');
}
================================================
FILE: lib/helper/ability.dart
================================================
import 'package:askaide/helper/constant.dart';
import 'package:askaide/helper/platform.dart';
import 'package:askaide/repo/api/info.dart';
import 'package:askaide/repo/api/model.dart';
import 'package:askaide/repo/settings_repo.dart';
class Ability {
late final SettingRepository setting;
late Capabilities capabilities;
init(SettingRepository setting, Capabilities capabilities) {
this.setting = setting;
this.capabilities = capabilities;
}
/// 单例
static final Ability _instance = Ability._internal();
Ability._internal();
factory Ability() {
return _instance;
}
/// 是否显示全局警告信息
bool get showGlobalAlert {
return true;
}
/// 服务状态页
String get serviceStatusPage {
return capabilities.serviceStatusPage;
}
/// 是否支持 Websocket
bool get supportWebSocket {
return capabilities.supportWebsocket;
}
/// 是否支持 API Keys 功能
bool get supportAPIKeys {
return capabilities.supportAPIKeys;
}
/// 更新能力
updateCapabilities(Capabilities capabilities) {
this.capabilities = capabilities;
}
/// 首页支持的模型列表
List get homeModels {
return capabilities.homeModels;
}
/// 是否显示首页模型描述
bool get showHomeModelDescription {
return capabilities.showHomeModelDescription;
}
/// 首页路由
String get homeRoute {
return capabilities.homeRoute;
}
/// 是否支持绘玩
bool get enableGallery {
return !capabilities.disableGallery;
}
/// 是否支持创作岛
bool get enableCreationIsland {
return !capabilities.disableCreationIsland;
}
/// 是否支持数字人
bool get enableDigitalHuman {
return !capabilities.disableDigitalHuman;
}
/// 是否支持聊一聊
bool get enableChat {
return !capabilities.disableChat;
}
/// 是否支持 OpenAI
bool get enableOpenAI {
return capabilities.openaiEnabled && (!capabilities.disableChat || !capabilities.disableDigitalHuman);
}
/// 是否支持 IOS 外支付
bool get enableOtherPay {
return capabilities.otherPayEnabled;
}
/// 是否支持 Stripe 支付
bool get enableStripe {
return capabilities.stripeEnabled;
}
/// 是否支持微信支付
bool get enableWechatPay {
return capabilities.wechatPayEnabled;
}
/// 是否支持 ApplePay
bool get enableApplePay {
return capabilities.applePayEnabled;
}
/// 是否显示 Apple 登录
bool get enableAppleSignin {
return enableApplePay && (PlatformTool.isIOS() || PlatformTool.isAndroid() || PlatformTool.isMacOS());
}
/// 是否支持微信登录
bool get enableWechatSignin {
return capabilities.wechatSigninEnabled && (PlatformTool.isIOS() || PlatformTool.isAndroid());
}
/// 是否支持支付功能
bool get enablePayment {
if (!enableApplePay && !enableOtherPay && !enableStripe) {
return false;
}
if (PlatformTool.isIOS() && enableApplePay) {
return true;
}
return true;
}
/// 是否用户已经登陆
bool isUserLogon() {
return setting.stringDefault(settingAPIServerToken, '') != '';
}
/// 是否启用了 OpenAI 自定义设置
bool get enableLocalOpenAI {
return setting.boolDefault(settingOpenAISelfHosted, false);
}
/// 是否使用本地的 OpenAI 模型
bool usingLocalOpenAIModel(String model) {
return setting.boolDefault(settingOpenAISelfHosted, false) &&
(model.startsWith('openai:') || model.startsWith('gpt-'));
}
/// 是否支持翻译功能
bool get supportTranslate {
return false;
// return setting.stringDefault(settingAPIServerToken, '') != '';
}
/// 是否支持语音合成功能
bool get supportSpeak {
if (!enableTextToVoice) {
return false;
}
if (PlatformTool.isWeb()) {
return false;
}
return true;
}
/// 是否支持语音聊天功能
bool get supportVoiceChat {
if (!enableVoiceToText) {
return false;
}
if (PlatformTool.isWeb()) {
return false;
}
return true;
}
/// 是否支持图片上传功能
bool get supportImageUploader {
return supportImglocUploader() || supportQiniuUploader();
}
/// 是否支持Imgloc图片上传功能
bool supportImglocUploader() {
return setting.boolDefault(settingImageManagerSelfHosted, false) &&
setting.stringDefault(settingImglocToken, '') != '';
}
/// 是否支持七牛云图片上传功能
bool supportQiniuUploader() {
return setting.stringDefault(settingAPIServerToken, '') != '';
}
/// 是否支持语音转文字
bool get enableVoiceToText {
return capabilities.enableVoiceToText;
}
/// 是否支持文字转语音
bool get enableTextToVoice {
return capabilities.enableTextToVoice;
}
/// 获取当前主题模式
String get themeMode {
return setting.stringDefault(settingThemeMode, 'system');
}
}
================================================
FILE: lib/helper/cache.dart
================================================
import 'package:askaide/repo/cache_repo.dart';
import 'package:askaide/repo/settings_repo.dart';
class Cache {
late final SettingRepository setting;
late final CacheRepository cacheRepo;
init(SettingRepository setting, CacheRepository cacheRepo) {
this.setting = setting;
this.cacheRepo = cacheRepo;
}
/// 单例
static final Cache _instance = Cache._internal();
Cache._internal();
factory Cache() {
return _instance;
}
Future boolGet({required String key}) async {
var value = await cacheRepo.get(key);
if (value == null || value.isEmpty || value == 'true') {
return true;
}
return false;
}
Future remove({required String key}) async {
await cacheRepo.remove(key);
}
Future clearAll() async {
await cacheRepo.clearAll();
}
Future setBool({
required String key,
required bool value,
Duration duration = const Duration(days: 1),
}) async {
await cacheRepo.set(key, value.toString(), duration);
}
Future setString({
required String key,
required String value,
Duration duration = const Duration(days: 1),
}) async {
await cacheRepo.set(key, value, duration);
}
Future stringGet({required String key}) async {
return await cacheRepo.get(key);
}
Future setInt({
required String key,
required int value,
Duration duration = const Duration(days: 1),
}) async {
await cacheRepo.set(key, value.toString(), duration);
return value;
}
Future intGet({required String key}) async {
var value = await cacheRepo.get(key);
if (value == null || value.isEmpty) {
return 0;
}
return int.parse(value);
}
}
================================================
FILE: lib/helper/chat_token.dart
================================================
import 'package:askaide/helper/logger.dart';
import 'package:tiktoken/tiktoken.dart';
/// 计算 message 包含的 token 数量
int tokenCount(String model, String message) {
try {
final encoding = encodingForModel(model);
return encoding.encode(message).length;
} catch (e) {
Logger.instance.e(e);
return -1;
}
}
================================================
FILE: lib/helper/color.dart
================================================
import 'package:flutter/material.dart';
/// 将颜色转换为字符串
String colorToString(Color color, {String defaultColor = 'FF000000'}) {
try {
return color.toString().split('(0x')[1].split(')')[0];
} catch (e) {
return defaultColor;
}
}
/// 将字符串转换为颜色
Color stringToColor(String colorString, {Color defaultColor = Colors.black}) {
try {
if (colorString.length == 6) {
colorString = 'FF$colorString';
}
return Color(int.parse(colorString, radix: 16));
} catch (e) {
return defaultColor;
}
}
================================================
FILE: lib/helper/constant.dart
================================================
import 'package:flutter/material.dart';
// 客户端应用版本号
const clientVersion = '2.0.0';
// 本地数据库版本号
const databaseVersion = 28;
const settingAPIServerToken = 'api-token';
const settingUserInfo = 'user-info';
const settingUsingGuestMode = 'using-guest-mode';
const settingForceShowLab = 'force-show-lab';
const chatAnywhereModel = 'openai:gpt-3.5-turbo';
const chatAnywhereRoomId = 1;
const creativeIslandModelTypeText = 'text-generation';
const creativeIslandModelTypeImage = 'image-generation';
const creativeIslandModelTypeImageToImage = 'image-to-image';
const creativeIslandCompletionTypeText = 'text';
const creativeIslandCompletionTypeBase64Image = 'base64-images';
const creativeIslandCompletionTypeURLImage = 'url-images';
// 用于标识是否已经加载过引导页
// 只有在第一次安装的时候才会加载引导页
const settingOnBoardingLoaded = 'on-boarding-loaded';
const settingLanguage = 'language';
const settingServerURL = 'server-url';
// 背景图片
const settingBackgroundImage = 'background-image';
const settingBackgroundImageBlur = 'background-image-blur';
const settingOpenAISelfHosted = 'openai-self-hosted';
const settingDeepAISelfHosted = 'deepai-self-hosted';
const settingStabilityAISelfHosted = 'stabilityai-self-hosted';
const settingImageManagerSelfHosted = 'image-manager-self-hosted';
const settingThemeMode = "dark-mode";
const settingImglocToken = 'imgloc-token';
const chatMessagePerPage = 300;
const contextBreakKey = 'context-break';
const defaultChatModel = 'gpt-3.5-turbo';
const defaultChatModelName = 'GPT-3.5';
const defaultImageModel = 'DALL·E';
const defaultModelNotChatDesc = '该模型不支持上下文,只能一问一答';
const defaultChatHistoryCount = 50;
// AI 模型类型
const modelTypeOpenAI = 'openai';
const modelTypeDeepAI = 'deepai';
const modelTypeLeapAI = "leapai";
const modelTypeStabilityAI = 'stabilityai';
const modelTypeFromston = 'fromston';
const modelTypeGetimg = 'getimgai';
final modelTypeTagColors = {
modelTypeOpenAI: Colors.blue,
modelTypeDeepAI: Colors.green,
modelTypeStabilityAI: Colors.purple,
modelTypeLeapAI: Colors.orange,
modelTypeFromston: Colors.blueAccent,
modelTypeGetimg: Colors.pinkAccent,
};
// OpenAI 相关设置
const settingOpenAIAPIToken = "openai-token";
const settingOpenAIOrganization = 'openai-organization';
const settingOpenAITemperature = "openai-temperature";
const settingOpenAIModel = "openai-model";
const settingOpenAIURL = "openai-url";
const defaultOpenAIServerURL = 'https://api.openai.com';
// DeepAI 相关设置
const settingDeepAIURL = 'deepai-url';
const settingDeepAIAPIToken = 'deepai-token';
const defaultDeepAIServerURL = 'https://api.deepai.org';
// StabilityAI 相关设置
const settingStabilityAIURL = 'stabilityai-url';
const settingStabilityAIAPIToken = 'stabilityai-token';
const settingStabilityAIOrganization = 'stabilityai-organization';
const defaultStabilityAIURL = 'https://api.stability.ai';
// 微信配置
const weixinAppId = 'wx52cc036cc770406d';
const universalLink = 'https://ai.aicode.cc/wechat-login/';
// 图床信息
const qiniuImageTypeAvatar = 'avatar';
const qiniuImageTypeThumb = 'thumb';
const qiniuImageTypeThumbMedium = 'thumb_500';
// 缓存相关的 Keys
// 最后一次使用的模型
const cacheKeyLastModel = 'last-model';
// 数据存储目录
const homePathDirName = '.aidea';
================================================
FILE: lib/helper/env.dart
================================================
/// 默认 API 服务器地址
/// 注意:当你使用自己的服务器时,请修改该地址为你自己的服务器地址
const defaultAPIServerURL = 'https://ai-api.aicode.cc';
/// API 服务器地址
String get apiServerURL {
var url = const String.fromEnvironment(
'API_SERVER_URL',
defaultValue: defaultAPIServerURL,
);
// 当配置的 URL 为 / 时,自动替换为空,用于 Web 端
if (url == '/') {
return '';
}
return url;
}
================================================
FILE: lib/helper/error.dart
================================================
import 'package:askaide/helper/ability.dart';
import 'package:askaide/lang/lang.dart';
import 'package:dart_openai/openai.dart';
Object resolveErrorMessage(dynamic e, {bool isChat = false}) {
// TODO
if (e is RequestFailedException) {
final msg =
resolveHTTPStatusCode(e.statusCode, isChat: isChat, message: e.message);
if (msg != null) {
return msg;
}
return e.message;
}
return e.toString();
}
Object? resolveHTTPStatusCode(int statusCode,
{bool isChat = false, String? message}) {
switch (statusCode) {
case 400:
return const LanguageText('请求参数错误');
case 401:
if (Ability().enableLocalOpenAI) {
return const LanguageText(AppLocale.openAIAuthFailed);
}
if (Ability().isUserLogon()) {
return const LanguageText(AppLocale.accountNeedReSignin,
action: 're-signin');
}
return const LanguageText(AppLocale.signInRequired, action: 'sign-in');
case 404:
if (isChat) {
return const LanguageText(AppLocale.modelNotFound);
}
break;
case 429:
if (isChat) {
return const LanguageText(AppLocale.tooManyRequestsOrPaymentRequired);
}
return const LanguageText(AppLocale.tooManyRequests);
case 451:
return const LanguageText(AppLocale.modelNotValid);
case 402:
return const LanguageText(AppLocale.quotaExceeded, action: 'payment');
case 500:
if (message != null && message.isNotEmpty) {
return message;
}
return const LanguageText(AppLocale.internalServerError);
case 502:
return const LanguageText(AppLocale.badGateway);
}
return null;
}
================================================
FILE: lib/helper/event.dart
================================================
class GlobalEvent {
/// 单例
static final GlobalEvent _instance = GlobalEvent._internal();
GlobalEvent._internal();
factory GlobalEvent() {
return _instance;
}
/// 事件监听器
final Map> _listeners = {};
/// 监听事件
Function() on(String event, Function(dynamic data) callback) {
if (_listeners[event] == null) {
_listeners[event] = [];
}
_listeners[event]!.add(callback);
return () {
_listeners[event]!.remove(callback);
};
}
/// 触发事件
void emit(String event, [dynamic data]) {
if (_listeners[event] == null) {
return;
}
for (var callback in _listeners[event]!) {
callback(data);
}
}
}
================================================
FILE: lib/helper/global_store.dart
================================================
import 'package:askaide/page/component/chat/file_upload.dart';
class GlobalStore {
static final GlobalStore _instance = GlobalStore._internal();
GlobalStore._internal();
factory GlobalStore() {
return _instance;
}
List uploadedFiles = [];
}
================================================
FILE: lib/helper/haptic_feedback.dart
================================================
import 'package:flutter/services.dart';
class HapticFeedbackHelper {
static Future lightImpact() async {
return HapticFeedback.lightImpact();
}
static Future mediumImpact() async {
return HapticFeedback.mediumImpact();
}
static Future heavyImpact() async {
return HapticFeedback.heavyImpact();
}
}
================================================
FILE: lib/helper/helper.dart
================================================
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:askaide/helper/logger.dart';
import 'package:askaide/lang/lang.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localization/flutter_localization.dart';
import 'package:intl/intl.dart';
import 'package:path_provider/path_provider.dart';
import 'package:uuid/uuid.dart';
String randomId() {
return const Uuid().v4();
}
/// 将 base64 转换为图片,存储到临时文件
Future writeImageFromBase64(String base64, String ext) async {
final directory = await getApplicationDocumentsDirectory();
// 确保目录存在
await Directory('${directory.path}/cache').create(recursive: true);
final file = File('${directory.path}/cache/temp_${randomId()}.$ext');
await file.writeAsBytes(base64Decode(base64));
return file.path.substring(directory.path.length + 1);
}
String filenameWithoutExt(String filePath) {
int slashIndex = filePath.lastIndexOf('/');
int dotIndex = filePath.lastIndexOf('.');
if (dotIndex < 0 || dotIndex < slashIndex) {
return filePath.substring(slashIndex + 1);
} else {
return filePath.substring(slashIndex + 1, dotIndex);
}
}
Future writeTempFile(String path, Uint8List bytes) async {
final directory = await getTemporaryDirectory();
final file = File('${directory.path}/$path');
return await file.writeAsBytes(bytes);
}
Future readTempFile(String path) async {
final directory = await getTemporaryDirectory();
final file = File('${directory.path}/$path');
return await file.readAsBytes();
}
Future writeStringFileToDocumentsDirectory(String path, String content) async {
try {
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/$path');
Logger.instance.e('${directory.path}/$path');
await file.writeAsString(content);
} catch (e) {
Logger.instance.e('写入文件失败: $e');
}
}
Future readStringFileFromDocumentsDirectory(String path) async {
try {
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/$path');
return await file.readAsString();
} catch (e) {
return '';
}
}
Future removeExternalFile(String externalFilepath) async {
// Get the external file
final File externalFile = File(externalFilepath);
// Check if the external file exists
if (!await externalFile.exists()) {
return;
}
await externalFile.delete();
}
Future copyExternalFileToAppDocs(String externalFilePath) async {
// Get the external file
final File externalFile = File(externalFilePath);
// Check if the external file exists
if (!await externalFile.exists()) {
throw Exception('External file not found at: $externalFilePath');
}
// Get the ApplicationDocumentsDirectory
final Directory appDocsDir = await getApplicationDocumentsDirectory();
// Generate a UUID for the new file name
final String uuid = const Uuid().v4();
// Get the file extension
final String fileExtension = externalFile.path.split('.').last;
// Create a new file in the ApplicationDocumentsDirectory with the UUID as its name
final File newFile = File('${appDocsDir.path}/$uuid.$fileExtension');
// Copy the external file to the new file in the ApplicationDocumentsDirectory
await externalFile.copy(newFile.path);
// print('File copied to: ${newFile.path}');
return newFile.path;
}
/// 将时间转换为友好的时间
String humanTime(DateTime? ts, {bool withTime = false}) {
if (ts == null || ts.millisecondsSinceEpoch == 0) {
return '';
}
var now = DateTime.now();
var diff = now.difference(ts);
if (diff.inDays > 0) {
if (withTime) {
return DateFormat('yyyy/MM/dd HH:mm').format(ts.toLocal());
}
return DateFormat('yyyy/MM/dd').format(ts.toLocal());
}
if (diff.inHours > 0) {
return '${diff.inHours} hours ago';
}
if (diff.inMinutes > 0) {
return '${diff.inMinutes} minutes ago';
}
return 'Just now';
}
/// 解析错误信息
String resolveError(BuildContext context, Object error) {
if (error is LanguageText) {
return error.message.getString(context);
}
return error.toString();
}
================================================
FILE: lib/helper/http.dart
================================================
import 'package:askaide/helper/logger.dart';
import 'package:dio/dio.dart';
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:dio_smart_retry/dio_smart_retry.dart';
class HttpClient {
static final dio = Dio();
static final cacheStore = MemCacheStore();
static final cacheOptions = CacheOptions(
store: cacheStore,
policy: CachePolicy.request,
hitCacheOnErrorExcept: [401, 403],
maxStale: const Duration(days: 7),
allowPostMethod: false,
keyBuilder: CacheOptions.defaultCacheKeyBuilder,
);
static init() {
dio.interceptors.add(DioCacheInterceptor(
options: cacheOptions,
));
dio.interceptors.add(RetryInterceptor(
dio: dio,
retries: 3,
logPrint: (message) {
Logger.instance.w(message);
},
retryDelays: const [
Duration(seconds: 1), // wait 1 sec before first retry
Duration(seconds: 2), // wait 2 sec before second retry
Duration(seconds: 3), // wait 3 sec before third retry
],
));
}
static Future get(
String url, {
Map? queryParameters,
Options? options,
}) async {
Logger.instance.d('API Request: [GET] $url');
return await dio.get(url, queryParameters: queryParameters, options: options);
}
static Future getCached(
String url, {
String? subKey,
Duration duration = const Duration(days: 1),
Map? queryParameters,
bool forceRefresh = false,
Options? options,
}) async {
options ??= Options();
Logger.instance.d('API Request: [GET with cache] $url');
final resp = await dio.get(
url,
queryParameters: queryParameters,
options: options.copyWith(
extra: cacheOptions
.copyWith(
maxStale: Nullable(duration),
policy: forceRefresh ? CachePolicy.refreshForceCache : CachePolicy.forceCache,
)
.toExtra()),
);
// print("=======================");
// Logger.instance.d("request: $url [${resp.statusCode}]");
// print("response: ${resp.data}");
return resp;
}
// 清空缓存
static Future cleanCache() async {
return await cacheStore.clean();
}
static Future post(
String url, {
Map? queryParameters,
Map? formData,
Options? options,
}) async {
Logger.instance.d('API Request: [POST] $url');
final resp = await dio.post(
url,
queryParameters: queryParameters,
data: formData != null ? FormData.fromMap(formData) : null,
options: options,
);
// print("=======================");
// print("request: $url");
// print("response: ${resp.data}");
return resp;
}
static Future postJSON(
String url, {
Map? queryParameters,
Map? data,
Options? options,
}) async {
Logger.instance.d('API Request: [POST JSON] $url');
final resp = await dio.post(
url,
queryParameters: queryParameters,
data: data,
options: options,
);
// print("=======================");
// print("request: $url");
// print("response: ${resp.data}");
return resp;
}
static Future put(
String url, {
Map? queryParameters,
Map? formData,
Options? options,
}) async {
Logger.instance.d('API Request: [PUT] $url');
return await dio.put(
url,
queryParameters: queryParameters,
data: formData != null ? FormData.fromMap(formData) : null,
options: options,
);
}
static Future putJSON(
String url, {
Map? queryParameters,
Map? data,
Options? options,
}) async {
Logger.instance.d('API Request: [PUT JSON] $url');
return await dio.put(
url,
queryParameters: queryParameters,
data: data,
options: options,
);
}
static Future delete(
String url, {
Map? queryParameters,
Map? formData,
Options? options,
}) async {
Logger.instance.d('API Request: [DELETE] $url');
return await dio.delete(
url,
queryParameters: queryParameters,
data: formData != null ? FormData.fromMap(formData) : null,
options: options,
);
}
}
================================================
FILE: lib/helper/image.dart
================================================
String imageURL(String url, String filter) {
if (!url.startsWith('https://ssl.aicode.cc/')) {
return url;
}
if (filter.isEmpty) {
return url;
}
if (isImage(url)) {
return '$url-$filter';
} else {
return url;
}
}
bool isImage(String url) {
final low = url.toLowerCase();
return low.endsWith('.jpg') ||
low.endsWith('.jpeg') ||
low.endsWith('.png') ||
low.endsWith('.gif') ||
low.endsWith('.webp');
}
================================================
FILE: lib/helper/logger.dart
================================================
import 'dart:io';
import 'package:askaide/helper/path.dart';
import 'package:askaide/helper/platform.dart';
import 'package:logger/logger.dart' as logger;
class Logger {
static final logger.Logger instance = logger.Logger(
printer: logger.PrettyPrinter(
lineLength: 120,
printTime: true,
colors: false,
noBoxingByDefault: true,
),
output: logger.MultiOutput(
[
logger.ConsoleOutput(),
if (!PlatformTool.isWeb())
logger.FileOutput(
file: File(PathHelper().getLogfilePath),
overrideExisting: true,
),
],
),
);
}
================================================
FILE: lib/helper/lru.dart
================================================
import 'dart:collection';
abstract class Disposable {
void dispose();
}
class LRUCache {
final int capacity;
final LinkedHashMap _cache = LinkedHashMap();
LRUCache(this.capacity);
bool containsKey(K key) {
return _cache.containsKey(key);
}
V? get(K key) {
if (_cache.containsKey(key)) {
final value = _cache.remove(key);
if (value != null) {
_cache[key] = value;
return value;
}
}
return null;
}
void put(K key, V value) {
if (_cache.containsKey(key)) {
_cache.remove(key);
} else if (_cache.length >= capacity) {
var removed = _cache.remove(_cache.keys.first);
if (removed != null) {
removed.dispose();
}
}
_cache[key] = value;
}
void remove(K key) {
var removed = _cache.remove(key);
if (removed != null) {
removed.dispose();
}
}
void clear() {
_cache.forEach((_, value) => value.dispose());
_cache.clear();
}
int get length => _cache.length;
}
================================================
FILE: lib/helper/model.dart
================================================
import 'package:askaide/helper/constant.dart';
import 'package:askaide/repo/api_server.dart';
import 'package:askaide/repo/model/model.dart' as mm;
import 'package:askaide/repo/settings_repo.dart';
/// 模型聚合,用于聚合多种厂商的模型
class ModelAggregate {
static late SettingRepository settings;
static void init(SettingRepository settings) {
ModelAggregate.settings = settings;
}
/// 支持的模型列表
static Future> models({bool cache = true}) async {
return (await APIServer().models(cache: cache))
.map(
(e) => mm.Model(
e.id.split(':').last,
e.name,
e.category,
shortName: e.shortName,
description: e.description,
priceInfo: e.priceInfo,
isChatModel: e.isChat,
disabled: e.disabled,
category: e.category,
tag: e.tag,
avatarUrl: e.avatarUrl,
supportVision: e.supportVision,
supportReasoning: e.supportReasoning,
supportSearch: e.supportSearch,
tagTextColor: e.tagTextColor,
tagBgColor: e.tagBgColor,
isNew: e.isNew,
isDefault: e.isDefault,
userNoPermission: e.userNoPermission,
),
)
.toList();
}
/// 根据模型唯一id查找模型
static Future model(String uid) async {
uid = uid.split(':').last;
final supportModels = await models();
return supportModels.firstWhere(
(element) => element.uid() == uid || element.id == uid,
orElse: () => mm.Model(defaultChatModel, defaultChatModel, 'openai', category: modelTypeOpenAI),
);
}
}
================================================
FILE: lib/helper/model_resolver.dart
================================================
import 'dart:io';
import 'package:askaide/helper/constant.dart';
import 'package:askaide/helper/error.dart';
import 'package:askaide/helper/helper.dart';
import 'package:askaide/repo/api_server.dart';
import 'package:askaide/repo/deepai_repo.dart';
import 'package:askaide/repo/model/chat_message.dart';
import 'package:askaide/repo/model/message.dart';
import 'package:askaide/repo/model/room.dart';
import 'package:askaide/repo/openai_repo.dart';
import 'package:askaide/repo/stabilityai_repo.dart';
import 'package:dart_openai/openai.dart';
/// 根据聊天类型,调用不同的 API 接口
class ModelResolver {
late final OpenAIRepository openAIRepo;
late final DeepAIRepository deepAIRepo;
late final StabilityAIRepository stabilityAIRepo;
/// 初始化,设置模型实现
void init({
required OpenAIRepository openAIRepo,
required DeepAIRepository deepAIRepo,
required StabilityAIRepository stabilityAIRepo,
}) {
this.openAIRepo = openAIRepo;
this.deepAIRepo = deepAIRepo;
this.stabilityAIRepo = stabilityAIRepo;
}
ModelResolver._();
static final instance = ModelResolver._();
/// 语音转文字
Future audioToText(File file) async {
try {
return await openAIRepo.audioTranscription(audioFile: file);
} catch (error) {
throw resolveErrorMessage(error);
}
}
/// 发起聊天请求
Future request({
required Room room,
required List contextMessages,
required Function(ChatStreamRespData value) onMessage,
int? maxTokens,
String? tempModel,
int? historyId,
List? flags,
}) async {
if (room.modelCategory() == modelTypeDeepAI) {
return await _deepAIModel(
room: room,
message: contextMessages.last,
contextMessages: contextMessages,
onMessage: (value) {
onMessage(ChatStreamRespData(content: value));
},
);
} else if (room.modelCategory() == modelTypeStabilityAI) {
return await _stabilityAIModel(
room: room,
message: contextMessages.last,
contextMessages: contextMessages,
onMessage: (value) {
onMessage(ChatStreamRespData(content: value));
},
);
} else {
return await _openAIModel(
room: room,
contextMessages: contextMessages,
onMessage: onMessage,
maxTokens: maxTokens,
tempModel: tempModel,
historyId: historyId,
flags: flags,
);
}
}
/// 调用 StabilityAI API
Future _stabilityAIModel({
required Room room,
required Message message,
required List contextMessages,
required Function(String value) onMessage,
}) async {
if (stabilityAIRepo.selfHosted) {
var res = await stabilityAIRepo.createImageBase64(
room.modelName(),
[StabilityAIPrompt(message.text, 0.5)],
);
for (var data in res) {
var path = await writeImageFromBase64(data, 'png');
// print('图片路径: $path');
onMessage('\n\n');
}
} else {
var taskId = await stabilityAIRepo.createImageBase64Async(
room.modelName(),
[StabilityAIPrompt(message.text, 0.5)],
);
await Future.delayed(const Duration(seconds: 10));
await _waitForTasks(taskId, onMessage);
}
}
Future _waitForTasks(
String taskId,
Function(String value) onMessage, {
int retry = 0,
}) async {
var res = await APIServer().asyncTaskStatus(taskId);
if (res.status == 'success') {
for (var data in res.resources!) {
onMessage('\n\n');
}
} else if (res.status == 'failed') {
throw 'Response failed: ${res.errors!.join("\n")}';
} else {
if (retry > 10) {
throw 'Response timeout';
}
await Future.delayed(const Duration(seconds: 5));
await _waitForTasks(taskId, onMessage, retry: retry + 1);
}
}
/// 调用 DeepAI API
Future _deepAIModel({
required Room room,
required Message message,
required List contextMessages,
required Function(String value) onMessage,
}) async {
if (deepAIRepo.selfHosted) {
var res = await deepAIRepo.painting(room.modelName(), message.text);
onMessage('\n\n');
} else {
var taskId = await deepAIRepo.paintingAsync(room.modelName(), message.text);
await Future.delayed(const Duration(seconds: 10));
await _waitForTasks(taskId, onMessage);
}
}
/// 调用 OpenAI API
Future _openAIModel({
required Room room,
required List contextMessages,
required Function(ChatStreamRespData value) onMessage,
int? maxTokens,
String? tempModel,
int? historyId,
List? flags,
}) async {
// 图像模式
if (OpenAIRepository.isImageModel(room.modelName())) {
var res = await openAIRepo.createImage(contextMessages.last.text, n: 2);
for (var url in res) {
onMessage(ChatStreamRespData(content: '\n\n'));
}
return;
}
// 聊天模型
return await openAIRepo.chatStream(
_buildRequestContext(room, contextMessages),
onMessage,
model: room.modelName(),
tempModel: tempModel,
maxTokens: maxTokens,
roomId: room.isLocalRoom ? null : room.id,
historyId: historyId,
flags: flags,
);
}
/// 构建机器人请求上下文
List _buildRequestContext(
Room room,
List messages,
) {
// // N 小时内的消息作为一个上下文
// var recentMessages = messages
// .where((e) => e.ts!.millisecondsSinceEpoch > lastAliveTime())
// .toList();
var recentMessages = messages.toList();
int contextBreakIndex =
recentMessages.lastIndexWhere((element) => element.isSystem() && element.type == MessageType.contextBreak);
if (contextBreakIndex > -1) {
recentMessages = recentMessages.sublist(contextBreakIndex + 1);
}
var contextMessages = recentMessages
.where((e) => !e.isSystem() && !e.isInitMessage())
.map((e) => e.role == Role.receiver
? ChatMessage(
role: OpenAIChatMessageRole.assistant,
content: e.text,
images: e.images,
file: e.file,
)
: ChatMessage(
role: OpenAIChatMessageRole.user,
content: e.text,
images: e.images,
file: e.file,
))
.toList();
if (contextMessages.length > room.maxContext * 2) {
contextMessages = contextMessages.sublist(contextMessages.length - room.maxContext * 2);
}
if (room.systemPrompt != null && room.systemPrompt != '') {
contextMessages.insert(
0,
ChatMessage(
role: OpenAIChatMessageRole.system,
content: room.systemPrompt!,
),
);
}
return contextMessages;
}
}
================================================
FILE: lib/helper/path.dart
================================================
import 'dart:io' show Directory, Platform;
import 'package:askaide/helper/constant.dart';
import 'package:askaide/helper/logger.dart';
import 'package:askaide/helper/platform.dart';
import 'package:path_provider/path_provider.dart';
class PathHelper {
late final String cachePath;
late final String documentsPath;
late final String supportPath;
init() async {
try {
cachePath = (await getApplicationCacheDirectory()).path.replaceAll('\\', '/');
} catch (e) {
cachePath = '';
}
try {
documentsPath = (await getApplicationDocumentsDirectory()).path.replaceAll('\\', '/');
} catch (e) {
documentsPath = '';
}
try {
supportPath = (await getApplicationSupportDirectory()).path.replaceAll('\\', '/');
} catch (e) {
supportPath = '';
}
// 确保 .aidea 目录存在
try {
Directory(getHomePath).create(recursive: true);
} catch (e) {
Logger.instance.e('Create $getHomePath directory failed: $e');
}
}
String get getHomePath {
if (PlatformTool.isMacOS() || PlatformTool.isLinux()) {
return '${Platform.environment['HOME'] ?? ''}/$homePathDirName'.replaceAll('\\', '/');
} else if (PlatformTool.isWindows()) {
return '${Platform.environment['UserProfile'] ?? ''}/$homePathDirName'.replaceAll('\\', '/');
} else if (PlatformTool.isAndroid() || PlatformTool.isIOS()) {
return '$documentsPath/$homePathDirName'.replaceAll('\\', '/');
}
return homePathDirName;
}
String get getLogfilePath {
return '$getHomePath/aidea.log';
}
String get getCachePath {
return getHomePath;
}
/// 单例
static final PathHelper _instance = PathHelper._internal();
PathHelper._internal();
factory PathHelper() {
return _instance;
}
Map toMap() {
return {
'cachePath': cachePath,
'cachePathReal': getCachePath,
'documentsPath': documentsPath,
'supportPath': supportPath,
'homePath': getHomePath,
'logfilePath': getLogfilePath,
};
}
}
================================================
FILE: lib/helper/platform.dart
================================================
import 'dart:io';
import 'package:flutter/foundation.dart';
class PlatformTool {
static bool isDesktop() {
return isWindows() || isLinux() || isMacOS();
}
static bool isDesktopAndWeb() {
return isDesktop() || isWeb();
}
static bool isMobile() {
return isIOS() || isAndroid();
}
static bool isIOS() {
try {
return Platform.isIOS;
} catch (e) {
return false;
}
}
static bool isAndroid() {
try {
return Platform.isAndroid;
} catch (e) {
return false;
}
}
static bool isWeb() {
return kIsWeb;
}
static bool isMacOS() {
try {
return Platform.isMacOS;
} catch (e) {
return false;
}
}
static bool isWindows() {
try {
return Platform.isWindows;
} catch (e) {
return false;
}
}
static bool isLinux() {
try {
return Platform.isLinux;
} catch (e) {
return false;
}
}
static String operatingSystem() {
try {
return Platform.operatingSystem;
} catch (e) {
return 'unknown';
}
}
static String operatingSystemVersion() {
try {
return Platform.operatingSystemVersion;
} catch (e) {
return 'unknown';
}
}
static String localeName() {
try {
return Platform.localeName;
} catch (e) {
return 'zh_Hans_CN';
}
}
}
================================================
FILE: lib/helper/queue.dart
================================================
import 'dart:async';
import 'dart:collection';
class QueueFinishedException implements Exception {
final String message;
QueueFinishedException(this.message);
}
/// 该队列以一定的时间间隔将队列中的元素传递给回调函数,实现平滑的队列处理
class GracefulQueue {
final Queue _queue = Queue();
bool finished = false;
Timer? _timer;
void add(T item) {
if (finished) {
throw QueueFinishedException('Queue is finished');
}
_queue.add(item);
}
void dispose() {
_timer?.cancel();
}
Future listen(
Duration duration, Function(List items) callback) async {
Completer completer = Completer();
_timer = Timer.periodic(duration, (timer) {
if (_queue.isNotEmpty) {
List items = [];
for (var i = 0; i < _queue.length; i++) {
items.add(_queue.removeFirst());
}
callback(items);
} else if (finished) {
// print(_queue.length);
timer.cancel();
completer.complete();
}
});
return completer.future;
}
void finish() {
finished = true;
}
}
================================================
FILE: lib/helper/tips.dart
================================================
final inputTips = [
'有问题尽管问我...',
'开启新对话?往右侧滑动我试试',
];
================================================
FILE: lib/helper/upload.dart
================================================
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:askaide/helper/constant.dart';
import 'package:askaide/helper/helper.dart';
import 'package:askaide/helper/platform.dart';
import 'package:askaide/repo/api_server.dart';
import 'package:askaide/repo/settings_repo.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:http/http.dart' as http;
import 'package:image/image.dart';
import 'package:isolate_image_compress/isolate_image_compress.dart';
import 'package:qiniu_flutter_sdk/qiniu_flutter_sdk.dart';
class ImageUploader {
QiniuUploader? _qiniuUploader;
ImageUploader(SettingRepository setting) {
_qiniuUploader = QiniuUploader(setting);
}
final _compressWidth = 1024;
final _compressHeight = 1024;
/// 上传文件
Future upload(String path, {String? usage}) async {
Uint8List? data = await _imageCompress(path);
if (data == null || data.isEmpty) {
throw Exception('图片读取失败');
}
return _qiniuUploader!.upload(path, data, usage: usage);
}
/// 文件压缩后转为 base64
Future