├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── build.yml │ └── ci.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── CONTRIBUTING.md ├── LICENSE ├── NOTATION_FORMAT.md ├── README.md ├── android_screenshot.png ├── index.html ├── linux_screenshot.png ├── package-lock.json ├── package.json ├── reva.png ├── screenshot.png ├── src-tauri ├── .gitignore ├── Cargo.toml ├── build.rs ├── capabilities │ └── default.json ├── gen │ └── android │ │ ├── .editorconfig │ │ ├── .gitignore │ │ ├── app │ │ ├── .gitignore │ │ ├── build.gradle.kts │ │ ├── proguard-rules.pro │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── jieqibox │ │ │ │ └── app │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ ├── layout │ │ │ └── activity_main.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── values-night │ │ │ └── themes.xml │ │ │ ├── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── themes.xml │ │ │ └── xml │ │ │ └── file_paths.xml │ │ ├── build.gradle.kts │ │ ├── buildSrc │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── main │ │ │ └── java │ │ │ └── com │ │ │ └── jieqibox │ │ │ └── app │ │ │ └── kotlin │ │ │ ├── BuildTask.kt │ │ │ └── RustPlugin.kt │ │ ├── gradle.properties │ │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ └── settings.gradle ├── icons │ ├── 128x128.png │ ├── 128x128@2x.png │ ├── 32x32.png │ ├── Square107x107Logo.png │ ├── Square142x142Logo.png │ ├── Square150x150Logo.png │ ├── Square284x284Logo.png │ ├── Square30x30Logo.png │ ├── Square310x310Logo.png │ ├── Square44x44Logo.png │ ├── Square71x71Logo.png │ ├── Square89x89Logo.png │ ├── StoreLogo.png │ ├── icon.icns │ ├── icon.ico │ ├── icon.png │ └── icon_temp.png ├── src │ ├── lib.rs │ ├── main.rs │ └── opening_book.rs └── tauri.conf.json ├── src ├── App.vue ├── assets │ ├── black_advisor.svg │ ├── black_cannon.svg │ ├── black_chariot.svg │ ├── black_elephant.svg │ ├── black_horse.svg │ ├── black_king.svg │ ├── black_pawn.svg │ ├── dark_piece.svg │ ├── red_advisor.svg │ ├── red_cannon.svg │ ├── red_chariot.svg │ ├── red_elephant.svg │ ├── red_horse.svg │ ├── red_king.svg │ ├── red_pawn.svg │ └── xiangqi.png ├── components │ ├── AboutDialog.vue │ ├── AnalysisSidebar.vue │ ├── CaptureHistoryPanel.vue │ ├── Chessboard.vue │ ├── ClearHistoryConfirmDialog.vue │ ├── DraggablePanel.vue │ ├── EloCalculatorDialog.vue │ ├── EngineManagerDialog.vue │ ├── EvaluationChart.vue │ ├── FenInputDialog.vue │ ├── FlipPromptDialog.vue │ ├── GameEndDialog.vue │ ├── HumanVsAiModeDialog.vue │ ├── InterfaceSettingsDialog.vue │ ├── JaiOptionsDialog.vue │ ├── LanguageSelector.vue │ ├── NotationTextDialog.vue │ ├── OpeningBookDialog.vue │ ├── OpeningBookPanel.vue │ ├── PositionEditorDialog.vue │ ├── ReviewAnalysisDialog.vue │ ├── TimeDialog.vue │ ├── TopToolbar.vue │ ├── UciOptionsDialog.vue │ └── UciTerminalDialog.vue ├── composables │ ├── image-recognition │ │ ├── index.ts │ │ ├── index.ts.stub │ │ ├── types.ts │ │ ├── useImageRecognition.real.ts │ │ └── useImageRecognition.stub.ts │ ├── useAutosave.ts │ ├── useChessGame.ts │ ├── useConfigManager.ts │ ├── useEvaluationChartSettings.ts │ ├── useGameSettings.ts │ ├── useHumanVsAiSettings.ts │ ├── useInterfaceSettings.ts │ ├── useJaiEngine.ts │ ├── useOpeningBook.ts │ ├── usePanelManager.ts │ ├── useUciEngine.ts │ └── useWindowManager.ts ├── i18n │ ├── index.ts │ └── locales │ │ ├── en.ts │ │ ├── ja.ts │ │ ├── vi.ts │ │ ├── zh_cn.ts │ │ └── zh_tw.ts ├── main.ts ├── types │ ├── android.d.ts │ └── openingBook.ts ├── utils │ ├── advancedScriptInterpreter.ts │ ├── chineseNotation.ts │ ├── constants.ts │ ├── eloCalculator.ts │ ├── fenValidator.ts │ ├── platform.ts │ └── xqf.ts └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── wood_yellow_logo.png /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '[BUG] ' 5 | labels: ['bug'] 6 | assignees: '' 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Environment (please complete the following information):** 27 | 28 | - OS: [e.g. Windows 10, macOS 12.0, Ubuntu 20.04] 29 | - Node.js Version: [e.g. 18.0.0] 30 | - Rust Version: [e.g. 1.70.0] 31 | - JieqiBox Version: [e.g. 0.1.0] 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '[FEATURE] ' 5 | labels: ['enhancement'] 6 | assignees: '' 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main, master, develop] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | jobs: 10 | lint-and-test: 11 | name: Lint and Test 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Setup Node.js 17 | uses: actions/setup-node@v4 18 | with: 19 | node-version: '18' 20 | cache: 'npm' 21 | 22 | - name: Setup Rust 23 | uses: dtolnay/rust-toolchain@stable 24 | 25 | - name: Install system dependencies 26 | run: | 27 | sudo apt-get update 28 | sudo apt-get install -y \ 29 | pkg-config \ 30 | libglib2.0-dev \ 31 | libwebkit2gtk-4.1-dev \ 32 | libgtk-3-dev 33 | 34 | - name: Install dependencies 35 | run: npm ci 36 | 37 | - name: Check TypeScript 38 | run: npm run build 39 | 40 | - name: Install Clippy 41 | run: rustup component add clippy 42 | 43 | - name: Check Rust 44 | run: | 45 | cd src-tauri 46 | cargo check 47 | cargo clippy -- -D warnings 48 | 49 | - name: Run tests (if available) 50 | run: npm test || echo "No tests configured yet" 51 | 52 | - name: Check formatting 53 | run: | 54 | npm run lint || echo "No linting configured yet" 55 | npx prettier --check . || echo "No prettier configured yet" 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | pnpm-debug.log* 7 | lerna-debug.log* 8 | 9 | # Build outputs 10 | dist/ 11 | build/ 12 | out/ 13 | 14 | # Tauri build outputs 15 | src-tauri/target/ 16 | src-tauri/Cargo.lock 17 | 18 | # Environment variables 19 | .env 20 | .env.local 21 | .env.development.local 22 | .env.test.local 23 | .env.production.local 24 | 25 | # IDE and editor files 26 | .vscode/ 27 | .idea/ 28 | *.swp 29 | *.swo 30 | *~ 31 | 32 | # OS generated files 33 | .DS_Store 34 | .DS_Store? 35 | ._* 36 | .Spotlight-V100 37 | .Trashes 38 | ehthumbs.db 39 | Thumbs.db 40 | 41 | # Logs 42 | logs 43 | *.log 44 | 45 | # Runtime data 46 | pids 47 | *.pid 48 | *.seed 49 | *.pid.lock 50 | 51 | # Coverage directory used by tools like istanbul 52 | coverage/ 53 | *.lcov 54 | 55 | # nyc test coverage 56 | .nyc_output 57 | 58 | # Dependency directories 59 | jspm_packages/ 60 | 61 | # Optional npm cache directory 62 | .npm 63 | 64 | # Optional eslint cache 65 | .eslintcache 66 | 67 | # Optional stylelint cache 68 | .stylelintcache 69 | 70 | # Microbundle cache 71 | .rpt2_cache/ 72 | .rts2_cache_cjs/ 73 | .rts2_cache_es/ 74 | .rts2_cache_umd/ 75 | 76 | # Optional REPL history 77 | .node_repl_history 78 | 79 | # Output of 'npm pack' 80 | *.tgz 81 | 82 | # Yarn Integrity file 83 | .yarn-integrity 84 | 85 | # parcel-bundler cache (https://parceljs.org/) 86 | .cache 87 | .parcel-cache 88 | 89 | # Next.js build output 90 | .next 91 | 92 | # Nuxt.js build / generate output 93 | .nuxt 94 | 95 | # Gatsby files 96 | .cache/ 97 | public 98 | 99 | # Storybook build outputs 100 | .out 101 | .storybook-out 102 | 103 | # Temporary folders 104 | tmp/ 105 | temp/ 106 | 107 | # Rust specific 108 | **/*.rs.bk 109 | *.pdb 110 | 111 | # Tauri specific 112 | src-tauri/target/ 113 | src-tauri/Cargo.lock 114 | 115 | # Generated files 116 | *.generated.* 117 | 118 | # GUI config file 119 | src-tauri/config.ini 120 | src-tauri/jieqi_openings.jb 121 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules/ 3 | dist/ 4 | target/ 5 | 6 | # Build outputs 7 | build/ 8 | out/ 9 | 10 | # Log files 11 | *.log 12 | 13 | # Environment files 14 | .env 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | # System files 21 | .DS_Store 22 | Thumbs.db 23 | 24 | # Images and binary files 25 | *.png 26 | *.jpg 27 | *.jpeg 28 | *.gif 29 | *.svg 30 | *.ico 31 | *.woff 32 | *.woff2 33 | *.ttf 34 | *.eot 35 | 36 | # Archive files 37 | *.zip 38 | *.tar.gz 39 | *.rar 40 | 41 | # Others 42 | *.lock 43 | package-lock.json 44 | yarn.lock 45 | pnpm-lock.yaml 46 | 47 | # Tauri related 48 | src-tauri/target/ 49 | src-tauri/gen/ -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "tabWidth": 2, 5 | "useTabs": false, 6 | "trailingComma": "es5", 7 | "printWidth": 80, 8 | "bracketSpacing": true, 9 | "arrowParens": "avoid", 10 | "endOfLine": "lf", 11 | "vueIndentScriptAndStyle": true 12 | } 13 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to JieqiBox 2 | 3 | Thank you for your interest in contributing to JieqiBox! This document provides guidelines and information for contributors. 4 | 5 | ## Getting Started 6 | 7 | 1. **Fork the repository** on GitHub 8 | 2. **Clone your fork** locally 9 | 3. **Set up the development environment**: 10 | ```bash 11 | npm install 12 | cd src-tauri && cargo build && cd .. 13 | ``` 14 | 15 | ## Development Workflow 16 | 17 | ### Code Style 18 | 19 | - **Vue Components**: Use ` 90 | 91 | 92 | -------------------------------------------------------------------------------- /linux_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/linux_screenshot.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jieqibox", 3 | "private": true, 4 | "version": "0.6.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vue-tsc --noEmit && vite build", 9 | "preview": "vite preview", 10 | "tauri": "tauri", 11 | "format": "prettier --write .", 12 | "format:check": "prettier --check ." 13 | }, 14 | "dependencies": { 15 | "@tauri-apps/api": "^2.8.0", 16 | "@tauri-apps/plugin-dialog": "^2.3.0", 17 | "@tauri-apps/plugin-opener": "^2", 18 | "@types/ini": "^4.1.1", 19 | "crypto-js": "^4.2.0", 20 | "dompurify": "^3.2.6", 21 | "highlight.js": "^11.11.1", 22 | "ini": "^5.0.0", 23 | "marked": "^16.1.2", 24 | "mersenne-twister": "^1.1.0", 25 | "onnxruntime-web": "1.22.0", 26 | "vue": "^3.5.13", 27 | "vue-i18n": "^9.14.4", 28 | "vue3-draggable-resizable": "^1.6.5", 29 | "vuetify": "^3.8.12" 30 | }, 31 | "devDependencies": { 32 | "@mdi/font": "^7.4.47", 33 | "@tauri-apps/cli": "^2.6.2", 34 | "@types/crypto-js": "^4.2.2", 35 | "@types/dompurify": "^3.0.5", 36 | "@types/mersenne-twister": "^1.1.7", 37 | "@vitejs/plugin-vue": "^5.2.1", 38 | "prettier": "^3.6.2", 39 | "sass-embedded": "^1.89.2", 40 | "typescript": "~5.6.2", 41 | "vite": "^6.0.3", 42 | "vite-plugin-static-copy": "^3.1.2", 43 | "vue-tsc": "^2.1.10" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /reva.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/reva.png -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/screenshot.png -------------------------------------------------------------------------------- /src-tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Generated by Tauri 6 | # will have schema files for capabilities auto-completion 7 | /gen/schemas 8 | 9 | # Autosave and config files 10 | Autosave.json 11 | config.ini 12 | -------------------------------------------------------------------------------- /src-tauri/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "jieqibox" 3 | version = "0.6.0" 4 | description = "A Tauri App" 5 | authors = ["you"] 6 | edition = "2021" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [lib] 11 | # The `_lib` suffix may seem redundant but it is necessary 12 | # to make the lib name unique and wouldn't conflict with the bin name. 13 | # This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519 14 | name = "jieqibox_lib" 15 | crate-type = ["staticlib", "cdylib", "rlib"] 16 | 17 | [build-dependencies] 18 | tauri-build = { version = "2", features = [] } 19 | 20 | [dependencies] 21 | tauri = { version = "2", features = [] } 22 | tauri-plugin-shell = "2.0.0-beta" 23 | tauri-plugin-dialog = "2.0.0-beta.0" 24 | tauri-plugin-opener = "2" 25 | serde = { version = "1", features = ["derive"] } 26 | serde_json = "1" 27 | encoding_rs = "0.8.35" 28 | chrono = { version = "0.4", features = ["serde"] } 29 | base64 = "0.22" 30 | rusqlite = { version = "0.37.0", features = ["bundled"] } 31 | sha2 = "0.10" 32 | hex = "0.4" 33 | -------------------------------------------------------------------------------- /src-tauri/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tauri_build::build() 3 | } 4 | -------------------------------------------------------------------------------- /src-tauri/capabilities/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../gen/schemas/desktop-schema.json", 3 | "identifier": "default", 4 | "description": "Capability for the main window", 5 | "windows": ["main"], 6 | "permissions": [ 7 | "core:default", 8 | "opener:default", 9 | "dialog:default", 10 | "shell:allow-spawn", 11 | "shell:allow-stdin-write", 12 | "shell:allow-kill", 13 | "core:window:allow-inner-size", 14 | "core:window:allow-set-size", 15 | "core:window:allow-outer-position", 16 | "core:window:allow-set-position", 17 | "core:window:allow-maximize" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src-tauri/gen/android/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = false 12 | insert_final_newline = false -------------------------------------------------------------------------------- /src-tauri/gen/android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | key.properties 17 | 18 | /.tauri 19 | /tauri.settings.gradle -------------------------------------------------------------------------------- /src-tauri/gen/android/app/.gitignore: -------------------------------------------------------------------------------- 1 | /src/main/java/com/jieqibox/app/generated 2 | /src/main/jniLibs/**/*.so 3 | /src/main/assets/tauri.conf.json 4 | /tauri.build.gradle.kts 5 | /proguard-tauri.pro 6 | /tauri.properties -------------------------------------------------------------------------------- /src-tauri/gen/android/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import java.util.Properties 2 | 3 | plugins { 4 | id("com.android.application") 5 | id("org.jetbrains.kotlin.android") 6 | id("rust") 7 | } 8 | 9 | val tauriProperties = Properties().apply { 10 | val propFile = file("tauri.properties") 11 | if (propFile.exists()) { 12 | propFile.inputStream().use { load(it) } 13 | } 14 | } 15 | 16 | android { 17 | compileSdk = 34 18 | namespace = "com.jieqibox.app" 19 | defaultConfig { 20 | manifestPlaceholders["usesCleartextTraffic"] = "false" 21 | applicationId = "com.jieqibox.app" 22 | minSdk = 24 23 | targetSdk = 28 24 | versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt() 25 | versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0") 26 | } 27 | buildTypes { 28 | getByName("debug") { 29 | manifestPlaceholders["usesCleartextTraffic"] = "true" 30 | isDebuggable = true 31 | isJniDebuggable = true 32 | isMinifyEnabled = false 33 | packaging { jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so") 34 | jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so") 35 | jniLibs.keepDebugSymbols.add("*/x86/*.so") 36 | jniLibs.keepDebugSymbols.add("*/x86_64/*.so") 37 | } 38 | } 39 | getByName("release") { 40 | isMinifyEnabled = true 41 | proguardFiles( 42 | *fileTree(".") { include("**/*.pro") } 43 | .plus(getDefaultProguardFile("proguard-android-optimize.txt")) 44 | .toList().toTypedArray() 45 | ) 46 | } 47 | } 48 | kotlinOptions { 49 | jvmTarget = "1.8" 50 | } 51 | buildFeatures { 52 | buildConfig = true 53 | } 54 | lint { 55 | warningsAsErrors = false 56 | abortOnError = false 57 | disable.add("ExpiredTargetSdkVersion") 58 | } 59 | } 60 | 61 | rust { 62 | rootDirRel = "../../../" 63 | } 64 | 65 | dependencies { 66 | implementation("androidx.webkit:webkit:1.6.1") 67 | implementation("androidx.appcompat:appcompat:1.6.1") 68 | implementation("com.google.android.material:material:1.8.0") 69 | testImplementation("junit:junit:4.13.2") 70 | androidTestImplementation("androidx.test.ext:junit:1.1.4") 71 | androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0") 72 | } 73 | 74 | apply(from = "tauri.build.gradle.kts") -------------------------------------------------------------------------------- /src-tauri/gen/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 44 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | JieqiBox 3 | JieqiBox 4 | -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/xml/file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src-tauri/gen/android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | dependencies { 7 | classpath("com.android.tools.build:gradle:8.5.1") 8 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.25") 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | mavenCentral() 16 | } 17 | } 18 | 19 | tasks.register("clean").configure { 20 | delete("build") 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src-tauri/gen/android/buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | gradlePlugin { 6 | plugins { 7 | create("pluginsForCoolKids") { 8 | id = "rust" 9 | implementationClass = "RustPlugin" 10 | } 11 | } 12 | } 13 | 14 | repositories { 15 | google() 16 | mavenCentral() 17 | } 18 | 19 | dependencies { 20 | compileOnly(gradleApi()) 21 | implementation("com.android.tools.build:gradle:8.5.1") 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src-tauri/gen/android/buildSrc/src/main/java/com/jieqibox/app/kotlin/BuildTask.kt: -------------------------------------------------------------------------------- 1 | import java.io.File 2 | import org.apache.tools.ant.taskdefs.condition.Os 3 | import org.gradle.api.DefaultTask 4 | import org.gradle.api.GradleException 5 | import org.gradle.api.logging.LogLevel 6 | import org.gradle.api.tasks.Input 7 | import org.gradle.api.tasks.TaskAction 8 | 9 | open class BuildTask : DefaultTask() { 10 | @Input 11 | var rootDirRel: String? = null 12 | @Input 13 | var target: String? = null 14 | @Input 15 | var release: Boolean? = null 16 | 17 | @TaskAction 18 | fun assemble() { 19 | val executable = """npm"""; 20 | try { 21 | runTauriCli(executable) 22 | } catch (e: Exception) { 23 | if (Os.isFamily(Os.FAMILY_WINDOWS)) { 24 | runTauriCli("$executable.cmd") 25 | } else { 26 | throw e; 27 | } 28 | } 29 | } 30 | 31 | fun runTauriCli(executable: String) { 32 | val rootDirRel = rootDirRel ?: throw GradleException("rootDirRel cannot be null") 33 | val target = target ?: throw GradleException("target cannot be null") 34 | val release = release ?: throw GradleException("release cannot be null") 35 | val args = listOf("run", "--", "tauri", "android", "android-studio-script"); 36 | 37 | project.exec { 38 | workingDir(File(project.projectDir, rootDirRel)) 39 | executable(executable) 40 | args(args) 41 | if (project.logger.isEnabled(LogLevel.DEBUG)) { 42 | args("-vv") 43 | } else if (project.logger.isEnabled(LogLevel.INFO)) { 44 | args("-v") 45 | } 46 | if (release) { 47 | args("--release") 48 | } 49 | args(listOf("--target", target)) 50 | }.assertNormalExitValue() 51 | } 52 | } -------------------------------------------------------------------------------- /src-tauri/gen/android/buildSrc/src/main/java/com/jieqibox/app/kotlin/RustPlugin.kt: -------------------------------------------------------------------------------- 1 | import com.android.build.api.dsl.ApplicationExtension 2 | import org.gradle.api.DefaultTask 3 | import org.gradle.api.Plugin 4 | import org.gradle.api.Project 5 | import org.gradle.kotlin.dsl.configure 6 | import org.gradle.kotlin.dsl.get 7 | 8 | const val TASK_GROUP = "rust" 9 | 10 | open class Config { 11 | lateinit var rootDirRel: String 12 | } 13 | 14 | open class RustPlugin : Plugin { 15 | private lateinit var config: Config 16 | 17 | override fun apply(project: Project) = with(project) { 18 | config = extensions.create("rust", Config::class.java) 19 | 20 | val defaultAbiList = listOf("arm64-v8a", "armeabi-v7a", "x86", "x86_64"); 21 | val abiList = (findProperty("abiList") as? String)?.split(',') ?: defaultAbiList 22 | 23 | val defaultArchList = listOf("arm64", "arm", "x86", "x86_64"); 24 | val archList = (findProperty("archList") as? String)?.split(',') ?: defaultArchList 25 | 26 | val targetsList = (findProperty("targetList") as? String)?.split(',') ?: listOf("aarch64", "armv7", "i686", "x86_64") 27 | 28 | extensions.configure { 29 | @Suppress("UnstableApiUsage") 30 | flavorDimensions.add("abi") 31 | productFlavors { 32 | create("universal") { 33 | dimension = "abi" 34 | ndk { 35 | abiFilters += abiList 36 | } 37 | } 38 | defaultArchList.forEachIndexed { index, arch -> 39 | create(arch) { 40 | dimension = "abi" 41 | ndk { 42 | abiFilters.add(defaultAbiList[index]) 43 | } 44 | } 45 | } 46 | } 47 | } 48 | 49 | afterEvaluate { 50 | for (profile in listOf("debug", "release")) { 51 | val profileCapitalized = profile.replaceFirstChar { it.uppercase() } 52 | val buildTask = tasks.maybeCreate( 53 | "rustBuildUniversal$profileCapitalized", 54 | DefaultTask::class.java 55 | ).apply { 56 | group = TASK_GROUP 57 | description = "Build dynamic library in $profile mode for all targets" 58 | } 59 | 60 | tasks["mergeUniversal${profileCapitalized}JniLibFolders"].dependsOn(buildTask) 61 | 62 | for (targetPair in targetsList.withIndex()) { 63 | val targetName = targetPair.value 64 | val targetArch = archList[targetPair.index] 65 | val targetArchCapitalized = targetArch.replaceFirstChar { it.uppercase() } 66 | val targetBuildTask = project.tasks.maybeCreate( 67 | "rustBuild$targetArchCapitalized$profileCapitalized", 68 | BuildTask::class.java 69 | ).apply { 70 | group = TASK_GROUP 71 | description = "Build dynamic library in $profile mode for $targetArch" 72 | rootDirRel = config.rootDirRel 73 | target = targetName 74 | release = profile == "release" 75 | } 76 | 77 | buildTask.dependsOn(targetBuildTask) 78 | tasks["merge$targetArchCapitalized${profileCapitalized}JniLibFolders"].dependsOn( 79 | targetBuildTask 80 | ) 81 | } 82 | } 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /src-tauri/gen/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true 24 | android.nonFinalResIds=false -------------------------------------------------------------------------------- /src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue May 10 19:22:52 CST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /src-tauri/gen/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /src-tauri/gen/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /src-tauri/gen/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | apply from: 'tauri.settings.gradle' -------------------------------------------------------------------------------- /src-tauri/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/icons/128x128.png -------------------------------------------------------------------------------- /src-tauri/icons/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/icons/128x128@2x.png -------------------------------------------------------------------------------- /src-tauri/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/icons/32x32.png -------------------------------------------------------------------------------- /src-tauri/icons/Square107x107Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/icons/Square107x107Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square142x142Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/icons/Square142x142Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/icons/Square150x150Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square284x284Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/icons/Square284x284Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square30x30Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/icons/Square30x30Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square310x310Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/icons/Square310x310Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/icons/Square44x44Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square71x71Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/icons/Square71x71Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square89x89Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/icons/Square89x89Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/icons/StoreLogo.png -------------------------------------------------------------------------------- /src-tauri/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/icons/icon.icns -------------------------------------------------------------------------------- /src-tauri/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/icons/icon.ico -------------------------------------------------------------------------------- /src-tauri/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/icons/icon.png -------------------------------------------------------------------------------- /src-tauri/icons/icon_temp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src-tauri/icons/icon_temp.png -------------------------------------------------------------------------------- /src-tauri/src/main.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] 2 | 3 | fn main() { 4 | // Start only once 5 | jieqibox_lib::run(); 6 | } 7 | -------------------------------------------------------------------------------- /src-tauri/tauri.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.tauri.app/config/2", 3 | "productName": "jieqibox", 4 | "version": "0.6.0", 5 | "identifier": "com.jieqibox.app", 6 | 7 | "build": { 8 | "beforeDevCommand": "npm run dev", 9 | "devUrl": "http://localhost:1420", 10 | "beforeBuildCommand": "npm run build", 11 | "frontendDist": "../dist" 12 | }, 13 | 14 | "app": { 15 | "windows": [{ "title": "JieqiBox", "width": 800, "height": 600 }], 16 | "security": { "csp": null } 17 | }, 18 | 19 | "bundle": { 20 | "active": true, 21 | "targets": "all", 22 | "icon": [ 23 | "icons/32x32.png", 24 | "icons/128x128.png", 25 | "icons/128x128@2x.png", 26 | "icons/icon.icns", 27 | "icons/icon.ico" 28 | ] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 116 | 117 | 138 | 139 | 204 | -------------------------------------------------------------------------------- /src/assets/black_advisor.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 30 | 37 | 43 | 48 | 51 | 52 | 58 | 62 | 66 | 67 | 73 | 77 | 81 | 82 | 93 | 97 | 102 | 108 | 113 | 118 | 124 | 125 | 126 | 151 | 156 | 160 | 162 | 168 | 174 | 175 | 176 | 177 | 182 | 186 | 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /src/assets/black_chariot.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 30 | 37 | 43 | 48 | 51 | 52 | 58 | 62 | 66 | 67 | 73 | 77 | 81 | 82 | 93 | 97 | 102 | 108 | 113 | 118 | 124 | 125 | 126 | 151 | 156 | 160 | 162 | 168 | 174 | 175 | 176 | 180 | 184 | 185 | 186 | 191 | 192 | -------------------------------------------------------------------------------- /src/assets/black_pawn.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 30 | 37 | 43 | 48 | 51 | 52 | 58 | 62 | 66 | 67 | 73 | 77 | 81 | 82 | 93 | 97 | 102 | 108 | 113 | 118 | 124 | 125 | 126 | 151 | 156 | 160 | 162 | 168 | 174 | 175 | 176 | 177 | 182 | 186 | 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /src/assets/dark_piece.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 29 | 36 | 42 | 47 | 50 | 51 | 57 | 61 | 65 | 66 | 72 | 76 | 80 | 81 | 92 | 100 | 105 | 111 | 116 | 121 | 127 | 128 | 129 | 156 | 161 | 165 | 167 | 173 | 174 | 175 | 176 | 181 | 182 | -------------------------------------------------------------------------------- /src/assets/red_advisor.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 30 | 37 | 43 | 48 | 51 | 52 | 58 | 62 | 66 | 67 | 73 | 77 | 81 | 82 | 93 | 97 | 102 | 108 | 113 | 118 | 124 | 125 | 126 | 151 | 156 | 160 | 162 | 168 | 174 | 175 | 176 | 180 | 184 | 185 | 186 | 191 | 192 | -------------------------------------------------------------------------------- /src/assets/red_pawn.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 30 | 37 | 43 | 48 | 51 | 52 | 58 | 62 | 66 | 67 | 73 | 77 | 81 | 82 | 93 | 97 | 102 | 108 | 113 | 118 | 124 | 125 | 126 | 151 | 156 | 160 | 162 | 168 | 174 | 175 | 176 | 180 | 184 | 185 | 186 | 191 | 192 | -------------------------------------------------------------------------------- /src/assets/xiangqi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/src/assets/xiangqi.png -------------------------------------------------------------------------------- /src/components/AboutDialog.vue: -------------------------------------------------------------------------------- 1 | 86 | 87 | 151 | 152 | 230 | -------------------------------------------------------------------------------- /src/components/ClearHistoryConfirmDialog.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 29 | 30 | 95 | -------------------------------------------------------------------------------- /src/components/DraggablePanel.vue: -------------------------------------------------------------------------------- 1 | 56 | 57 | 129 | 130 | 179 | -------------------------------------------------------------------------------- /src/components/FenInputDialog.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 112 | -------------------------------------------------------------------------------- /src/components/GameEndDialog.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 50 | 51 | 123 | -------------------------------------------------------------------------------- /src/components/HumanVsAiModeDialog.vue: -------------------------------------------------------------------------------- 1 | 83 | 84 | 125 | 126 | 145 | -------------------------------------------------------------------------------- /src/components/InterfaceSettingsDialog.vue: -------------------------------------------------------------------------------- 1 | 82 | 83 | 108 | -------------------------------------------------------------------------------- /src/components/LanguageSelector.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 61 | 62 | 97 | -------------------------------------------------------------------------------- /src/components/NotationTextDialog.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /src/components/OpeningBookPanel.vue: -------------------------------------------------------------------------------- 1 | 127 | 128 | 233 | 234 | 284 | -------------------------------------------------------------------------------- /src/composables/image-recognition/index.ts: -------------------------------------------------------------------------------- 1 | // Real version for yolo-enabled builds 2 | // This file is used when YOLO functionality is enabled 3 | 4 | // Import the real implementation 5 | import { useImageRecognition as useImageRecognitionReal } from './useImageRecognition.real' 6 | 7 | // Export the real implementation 8 | export const useImageRecognition = useImageRecognitionReal 9 | 10 | // Also, re-export the shared types so consumers can import them from one place. 11 | export * from './types' 12 | -------------------------------------------------------------------------------- /src/composables/image-recognition/index.ts.stub: -------------------------------------------------------------------------------- 1 | // Stub version for no-yolo builds 2 | // This file is used when YOLO functionality is disabled 3 | 4 | // Always import the stub implementation 5 | import { useImageRecognition as useImageRecognitionStub } from './useImageRecognition.stub' 6 | 7 | // Export the stub implementation 8 | export const useImageRecognition = useImageRecognitionStub 9 | 10 | // Also, re-export the shared types so consumers can import them from one place. 11 | export * from './types' 12 | -------------------------------------------------------------------------------- /src/composables/image-recognition/types.ts: -------------------------------------------------------------------------------- 1 | // Label definitions and colors 2 | export const LABELS: { [key: number]: { name: string; color: string } } = { 3 | 0: { name: '2', color: '#AA00FF' }, 4 | 1: { name: '3', color: '#CC00CC' }, 5 | 2: { name: '4', color: '#FF0099' }, 6 | 3: { name: '5', color: '#FF0066' }, 7 | 4: { name: 'Board', color: 'orange' }, 8 | 5: { name: 'b_advisor', color: '#0033FF' }, 9 | 6: { name: 'b_cannon', color: '#00FFFF' }, 10 | 7: { name: 'b_chariot', color: '#00CCFF' }, 11 | 8: { name: 'b_elephant', color: '#0066FF' }, 12 | 9: { name: 'b_general', color: '#0000FF' }, 13 | 10: { name: 'b_horse', color: '#0099FF' }, 14 | 11: { name: 'b_soldier', color: '#33CCFF' }, 15 | 12: { name: 'dark', color: '#222222' }, 16 | 13: { name: 'dark_b_advisor', color: '#001199' }, 17 | 14: { name: 'dark_b_cannon', color: '#009999' }, 18 | 15: { name: 'dark_b_chariot', color: '#007799' }, 19 | 16: { name: 'dark_b_elephant', color: '#003399' }, 20 | 17: { name: 'dark_b_general', color: '#000099' }, 21 | 18: { name: 'dark_b_horse', color: '#005599' }, 22 | 19: { name: 'dark_b_soldier', color: '#339999' }, 23 | 20: { name: 'dark_r_advisor', color: '#992200' }, 24 | 21: { name: 'dark_r_cannon', color: '#999900' }, 25 | 22: { name: 'dark_r_chariot', color: '#998800' }, 26 | 23: { name: 'dark_r_elephant', color: '#994400' }, 27 | 24: { name: 'dark_r_general', color: '#990000' }, 28 | 25: { name: 'dark_r_horse', color: '#996600' }, 29 | 26: { name: 'dark_r_soldier', color: '#997733' }, 30 | 27: { name: 'r_advisor', color: '#FF3300' }, 31 | 28: { name: 'r_cannon', color: '#FFFF00' }, 32 | 29: { name: 'r_chariot', color: '#FFCC00' }, 33 | 30: { name: 'r_elephant', color: '#FF6600' }, 34 | 31: { name: 'r_general', color: '#FF0000' }, 35 | 32: { name: 'r_horse', color: '#FF9900' }, 36 | 33: { name: 'r_soldier', color: '#FFCC33' }, 37 | } 38 | 39 | // Piece name mapping 40 | export const PIECE_MAP = { 41 | r_general: { char: '帅', color: 'red' }, 42 | r_advisor: { char: '仕', color: 'red' }, 43 | r_elephant: { char: '相', color: 'red' }, 44 | r_horse: { char: '马', color: 'red' }, 45 | r_chariot: { char: '车', color: 'red' }, 46 | r_cannon: { char: '炮', color: 'red' }, 47 | r_soldier: { char: '兵', color: 'red' }, 48 | b_general: { char: '将', color: 'black' }, 49 | b_advisor: { char: '士', color: 'black' }, 50 | b_elephant: { char: '象', color: 'black' }, 51 | b_horse: { char: '马', color: 'black' }, 52 | b_chariot: { char: '车', color: 'black' }, 53 | b_cannon: { char: '炮', color: 'black' }, 54 | b_soldier: { char: '卒', color: 'black' }, 55 | dark: { char: '暗', color: 'grey' }, 56 | } 57 | 58 | export interface DetectionBox { 59 | box: [number, number, number, number] // [x, y, w, h] in original image coordinates 60 | score: number 61 | labelIndex: number 62 | } 63 | 64 | export interface ProcessedImage { 65 | canvas: HTMLCanvasElement 66 | context: CanvasRenderingContext2D 67 | meta: { 68 | r: number 69 | dw: number 70 | dh: number 71 | newW: number 72 | newH: number 73 | imgW: number 74 | imgH: number 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/composables/image-recognition/useImageRecognition.stub.ts: -------------------------------------------------------------------------------- 1 | import { ref, readonly } from 'vue' 2 | import type { DetectionBox } from './types' 3 | 4 | // This is the stub implementation. It returns the same structure 5 | // but with disabled/default values and functions that do nothing. 6 | export const useImageRecognition = () => { 7 | const status = readonly(ref('Image recognition is disabled in this build.')) 8 | 9 | // Return an API-compatible object 10 | return { 11 | session: readonly(ref(null)), 12 | isModelLoading: readonly(ref(false)), 13 | isProcessing: readonly(ref(false)), 14 | status, 15 | detectedBoxes: ref([]), 16 | inputImage: readonly(ref(null)), 17 | outputCanvas: readonly(ref(null)), 18 | showBoundingBoxes: ref(false), // Can still be toggled, won't do anything 19 | processImage: async (_file: File): Promise => { 20 | console.warn('Image recognition is disabled in this build.') 21 | return Promise.resolve() 22 | }, 23 | drawBoundingBoxes: ( 24 | _boxes: DetectionBox[], 25 | _imgElement: HTMLImageElement, 26 | _canvasElement: HTMLCanvasElement 27 | ) => { 28 | /* Do nothing */ 29 | }, 30 | updateBoardGrid: (_boxes: DetectionBox[]) => 31 | Array(10) 32 | .fill(null) 33 | .map(() => Array(9).fill(null)), 34 | initializeModel: async (): Promise => { 35 | console.warn('Image recognition is disabled in this build.') 36 | return Promise.resolve() 37 | }, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/composables/useAutosave.ts: -------------------------------------------------------------------------------- 1 | import { ref, watch } from 'vue' 2 | import { invoke } from '@tauri-apps/api/core' 3 | import { useInterfaceSettings } from './useInterfaceSettings' 4 | 5 | // Autosave functionality composable 6 | export function useAutosave() { 7 | const { autosave } = useInterfaceSettings() 8 | 9 | const isAutosaveEnabled = ref(false) 10 | const lastAutosaveTime = ref(0) 11 | const autosaveInterval = 5000 // Save every 5 seconds when enabled 12 | 13 | // Load autosave file on startup if autosave is enabled 14 | const loadAutosaveOnStartup = async (gameState: any) => { 15 | try { 16 | const autosaveContent = await invoke('load_autosave') 17 | if (autosaveContent && autosaveContent.trim()) { 18 | // Parse and load the autosave content 19 | const gameData = JSON.parse(autosaveContent) 20 | 21 | // Load the game state from autosave 22 | if (gameData.metadata && gameData.metadata.currentFen) { 23 | if (gameState) { 24 | // Load the current position 25 | gameState.loadFen(gameData.metadata.currentFen, false) 26 | 27 | // Load move history if available 28 | if (gameData.moves && gameData.moves.length > 0) { 29 | gameState.history.value = [...gameData.moves] 30 | gameState.currentMoveIndex.value = gameData.moves.length 31 | console.log('Autosave loaded with moves:', gameData.moves.length) 32 | } 33 | 34 | console.log('Autosave loaded successfully') 35 | } 36 | } 37 | } 38 | } catch (error) { 39 | console.error('Failed to load autosave on startup:', error) 40 | } 41 | } 42 | 43 | // Save current game state to autosave file 44 | const saveAutosave = async (gameState: any) => { 45 | if (!isAutosaveEnabled.value) { 46 | console.log('Autosave is disabled, skipping save') 47 | return 48 | } 49 | 50 | try { 51 | if (!gameState) { 52 | console.error('Game state not available for autosave') 53 | return 54 | } 55 | 56 | const gameNotation = gameState.generateGameNotation() 57 | console.log('Autosave - Game notation:', { 58 | movesCount: gameNotation.moves.length, 59 | currentFen: gameNotation.metadata.currentFen, 60 | historyLength: gameState.history.value.length, 61 | }) 62 | 63 | const autosaveContent = JSON.stringify(gameNotation, null, 2) 64 | 65 | await invoke('save_autosave', { content: autosaveContent }) 66 | lastAutosaveTime.value = Date.now() 67 | 68 | console.log('Autosave completed successfully') 69 | } catch (error) { 70 | console.error('Failed to save autosave:', error) 71 | } 72 | } 73 | 74 | // Set up periodic autosave when enabled 75 | let autosaveTimer: ReturnType | null = null 76 | let currentGameState: any = null 77 | let isInitialized = false // Flag to prevent multiple initializations 78 | 79 | const startAutosaveTimer = (gameState: any) => { 80 | // Clear existing timer first 81 | if (autosaveTimer) { 82 | clearInterval(autosaveTimer) 83 | autosaveTimer = null 84 | } 85 | 86 | currentGameState = gameState 87 | console.log('Autosave: Setting currentGameState:', !!gameState) 88 | 89 | if (isAutosaveEnabled.value) { 90 | console.log('Starting autosave timer with interval:', autosaveInterval) 91 | autosaveTimer = setInterval(async () => { 92 | console.log( 93 | 'Autosave timer triggered, currentGameState:', 94 | !!currentGameState 95 | ) 96 | if (currentGameState) { 97 | await saveAutosave(currentGameState) 98 | } else { 99 | console.error('Autosave: currentGameState is null in timer callback') 100 | } 101 | }, autosaveInterval) 102 | } else { 103 | console.log('Autosave is disabled, not starting timer') 104 | } 105 | } 106 | 107 | const stopAutosaveTimer = () => { 108 | if (autosaveTimer) { 109 | clearInterval(autosaveTimer) 110 | autosaveTimer = null 111 | } 112 | currentGameState = null 113 | console.log('Autosave: Cleared currentGameState') 114 | } 115 | 116 | // Watch for autosave setting changes to start/stop timer 117 | watch(autosave, newValue => { 118 | isAutosaveEnabled.value = newValue 119 | console.log( 120 | 'Autosave setting changed to:', 121 | newValue, 122 | 'currentGameState:', 123 | !!currentGameState, 124 | 'isInitialized:', 125 | isInitialized 126 | ) 127 | 128 | // Always stop existing timer first 129 | if (autosaveTimer) { 130 | clearInterval(autosaveTimer) 131 | autosaveTimer = null 132 | } 133 | 134 | // Only start timer if we have a game state, autosave is enabled, and we're initialized 135 | if (newValue && currentGameState && isInitialized) { 136 | console.log('Starting autosave timer due to setting change') 137 | startAutosaveTimer(currentGameState) 138 | } else if (!newValue) { 139 | console.log('Stopping autosave timer due to setting change') 140 | stopAutosaveTimer() 141 | } 142 | }) 143 | 144 | // Initialize autosave when called from App.vue 145 | const initializeAutosave = async (gameState: any) => { 146 | // Prevent multiple initializations 147 | if (isInitialized) { 148 | console.log('Autosave already initialized, skipping') 149 | return 150 | } 151 | 152 | try { 153 | console.log('Autosave: Initializing with gameState:', !!gameState) 154 | 155 | // Load the autosave setting from interface settings 156 | isAutosaveEnabled.value = autosave.value 157 | console.log( 158 | 'Autosave initialization - setting enabled:', 159 | isAutosaveEnabled.value 160 | ) 161 | 162 | // If autosave is enabled, try to load the autosave file on startup 163 | if (isAutosaveEnabled.value) { 164 | await loadAutosaveOnStartup(gameState) 165 | } 166 | 167 | // Start the timer if autosave is enabled 168 | if (isAutosaveEnabled.value) { 169 | startAutosaveTimer(gameState) 170 | } 171 | 172 | isInitialized = true 173 | } catch (error) { 174 | console.error('Failed to initialize autosave:', error) 175 | } 176 | } 177 | 178 | return { 179 | isAutosaveEnabled, 180 | saveAutosave, 181 | loadAutosaveOnStartup, 182 | startAutosaveTimer, 183 | stopAutosaveTimer, 184 | initializeAutosave, 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/composables/useEvaluationChartSettings.ts: -------------------------------------------------------------------------------- 1 | import { ref, watch } from 'vue' 2 | import { useConfigManager } from './useConfigManager' 3 | 4 | export type ChartViewMode = 'evaluation' | 'time' | 'depth' 5 | 6 | // Configuration manager 7 | const configManager = useConfigManager() 8 | 9 | /** 10 | * Get initial settings from the config manager 11 | * @returns {object} - Object containing initial values for evaluation chart settings 12 | */ 13 | const getInitialSettings = () => { 14 | // Only access in client environment 15 | if (typeof window === 'undefined') { 16 | return { 17 | showMoveLabels: true, 18 | useLinearYAxis: false, 19 | showOnlyLines: false, 20 | blackPerspective: false, 21 | enableYAxisClamp: false, 22 | yAxisClampValue: 500, 23 | showSeparateLines: false, 24 | viewMode: 'evaluation', 25 | } 26 | } 27 | 28 | try { 29 | const settings = configManager.getEvaluationChartSettings() 30 | return { 31 | showMoveLabels: settings.showMoveLabels !== false, // Default to true 32 | useLinearYAxis: !!settings.useLinearYAxis, // Default to false 33 | showOnlyLines: !!settings.showOnlyLines, // Default to false 34 | blackPerspective: !!settings.blackPerspective, // Default to false 35 | enableYAxisClamp: !!settings.enableYAxisClamp, // Default to false 36 | yAxisClampValue: settings.yAxisClampValue || 500, // Default to 500 37 | colorScheme: settings.colorScheme || 'default', // Default to 'default' 38 | showSeparateLines: !!settings.showSeparateLines, // Default to false 39 | viewMode: settings.viewMode || 'evaluation', 40 | } 41 | } catch (e) { 42 | console.error('Failed to get evaluation chart settings:', e) 43 | // Return default values on error 44 | return { 45 | showMoveLabels: true, 46 | useLinearYAxis: false, 47 | showOnlyLines: false, 48 | blackPerspective: false, 49 | enableYAxisClamp: false, 50 | yAxisClampValue: 500, 51 | colorScheme: 'default', 52 | showSeparateLines: false, 53 | viewMode: 'evaluation', 54 | } 55 | } 56 | } 57 | 58 | // Create reactive references shared across the application 59 | const { 60 | showMoveLabels: initialShowMoveLabels, 61 | useLinearYAxis: initialUseLinearYAxis, 62 | showOnlyLines: initialShowOnlyLines, 63 | blackPerspective: initialBlackPerspective, 64 | enableYAxisClamp: initialEnableYAxisClamp, 65 | yAxisClampValue: initialYAxisClampValue, 66 | colorScheme: initialColorScheme, 67 | showSeparateLines: initialShowSeparateLines, 68 | viewMode: initialViewMode, 69 | } = getInitialSettings() 70 | 71 | const showMoveLabels = ref(initialShowMoveLabels) 72 | const useLinearYAxis = ref(initialUseLinearYAxis) 73 | const showOnlyLines = ref(initialShowOnlyLines) 74 | const blackPerspective = ref(initialBlackPerspective) 75 | const enableYAxisClamp = ref(initialEnableYAxisClamp) 76 | const yAxisClampValue = ref(initialYAxisClampValue) 77 | const colorScheme = ref(initialColorScheme || 'default') 78 | const showSeparateLines = ref(initialShowSeparateLines) 79 | const viewMode = ref( 80 | (initialViewMode as ChartViewMode) || 'evaluation' 81 | ) 82 | 83 | // Flag to track if config is loaded 84 | const isConfigLoaded = ref(false) 85 | 86 | // Watch for changes and persist to config file 87 | watch( 88 | [ 89 | showMoveLabels, 90 | useLinearYAxis, 91 | showOnlyLines, 92 | blackPerspective, 93 | enableYAxisClamp, 94 | yAxisClampValue, 95 | colorScheme, 96 | showSeparateLines, 97 | viewMode, 98 | ], 99 | async ([ 100 | newShowMoveLabels, 101 | newUseLinearYAxis, 102 | newShowOnlyLines, 103 | newBlackPerspective, 104 | newEnableYAxisClamp, 105 | newYAxisClampValue, 106 | newColorScheme, 107 | newShowSeparateLines, 108 | newViewMode, 109 | ]) => { 110 | // Only save if config is already loaded to avoid overwriting during initialization 111 | if (!isConfigLoaded.value) return 112 | 113 | const settings = { 114 | showMoveLabels: newShowMoveLabels, 115 | useLinearYAxis: newUseLinearYAxis, 116 | showOnlyLines: newShowOnlyLines, 117 | blackPerspective: newBlackPerspective, 118 | enableYAxisClamp: newEnableYAxisClamp, 119 | yAxisClampValue: newYAxisClampValue, 120 | colorScheme: newColorScheme, 121 | showSeparateLines: newShowSeparateLines, 122 | viewMode: newViewMode, 123 | } 124 | 125 | try { 126 | await configManager.updateEvaluationChartSettings(settings) 127 | } catch (error) { 128 | console.error('Failed to save evaluation chart settings:', error) 129 | } 130 | } 131 | ) 132 | 133 | // Evaluation chart settings composable 134 | export function useEvaluationChartSettings() { 135 | // Load configuration and update reactive refs 136 | const loadSettings = async () => { 137 | try { 138 | await configManager.loadConfig() 139 | const settings = configManager.getEvaluationChartSettings() 140 | 141 | // Update reactive refs 142 | showMoveLabels.value = settings.showMoveLabels !== false 143 | useLinearYAxis.value = !!settings.useLinearYAxis 144 | showOnlyLines.value = !!settings.showOnlyLines 145 | blackPerspective.value = !!settings.blackPerspective 146 | enableYAxisClamp.value = !!settings.enableYAxisClamp 147 | yAxisClampValue.value = settings.yAxisClampValue || 500 148 | colorScheme.value = settings.colorScheme || 'default' 149 | showSeparateLines.value = !!settings.showSeparateLines 150 | viewMode.value = (settings.viewMode as ChartViewMode) || 'evaluation' 151 | 152 | isConfigLoaded.value = true 153 | } catch (error) { 154 | console.error('Failed to load evaluation chart settings:', error) 155 | isConfigLoaded.value = true // Still mark as loaded to enable saving 156 | } 157 | } 158 | 159 | // Initialize settings on first import 160 | if (typeof window !== 'undefined') { 161 | loadSettings() 162 | } 163 | 164 | return { 165 | showMoveLabels, 166 | useLinearYAxis, 167 | showOnlyLines, 168 | blackPerspective, 169 | enableYAxisClamp, 170 | yAxisClampValue, 171 | colorScheme, 172 | showSeparateLines, 173 | viewMode, 174 | loadSettings, 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/composables/useGameSettings.ts: -------------------------------------------------------------------------------- 1 | import { ref, watch } from 'vue' 2 | import { useConfigManager } from './useConfigManager' 3 | 4 | // Configuration manager 5 | const configManager = useConfigManager() 6 | 7 | /** 8 | * Get initial settings from the config manager 9 | * @returns {object} - Object containing initial values for game settings 10 | */ 11 | const getInitialSettings = () => { 12 | // Only access in client environment 13 | if (typeof window === 'undefined') { 14 | return { 15 | flipMode: 'random' as 'random' | 'free', 16 | enablePonder: false, 17 | } 18 | } 19 | 20 | try { 21 | const settings = configManager.getGameSettings() 22 | return { 23 | flipMode: settings.flipMode || 'random', 24 | enablePonder: !!settings.enablePonder, // Default to false 25 | } 26 | } catch (e) { 27 | console.error('Failed to get game settings:', e) 28 | // Return default values on error 29 | return { 30 | flipMode: 'random' as 'random' | 'free', 31 | enablePonder: false, 32 | } 33 | } 34 | } 35 | 36 | // Create reactive references shared across the application 37 | const { flipMode: initialFlipMode, enablePonder: initialEnablePonder } = 38 | getInitialSettings() 39 | 40 | const flipMode = ref<'random' | 'free'>(initialFlipMode) 41 | const enablePonder = ref(initialEnablePonder) 42 | 43 | // Flag to track if config is loaded 44 | const isConfigLoaded = ref(false) 45 | 46 | // Watch for changes and persist to config file 47 | watch([flipMode, enablePonder], async ([newFlipMode, newEnablePonder]) => { 48 | // Only save if config is already loaded to avoid overwriting during initialization 49 | if (!isConfigLoaded.value) return 50 | 51 | const settings = { 52 | flipMode: newFlipMode, 53 | enablePonder: newEnablePonder, 54 | } 55 | 56 | try { 57 | await configManager.updateGameSettings(settings) 58 | } catch (error) { 59 | console.error('Failed to save game settings:', error) 60 | } 61 | }) 62 | 63 | // Game settings composable 64 | export function useGameSettings() { 65 | // Load configuration and update reactive refs 66 | const loadSettings = async () => { 67 | try { 68 | await configManager.loadConfig() 69 | const settings = configManager.getGameSettings() 70 | 71 | // Update reactive refs 72 | flipMode.value = settings.flipMode || 'random' 73 | enablePonder.value = !!settings.enablePonder 74 | 75 | isConfigLoaded.value = true 76 | } catch (error) { 77 | console.error('Failed to load game settings:', error) 78 | isConfigLoaded.value = true // Still mark as loaded to enable saving 79 | } 80 | } 81 | 82 | // Initialize settings on first import 83 | if (typeof window !== 'undefined') { 84 | loadSettings() 85 | } 86 | 87 | return { 88 | flipMode, 89 | enablePonder, 90 | loadSettings, 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/composables/useHumanVsAiSettings.ts: -------------------------------------------------------------------------------- 1 | import { ref, watch } from 'vue' 2 | import { useConfigManager } from './useConfigManager' 3 | 4 | // Config manager instance 5 | const configManager = useConfigManager() 6 | 7 | // Get initial settings from config file 8 | const getInitialSettings = () => { 9 | try { 10 | return configManager.getHumanVsAiSettings() 11 | } catch (error) { 12 | console.warn('Failed to load human vs AI settings:', error) 13 | return { 14 | isHumanVsAiMode: false, 15 | aiSide: 'black' as 'red' | 'black', 16 | showEngineAnalysis: false, 17 | } 18 | } 19 | } 20 | 21 | // Create reactive references shared across the application 22 | const { 23 | isHumanVsAiMode: initialIsHumanVsAiMode, 24 | aiSide: initialAiSide, 25 | showEngineAnalysis: initialShowEngineAnalysis, 26 | } = getInitialSettings() 27 | 28 | const isHumanVsAiMode = ref(initialIsHumanVsAiMode) 29 | const aiSide = ref<'red' | 'black'>(initialAiSide) 30 | const showEngineAnalysis = ref(initialShowEngineAnalysis) 31 | 32 | // Flag to track if config is loaded 33 | const isConfigLoaded = ref(false) 34 | 35 | // Watch for changes and persist to config file 36 | watch( 37 | [isHumanVsAiMode, aiSide, showEngineAnalysis], 38 | async ([newIsHumanVsAiMode, newAiSide, newShowEngineAnalysis]) => { 39 | // Only save if config is already loaded to avoid overwriting during initialization 40 | if (!isConfigLoaded.value) return 41 | 42 | const settings = { 43 | isHumanVsAiMode: newIsHumanVsAiMode, 44 | aiSide: newAiSide, 45 | showEngineAnalysis: newShowEngineAnalysis, 46 | } 47 | 48 | try { 49 | await configManager.updateHumanVsAiSettings(settings) 50 | } catch (error) { 51 | console.error('Failed to save human vs AI settings:', error) 52 | } 53 | } 54 | ) 55 | 56 | // Human vs AI settings composable 57 | export function useHumanVsAiSettings() { 58 | // Load configuration and update reactive refs 59 | const loadSettings = async () => { 60 | try { 61 | const humanVsAiSettings = configManager.getHumanVsAiSettings() 62 | 63 | if (humanVsAiSettings) { 64 | isHumanVsAiMode.value = humanVsAiSettings.isHumanVsAiMode 65 | aiSide.value = humanVsAiSettings.aiSide 66 | showEngineAnalysis.value = humanVsAiSettings.showEngineAnalysis 67 | } 68 | 69 | // Mark config as loaded 70 | isConfigLoaded.value = true 71 | } catch (error) { 72 | console.error('Failed to load human vs AI settings from config:', error) 73 | } 74 | } 75 | 76 | // Toggle human vs AI mode 77 | const toggleHumanVsAiMode = () => { 78 | isHumanVsAiMode.value = !isHumanVsAiMode.value 79 | } 80 | 81 | // Set AI side 82 | const setAiSide = (side: 'red' | 'black') => { 83 | aiSide.value = side 84 | } 85 | 86 | // Toggle engine analysis visibility 87 | const toggleShowEngineAnalysis = () => { 88 | showEngineAnalysis.value = !showEngineAnalysis.value 89 | } 90 | 91 | return { 92 | isHumanVsAiMode, 93 | aiSide, 94 | showEngineAnalysis, 95 | loadSettings, 96 | toggleHumanVsAiMode, 97 | setAiSide, 98 | toggleShowEngineAnalysis, 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/composables/useInterfaceSettings.ts: -------------------------------------------------------------------------------- 1 | import { ref, watch } from 'vue' 2 | import { useConfigManager } from './useConfigManager' 3 | 4 | // Configuration manager 5 | const configManager = useConfigManager() 6 | 7 | /** 8 | * Get initial settings from the config manager 9 | * @returns {object} - Object containing initial values for interface settings 10 | */ 11 | const getInitialSettings = () => { 12 | // Only access in client environment 13 | if (typeof window === 'undefined') { 14 | return { 15 | showCoordinates: false, 16 | parseUciInfo: true, 17 | showAnimations: true, 18 | showPositionChart: false, 19 | showEvaluationBar: true, 20 | darkMode: false, 21 | autosave: true, 22 | useNewFenFormat: true, 23 | engineLogLineLimit: 256, 24 | showChineseNotation: true, 25 | showLuckIndex: false, 26 | showArrows: true, 27 | showBookMoves: true, 28 | openingBookEnableInGame: true, 29 | openingBookPreferHighPriority: true, 30 | } 31 | } 32 | 33 | try { 34 | const settings = configManager.getInterfaceSettings() 35 | return { 36 | showCoordinates: !!settings.showCoordinates, 37 | parseUciInfo: settings.parseUciInfo !== false, // Default to true 38 | showAnimations: settings.showAnimations !== false, // Default to true 39 | showPositionChart: !!settings.showPositionChart, // Default to false 40 | showEvaluationBar: settings.showEvaluationBar !== false, // Default to true 41 | darkMode: !!settings.darkMode, // Default to false 42 | autosave: settings.autosave !== false, // Default to true 43 | useNewFenFormat: settings.useNewFenFormat !== false, // Default to true 44 | engineLogLineLimit: settings.engineLogLineLimit || 256, // Default to 256 45 | showChineseNotation: settings.showChineseNotation !== false, // Default to true 46 | showLuckIndex: !!settings.showLuckIndex, // Default to false 47 | showArrows: settings.showArrows !== false, // Default to true 48 | showBookMoves: settings.showBookMoves !== false, // Default to true 49 | openingBookEnableInGame: settings.openingBookEnableInGame !== false, // Default to true 50 | openingBookPreferHighPriority: 51 | settings.openingBookPreferHighPriority !== false, // Default to true 52 | } 53 | } catch (e) { 54 | console.error('Failed to get interface settings:', e) 55 | // Return default values on error 56 | return { 57 | showCoordinates: false, 58 | parseUciInfo: true, 59 | showAnimations: true, 60 | showPositionChart: false, 61 | showEvaluationBar: true, 62 | darkMode: false, 63 | autosave: true, 64 | useNewFenFormat: true, 65 | engineLogLineLimit: 256, 66 | showChineseNotation: true, 67 | showLuckIndex: false, 68 | showArrows: true, 69 | showBookMoves: true, 70 | openingBookEnableInGame: true, 71 | openingBookPreferHighPriority: true, 72 | } 73 | } 74 | } 75 | 76 | // Create reactive references shared across the application 77 | const { 78 | showCoordinates: initialShowCoordinates, 79 | parseUciInfo: initialParseUciInfo, 80 | showAnimations: initialShowAnimations, 81 | showPositionChart: initialShowPositionChart, 82 | showEvaluationBar: initialShowEvaluationBar, 83 | darkMode: initialDarkMode, 84 | autosave: initialAutosave, 85 | useNewFenFormat: initialUseNewFenFormat, 86 | engineLogLineLimit: initialEngineLogLineLimit, 87 | showChineseNotation: initialShowChineseNotation, 88 | showLuckIndex: initialShowLuckIndex, 89 | showArrows: initialShowArrows, 90 | showBookMoves: initialShowBookMoves, 91 | openingBookEnableInGame: initialOpeningBookEnableInGame, 92 | openingBookPreferHighPriority: initialOpeningBookPreferHighPriority, 93 | } = getInitialSettings() 94 | 95 | const showCoordinates = ref(initialShowCoordinates) 96 | const parseUciInfo = ref(initialParseUciInfo) 97 | const showAnimations = ref(initialShowAnimations) 98 | const showPositionChart = ref(initialShowPositionChart) 99 | const showEvaluationBar = ref(initialShowEvaluationBar) 100 | const darkMode = ref(initialDarkMode) 101 | const autosave = ref(initialAutosave) 102 | const useNewFenFormat = ref(initialUseNewFenFormat) 103 | const engineLogLineLimit = ref(initialEngineLogLineLimit) 104 | const showChineseNotation = ref(initialShowChineseNotation) 105 | const showLuckIndex = ref(initialShowLuckIndex) 106 | const showArrows = ref(initialShowArrows) 107 | const showBookMoves = ref(initialShowBookMoves) 108 | const openingBookEnableInGame = ref(initialOpeningBookEnableInGame) 109 | const openingBookPreferHighPriority = ref( 110 | initialOpeningBookPreferHighPriority 111 | ) 112 | 113 | // Flag to track if config is loaded 114 | const isConfigLoaded = ref(false) 115 | 116 | // Watch for changes and persist to config file 117 | watch( 118 | [ 119 | showCoordinates, 120 | parseUciInfo, 121 | showAnimations, 122 | showPositionChart, 123 | showEvaluationBar, 124 | darkMode, 125 | autosave, 126 | useNewFenFormat, 127 | engineLogLineLimit, 128 | showChineseNotation, 129 | showLuckIndex, 130 | showArrows, 131 | showBookMoves, 132 | openingBookEnableInGame, 133 | openingBookPreferHighPriority, 134 | ], 135 | async ([ 136 | newShowCoordinates, 137 | newParseUciInfo, 138 | newShowAnimations, 139 | newShowPositionChart, 140 | newShowEvaluationBar, 141 | newDarkMode, 142 | newAutosave, 143 | newUseNewFenFormat, 144 | newEngineLogLineLimit, 145 | newShowChineseNotation, 146 | newShowLuckIndex, 147 | newShowArrows, 148 | newShowBookMoves, 149 | newOpeningBookEnableInGame, 150 | newOpeningBookPreferHighPriority, 151 | ]) => { 152 | // Only save if config is already loaded to avoid overwriting during initialization 153 | if (!isConfigLoaded.value) return 154 | 155 | const settings = { 156 | showCoordinates: newShowCoordinates, 157 | parseUciInfo: newParseUciInfo, 158 | showAnimations: newShowAnimations, 159 | showPositionChart: newShowPositionChart, 160 | showEvaluationBar: newShowEvaluationBar, 161 | darkMode: newDarkMode, 162 | autosave: newAutosave, 163 | useNewFenFormat: newUseNewFenFormat, 164 | engineLogLineLimit: newEngineLogLineLimit, 165 | showChineseNotation: newShowChineseNotation, 166 | showLuckIndex: newShowLuckIndex, 167 | showArrows: newShowArrows, 168 | showBookMoves: newShowBookMoves, 169 | openingBookEnableInGame: newOpeningBookEnableInGame, 170 | openingBookPreferHighPriority: newOpeningBookPreferHighPriority, 171 | } 172 | 173 | try { 174 | await configManager.updateInterfaceSettings(settings) 175 | } catch (error) { 176 | console.error('Failed to save interface settings:', error) 177 | } 178 | } 179 | ) 180 | 181 | // Interface settings composable 182 | export function useInterfaceSettings() { 183 | // Load configuration and update reactive refs 184 | const loadSettings = async () => { 185 | try { 186 | await configManager.loadConfig() 187 | const settings = configManager.getInterfaceSettings() 188 | 189 | // Update reactive refs 190 | showCoordinates.value = !!settings.showCoordinates 191 | parseUciInfo.value = settings.parseUciInfo !== false 192 | showAnimations.value = settings.showAnimations !== false 193 | showPositionChart.value = !!settings.showPositionChart 194 | showEvaluationBar.value = settings.showEvaluationBar !== false 195 | darkMode.value = !!settings.darkMode 196 | autosave.value = settings.autosave !== false 197 | useNewFenFormat.value = settings.useNewFenFormat !== false 198 | engineLogLineLimit.value = settings.engineLogLineLimit || 256 199 | showChineseNotation.value = !!settings.showChineseNotation 200 | showLuckIndex.value = settings.showLuckIndex !== false 201 | showArrows.value = settings.showArrows !== false // Default to true 202 | showBookMoves.value = settings.showBookMoves !== false // Default to true 203 | openingBookEnableInGame.value = settings.openingBookEnableInGame !== false // Default to true 204 | openingBookPreferHighPriority.value = 205 | settings.openingBookPreferHighPriority !== false // Default to true 206 | 207 | isConfigLoaded.value = true 208 | } catch (error) { 209 | console.error('Failed to load interface settings:', error) 210 | isConfigLoaded.value = true // Still mark as loaded to enable saving 211 | } 212 | } 213 | 214 | // Initialize settings on first import 215 | if (typeof window !== 'undefined') { 216 | loadSettings() 217 | } 218 | 219 | return { 220 | showCoordinates, 221 | parseUciInfo, 222 | showAnimations, 223 | showPositionChart, 224 | showEvaluationBar, 225 | darkMode, 226 | autosave, 227 | useNewFenFormat, 228 | engineLogLineLimit, 229 | showChineseNotation, 230 | showLuckIndex, 231 | showArrows, 232 | showBookMoves, 233 | openingBookEnableInGame, 234 | openingBookPreferHighPriority, 235 | loadSettings, 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/composables/usePanelManager.ts: -------------------------------------------------------------------------------- 1 | import { reactive, toRefs, watch } from 'vue' 2 | 3 | interface PanelState { 4 | id: string 5 | isDocked: boolean 6 | x: number 7 | y: number 8 | width: number 9 | height: number 10 | } 11 | 12 | const PANELS_STORAGE_KEY = 'jieqibox-panels-layout' 13 | 14 | const defaultPanelStates: Record> = { 15 | 'dark-piece-pool': { 16 | isDocked: true, 17 | x: 0, 18 | y: 0, 19 | width: 300, 20 | height: 250, 21 | }, 22 | 'capture-history': { 23 | isDocked: true, 24 | x: 0, 25 | y: 0, 26 | width: 300, 27 | height: 200, 28 | }, 29 | 'engine-analysis': { 30 | isDocked: true, 31 | x: 0, 32 | y: 0, 33 | width: 400, 34 | height: 200, 35 | }, 36 | 'luck-index': { isDocked: true, x: 0, y: 0, width: 400, height: 120 }, 37 | notation: { isDocked: true, x: 0, y: 0, width: 400, height: 200 }, 38 | 'move-comments': { isDocked: true, x: 0, y: 0, width: 400, height: 250 }, 39 | 'engine-log': { isDocked: true, x: 0, y: 0, width: 400, height: 200 }, 40 | 'opening-book': { isDocked: true, x: 0, y: 0, width: 400, height: 260 }, 41 | } 42 | 43 | const store = reactive<{ 44 | panels: Record 45 | }>({ 46 | panels: {}, 47 | }) 48 | 49 | function saveStateToLocalStorage() { 50 | try { 51 | localStorage.setItem(PANELS_STORAGE_KEY, JSON.stringify(store.panels)) 52 | } catch (e) { 53 | console.error('Failed to save panel states to local storage', e) 54 | } 55 | } 56 | 57 | function loadStateFromLocalStorage(): Record { 58 | try { 59 | const savedState = localStorage.getItem(PANELS_STORAGE_KEY) 60 | if (savedState) { 61 | const parsed = JSON.parse(savedState) 62 | // Basic validation to ensure loaded data is not malformed 63 | if (typeof parsed === 'object' && parsed !== null) { 64 | return parsed 65 | } 66 | } 67 | } catch (e) { 68 | console.error('Failed to load panel states from local storage', e) 69 | } 70 | return {} 71 | } 72 | 73 | export function usePanelManager() { 74 | const initialize = () => { 75 | const loadedPanels = loadStateFromLocalStorage() 76 | const initialPanels: Record = {} 77 | 78 | Object.keys(defaultPanelStates).forEach(panelId => { 79 | const defaultState = defaultPanelStates[panelId] 80 | const loadedState = loadedPanels[panelId] 81 | 82 | initialPanels[panelId] = { 83 | ...(loadedState || defaultState), 84 | id: panelId, 85 | } 86 | }) 87 | 88 | store.panels = initialPanels 89 | 90 | watch( 91 | () => store.panels, 92 | () => { 93 | saveStateToLocalStorage() 94 | }, 95 | { deep: true } 96 | ) 97 | } 98 | 99 | const getPanelState = (panelId: string) => { 100 | if (!store.panels[panelId]) { 101 | console.warn(`Panel with id ${panelId} not found.`) 102 | return null 103 | } 104 | return toRefs(store.panels[panelId]) 105 | } 106 | 107 | const updatePanelState = (panelId: string, newState: Partial) => { 108 | if (store.panels[panelId]) { 109 | Object.assign(store.panels[panelId], newState) 110 | } 111 | } 112 | 113 | const dockPanel = (panelId: string) => { 114 | if (store.panels[panelId]) { 115 | store.panels[panelId].isDocked = true 116 | } 117 | } 118 | 119 | const undockPanel = ( 120 | panelId: string, 121 | position?: { x: number; y: number; width: number; height: number } 122 | ) => { 123 | if (store.panels[panelId]) { 124 | store.panels[panelId].isDocked = false 125 | if (position) { 126 | store.panels[panelId].x = position.x 127 | store.panels[panelId].y = position.y 128 | store.panels[panelId].width = position.width 129 | store.panels[panelId].height = position.height 130 | } 131 | } 132 | } 133 | 134 | const restoreDefaultLayout = () => { 135 | Object.keys(defaultPanelStates).forEach(panelId => { 136 | if (store.panels[panelId]) { 137 | Object.assign(store.panels[panelId], defaultPanelStates[panelId]) 138 | } 139 | }) 140 | } 141 | 142 | return { 143 | panels: toRefs(store).panels, 144 | initialize, 145 | getPanelState, 146 | updatePanelState, 147 | dockPanel, 148 | undockPanel, 149 | restoreDefaultLayout, 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/i18n/index.ts: -------------------------------------------------------------------------------- 1 | import { createI18n } from 'vue-i18n' 2 | import zh_cn from './locales/zh_cn' 3 | import zh_tw from './locales/zh_tw' 4 | import en from './locales/en' 5 | import vi from './locales/vi' 6 | import ja from './locales/ja' 7 | import { useConfigManager } from '../composables/useConfigManager' 8 | 9 | // Get user's preferred language 10 | const getDefaultLocale = () => { 11 | try { 12 | // Try to get saved language setting from config manager 13 | const configManager = useConfigManager() 14 | const savedLocale = configManager.getLocale() 15 | if ( 16 | savedLocale && 17 | ['zh_cn', 'zh_tw', 'en', 'vi', 'ja'].includes(savedLocale) 18 | ) { 19 | return savedLocale 20 | } 21 | } catch (error) { 22 | // Fallback if config manager is not available yet 23 | console.warn( 24 | 'Config manager not available during i18n initialization:', 25 | error 26 | ) 27 | } 28 | 29 | // Detect browser language 30 | const browserLang = navigator.language.toLowerCase() 31 | if (browserLang.startsWith('zh-cn')) return 'zh_cn' 32 | if (browserLang.startsWith('zh-tw') || browserLang.startsWith('zh-hk')) 33 | return 'zh_tw' 34 | if (browserLang.startsWith('vi')) return 'vi' 35 | if (browserLang.startsWith('ja')) return 'ja' 36 | if (browserLang.startsWith('en')) return 'en' 37 | 38 | // Default to Simplified Chinese 39 | return 'zh_cn' 40 | } 41 | 42 | const i18n = createI18n({ 43 | legacy: false, // Use Composition API mode 44 | locale: getDefaultLocale(), 45 | fallbackLocale: 'zh_cn', 46 | messages: { 47 | zh_cn, 48 | zh_tw, 49 | en, 50 | vi, 51 | ja, 52 | }, 53 | }) 54 | 55 | export default i18n 56 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | // Import Vuetify 5 | import 'vuetify/styles' 6 | import { createVuetify } from 'vuetify' 7 | import * as components from 'vuetify/components' 8 | import * as directives from 'vuetify/directives' 9 | import '@mdi/font/css/materialdesignicons.css' // Import MDI icon styles 10 | 11 | // Import i18n 12 | import i18n from './i18n' 13 | import { usePanelManager } from './composables/usePanelManager' 14 | 15 | const vuetify = createVuetify({ 16 | components, 17 | directives, 18 | icons: { 19 | defaultSet: 'mdi', // Set default icon set to mdi 20 | }, 21 | // Add theme configuration for dark mode support 22 | theme: { 23 | defaultTheme: 'light', 24 | themes: { 25 | light: { 26 | dark: false, 27 | colors: { 28 | primary: '#1976D2', 29 | secondary: '#424242', 30 | accent: '#82B1FF', 31 | error: '#FF5252', 32 | info: '#2196F3', 33 | success: '#4CAF50', 34 | warning: '#FF9800', 35 | background: '#F5F5F5', 36 | surface: '#FFFFFF', 37 | button: '#FFFFFF', 38 | 'on-background': '#000000', 39 | 'on-surface': '#000000', 40 | }, 41 | }, 42 | dark: { 43 | dark: true, 44 | colors: { 45 | primary: '#2196F3', 46 | secondary: '#424242', 47 | accent: '#FF4081', 48 | error: '#FF5252', 49 | info: '#2196F3', 50 | success: '#4CAF50', 51 | warning: '#FF9800', 52 | background: '#202020', 53 | surface: '#121212', 54 | button: '#1A1A1A', 55 | 'on-background': '#FFFFFF', 56 | 'on-surface': '#FFFFFF', 57 | }, 58 | }, 59 | }, 60 | }, 61 | }) 62 | 63 | const app = createApp(App) 64 | 65 | // Initialize i18n 66 | app.use(i18n) 67 | 68 | // Initialize Vuetify 69 | app.use(vuetify) 70 | 71 | // Initialize panel manager 72 | const panelManager = usePanelManager() 73 | panelManager.initialize() 74 | 75 | // Mount the app 76 | app.mount('#app') 77 | -------------------------------------------------------------------------------- /src/types/android.d.ts: -------------------------------------------------------------------------------- 1 | // Android JavaScript interface types 2 | declare global { 3 | interface Window { 4 | ExternalUrlInterface?: { 5 | openExternalUrl(url: string): void 6 | } 7 | SafFileInterface?: { 8 | startFileSelection(): void 9 | } 10 | } 11 | } 12 | 13 | export {} 14 | -------------------------------------------------------------------------------- /src/types/openingBook.ts: -------------------------------------------------------------------------------- 1 | // Opening book types and interfaces for JieqiBox 2 | export interface MoveData { 3 | uci_move: string 4 | priority: number 5 | wins: number 6 | draws: number 7 | losses: number 8 | allowed: boolean 9 | comment: string 10 | } 11 | 12 | export interface OpeningBookEntry { 13 | key: string 14 | fen: string 15 | moves: MoveData[] 16 | } 17 | 18 | export interface OpeningBookStats { 19 | totalPositions: number 20 | totalMoves: number 21 | allowedMoves: number 22 | disallowedMoves: number 23 | } 24 | 25 | export interface OpeningBookImportResult { 26 | success: boolean 27 | imported: number 28 | errors: string[] 29 | duplicates: number 30 | } 31 | 32 | export interface OpeningBookExportOptions { 33 | format: 'jbb' | 'json' | 'pgn' 34 | includeComments: boolean 35 | includeStats: boolean 36 | onlyAllowedMoves: boolean 37 | } 38 | 39 | export interface JieqiOpeningBookConfig { 40 | dbPath: string 41 | autoLoad: boolean 42 | enableInGame: boolean 43 | showBookMoves: boolean 44 | preferHighPriority: boolean 45 | } 46 | 47 | // Hash generation utilities interface 48 | export interface HashUtilities { 49 | generateKeyFromFen(fen: string): string 50 | normalizeFen(fen: string): string 51 | flipBoard(fenString: string): string 52 | swapColorsFen(normalizedFen: string): string 53 | } 54 | 55 | // Move conversion utilities interface 56 | export interface MoveUtilities { 57 | uciToInt(uci: string): number 58 | intToUci(moveInt: number): string 59 | } 60 | 61 | // Pool parsing utilities for FEN normalization 62 | export interface PoolParsingResult { 63 | redPool: Record 64 | blackPool: Record 65 | } 66 | 67 | export interface FenNormalizationResult { 68 | normalizedFen: string 69 | key: string 70 | variations: string[] 71 | } 72 | -------------------------------------------------------------------------------- /src/utils/constants.ts: -------------------------------------------------------------------------------- 1 | // Jieqi game constants 2 | 3 | /** 4 | * Standard starting FEN string 5 | * Format: board layout current side hidden pieces halfmove fullmove (New FEN format) 6 | */ 7 | export const START_FEN = 8 | 'xxxxkxxxx/9/1x5x1/x1x1x1x1x/9/9/X1X1X1X1X/1X5X1/9/XXXXKXXXX w A2B2N2R2C2P5a2b2n2r2c2p5 - 0 1' 9 | 10 | /** 11 | * Piece type mapping table 12 | */ 13 | export const FEN_MAP: { [k: string]: string } = { 14 | red_chariot: 'R', 15 | red_horse: 'N', 16 | red_elephant: 'B', 17 | red_advisor: 'A', 18 | red_king: 'K', 19 | red_cannon: 'C', 20 | red_pawn: 'P', 21 | black_chariot: 'r', 22 | black_horse: 'n', 23 | black_elephant: 'b', 24 | black_advisor: 'a', 25 | black_king: 'k', 26 | black_cannon: 'c', 27 | black_pawn: 'p', 28 | } 29 | 30 | /** 31 | * Reverse piece type mapping table 32 | */ 33 | export const REVERSE_FEN_MAP: { [k: string]: string } = { 34 | R: 'chariot', 35 | N: 'horse', 36 | B: 'elephant', 37 | A: 'advisor', 38 | K: 'king', 39 | C: 'cannon', 40 | P: 'pawn', 41 | r: 'chariot', 42 | n: 'horse', 43 | b: 'elephant', 44 | a: 'advisor', 45 | k: 'king', 46 | c: 'cannon', 47 | p: 'pawn', 48 | } 49 | 50 | /** 51 | * Initial piece counts 52 | */ 53 | export const INITIAL_PIECE_COUNTS: { [k: string]: number } = { 54 | R: 2, 55 | N: 2, 56 | B: 2, 57 | A: 2, 58 | C: 2, 59 | P: 5, 60 | K: 1, 61 | r: 2, 62 | n: 2, 63 | b: 2, 64 | a: 2, 65 | c: 2, 66 | p: 5, 67 | k: 1, 68 | } 69 | 70 | /** 71 | * Language to HTML lang attribute mapping 72 | */ 73 | export const LANGUAGE_TO_HTML_LANG: { [key: string]: string } = { 74 | zh_cn: 'zh-CN', 75 | zh_tw: 'zh-TW', 76 | ja: 'ja-JP', 77 | en: 'en-US', 78 | vi: 'vi-VN', 79 | } 80 | 81 | /** 82 | * Base absolute score used to represent mate evaluations. 83 | * A mate in N plies will be encoded as sign * (MATE_SCORE_BASE - N). 84 | */ 85 | export const MATE_SCORE_BASE = 30000 86 | 87 | /** 88 | * Luck Index (Jieqi win rate model) constants 89 | */ 90 | export const JIEQI_MODEL_FEATURE_DIM = 25 91 | 92 | // Piece order: R, N, B, A, C, P, r, n, b, a, c, p 93 | export const JIEQI_MODEL_PIECE_TO_INDEX: Record = { 94 | R: 0, 95 | N: 1, 96 | B: 2, 97 | A: 3, 98 | C: 4, 99 | P: 5, 100 | r: 6, 101 | n: 7, 102 | b: 8, 103 | a: 9, 104 | c: 10, 105 | p: 11, 106 | } 107 | 108 | export const JIEQI_MODEL_WEIGHTS = [ 109 | 0.08772514682756707, -0.24187006347352064, -0.3277591260822385, 110 | -0.312428548861004, -0.13957569975595147, -0.5124603920014706, 111 | -0.07820118002491627, 0.21484638534211742, 0.3250864339965009, 112 | 0.2541801314439757, 0.16834379121268697, 0.48605787168400955, 113 | -0.0006295471076094379, 0.06329823027051631, 0.06209210825092886, 114 | 0.03310532573822678, 0.04841754100544951, -0.019865475136336328, 115 | 0.013743096400146286, -0.04947095317317941, -0.0575693341961395, 116 | -0.02867090449408315, -0.052352286948066054, 0.04847843129288896, 117 | -0.0075537611173792036, 118 | ] 119 | 120 | export const JIEQI_MODEL_INTERCEPT = -0.0597125196819204 121 | 122 | export const JIEQI_MODEL_SCALER_MEANS = [ 123 | 1.0100588086733773, 0.9824872821733954, 0.9716996788974276, 0.976144604394415, 124 | 0.9974239636324278, 2.419136270159108, 0.9730057365515749, 0.9414799581484288, 125 | 0.9308583179997836, 0.9257278926290724, 0.9482988779449435, 126 | 2.3045712017895155, 5.002417289028394, 4.923108561532634, 4.8646895407150845, 127 | 4.8591478154201395, 4.976534256954216, 3.385792113143558, 5.3134321896309125, 128 | 5.24750153335498, 5.171411047371649, 5.178071219828986, 5.284222679222138, 129 | 4.041461918678068, 14.38089259299347, 130 | ] 131 | 132 | export const JIEQI_MODEL_SCALER_SCALES = [ 133 | 0.790981648494508, 0.7926789915520083, 0.7925629625773608, 0.7959431632239728, 134 | 0.7874895837632937, 1.6042211937577169, 0.7939774680651416, 0.798189505511732, 135 | 0.7989217957150805, 0.7968645359847384, 0.7934708555204867, 136 | 1.6371665393205772, 5.640202636038783, 5.739798544499773, 5.733844115971285, 137 | 5.711851899532759, 5.686190380640218, 3.522031970516342, 5.702074348990358, 138 | 5.8311527974946875, 5.820767759163146, 5.855302986745835, 5.786105625793965, 139 | 3.626781358558223, 8.18111371795009, 140 | ] 141 | -------------------------------------------------------------------------------- /src/utils/platform.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Platform detection utilities 3 | * Centralized platform detection logic to avoid code duplication 4 | */ 5 | 6 | /** 7 | * Check if the current platform is Android 8 | * Uses multiple detection methods for reliability 9 | * @returns true if running on Android platform 10 | */ 11 | export const isAndroidPlatform = (): boolean => { 12 | if (typeof window !== 'undefined') { 13 | // Check Tauri platform if available 14 | const tauriPlatform = (window as any).__TAURI__?.platform 15 | if (tauriPlatform === 'android') return true 16 | 17 | // Check user agent 18 | if (navigator.userAgent.includes('Android')) return true 19 | if (/Android/i.test(navigator.userAgent)) return true 20 | } 21 | return false 22 | } 23 | 24 | /** 25 | * Check if the current platform is mobile (Android, iOS, or other mobile devices) 26 | * Uses multiple detection methods for reliability 27 | * @returns true if running on mobile platform 28 | */ 29 | export const isMobilePlatform = (): boolean => { 30 | if (typeof window !== 'undefined') { 31 | // Check Tauri platform if available 32 | const tauriPlatform = (window as any).__TAURI__?.platform 33 | if (tauriPlatform === 'android' || tauriPlatform === 'ios') return true 34 | 35 | // Check user agent for mobile devices 36 | const userAgent = navigator.userAgent.toLowerCase() 37 | if ( 38 | userAgent.includes('android') || 39 | userAgent.includes('iphone') || 40 | userAgent.includes('ipad') || 41 | userAgent.includes('ipod') || 42 | userAgent.includes('mobile') || 43 | userAgent.includes('tablet') 44 | ) { 45 | return true 46 | } 47 | 48 | // Check screen size as fallback 49 | if (window.innerWidth <= 768) return true 50 | } 51 | return false 52 | } 53 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | // Tauri API type definitions for Android UCI engine support 4 | declare module '@tauri-apps/api/core' { 5 | export function invoke( 6 | cmd: string, 7 | args?: Record 8 | ): Promise 9 | } 10 | 11 | // Android-specific types 12 | interface AndroidEngineInfo { 13 | defaultPath: string 14 | isExecutable: boolean 15 | error?: string 16 | } 17 | 18 | // Platform detection 19 | declare global { 20 | interface Window { 21 | __TAURI__?: { 22 | platform: string 23 | } 24 | } 25 | } 26 | 27 | declare module '*.vue' { 28 | import type { DefineComponent } from 'vue' 29 | const component: DefineComponent<{}, {}, any> 30 | export default component 31 | } 32 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "preserve", 16 | 17 | /* Path alias configuration - Let TypeScript know that @ symbol points to src directory */ 18 | "baseUrl": ".", 19 | "paths": { 20 | "@/*": ["src/*"] 21 | }, 22 | 23 | /* Linting */ 24 | "strict": true, 25 | "noUnusedLocals": true, 26 | "noUnusedParameters": true, 27 | "noFallthroughCasesInSwitch": true 28 | }, 29 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], 30 | "references": [{ "path": "./tsconfig.node.json" }] 31 | } 32 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | import path from 'path' 4 | import { viteStaticCopy } from 'vite-plugin-static-copy' 5 | 6 | const host = process.env.TAURI_DEV_HOST 7 | 8 | // https://vitejs.dev/config/ 9 | export default defineConfig(async () => ({ 10 | plugins: [ 11 | vue(), 12 | viteStaticCopy({ 13 | targets: [ 14 | { src: 'node_modules/onnxruntime-web/dist/*.wasm', dest: 'ort' }, 15 | { src: 'node_modules/onnxruntime-web/dist/*.mjs', dest: 'ort' }, 16 | ], 17 | }), 18 | ], 19 | resolve: { 20 | alias: { 21 | '@': path.resolve(__dirname, './src'), 22 | }, 23 | }, 24 | 25 | // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` 26 | // 27 | // 1. prevent vite from obscuring rust errors 28 | clearScreen: false, 29 | // 2. tauri expects a fixed port, fail if that port is not available 30 | server: { 31 | port: 1420, 32 | strictPort: true, 33 | host: host || false, 34 | hmr: host 35 | ? { 36 | protocol: 'ws', 37 | host, 38 | port: 1421, 39 | } 40 | : undefined, 41 | watch: { 42 | // 3. tell vite to ignore watching `src-tauri` and autosave files 43 | ignored: [ 44 | '**/src-tauri/**', 45 | '**/Autosave.json', 46 | '**/config.ini', 47 | '**/jieqi_openings.jb', 48 | ], 49 | }, 50 | }, 51 | })) 52 | -------------------------------------------------------------------------------- /wood_yellow_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Velithia/JieqiBox/2a90e6d87c4393e8c9256b471c35d4d676d25406/wood_yellow_logo.png --------------------------------------------------------------------------------