commit f4969fd7d1cb1173d1994862aea6868d98d169ea Author: 徐涛 Date: Thu Dec 21 11:05:43 2023 +0800 build(init):项目初始化建立,迁移无需重构部分内容。 diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..084fd1f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true +charset = utf-8 +spaces_around_operators = true + +[*.rs] +indent_size = 4 +indent_style = space +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true +charset = utf-8 +spaces_around_operators = true diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..c26b9d8 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,16 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: ["eslint:recommended", "plugin:react-hooks/recommended"], + ignorePatterns: ["dist", ".eslintrc.cjs"], + plugins: ["react-refresh"], + parser: "@typescript-eslint/parser", + parserOptions: { + parser: "babel-eslint", + }, + rules: { + "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], + "react-hooks/exhaustive-deps": "off", + "no-unused-vars": "off", + }, +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..81a0631 --- /dev/null +++ b/.gitignore @@ -0,0 +1,200 @@ +node_modules +dist + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +!.vscode/settings.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb diff --git a/License b/License new file mode 100644 index 0000000..c61b663 --- /dev/null +++ b/License @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..5efabb1 --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# 拼音输入法词库编辑工具 + +这个项目主要设计用于落格输入法和 Rime 输入法的词库构建和编辑。 + +## 安装 + +项目推荐使用 bun 进行依赖管理,可以直接使用 `bun install` 命令安装前端依赖,也可以直接使用 `bun run tauri dev` 使 Tauri 自行安装所有依赖。 + +### 技术栈 + +- [Tauri](https://tauri.app/) +- [Rust](https://www.rust-lang.org/) +- [bun](https://bun.sh/) +- [Vite](https://vitejs.dev/) +- [SolidJS](https://www.solidjs.com/) +- [PostCSS](https://postcss.org/) + +### 推荐 IDE 插件配置 + +- [VS Code](https://code.visualstudio.com/) +- [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) +- [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) + +## 使用 + +执行以下命令可以启动开发服务。 + +```bash +bun run tauri dev +``` + +## License + +Apache License 2.0 © Midnite diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000..647f29f Binary files /dev/null and b/bun.lockb differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..c020235 --- /dev/null +++ b/index.html @@ -0,0 +1,17 @@ + + + + + + + + Tauri + Solid App + + + + +
+ + + + diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..927f3ef --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES6", + "module": "commonjs", + "allowSyntheticDefaultImports": true, + "baseUrl": "./", + "paths": { + "@/*": [ + "src/*" + ], + } + }, + "exclude": [ + "node_modules", + "dist" + ] +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..f2b23b4 --- /dev/null +++ b/package.json @@ -0,0 +1,44 @@ +{ + "name": "pylib_editor", + "version": "0.1.0", + "description": "Pinyin Input Method Library Editor.", + "type": "module", + "license": "Apache-2.0", + "author": { + "name": "Midnite" + }, + "repository": { + "type": "git", + "url": "https://github.com/vixalie/pylib-editor" + }, + "scripts": { + "start": "vite", + "dev": "vite", + "build": "vite build", + "serve": "vite preview", + "tauri": "tauri" + }, + "dependencies": { + "clsx": "^2.0.0", + "dayjs": "^1.11.10", + "events": "^3.3.0", + "ramda": "^0.29.1", + "solid-js": "^1.7.8", + "@tauri-apps/api": "^1.5.2" + }, + "devDependencies": { + "@types/events": "^3.0.1", + "@types/ramda": "^0.29.6", + "@typescript-eslint/parser": "^6.13.0", + "eslint": "^8.45.0", + "lost": "^9.0.2", + "postcss": "^8.4.31", + "postcss-preset-env": "^9.2.0", + "postcss-preset-mantine": "^1.8.0", + "postcss-simple-vars": "^7.0.1", + "postcss-utilities": "^0.8.4", + "vite": "^5.0.0", + "vite-plugin-solid": "^2.8.0", + "@tauri-apps/cli": "^1.5.8" + } +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..0d2619d --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,20 @@ +// eslint-disable-next-line no-undef +export default { + plugins: { + "postcss-preset-mantine": {}, + "postcss-simple-vars": { + variables: { + "mantine-breakpoint-xs": "36em", + "mantine-breakpoint-sm": "48em", + "mantine-breakpoint-md": "62em", + "mantine-breakpoint-lg": "75em", + "mantine-breakpoint-xl": "88em", + }, + }, + "postcss-utilities": {}, + lost: {}, + "postcss-preset-env": { + stage: 2, + }, + }, +}; diff --git a/public/tauri.svg b/public/tauri.svg new file mode 100644 index 0000000..31b62c9 --- /dev/null +++ b/public/tauri.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/vite.svg b/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src-tauri/.gitignore b/src-tauri/.gitignore new file mode 100644 index 0000000..f4dfb82 --- /dev/null +++ b/src-tauri/.gitignore @@ -0,0 +1,4 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml new file mode 100644 index 0000000..358d3be --- /dev/null +++ b/src-tauri/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "pylib_editor" +version = "0.1.0" +description = "Pinyin Input Method Library Editor." +authors = ["midnite"] +license = "Apache-2.0" +repository = "https://github.com/vixalie/pylib-editor" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[build-dependencies] +tauri-build = { version = "1.5", features = [] } + +[dependencies] +tauri = { version = "1.5", features = ["macos-private-api", "window-unminimize", "window-close", "window-start-dragging", "window-show", "window-hide", "window-maximize", "window-unmaximize", "window-minimize", "shell-open"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +window-vibrancy = "0.4.3" +once_cell = "1.18.0" +anyhow = "1.0.75" +thiserror = "1.0.50" +serde_repr = "0.1.17" +tokio = { version = "1.34.0", features = ["full"] } +uuid = "1.6.1" +rusqlite = { version = "0.30.0", features = ["modern-full"] } +directories = "5.0.1" +toml = "0.8.8" + +[features] +# this feature is used for production builds or when `devPath` points to the filesystem +# DO NOT REMOVE!! +custom-protocol = ["tauri/custom-protocol"] diff --git a/src-tauri/build.rs b/src-tauri/build.rs new file mode 100644 index 0000000..d860e1e --- /dev/null +++ b/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png new file mode 100644 index 0000000..6be5e50 Binary files /dev/null and b/src-tauri/icons/128x128.png differ diff --git a/src-tauri/icons/128x128@2x.png b/src-tauri/icons/128x128@2x.png new file mode 100644 index 0000000..e81bece Binary files /dev/null and b/src-tauri/icons/128x128@2x.png differ diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png new file mode 100644 index 0000000..a437dd5 Binary files /dev/null and b/src-tauri/icons/32x32.png differ diff --git a/src-tauri/icons/Square107x107Logo.png b/src-tauri/icons/Square107x107Logo.png new file mode 100644 index 0000000..0ca4f27 Binary files /dev/null and b/src-tauri/icons/Square107x107Logo.png differ diff --git a/src-tauri/icons/Square142x142Logo.png b/src-tauri/icons/Square142x142Logo.png new file mode 100644 index 0000000..b81f820 Binary files /dev/null and b/src-tauri/icons/Square142x142Logo.png differ diff --git a/src-tauri/icons/Square150x150Logo.png b/src-tauri/icons/Square150x150Logo.png new file mode 100644 index 0000000..624c7bf Binary files /dev/null and b/src-tauri/icons/Square150x150Logo.png differ diff --git a/src-tauri/icons/Square284x284Logo.png b/src-tauri/icons/Square284x284Logo.png new file mode 100644 index 0000000..c021d2b Binary files /dev/null and b/src-tauri/icons/Square284x284Logo.png differ diff --git a/src-tauri/icons/Square30x30Logo.png b/src-tauri/icons/Square30x30Logo.png new file mode 100644 index 0000000..6219700 Binary files /dev/null and b/src-tauri/icons/Square30x30Logo.png differ diff --git a/src-tauri/icons/Square310x310Logo.png b/src-tauri/icons/Square310x310Logo.png new file mode 100644 index 0000000..f9bc048 Binary files /dev/null and b/src-tauri/icons/Square310x310Logo.png differ diff --git a/src-tauri/icons/Square44x44Logo.png b/src-tauri/icons/Square44x44Logo.png new file mode 100644 index 0000000..d5fbfb2 Binary files /dev/null and b/src-tauri/icons/Square44x44Logo.png differ diff --git a/src-tauri/icons/Square71x71Logo.png b/src-tauri/icons/Square71x71Logo.png new file mode 100644 index 0000000..63440d7 Binary files /dev/null and b/src-tauri/icons/Square71x71Logo.png differ diff --git a/src-tauri/icons/Square89x89Logo.png b/src-tauri/icons/Square89x89Logo.png new file mode 100644 index 0000000..f3f705a Binary files /dev/null and b/src-tauri/icons/Square89x89Logo.png differ diff --git a/src-tauri/icons/StoreLogo.png b/src-tauri/icons/StoreLogo.png new file mode 100644 index 0000000..4556388 Binary files /dev/null and b/src-tauri/icons/StoreLogo.png differ diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns new file mode 100644 index 0000000..12a5bce Binary files /dev/null and b/src-tauri/icons/icon.icns differ diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico new file mode 100644 index 0000000..b3636e4 Binary files /dev/null and b/src-tauri/icons/icon.ico differ diff --git a/src-tauri/icons/icon.png b/src-tauri/icons/icon.png new file mode 100644 index 0000000..e1cd261 Binary files /dev/null and b/src-tauri/icons/icon.png differ diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs new file mode 100644 index 0000000..855ddb8 --- /dev/null +++ b/src-tauri/src/config.rs @@ -0,0 +1,67 @@ +use std::{ + fs::{DirBuilder, File}, + io::{Read, Write}, + ops::Deref, + path::{Path, PathBuf}, + sync::{Mutex, MutexGuard}, +}; + +use serde::{Deserialize, Serialize}; + +use crate::keyboard_mapping::KeyboardMapping; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Configuration { + pub mappings: Vec, +} + +static CONFIG_INSTANCE: Mutex = Mutex::new(Configuration { + mappings: Vec::new(), +}); + +impl Configuration { + // 获取一个可以用于访问应用配置信息的引用。 + pub fn get() -> MutexGuard<'static, Configuration> { + CONFIG_INSTANCE.lock().unwrap() + } + + // 确认应用数据目录的存在,如果数据目录不存在,那么将自动创建。 + fn ensure_data_dir() -> anyhow::Result { + let config_dir = directories::ProjectDirs::from("xyz", "archgrid", "pylib").ok_or( + anyhow::anyhow!("unable to fetch application data directory"), + )?; + let data_dir = config_dir.data_dir(); + if !data_dir.exists() { + let mut dir_creator = DirBuilder::new(); + dir_creator + .recursive(true) + .create(config_dir.data_dir()) + .unwrap(); + } + Ok(data_dir.to_path_buf()) + } + + // 确认应用配置文件存在,如果配置文件不存在,那么将自动创建一个空白的配置文件。 + fn ensure_config_file>(config_path: P) -> anyhow::Result { + let config_file_path = config_path.as_ref().join("config.toml"); + if !config_file_path.exists() { + let mut file = File::create(config_file_path.clone())?; + let configuration = Self::get(); + let toml_string = toml::to_string(configuration.deref())?; + file.write_all(toml_string.as_bytes())?; + } + let config_file = File::open(config_file_path)?; + Ok(config_file) + } + + // 从应用配置文件中加载配置信息,即便应用配置文件是空白的。 + pub fn load_settings() -> anyhow::Result<()> { + let mut config_file = Self::ensure_config_file(Self::ensure_data_dir()?)?; + let mut config_file_content = String::new(); + config_file.read_to_string(&mut config_file_content)?; + let config: Configuration = toml::from_str(&config_file_content).unwrap(); + let mut configuration = Self::get(); + *configuration = config; + Ok(()) + } +} diff --git a/src-tauri/src/keyboard_mapping.rs b/src-tauri/src/keyboard_mapping.rs new file mode 100644 index 0000000..b53ae30 --- /dev/null +++ b/src-tauri/src/keyboard_mapping.rs @@ -0,0 +1,29 @@ +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct KeyboardMapping { + pub name: String, + pub keys: HashMap>, +} + +impl KeyboardMapping { + /// 创建一个新的键盘映射。 + pub fn new>(name: S, key: S) -> Self { + let mut keys = HashMap::new(); + keys.insert(key.as_ref().to_string(), vec![key.as_ref().to_string()]); + Self { + name: name.as_ref().to_string(), + keys, + } + } + + // 重命名键盘映射。 + pub fn rename>(self, name: S) -> Self { + Self { + name: name.as_ref().to_string(), + ..self + } + } +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs new file mode 100644 index 0000000..74e11f6 --- /dev/null +++ b/src-tauri/src/main.rs @@ -0,0 +1,21 @@ +// Prevents additional console window on Windows in release, DO NOT REMOVE!! +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +#![allow(dead_code)] + +mod config; +mod keyboard_mapping; +mod setup; + +// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command +#[tauri::command] +fn greet(name: &str) -> String { + format!("Hello, {}! You've been greeted from Rust!", name) +} + +fn main() { + tauri::Builder::default() + .setup(setup::init) + .invoke_handler(tauri::generate_handler![greet]) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} diff --git a/src-tauri/src/setup.rs b/src-tauri/src/setup.rs new file mode 100644 index 0000000..1b3f430 --- /dev/null +++ b/src-tauri/src/setup.rs @@ -0,0 +1,19 @@ +use tauri::{App, Manager}; +use window_vibrancy::{self, NSVisualEffectMaterial}; + +/// setup +pub fn init(app: &mut App) -> std::result::Result<(), Box> { + let win = app.get_window("main").unwrap(); + + // 仅在 macOS 下执行 + #[cfg(target_os = "macos")] + window_vibrancy::apply_vibrancy(&win, NSVisualEffectMaterial::FullScreenUI, None, None) + .expect("Unsupported platform! 'apply_vibrancy' is only supported on macOS"); + + // 仅在 windows 下执行 + #[cfg(target_os = "windows")] + window_vibrancy::apply_blur(&win, Some((18, 18, 18, 125))) + .expect("Unsupported platform! 'apply_blur' is only supported on Windows"); + + Ok(()) +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json new file mode 100644 index 0000000..40ce09d --- /dev/null +++ b/src-tauri/tauri.conf.json @@ -0,0 +1,63 @@ +{ + "build": { + "beforeDevCommand": "bun run dev", + "beforeBuildCommand": "bun run build", + "devPath": "http://localhost:1420", + "distDir": "../dist" + }, + "package": { + "productName": "拼音词库工具", + "version": "0.1.0" + }, + "tauri": { + "macOSPrivateApi": true, + "allowlist": { + "all": false, + "shell": { + "all": false, + "open": true + }, + "window": { + "all": false, + "close": true, + "hide": true, + "show": true, + "maximize": true, + "minimize": true, + "unmaximize": true, + "unminimize": true, + "startDragging": true + } + }, + "bundle": { + "active": true, + "targets": "all", + "identifier": "xyz.archgrid.pylib", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ] + }, + "security": { + "csp": null + }, + "windows": [ + { + "fullscreen": false, + "resizable": true, + "title": "拼音词库工具", + "width": 1200, + "height": 800, + "minWidth": 1200, + "minHeight": 800, + "titleBarStyle": "Overlay", + "decorations": true, + "hiddenTitle": true, + "transparent": true + } + ] + } +} diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..46d328a --- /dev/null +++ b/src/App.css @@ -0,0 +1,7 @@ +.logo.vite:hover { + filter: drop-shadow(0 0 2em #747bff); +} + +.logo.solid:hover { + filter: drop-shadow(0 0 2em #2f5d90); +} diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..0443046 --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,53 @@ +import { createSignal } from "solid-js"; +import logo from "./assets/logo.svg"; +import { invoke } from "@tauri-apps/api/tauri"; +import "./App.css"; + +function App() { + const [greetMsg, setGreetMsg] = createSignal(""); + const [name, setName] = createSignal(""); + + async function greet() { + // Learn more about Tauri commands at https://tauri.app/v1/guides/features/command + setGreetMsg(await invoke("greet", { name: name() })); + } + + return ( +
+

Welcome to Tauri!

+ + + +

Click on the Tauri, Vite, and Solid logos to learn more.

+ +
{ + e.preventDefault(); + greet(); + }} + > + setName(e.currentTarget.value)} + placeholder="Enter a name..." + /> + +
+ +

{greetMsg()}

+
+ ); +} + +export default App; diff --git a/src/assets/logo.svg b/src/assets/logo.svg new file mode 100644 index 0000000..025aa30 --- /dev/null +++ b/src/assets/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/index.jsx b/src/index.jsx new file mode 100644 index 0000000..187a784 --- /dev/null +++ b/src/index.jsx @@ -0,0 +1,7 @@ +/* @refresh reload */ +import { render } from "solid-js/web"; + +import "./styles.css"; +import App from "./App"; + +render(() => , document.getElementById("root")); diff --git a/src/styles.css b/src/styles.css new file mode 100644 index 0000000..f7de85b --- /dev/null +++ b/src/styles.css @@ -0,0 +1,109 @@ +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color: #0f0f0f; + background-color: #f6f6f6; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +.container { + margin: 0; + padding-top: 10vh; + display: flex; + flex-direction: column; + justify-content: center; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: 0.75s; +} + +.logo.tauri:hover { + filter: drop-shadow(0 0 2em #24c8db); +} + +.row { + display: flex; + justify-content: center; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +h1 { + text-align: center; +} + +input, +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + color: #0f0f0f; + background-color: #ffffff; + transition: border-color 0.25s; + box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); +} + +button { + cursor: pointer; +} + +button:hover { + border-color: #396cd8; +} +button:active { + border-color: #396cd8; + background-color: #e8e8e8; +} + +input, +button { + outline: none; +} + +#greet-input { + margin-right: 5px; +} + +@media (prefers-color-scheme: dark) { + :root { + color: #f6f6f6; + background-color: #2f2f2f; + } + + a:hover { + color: #24c8db; + } + + input, + button { + color: #ffffff; + background-color: #0f0f0f98; + } + button:active { + background-color: #0f0f0f69; + } +} diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..e238981 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,21 @@ +import { defineConfig } from "vite"; +import solid from "vite-plugin-solid"; + +// https://vitejs.dev/config/ +export default defineConfig(async () => ({ + plugins: [solid()], + + // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` + // + // 1. prevent vite from obscuring rust errors + clearScreen: false, + // 2. tauri expects a fixed port, fail if that port is not available + server: { + port: 1420, + strictPort: true, + watch: { + // 3. tell vite to ignore watching `src-tauri` + ignored: ["**/src-tauri/**"], + }, + }, +}));