diff --git a/Cargo.lock b/Cargo.lock index b0b561c4..ed09c5f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4408,6 +4408,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "tauri-plugin-camera" +version = "0.0.0" +dependencies = [ + "serde", + "serde_json", + "tauri", + "tauri-build", + "thiserror", +] + [[package]] name = "tauri-plugin-fs-extra" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 46b0ee6a..a7f742f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,4 @@ edition = "2021" authors = [ "Tauri Programme within The Commons Conservancy" ] license = "Apache-2.0 OR MIT" rust-version = "1.64" +exclude = ["/examples"] diff --git a/plugins/camera/.gitignore b/plugins/camera/.gitignore new file mode 100644 index 00000000..d29055d3 --- /dev/null +++ b/plugins/camera/.gitignore @@ -0,0 +1,4 @@ +/target +/Cargo.lock + +!dist-js \ No newline at end of file diff --git a/plugins/camera/Cargo.toml b/plugins/camera/Cargo.toml new file mode 100644 index 00000000..5b53ef67 --- /dev/null +++ b/plugins/camera/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "tauri-plugin-camera" +version = "0.0.0" +description = "Ask the user take a photo with the camera or select an image from the gallery." +authors.workspace = true +license.workspace = true +edition.workspace = true +rust-version.workspace = true +exclude.workspace = true + +[dependencies] +serde.workspace = true +serde_json.workspace = true +tauri.workspace = true +thiserror.workspace = true + +[build-dependencies] +tauri-build.workspace = true diff --git a/plugins/camera/LICENSE.spdx b/plugins/camera/LICENSE.spdx new file mode 100644 index 00000000..cdd0df5a --- /dev/null +++ b/plugins/camera/LICENSE.spdx @@ -0,0 +1,20 @@ +SPDXVersion: SPDX-2.1 +DataLicense: CC0-1.0 +PackageName: tauri +DataFormat: SPDXRef-1 +PackageSupplier: Organization: The Tauri Programme in the Commons Conservancy +PackageHomePage: https://tauri.app +PackageLicenseDeclared: Apache-2.0 +PackageLicenseDeclared: MIT +PackageCopyrightText: 2019-2022, The Tauri Programme in the Commons Conservancy +PackageSummary: Tauri is a rust project that enables developers to make secure +and small desktop applications using a web frontend. + +PackageComment: The package includes the following libraries; see +Relationship information. + +Created: 2019-05-20T09:00:00Z +PackageDownloadLocation: git://github.com/tauri-apps/tauri +PackageDownloadLocation: git+https://github.com/tauri-apps/tauri.git +PackageDownloadLocation: git+ssh://github.com/tauri-apps/tauri.git +Creator: Person: Daniel Thompson-Yvetot \ No newline at end of file diff --git a/plugins/camera/LICENSE_APACHE-2.0 b/plugins/camera/LICENSE_APACHE-2.0 new file mode 100644 index 00000000..4947287f --- /dev/null +++ b/plugins/camera/LICENSE_APACHE-2.0 @@ -0,0 +1,177 @@ + + 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 \ No newline at end of file diff --git a/plugins/camera/LICENSE_MIT b/plugins/camera/LICENSE_MIT new file mode 100644 index 00000000..4d754725 --- /dev/null +++ b/plugins/camera/LICENSE_MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 - Present Tauri Apps Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/plugins/camera/README.md b/plugins/camera/README.md new file mode 100644 index 00000000..605789a2 --- /dev/null +++ b/plugins/camera/README.md @@ -0,0 +1,84 @@ +# Camera Plugin + +Prompt the user to take a photo using the camera or pick an image from the gallery. Mobile only. + +## Install + +There are three general methods of installation that we can recommend. + +1. Use crates.io and npm (easiest, and requires you to trust that our publishing pipeline worked) +2. Pull sources directly from Github using git tags / revision hashes (most secure) +3. Git submodule install this repo in your tauri project and then use file protocol to ingest the source (most secure, but inconvenient to use) + +Install the Core plugin by adding the following to your `Cargo.toml` file: + +```toml +[dependencies] +tauri-plugin-camera = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "feat/camera" } +``` + +You can install the JavaScript Guest bindings using your preferred JavaScript package manager: + +> Note: Since most JavaScript package managers are unable to install packages from git monorepos we provide read-only mirrors of each plugin. This makes installation option 2 more ergonomic to use. + +```sh +npm install 'https://gitpkg.now.sh/tauri-apps/plugins-workspace/plugins/camera?feat/camera' +# or +yarn add 'https://gitpkg.now.sh/tauri-apps/plugins-workspace/plugins/camera?feat/camera' +``` + +**NOT AVAILABLE YET, WILL BE READY WHEN WE MERGE THE BRANCH:** +```sh +pnpm add https://github.com/tauri-apps/tauri-plugin-camera +# or +npm add https://github.com/tauri-apps/tauri-plugin-camera +# or +yarn add https://github.com/tauri-apps/tauri-plugin-camera +``` + +## Usage + +Register the core plugin with Tauri: + +`src-tauri/src/lib.rs` + +```rust +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .plugin(tauri_plugin_camera::init()) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} + +``` + +Afterwards all the plugin's APIs are available through the JavaScript guest bindings: + +```javascript +import { getPhoto } from "tauri-plugin-camera-api"; +const image = await getPhoto(); +``` + +### Android + +Add the following permissions on the `src-tauri/gen/android/$(APPNAME)/app/src/main/AndroidManifest.xml` file: + +```xml + + +``` + +### iOS + +Configure the following properties on `src-tauri/gen/apple/$(APPNAME)_iOS/Info.plist`: + +```xml +NSCameraUsageDescription +Description for the camera usage here +NSPhotoLibraryAddUsageDescription +Description for the library add usage here +NSPhotoLibraryUsageDescription +Description for the library usage here +``` + diff --git a/plugins/camera/android/.gitignore b/plugins/camera/android/.gitignore new file mode 100644 index 00000000..fb9b198b --- /dev/null +++ b/plugins/camera/android/.gitignore @@ -0,0 +1,2 @@ +/build +/tauri-api diff --git a/plugins/camera/android/build.gradle.kts b/plugins/camera/android/build.gradle.kts new file mode 100644 index 00000000..1b536c19 --- /dev/null +++ b/plugins/camera/android/build.gradle.kts @@ -0,0 +1,46 @@ +plugins { + id("com.android.library") + id("org.jetbrains.kotlin.android") +} + +android { + namespace = "app.tauri.camera" + compileSdk = 32 + + defaultConfig { + minSdk = 24 + targetSdk = 32 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + + implementation("androidx.core:core-ktx:1.9.0") + implementation("androidx.appcompat:appcompat:1.6.0") + implementation("com.google.android.material:material:1.7.0") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") + implementation("androidx.exifinterface:exifinterface:1.3.3") + implementation(project(":tauri-android")) +} diff --git a/plugins/camera/android/proguard-rules.pro b/plugins/camera/android/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/plugins/camera/android/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/plugins/camera/android/settings.gradle b/plugins/camera/android/settings.gradle new file mode 100644 index 00000000..32f9aac2 --- /dev/null +++ b/plugins/camera/android/settings.gradle @@ -0,0 +1,2 @@ +include ':tauri-android' +project(':tauri-android').projectDir = new File('./tauri-api') diff --git a/plugins/camera/android/src/androidTest/java/app/tauri/camera/ExampleInstrumentedTest.kt b/plugins/camera/android/src/androidTest/java/app/tauri/camera/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..5f729a50 --- /dev/null +++ b/plugins/camera/android/src/androidTest/java/app/tauri/camera/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package app.tauri.camera + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("app.tauri.camera", appContext.packageName) + } +} diff --git a/plugins/camera/android/src/main/AndroidManifest.xml b/plugins/camera/android/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/plugins/camera/android/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/plugins/camera/android/src/main/java/app/tauri/camera/CameraBottomSheetDialogFragment.kt b/plugins/camera/android/src/main/java/app/tauri/camera/CameraBottomSheetDialogFragment.kt new file mode 100644 index 00000000..655723b1 --- /dev/null +++ b/plugins/camera/android/src/main/java/app/tauri/camera/CameraBottomSheetDialogFragment.kt @@ -0,0 +1,102 @@ +package app.tauri.camera + +import android.annotation.SuppressLint +import android.app.Dialog +import android.content.DialogInterface +import android.graphics.Color +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import androidx.coordinatorlayout.widget.CoordinatorLayout +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback +import com.google.android.material.bottomsheet.BottomSheetDialogFragment + +class CameraBottomSheetDialogFragment : BottomSheetDialogFragment() { + fun interface BottomSheetOnSelectedListener { + fun onSelected(index: Int) + } + + fun interface BottomSheetOnCanceledListener { + fun onCanceled() + } + + private var selectedListener: BottomSheetOnSelectedListener? = null + private var canceledListener: BottomSheetOnCanceledListener? = null + private var options: List? = null + private var title: String? = null + fun setTitle(title: String?) { + this.title = title + } + + fun setOptions( + options: List?, + selectedListener: BottomSheetOnSelectedListener, + canceledListener: BottomSheetOnCanceledListener + ) { + this.options = options + this.selectedListener = selectedListener + this.canceledListener = canceledListener + } + + override fun onCancel(dialog: DialogInterface) { + super.onCancel(dialog) + if (canceledListener != null) { + canceledListener!!.onCanceled() + } + } + + private val mBottomSheetBehaviorCallback: BottomSheetCallback = object : BottomSheetCallback() { + override fun onStateChanged(bottomSheet: View, newState: Int) { + if (newState == BottomSheetBehavior.STATE_HIDDEN) { + dismiss() + } + } + + override fun onSlide(bottomSheet: View, slideOffset: Float) {} + } + + @SuppressLint("RestrictedApi") + override fun setupDialog(dialog: Dialog, style: Int) { + super.setupDialog(dialog, style) + if (options == null || options!!.size == 0) { + return + } + val scale = resources.displayMetrics.density + val layoutPaddingDp16 = 16.0f + val layoutPaddingDp12 = 12.0f + val layoutPaddingDp8 = 8.0f + val layoutPaddingPx16 = (layoutPaddingDp16 * scale + 0.5f).toInt() + val layoutPaddingPx12 = (layoutPaddingDp12 * scale + 0.5f).toInt() + val layoutPaddingPx8 = (layoutPaddingDp8 * scale + 0.5f).toInt() + val parentLayout = CoordinatorLayout(requireContext()) + val layout = LinearLayout(context) + layout.orientation = LinearLayout.VERTICAL + layout.setPadding(layoutPaddingPx16, layoutPaddingPx16, layoutPaddingPx16, layoutPaddingPx16) + val ttv = TextView(context) + ttv.setTextColor(Color.parseColor("#757575")) + ttv.setPadding(layoutPaddingPx8, layoutPaddingPx8, layoutPaddingPx8, layoutPaddingPx8) + ttv.text = title + layout.addView(ttv) + for (i in options!!.indices) { + val tv = TextView(context) + tv.setTextColor(Color.parseColor("#000000")) + tv.setPadding(layoutPaddingPx12, layoutPaddingPx12, layoutPaddingPx12, layoutPaddingPx12) + tv.text = options!![i] + tv.setOnClickListener { + if (selectedListener != null) { + selectedListener!!.onSelected(i) + } + dismiss() + } + layout.addView(tv) + } + parentLayout.addView(layout.rootView) + dialog.setContentView(parentLayout.rootView) + val params = (parentLayout.parent as View).layoutParams as CoordinatorLayout.LayoutParams + val behavior = params.behavior + if (behavior != null && behavior is BottomSheetBehavior<*>) { + behavior.addBottomSheetCallback(mBottomSheetBehaviorCallback) + } + } +} diff --git a/plugins/camera/android/src/main/java/app/tauri/camera/CameraPlugin.kt b/plugins/camera/android/src/main/java/app/tauri/camera/CameraPlugin.kt new file mode 100644 index 00000000..ada2984c --- /dev/null +++ b/plugins/camera/android/src/main/java/app/tauri/camera/CameraPlugin.kt @@ -0,0 +1,827 @@ +package app.tauri.camera + +import android.Manifest +import android.app.Activity +import android.content.* +import android.content.pm.PackageManager +import android.content.pm.ResolveInfo +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.os.Environment +import android.os.Parcelable +import android.provider.MediaStore +import android.util.Base64 +import androidx.activity.result.ActivityResult +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.FileProvider +import androidx.exifinterface.media.ExifInterface.* +import app.tauri.* +import app.tauri.annotation.* +import app.tauri.plugin.* +import org.json.JSONException +import java.io.* +import java.util.* +import java.util.concurrent.Executor +import java.util.concurrent.Executors + +enum class CameraSource(val source: String) { + PROMPT("PROMPT"), CAMERA("CAMERA"), PHOTOS("PHOTOS"); +} + +enum class CameraResultType(val type: String) { + BASE64("base64"), URI("uri"), DATAURL("dataUrl"); +} + +class CameraSettings { + var resultType: CameraResultType = CameraResultType.BASE64 + var quality = DEFAULT_QUALITY + var isShouldResize = false + var isShouldCorrectOrientation = DEFAULT_CORRECT_ORIENTATION + var isSaveToGallery = DEFAULT_SAVE_IMAGE_TO_GALLERY + var isAllowEditing = false + var width = 0 + var height = 0 + var source: CameraSource = CameraSource.PROMPT + + companion object { + const val DEFAULT_QUALITY = 90 + const val DEFAULT_SAVE_IMAGE_TO_GALLERY = false + const val DEFAULT_CORRECT_ORIENTATION = true + } +} + +@TauriPlugin( + permissions = [ + Permission(strings = [Manifest.permission.CAMERA], alias = "camera"), + Permission( + strings = [Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE], + alias = "photos" + )] +) +class CameraPlugin(private val activity: Activity): Plugin(activity) { + // Permission alias constants + val CAMERA = "camera" + val PHOTOS = "photos" + + // Message constants + private val INVALID_RESULT_TYPE_ERROR = "Invalid resultType option" + private val PERMISSION_DENIED_ERROR_CAMERA = "User denied access to camera" + private val PERMISSION_DENIED_ERROR_PHOTOS = "User denied access to photos" + private val NO_CAMERA_ERROR = "Device doesn't have a camera available" + private val NO_CAMERA_ACTIVITY_ERROR = "Unable to resolve camera activity" + private val NO_PHOTO_ACTIVITY_ERROR = "Unable to resolve photo activity" + private val IMAGE_FILE_SAVE_ERROR = "Unable to create photo on disk" + private val IMAGE_PROCESS_NO_FILE_ERROR = "Unable to process image, file not found on disk" + private val UNABLE_TO_PROCESS_IMAGE = "Unable to process image" + private val IMAGE_EDIT_ERROR = "Unable to edit image" + private val IMAGE_GALLERY_SAVE_ERROR = "Unable to save the image in the gallery" + + private var imageFileSavePath: String? = null + private var imageEditedFileSavePath: String? = null + private var imageFileUri: Uri? = null + private var imagePickedContentUri: Uri? = null + private var isEdited = false + private var isFirstRequest = true + private var isSaved = false + + private var settings: CameraSettings = CameraSettings() + + @PluginMethod + fun getPhoto(invoke: Invoke) { + isEdited = false + settings = getSettings(invoke) + doShow(invoke) + } + + @PluginMethod + fun pickImages(invoke: Invoke) { + settings = getSettings(invoke) + openPhotos(invoke, multiple = true, skipPermission = false) + } + + @PluginMethod + fun pickLimitedLibraryPhotos(invoke: Invoke) { + invoke.reject("not supported on android") + } + + @PluginMethod + fun getLimitedLibraryPhotos(invoke: Invoke) { + invoke.reject("not supported on android") + } + + private fun doShow(invoke: Invoke) { + when (settings.source) { + CameraSource.CAMERA -> showCamera(invoke) + CameraSource.PHOTOS -> showPhotos(invoke) + else -> showPrompt(invoke) + } + } + + private fun showPrompt(invoke: Invoke) { + // We have all necessary permissions, open the camera + val options: MutableList = ArrayList() + options.add(invoke.getString("promptLabelPhoto", "From Photos")) + options.add(invoke.getString("promptLabelPicture", "Take Picture")) + val fragment = CameraBottomSheetDialogFragment() + fragment.setTitle(invoke.getString("promptLabelHeader", "Photo")) + fragment.setOptions( + options, + { index: Int -> + if (index == 0) { + settings.source = CameraSource.PHOTOS + openPhotos(invoke) + } else if (index == 1) { + settings.source = CameraSource.CAMERA + openCamera(invoke) + } + }, + { invoke.reject("User cancelled photos app") }) + fragment.show((activity as AppCompatActivity).supportFragmentManager, "capacitorModalsActionSheet") + } + + private fun showCamera(invoke: Invoke) { + if (!activity.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)) { + invoke.reject(NO_CAMERA_ERROR) + return + } + openCamera(invoke) + } + + private fun showPhotos(invoke: Invoke) { + openPhotos(invoke) + } + + private fun checkCameraPermissions(invoke: Invoke): Boolean { + // if the manifest does not contain the camera permissions key, we don't need to ask the user + val needCameraPerms = isPermissionDeclared(CAMERA) + val hasCameraPerms = !needCameraPerms || getPermissionState(CAMERA) === PermissionState.GRANTED + val hasPhotoPerms = getPermissionState(PHOTOS) === PermissionState.GRANTED + + // If we want to save to the gallery, we need two permissions + if (settings.isSaveToGallery && !(hasCameraPerms && hasPhotoPerms) && isFirstRequest) { + isFirstRequest = false + val aliases = if (needCameraPerms) { + arrayOf(CAMERA, PHOTOS) + } else { + arrayOf(PHOTOS) + } + requestPermissionForAliases(aliases, invoke, "cameraPermissionsCallback") + return false + } else if (!hasCameraPerms) { + requestPermissionForAlias(CAMERA, invoke, "cameraPermissionsCallback") + return false + } + return true + } + + private fun checkPhotosPermissions(invoke: Invoke): Boolean { + if (getPermissionState(PHOTOS) !== PermissionState.GRANTED) { + requestPermissionForAlias(PHOTOS, invoke, "cameraPermissionsCallback") + return false + } + return true + } + + /** + * Completes the plugin invoke after a camera permission request + * + * @see .getPhoto + * @param invoke the plugin invoke + */ + @PermissionCallback + private fun cameraPermissionsCallback(invoke: Invoke) { + // TODO: invoke.methodName() + val methodName = "pickImages" + if (methodName == "pickImages") { + openPhotos(invoke, multiple = true, skipPermission = true) + } else { + if (settings.source === CameraSource.CAMERA && getPermissionState(CAMERA) !== PermissionState.GRANTED) { + Logger.debug( + getLogTag(), + "User denied camera permission: " + getPermissionState(CAMERA).toString() + ) + invoke.reject(PERMISSION_DENIED_ERROR_CAMERA) + return + } else if (settings.source === CameraSource.PHOTOS && getPermissionState(PHOTOS) !== PermissionState.GRANTED) { + Logger.debug( + getLogTag(), + "User denied photos permission: " + getPermissionState(PHOTOS).toString() + ) + invoke.reject(PERMISSION_DENIED_ERROR_PHOTOS) + return + } + doShow(invoke) + } + } + + private fun getSettings(invoke: Invoke): CameraSettings { + val settings = CameraSettings() + val resultType = getResultType(invoke.getString("resultType")) + if (resultType != null) { + settings.resultType = resultType + } + settings.isSaveToGallery = + invoke.getBoolean( + "saveToGallery", + CameraSettings.DEFAULT_SAVE_IMAGE_TO_GALLERY + ) + settings.isAllowEditing = invoke.getBoolean("allowEditing", false) + settings.quality = invoke.getInt("quality", CameraSettings.DEFAULT_QUALITY) + settings.width = invoke.getInt("width", 0) + settings.height = invoke.getInt("height", 0) + settings.isShouldResize = settings.width > 0 || settings.height > 0 + settings.isShouldCorrectOrientation = + invoke.getBoolean( + "correctOrientation", + CameraSettings.DEFAULT_CORRECT_ORIENTATION + ) + + try { + settings.source = + CameraSource.valueOf( + invoke.getString( + "source", + CameraSource.PROMPT.source + ) + ) + + } catch (ex: IllegalArgumentException) { + settings.source = CameraSource.PROMPT + } + return settings + } + + private fun getResultType(resultType: String?): CameraResultType? { + return if (resultType == null) { + null + } else try { + CameraResultType.valueOf(resultType.uppercase(Locale.ROOT)) + } catch (ex: IllegalArgumentException) { + Logger.debug(getLogTag(), "Invalid result type \"$resultType\", defaulting to base64") + CameraResultType.BASE64 + } + } + + private fun openCamera(invoke: Invoke) { + if (checkCameraPermissions(invoke)) { + val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) + if (takePictureIntent.resolveActivity(activity.packageManager) != null) { + // If we will be saving the photo, send the target file along + try { + val appId: String = activity.packageName + val photoFile: File = CameraUtils.createImageFile(activity) + imageFileSavePath = photoFile.absolutePath + // TODO: Verify provider config exists + imageFileUri = FileProvider.getUriForFile( + activity, + "$appId.fileprovider", photoFile + ) + takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageFileUri) + } catch (ex: Exception) { + invoke.reject(IMAGE_FILE_SAVE_ERROR, ex) + return + } + startActivityForResult(invoke, takePictureIntent, "processCameraImage") + } else { + invoke.reject(NO_CAMERA_ACTIVITY_ERROR) + } + } + } + + private fun openPhotos(invoke: Invoke) { + openPhotos(invoke, multiple = false, skipPermission = false) + } + + private fun openPhotos(invoke: Invoke, multiple: Boolean, skipPermission: Boolean) { + if (skipPermission || checkPhotosPermissions(invoke)) { + val intent = Intent(Intent.ACTION_PICK) + intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiple) + intent.setType("image/*") + try { + if (multiple) { + intent.putExtra("multi-pick", multiple) + intent.putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/*")) + startActivityForResult(invoke, intent, "processPickedImages") + } else { + startActivityForResult(invoke, intent, "processPickedImage") + } + } catch (ex: ActivityNotFoundException) { + invoke.reject(NO_PHOTO_ACTIVITY_ERROR) + } + } + } + + @ActivityCallback + fun processCameraImage(invoke: Invoke, result: ActivityResult?) { + settings = getSettings(invoke) + if (imageFileSavePath == null) { + invoke.reject(IMAGE_PROCESS_NO_FILE_ERROR) + return + } + // Load the image as a Bitmap + val f = File(imageFileSavePath!!) + val bmOptions: BitmapFactory.Options = BitmapFactory.Options() + val contentUri: Uri = Uri.fromFile(f) + val bitmap: Bitmap = BitmapFactory.decodeFile(imageFileSavePath, bmOptions) + returnResult(invoke, bitmap, contentUri) + } + + @ActivityCallback + fun processPickedImage(invoke: Invoke, result: ActivityResult) { + settings = getSettings(invoke) + val data: Intent? = result.data + if (data == null) { + invoke.reject("No image picked") + return + } + val u: Uri = data.data!! + imagePickedContentUri = u + processPickedImage(u, invoke) + } + + @ActivityCallback + fun processPickedImages(invoke: Invoke, result: ActivityResult) { + val data: Intent? = result.data + if (data != null) { + val executor: Executor = Executors.newSingleThreadExecutor() + executor.execute { + val ret = JSObject() + val photos = JSArray() + if (data.clipData != null) { + val count: Int = data.clipData!!.itemCount + for (i in 0 until count) { + val imageUri: Uri = data.clipData!!.getItemAt(i).uri + val processResult = processPickedImages(imageUri) + if (processResult.getString("error").isNotEmpty() + ) { + invoke.reject(processResult.getString("error")) + return@execute + } else { + photos.put(processResult) + } + } + } else if (data.data != null) { + val imageUri: Uri = data.data!! + val processResult = processPickedImages(imageUri) + if (processResult.getString("error").isNotEmpty() + ) { + invoke.reject(processResult.getString("error")) + return@execute + } else { + photos.put(processResult) + } + } else if (data.extras != null) { + val bundle: Bundle = data.extras!! + if (bundle.keySet().contains("selectedItems")) { + val fileUris: ArrayList? = bundle.getParcelableArrayList("selectedItems") + if (fileUris != null) { + for (fileUri in fileUris) { + if (fileUri is Uri) { + val imageUri: Uri = fileUri + try { + val processResult = processPickedImages(imageUri) + if (processResult.getString("error").isNotEmpty() + ) { + invoke.reject(processResult.getString("error")) + return@execute + } else { + photos.put(processResult) + } + } catch (ex: SecurityException) { + invoke.reject("SecurityException") + } + } + } + } + } + } + ret.put("photos", photos) + invoke.resolve(ret) + } + } else { + invoke.reject("No images picked") + } + } + + private fun processPickedImage(imageUri: Uri, invoke: Invoke) { + var imageStream: InputStream? = null + try { + imageStream = activity.contentResolver.openInputStream(imageUri) + val bitmap = BitmapFactory.decodeStream(imageStream) + if (bitmap == null) { + invoke.reject("Unable to process bitmap") + return + } + returnResult(invoke, bitmap, imageUri) + } catch (err: OutOfMemoryError) { + invoke.reject("Out of memory") + } catch (ex: FileNotFoundException) { + invoke.reject("No such image found", ex) + } finally { + if (imageStream != null) { + try { + imageStream.close() + } catch (e: IOException) { + Logger.error(getLogTag(), UNABLE_TO_PROCESS_IMAGE, e) + } + } + } + } + + private fun processPickedImages(imageUri: Uri): JSObject { + var imageStream: InputStream? = null + val ret = JSObject() + try { + imageStream = activity.contentResolver.openInputStream(imageUri) + var bitmap = BitmapFactory.decodeStream(imageStream) + if (bitmap == null) { + ret.put("error", "Unable to process bitmap") + return ret + } + val exif: ExifWrapper = ImageUtils.getExifData(activity, bitmap, imageUri) + bitmap = try { + prepareBitmap(bitmap, imageUri, exif) + } catch (e: IOException) { + ret.put("error", UNABLE_TO_PROCESS_IMAGE) + return ret + } + // Compress the final image and prepare for output to client + val bitmapOutputStream = ByteArrayOutputStream() + bitmap.compress(Bitmap.CompressFormat.JPEG, settings.quality, bitmapOutputStream) + val newUri: Uri? = getTempImage(imageUri, bitmapOutputStream) + exif.copyExif(newUri?.path) + if (newUri != null) { + ret.put("format", "jpeg") + ret.put("exif", exif.toJson()) + ret.put("data", newUri.toString()) + ret.put("assetUrl", assetUrl(newUri)) + } else { + ret.put("error", UNABLE_TO_PROCESS_IMAGE) + } + return ret + } catch (err: OutOfMemoryError) { + ret.put("error", "Out of memory") + } catch (ex: FileNotFoundException) { + ret.put("error", "No such image found") + Logger.error(getLogTag(), "No such image found", ex) + } finally { + if (imageStream != null) { + try { + imageStream.close() + } catch (e: IOException) { + Logger.error(getLogTag(), UNABLE_TO_PROCESS_IMAGE, e) + } + } + } + return ret + } + + @ActivityCallback + private fun processEditedImage(invoke: Invoke, result: ActivityResult) { + isEdited = true + settings = getSettings(invoke) + if (result.resultCode == Activity.RESULT_CANCELED) { + // User cancelled the edit operation, if this file was picked from photos, + // process the original picked image, otherwise process it as a camera photo + if (imagePickedContentUri != null) { + processPickedImage(imagePickedContentUri!!, invoke) + } else { + processCameraImage(invoke, result) + } + } else { + processPickedImage(invoke, result) + } + } + + /** + * Save the modified image on the same path, + * or on a temporary location if it's a content url + * @param uri + * @param is + * @return + * @throws IOException + */ + @Throws(IOException::class) + private fun saveImage(uri: Uri, input: InputStream): Uri? { + var outFile = if (uri.scheme.equals("content")) { + getTempFile(uri) + } else { + uri.path?.let { File(it) } + } + try { + writePhoto(outFile!!, input) + } catch (ex: FileNotFoundException) { + // Some gallery apps return read only file url, create a temporary file for modifications + outFile = getTempFile(uri) + writePhoto(outFile, input) + } + return Uri.fromFile(outFile) + } + + @Throws(IOException::class) + private fun writePhoto(outFile: File, input: InputStream) { + val fos = FileOutputStream(outFile) + val buffer = ByteArray(1024) + var len: Int + while (input.read(buffer).also { len = it } != -1) { + fos.write(buffer, 0, len) + } + fos.close() + } + + private fun getTempFile(uri: Uri): File { + var filename: String = Uri.parse(Uri.decode(uri.toString())).lastPathSegment!! + if (!filename.contains(".jpg") && !filename.contains(".jpeg")) { + filename += "." + Date().time + ".jpeg" + } + val cacheDir: File = activity.getCacheDir() + return File(cacheDir, filename) + } + + /** + * After processing the image, return the final result back to the invokeer. + * @param invoke + * @param bitmap + * @param u + */ + private fun returnResult(invoke: Invoke, bitmap: Bitmap, u: Uri) { + val exif: ExifWrapper = ImageUtils.getExifData(activity, bitmap, u) + val preparedBitmap = try { + prepareBitmap(bitmap, u, exif) + } catch (e: IOException) { + invoke.reject(UNABLE_TO_PROCESS_IMAGE) + return + } + // Compress the final image and prepare for output to client + val bitmapOutputStream = ByteArrayOutputStream() + preparedBitmap.compress(Bitmap.CompressFormat.JPEG, settings.quality, bitmapOutputStream) + if (settings.isAllowEditing && !isEdited) { + editImage(invoke, u, bitmapOutputStream) + return + } + val saveToGallery: Boolean = + invoke.getBoolean("saveToGallery", CameraSettings.DEFAULT_SAVE_IMAGE_TO_GALLERY) + if (saveToGallery && (imageEditedFileSavePath != null || imageFileSavePath != null)) { + isSaved = true + try { + val fileToSavePath = + if (imageEditedFileSavePath != null) imageEditedFileSavePath!! else imageFileSavePath!! + val fileToSave = File(fileToSavePath) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + val resolver: ContentResolver = activity.contentResolver + val values = ContentValues() + values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileToSave.name) + values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg") + values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM) + val contentUri: Uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI + val uri: Uri = resolver.insert(contentUri, values) + ?: throw IOException("Failed to create new MediaStore record.") + val stream: OutputStream = resolver.openOutputStream(uri) + ?: throw IOException("Failed to open output stream.") + val inserted: Boolean = + preparedBitmap.compress(Bitmap.CompressFormat.JPEG, settings.quality, stream) + if (!inserted) { + isSaved = false + } + } else { + val inserted = MediaStore.Images.Media.insertImage( + activity.contentResolver, + fileToSavePath, + fileToSave.name, + "" + ) + if (inserted == null) { + isSaved = false + } + } + } catch (e: FileNotFoundException) { + isSaved = false + Logger.error(getLogTag(), IMAGE_GALLERY_SAVE_ERROR, e) + } catch (e: IOException) { + isSaved = false + Logger.error(getLogTag(), IMAGE_GALLERY_SAVE_ERROR, e) + } + } + if (settings.resultType === CameraResultType.BASE64) { + returnBase64(invoke, exif, bitmapOutputStream) + } else if (settings.resultType === CameraResultType.URI) { + returnFileURI(invoke, exif, bitmap, u, bitmapOutputStream) + } else if (settings.resultType === CameraResultType.DATAURL) { + returnDataUrl(invoke, exif, bitmapOutputStream) + } else { + invoke.reject(INVALID_RESULT_TYPE_ERROR) + } + // Result returned, clear stored paths and images + if (settings.resultType !== CameraResultType.URI) { + deleteImageFile() + } + imageFileSavePath = null + imageFileUri = null + imagePickedContentUri = null + imageEditedFileSavePath = null + } + + private fun deleteImageFile() { + if (imageFileSavePath != null && !settings.isSaveToGallery) { + val photoFile = File(imageFileSavePath!!) + if (photoFile.exists()) { + photoFile.delete() + } + } + } + + private fun returnFileURI( + invoke: Invoke, + exif: ExifWrapper, + bitmap: Bitmap, + u: Uri, + bitmapOutputStream: ByteArrayOutputStream + ) { + val newUri: Uri? = getTempImage(u, bitmapOutputStream) + exif.copyExif(newUri?.path) + if (newUri != null) { + val ret = JSObject() + ret.put("format", "jpeg") + ret.put("exif", exif.toJson()) + ret.put("data", newUri.toString()) + ret.put("assetUrl", assetUrl(newUri)) + ret.put("saved", isSaved) + invoke.resolve(ret) + } else { + invoke.reject(UNABLE_TO_PROCESS_IMAGE) + } + } + + private fun getTempImage(u: Uri, bitmapOutputStream: ByteArrayOutputStream): Uri? { + var bis: ByteArrayInputStream? = null + var newUri: Uri? = null + try { + bis = ByteArrayInputStream(bitmapOutputStream.toByteArray()) + newUri = saveImage(u, bis) + } catch (_: IOException) { + } finally { + if (bis != null) { + try { + bis.close() + } catch (e: IOException) { + Logger.error(getLogTag(), UNABLE_TO_PROCESS_IMAGE, e) + } + } + } + return newUri + } + + /** + * Apply our standard processing of the bitmap, returning a new one and + * recycling the old one in the process + * @param bitmap + * @param imageUri + * @param exif + * @return + */ + @Throws(IOException::class) + private fun prepareBitmap(bitmap: Bitmap, imageUri: Uri, exif: ExifWrapper): Bitmap { + var preparedBitmap: Bitmap = bitmap + if (settings.isShouldCorrectOrientation) { + val newBitmap: Bitmap = ImageUtils.correctOrientation(activity, preparedBitmap, imageUri, exif) + preparedBitmap = replaceBitmap(preparedBitmap, newBitmap) + } + if (settings.isShouldResize) { + val newBitmap: Bitmap = ImageUtils.resize(preparedBitmap, settings.width, settings.height) + preparedBitmap = replaceBitmap(preparedBitmap, newBitmap) + } + return preparedBitmap + } + + private fun replaceBitmap(bitmap: Bitmap, newBitmap: Bitmap): Bitmap { + if (bitmap !== newBitmap) { + bitmap.recycle() + } + return newBitmap + } + + private fun returnDataUrl( + invoke: Invoke, + exif: ExifWrapper, + bitmapOutputStream: ByteArrayOutputStream + ) { + val byteArray: ByteArray = bitmapOutputStream.toByteArray() + val encoded: String = Base64.encodeToString(byteArray, Base64.NO_WRAP) + val data = JSObject() + data.put("format", "jpeg") + data.put("data", "data:image/jpeg;base64,$encoded") + data.put("exif", exif.toJson()) + invoke.resolve(data) + } + + private fun returnBase64( + invoke: Invoke, + exif: ExifWrapper, + bitmapOutputStream: ByteArrayOutputStream + ) { + val byteArray: ByteArray = bitmapOutputStream.toByteArray() + val encoded: String = Base64.encodeToString(byteArray, Base64.NO_WRAP) + val data = JSObject() + data.put("format", "jpeg") + data.put("data", encoded) + data.put("exif", exif.toJson()) + invoke.resolve(data) + } + + @PluginMethod + override fun requestPermissions(invoke: Invoke) { + // If the camera permission is defined in the manifest, then we have to prompt the user + // or else we will get a security exception when trying to present the camera. If, however, + // it is not defined in the manifest then we don't need to prompt and it will just work. + if (isPermissionDeclared(CAMERA)) { + // just request normally + super.requestPermissions(invoke) + } else { + // the manifest does not define camera permissions, so we need to decide what to do + // first, extract the permissions being requested + val providedPerms = invoke.getArray("permissions", JSArray()) + var permsList: List? = null + try { + permsList = providedPerms.toList() + } catch (_: JSONException) { + } + if (permsList != null && permsList.size == 1 && permsList.contains(CAMERA)) { + // the only thing being asked for was the camera so we can just return the current state + checkPermissions(invoke) + } else { + // we need to ask about photos so request storage permissions + requestPermissionForAlias(PHOTOS, invoke, "checkPermissions") + } + } + } + + override fun getPermissionStates(): Map { + val permissionStates = super.getPermissionStates() as MutableMap + + // If Camera is not in the manifest and therefore not required, say the permission is granted + if (!isPermissionDeclared(CAMERA)) { + permissionStates[CAMERA] = PermissionState.GRANTED + } + return permissionStates + } + + private fun editImage(invoke: Invoke, uri: Uri, bitmapOutputStream: ByteArrayOutputStream) { + try { + val tempImage = getTempImage(uri, bitmapOutputStream) + val editIntent = createEditIntent(tempImage) + if (editIntent != null) { + startActivityForResult(invoke, editIntent, "processEditedImage") + } else { + invoke.reject(IMAGE_EDIT_ERROR) + } + } catch (ex: Exception) { + invoke.reject(IMAGE_EDIT_ERROR, ex) + } + } + + private fun createEditIntent(origPhotoUri: Uri?): Intent? { + return try { + val editFile = origPhotoUri?.path?.let { File(it) } + val editUri: Uri = FileProvider.getUriForFile( + activity, + activity.packageName + ".fileprovider", + editFile!! + ) + val editIntent = Intent(Intent.ACTION_EDIT) + editIntent.setDataAndType(editUri, "image/*") + imageEditedFileSavePath = editFile.absolutePath + val flags: Int = + Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION + editIntent.addFlags(flags) + editIntent.putExtra(MediaStore.EXTRA_OUTPUT, editUri) + val resInfoList: List = activity + .packageManager + .queryIntentActivities(editIntent, PackageManager.MATCH_DEFAULT_ONLY) + for (resolveInfo in resInfoList) { + val packageName: String = resolveInfo.activityInfo.packageName + activity.grantUriPermission(packageName, editUri, flags) + } + editIntent + } catch (ex: Exception) { + null + } + } + + /*protected fun saveInstanceState(): Bundle? { + val bundle: Bundle = super.saveInstanceState() + if (bundle != null) { + bundle.putString("cameraImageFileSavePath", imageFileSavePath) + } + return bundle + } + + protected fun restoreState(state: Bundle) { + val storedImageFileSavePath: String = state.getString("cameraImageFileSavePath") + if (storedImageFileSavePath != null) { + imageFileSavePath = storedImageFileSavePath + } + }*/ +} diff --git a/plugins/camera/android/src/main/java/app/tauri/camera/CameraUtils.kt b/plugins/camera/android/src/main/java/app/tauri/camera/CameraUtils.kt new file mode 100644 index 00000000..76972e3d --- /dev/null +++ b/plugins/camera/android/src/main/java/app/tauri/camera/CameraUtils.kt @@ -0,0 +1,39 @@ +package app.tauri.camera + +import android.app.Activity +import android.net.Uri +import android.os.Environment +import androidx.core.content.FileProvider +import app.tauri.Logger +import java.io.File +import java.io.IOException +import java.text.SimpleDateFormat +import java.util.* + +object CameraUtils { + @Throws(IOException::class) + fun createImageFileUri(activity: Activity, appId: String): Uri { + val photoFile = createImageFile(activity) + return FileProvider.getUriForFile( + activity, + "$appId.fileprovider", photoFile + ) + } + + @Throws(IOException::class) + fun createImageFile(activity: Activity): File { + // Create an image file name + val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date()) + val imageFileName = "JPEG_" + timeStamp + "_" + val storageDir = + activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES) + return File.createTempFile( + imageFileName, /* prefix */ + ".jpg", /* suffix */ + storageDir /* directory */ + ) + } + + internal val logTag: String + internal get() = Logger.tags("CameraUtils") +} diff --git a/plugins/camera/android/src/main/java/app/tauri/camera/ExifWrapper.kt b/plugins/camera/android/src/main/java/app/tauri/camera/ExifWrapper.kt new file mode 100644 index 00000000..1ef69ef4 --- /dev/null +++ b/plugins/camera/android/src/main/java/app/tauri/camera/ExifWrapper.kt @@ -0,0 +1,198 @@ +package app.tauri.camera + +import androidx.exifinterface.media.ExifInterface.* +import androidx.exifinterface.media.ExifInterface +import app.tauri.plugin.JSObject + +class ExifWrapper(private val exif: ExifInterface?) { + private val attributes = arrayOf( + TAG_APERTURE_VALUE, + TAG_ARTIST, + TAG_BITS_PER_SAMPLE, + TAG_BODY_SERIAL_NUMBER, + TAG_BRIGHTNESS_VALUE, + TAG_CAMERA_OWNER_NAME, + TAG_CFA_PATTERN, + TAG_COLOR_SPACE, + TAG_COMPONENTS_CONFIGURATION, + TAG_COMPRESSED_BITS_PER_PIXEL, + TAG_COMPRESSION, + TAG_CONTRAST, + TAG_COPYRIGHT, + TAG_CUSTOM_RENDERED, + TAG_DATETIME, + TAG_DATETIME_DIGITIZED, + TAG_DATETIME_ORIGINAL, + TAG_DEFAULT_CROP_SIZE, + TAG_DEVICE_SETTING_DESCRIPTION, + TAG_DIGITAL_ZOOM_RATIO, + TAG_DNG_VERSION, + TAG_EXIF_VERSION, + TAG_EXPOSURE_BIAS_VALUE, + TAG_EXPOSURE_INDEX, + TAG_EXPOSURE_MODE, + TAG_EXPOSURE_PROGRAM, + TAG_EXPOSURE_TIME, + TAG_FILE_SOURCE, + TAG_FLASH, + TAG_FLASHPIX_VERSION, + TAG_FLASH_ENERGY, + TAG_FOCAL_LENGTH, + TAG_FOCAL_LENGTH_IN_35MM_FILM, + TAG_FOCAL_PLANE_RESOLUTION_UNIT, + TAG_FOCAL_PLANE_X_RESOLUTION, + TAG_FOCAL_PLANE_Y_RESOLUTION, + TAG_F_NUMBER, + TAG_GAIN_CONTROL, + TAG_GAMMA, + TAG_GPS_ALTITUDE, + TAG_GPS_ALTITUDE_REF, + TAG_GPS_AREA_INFORMATION, + TAG_GPS_DATESTAMP, + TAG_GPS_DEST_BEARING, + TAG_GPS_DEST_BEARING_REF, + TAG_GPS_DEST_DISTANCE, + TAG_GPS_DEST_DISTANCE_REF, + TAG_GPS_DEST_LATITUDE, + TAG_GPS_DEST_LATITUDE_REF, + TAG_GPS_DEST_LONGITUDE, + TAG_GPS_DEST_LONGITUDE_REF, + TAG_GPS_DIFFERENTIAL, + TAG_GPS_DOP, + TAG_GPS_H_POSITIONING_ERROR, + TAG_GPS_IMG_DIRECTION, + TAG_GPS_IMG_DIRECTION_REF, + TAG_GPS_LATITUDE, + TAG_GPS_LATITUDE_REF, + TAG_GPS_LONGITUDE, + TAG_GPS_LONGITUDE_REF, + TAG_GPS_MAP_DATUM, + TAG_GPS_MEASURE_MODE, + TAG_GPS_PROCESSING_METHOD, + TAG_GPS_SATELLITES, + TAG_GPS_SPEED, + TAG_GPS_SPEED_REF, + TAG_GPS_STATUS, + TAG_GPS_TIMESTAMP, + TAG_GPS_TRACK, + TAG_GPS_TRACK_REF, + TAG_GPS_VERSION_ID, + TAG_IMAGE_DESCRIPTION, + TAG_IMAGE_LENGTH, + TAG_IMAGE_UNIQUE_ID, + TAG_IMAGE_WIDTH, + TAG_INTEROPERABILITY_INDEX, + TAG_ISO_SPEED, + TAG_ISO_SPEED_LATITUDE_YYY, + TAG_ISO_SPEED_LATITUDE_ZZZ, + TAG_JPEG_INTERCHANGE_FORMAT, + TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, + TAG_LENS_MAKE, + TAG_LENS_MODEL, + TAG_LENS_SERIAL_NUMBER, + TAG_LENS_SPECIFICATION, + TAG_LIGHT_SOURCE, + TAG_MAKE, + TAG_MAKER_NOTE, + TAG_MAX_APERTURE_VALUE, + TAG_METERING_MODE, + TAG_MODEL, + TAG_NEW_SUBFILE_TYPE, + TAG_OECF, + TAG_OFFSET_TIME, + TAG_OFFSET_TIME_DIGITIZED, + TAG_OFFSET_TIME_ORIGINAL, + TAG_ORF_ASPECT_FRAME, + TAG_ORF_PREVIEW_IMAGE_LENGTH, + TAG_ORF_PREVIEW_IMAGE_START, + TAG_ORF_THUMBNAIL_IMAGE, + TAG_ORIENTATION, + TAG_PHOTOGRAPHIC_SENSITIVITY, + TAG_PHOTOMETRIC_INTERPRETATION, + TAG_PIXEL_X_DIMENSION, + TAG_PIXEL_Y_DIMENSION, + TAG_PLANAR_CONFIGURATION, + TAG_PRIMARY_CHROMATICITIES, + TAG_RECOMMENDED_EXPOSURE_INDEX, + TAG_REFERENCE_BLACK_WHITE, + TAG_RELATED_SOUND_FILE, + TAG_RESOLUTION_UNIT, + TAG_ROWS_PER_STRIP, + TAG_RW2_ISO, + TAG_RW2_JPG_FROM_RAW, + TAG_RW2_SENSOR_BOTTOM_BORDER, + TAG_RW2_SENSOR_LEFT_BORDER, + TAG_RW2_SENSOR_RIGHT_BORDER, + TAG_RW2_SENSOR_TOP_BORDER, + TAG_SAMPLES_PER_PIXEL, + TAG_SATURATION, + TAG_SCENE_CAPTURE_TYPE, + TAG_SCENE_TYPE, + TAG_SENSING_METHOD, + TAG_SENSITIVITY_TYPE, + TAG_SHARPNESS, + TAG_SHUTTER_SPEED_VALUE, + TAG_SOFTWARE, + TAG_SPATIAL_FREQUENCY_RESPONSE, + TAG_SPECTRAL_SENSITIVITY, + TAG_STANDARD_OUTPUT_SENSITIVITY, + TAG_STRIP_BYTE_COUNTS, + TAG_STRIP_OFFSETS, + TAG_SUBFILE_TYPE, + TAG_SUBJECT_AREA, + TAG_SUBJECT_DISTANCE, + TAG_SUBJECT_DISTANCE_RANGE, + TAG_SUBJECT_LOCATION, + TAG_SUBSEC_TIME, + TAG_SUBSEC_TIME_DIGITIZED, + TAG_SUBSEC_TIME_ORIGINAL, + TAG_THUMBNAIL_IMAGE_LENGTH, + TAG_THUMBNAIL_IMAGE_WIDTH, + TAG_TRANSFER_FUNCTION, + TAG_USER_COMMENT, + TAG_WHITE_BALANCE, + TAG_WHITE_POINT, + TAG_XMP, + TAG_X_RESOLUTION, + TAG_Y_CB_CR_COEFFICIENTS, + TAG_Y_CB_CR_POSITIONING, + TAG_Y_CB_CR_SUB_SAMPLING, + TAG_Y_RESOLUTION + ) + + fun toJson(): JSObject { + val ret = JSObject() + if (exif == null) { + return ret + } + for (i in attributes.indices) { + p(ret, attributes[i]) + } + return ret + } + + fun p(o: JSObject, tag: String?) { + val value = exif!!.getAttribute(tag!!) + o.put(tag, value) + } + + fun copyExif(destFile: String?) { + try { + val destExif = ExifInterface( + destFile!! + ) + for (i in attributes.indices) { + val value = exif!!.getAttribute(attributes[i]) + if (value != null) { + destExif.setAttribute(attributes[i], value) + } + } + destExif.saveAttributes() + } catch (_: java.lang.Exception) { + } + } + + fun resetOrientation() { + exif!!.resetOrientation() + } +} diff --git a/plugins/camera/android/src/main/java/app/tauri/camera/ImageUtils.kt b/plugins/camera/android/src/main/java/app/tauri/camera/ImageUtils.kt new file mode 100644 index 00000000..b6369cf2 --- /dev/null +++ b/plugins/camera/android/src/main/java/app/tauri/camera/ImageUtils.kt @@ -0,0 +1,126 @@ +package app.tauri.camera + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Matrix +import android.net.Uri +import androidx.exifinterface.media.ExifInterface +import app.tauri.Logger +import java.io.IOException +import java.io.InputStream +import java.lang.Integer.min +import kotlin.math.roundToInt + +object ImageUtils { + /** + * Resize an image to the given max width and max height. Constraint can be put + * on one dimension, or both. Resize will always preserve aspect ratio. + * @param bitmap + * @param desiredMaxWidth + * @param desiredMaxHeight + * @return a new, scaled Bitmap + */ + fun resize(bitmap: Bitmap, desiredMaxWidth: Int, desiredMaxHeight: Int): Bitmap { + return resizePreservingAspectRatio(bitmap, desiredMaxWidth, desiredMaxHeight) + } + + /** + * Resize an image to the given max width and max height. Constraint can be put + * on one dimension, or both. Resize will always preserve aspect ratio. + * @param bitmap + * @param desiredMaxWidth + * @param desiredMaxHeight + * @return a new, scaled Bitmap + */ + private fun resizePreservingAspectRatio( + bitmap: Bitmap, + desiredMaxWidth: Int, + desiredMaxHeight: Int + ): Bitmap { + val width = bitmap.width + val height = bitmap.height + + // 0 is treated as 'no restriction' + val maxHeight = if (desiredMaxHeight == 0) height else desiredMaxHeight + val maxWidth = if (desiredMaxWidth == 0) width else desiredMaxWidth + + // resize with preserved aspect ratio + var newWidth = min(width, maxWidth).toFloat() + var newHeight = height * newWidth / width + if (newHeight > maxHeight) { + newWidth = (width * maxHeight / height).toFloat() + newHeight = maxHeight.toFloat() + } + return Bitmap.createScaledBitmap(bitmap, newWidth.roundToInt(), newHeight.roundToInt(), false) + } + + /** + * Transform an image with the given matrix + * @param bitmap + * @param matrix + * @return + */ + private fun transform(bitmap: Bitmap, matrix: Matrix): Bitmap { + return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) + } + + /** + * Correct the orientation of an image by reading its exif information and rotating + * the appropriate amount for portrait mode + * @param bitmap + * @param imageUri + * @param exif + * @return + */ + @Throws(IOException::class) + fun correctOrientation(c: Context, bitmap: Bitmap, imageUri: Uri, exif: ExifWrapper): Bitmap { + val orientation = getOrientation(c, imageUri) + return if (orientation != 0) { + val matrix = Matrix() + matrix.postRotate(orientation.toFloat()) + exif.resetOrientation() + transform(bitmap, matrix) + } else { + bitmap + } + } + + @Throws(IOException::class) + private fun getOrientation(c: Context, imageUri: Uri): Int { + var result = 0 + c.getContentResolver().openInputStream(imageUri).use { iStream -> + val exifInterface = ExifInterface(iStream!!) + val orientation: Int = exifInterface.getAttributeInt( + ExifInterface.TAG_ORIENTATION, + ExifInterface.ORIENTATION_NORMAL + ) + if (orientation == ExifInterface.ORIENTATION_ROTATE_90) { + result = 90 + } else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) { + result = 180 + } else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) { + result = 270 + } + } + return result + } + + fun getExifData(c: Context, bitmap: Bitmap?, imageUri: Uri): ExifWrapper { + var stream: InputStream? = null + try { + stream = c.getContentResolver().openInputStream(imageUri) + val exifInterface = ExifInterface(stream!!) + return ExifWrapper(exifInterface) + } catch (ex: IOException) { + Logger.error("Error loading exif data from image", ex) + } finally { + if (stream != null) { + try { + stream.close() + } catch (ignored: IOException) { + } + } + } + return ExifWrapper(null) + } +} diff --git a/plugins/camera/android/src/test/java/app/tauri/camera/ExampleUnitTest.kt b/plugins/camera/android/src/test/java/app/tauri/camera/ExampleUnitTest.kt new file mode 100644 index 00000000..7547c0b3 --- /dev/null +++ b/plugins/camera/android/src/test/java/app/tauri/camera/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package app.tauri.camera + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} diff --git a/plugins/camera/build.rs b/plugins/camera/build.rs new file mode 100644 index 00000000..6a5bcf5d --- /dev/null +++ b/plugins/camera/build.rs @@ -0,0 +1,16 @@ +// Copyright 2019-2022 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use std::process::exit; + +fn main() { + if let Err(error) = tauri_build::mobile::PluginBuilder::new() + .android_path("android") + .ios_path("ios") + .run() + { + println!("{error:#}"); + exit(1); + } +} diff --git a/plugins/camera/examples/tauri-app/.gitignore b/plugins/camera/examples/tauri-app/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/plugins/camera/examples/tauri-app/.gitignore @@ -0,0 +1,24 @@ +# 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 +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/plugins/camera/examples/tauri-app/README.md b/plugins/camera/examples/tauri-app/README.md new file mode 100644 index 00000000..21592436 --- /dev/null +++ b/plugins/camera/examples/tauri-app/README.md @@ -0,0 +1 @@ +# Camera Example diff --git a/plugins/camera/examples/tauri-app/index.html b/plugins/camera/examples/tauri-app/index.html new file mode 100644 index 00000000..fad1c5d9 --- /dev/null +++ b/plugins/camera/examples/tauri-app/index.html @@ -0,0 +1,14 @@ + + + + + + + Tauri + Svelte + + + +
+ + + diff --git a/plugins/camera/examples/tauri-app/jsconfig.json b/plugins/camera/examples/tauri-app/jsconfig.json new file mode 100644 index 00000000..ee5e92f2 --- /dev/null +++ b/plugins/camera/examples/tauri-app/jsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "moduleResolution": "Node", + "target": "ESNext", + "module": "ESNext", + /** + * svelte-preprocess cannot figure out whether you have + * a value or a type, so tell TypeScript to enforce using + * `import type` instead of `import` for Types. + */ + "importsNotUsedAsValues": "error", + "isolatedModules": true, + "resolveJsonModule": true, + /** + * To have warnings / errors of the Svelte compiler at the + * correct position, enable source maps by default. + */ + "sourceMap": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "baseUrl": ".", + /** + * Typecheck JS in `.svelte` and `.js` files by default. + * Disable this if you'd like to use dynamic types. + */ + "checkJs": true + }, + /** + * Use global.d.ts instead of compilerOptions.types + * to avoid limiting type declarations. + */ + "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] +} diff --git a/plugins/camera/examples/tauri-app/package.json b/plugins/camera/examples/tauri-app/package.json new file mode 100644 index 00000000..a6cc8e02 --- /dev/null +++ b/plugins/camera/examples/tauri-app/package.json @@ -0,0 +1,23 @@ +{ + "name": "tauri-app", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "tauri": "tauri" + }, + "dependencies": { + "@tauri-apps/api": "^1.1.0", + "tauri-plugin-camera-api": "link:../../" + }, + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "^1.0.1", + "@tauri-apps/cli": "^2.0.0-alpha.0", + "internal-ip": "^7.0.0", + "svelte": "^3.49.0", + "vite": "^3.0.2" + } +} diff --git a/plugins/camera/examples/tauri-app/src-tauri/.gitignore b/plugins/camera/examples/tauri-app/src-tauri/.gitignore new file mode 100644 index 00000000..c055de2d --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/.gitignore @@ -0,0 +1,5 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +.cargo diff --git a/plugins/camera/examples/tauri-app/src-tauri/Cargo.lock b/plugins/camera/examples/tauri-app/src-tauri/Cargo.lock new file mode 100644 index 00000000..13a0a9d5 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/Cargo.lock @@ -0,0 +1,3515 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "anyhow" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" + +[[package]] +name = "app" +version = "0.0.0" +dependencies = [ + "serde", + "serde_json", + "tauri", + "tauri-build", + "tauri-plugin-camera", +] + +[[package]] +name = "atk" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39991bc421ddf72f70159011b323ff49b0f783cc676a7287c59453da2e2531cf" +dependencies = [ + "atk-sys", + "bitflags", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ad703eb64dc058024f0e57ccfa069e15a413b98dbd50a1a950e743b7f11148" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "attohttpc" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d9a9bf8b79a749ee0b911b91b671cc2b6c670bdbc7e3dfd537576ddc94bb2a2" +dependencies = [ + "flate2", + "http", + "log", + "serde", + "serde_json", + "serde_urlencoded", + "url", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brotli" +version = "3.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bstr" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f0778972c64420fdedc63f09919c8a88bda7b25135357fd25a5d9f3257e832" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "bytemuck" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cairo-rs" +version = "0.16.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3125b15ec28b84c238f6f476c6034016a5f6cc0221cb514ca46c532139fc97d" +dependencies = [ + "bitflags", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c48f4af05fabdcfa9658178e1326efa061853f040ce7d72e33af6885196f421" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "cargo_toml" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfbc36312494041e2cdd5f06697b7e89d4b76f42773a0b5556ac290ff22acc2" +dependencies = [ + "serde", + "toml", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa" +dependencies = [ + "smallvec", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cocoa" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" +dependencies = [ + "bitflags", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" +dependencies = [ + "bitflags", + "block", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +dependencies = [ + "bitflags", + "core-foundation", + "foreign-types", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa 0.4.8", + "matches", + "phf 0.8.0", + "proc-macro2", + "quote", + "smallvec", + "syn", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfae75de57f2b2e85e8768c3ea840fd159c8f33e2b6522c7835b7abac81be16e" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "ctor" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "libc", + "libdbus-sys", + "winapi", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version 0.4.0", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dtoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" + +[[package]] +name = "dtoa-short" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03329ae10e79ede66c9ce4dc930aa8599043b0743008548680f25b91502d6" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" + +[[package]] +name = "embed_plist" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "field-offset" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92" +dependencies = [ + "memoffset", + "rustc_version 0.3.3", +] + +[[package]] +name = "filetime" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys 0.45.0", +] + +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures-channel" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" + +[[package]] +name = "futures-executor" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" + +[[package]] +name = "futures-macro" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-task" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" + +[[package]] +name = "futures-util" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +dependencies = [ + "futures-core", + "futures-macro", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9cb33da481c6c040404a11f8212d193889e9b435db2c14fd86987f630d3ce1" +dependencies = [ + "bitflags", + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.16.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3578c60dee9d029ad86593ed88cb40f35c1b83360e12498d055022385dd9a05" +dependencies = [ + "bitflags", + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3092cf797a5f1210479ea38070d9ae8a5b8e9f8f1be9f32f4643c529c7d70016" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76354f97a913e55b984759a997b693aa7dc71068c9e98bcce51aa167a0a5c5a" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4511710212ed3020b61a8622a37aa6f0dd2a84516575da92e9b96928dcbe83ba" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11-sys" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa2bf8b5b8c414bc5d05e48b271896d0fd3ddb57464a3108438082da61de6af" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generator" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266041a359dfa931b370ef684cceb84b166beb14f7f0421f4a6a3d0c446d12e" +dependencies = [ + "cc", + "libc", + "log", + "rustversion", + "windows 0.39.0", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gio" +version = "0.16.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a1c84b4534a290a29160ef5c6eff2a9c95833111472e824fc5cb78b513dd092" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9b693b8e39d042a95547fc258a7b07349b1f0b48f4b2fa3108ba3c51c0b5229" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.16.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd4df61a866ed7259d6189b8bcb1464989a77f1d85d25d002279bbe9dd38b2f" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e084807350b01348b6d9dbabb724d1a0bb987f47a2c85de200e98e12e30733bf" +dependencies = [ + "anyhow", + "heck 0.4.1", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61a4f46316d06bfa33a7ac22df6f0524c8be58e3db2d9ca99ccb1f357b62a65" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "globset" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "gobject-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3520bb9c07ae2a12c7f2fbb24d4efc11231c8146a86956413fb1a79bb760a0f1" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4d3507d43908c866c805f74c9dd593c0ce7ba5c38e576e41846639cdcd4bee6" +dependencies = [ + "atk", + "bitflags", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "once_cell", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b5f8946685d5fe44497007786600c2f368ff6b1e61a16251c89f72a97520a3" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cfd6557b1018b773e43c8de9d0d13581d6b36190d0501916cbec4731db5ccff" +dependencies = [ + "anyhow", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "html5ever" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5c13fb08e5d4dfc151ee5e88bae63f7773d61852f3bdc73c9f4b9e1bde03148" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.5", +] + +[[package]] +name = "http-range" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" + +[[package]] +name = "ico" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3804960be0bb5e4edb1e1ad67afd321a9ecfd875c3e65c099468fd2717d7cae" +dependencies = [ + "byteorder", + "png", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "ignore" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" +dependencies = [ + "crossbeam-utils", + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + +[[package]] +name = "image" +version = "0.24.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-rational", + "num-traits", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "infer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a898e4b7951673fce96614ce5751d13c40fc5674bc2d759288e46c3ab62598b3" +dependencies = [ + "cfb", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] +name = "javascriptcore-rs" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "110b9902c80c12bf113c432d0b71c7a94490b294a8234f326fd0abca2fac0b00" +dependencies = [ + "bitflags", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98a216519a52cd941a733a0ad3f1023cfdb1cd47f3955e8e863ed56f558f916c" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "json-patch" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e712e62827c382a77b87f590532febb1f8b2fdbc3eefa1ee37fe7281687075ef" +dependencies = [ + "serde", + "serde_json", + "thiserror", + "treediff", +] + +[[package]] +name = "kuchiki" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ea8e9c6e031377cff82ee3001dc8026cdf431ed4e2e6b51f98ab8c73484a358" +dependencies = [ + "cssparser", + "html5ever", + "matches", + "selectors", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "libdbus-sys" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f8d7ae751e1cb825c840ae5e682f59b098cdfd213c350ac268b61449a5f58a0" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "line-wrap" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" +dependencies = [ + "safemem", +] + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "loom" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "mac-notification-sys" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e72d50edb17756489e79d52eb146927bec8eba9dd48faadf9ef08bca3791ad5" +dependencies = [ + "cc", + "dirs-next", + "objc-foundation", + "objc_id", + "time", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "markup5ever" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd" +dependencies = [ + "log", + "phf 0.8.0", + "phf_codegen", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "ndk" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" +dependencies = [ + "bitflags", + "jni-sys", + "ndk-sys", + "num_enum", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nom8" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" +dependencies = [ + "memchr", +] + +[[package]] +name = "notify-rust" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "260208751689b605138bb55ab6af43ad75f628619a7e0b818d63bf6629e59467" +dependencies = [ + "dbus", + "mac-notification-sys", + "tauri-winrt-notification", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0072973714303aa6e3631c7e8e777970cf4bdd25dc4932e41031027b8bcc4e" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0629cbd6b897944899b1f10496d9c4a7ac5878d45fd61bc22e9e79bfbbc29597" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "open" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8" +dependencies = [ + "pathdiff", + "windows-sys 0.42.0", +] + +[[package]] +name = "os_info" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5209b2162b2c140df493a93689e04f8deab3a67634f5bc7a553c0a98e5b8d399" +dependencies = [ + "log", + "serde", + "winapi", +] + +[[package]] +name = "os_pipe" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53dbb20faf34b16087a931834cba2d7a73cc74af2b7ef345a4c8324e2409a12" +dependencies = [ + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pango" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdff66b271861037b89d028656184059e03b0b6ccb36003820be19f7200b1e94" +dependencies = [ + "bitflags", + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e134909a9a293e04d2cc31928aa95679c5e4df954d0b85483159bd20d8f047f" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pest" +version = "2.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028accff104c4e513bad663bbcd2ad7cfd5304144404c31ed0a77ac103d00660" +dependencies = [ + "thiserror", + "ucd-trie", +] + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_macros 0.10.0", + "phf_shared 0.10.0", + "proc-macro-hack", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "plist" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5329b8f106a176ab0dce4aae5da86bfcb139bb74fb00882859e03745011f3635" +dependencies = [ + "base64 0.13.1", + "indexmap", + "line-wrap", + "quick-xml 0.26.0", + "serde", + "time", +] + +[[package]] +name = "png" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" +dependencies = [ + "bitflags", + "crc32fast", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "proc-macro-crate" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11bafc859c6815fbaffbbbf4229ecb767ac913fecb27f9ad4343662e9ef099ea" +dependencies = [ + "memchr", +] + +[[package]] +name = "quick-xml" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.8", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a" +dependencies = [ + "cty", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom 0.2.8", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "rfd" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fe3bae427b011620cf4436d5dd7405c1a86dce3dfcdc0cda12b41fd31569ac3" +dependencies = [ + "block", + "dispatch", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc", + "objc-foundation", + "objc_id", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows 0.44.0", +] + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.16", +] + +[[package]] +name = "rustversion" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" + +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "selectors" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +dependencies = [ + "bitflags", + "cssparser", + "derive_more", + "fxhash", + "log", + "matches", + "phf 0.8.0", + "phf_codegen", + "precomputed-hash", + "servo_arc", + "smallvec", + "thin-slice", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +dependencies = [ + "serde", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +dependencies = [ + "itoa 1.0.5", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa 1.0.5", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +dependencies = [ + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serialize-to-javascript" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9823f2d3b6a81d98228151fdeaf848206a7855a7a042bbf9bf870449a66cafb" +dependencies = [ + "serde", + "serde_json", + "serialize-to-javascript-impl", +] + +[[package]] +name = "serialize-to-javascript-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "servo_arc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shared_child" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0d94659ad3c2137fef23ae75b03d5241d633f8acded53d672decfa0e6e0caef" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "soup3" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82bc46048125fefd69d30b32b9d263d6556c9ffe82a7a7df181a86d912da5616" +dependencies = [ + "bitflags", + "futures-channel", + "gio", + "glib", + "libc", + "once_cell", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "014bbeb1c4cdb30739dc181e8d98b7908f124d9555843afa89b5570aaf4ec62b" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "state" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b" +dependencies = [ + "loom", +] + +[[package]] +name = "string_cache" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213494b7a2b503146286049378ce02b482200519accc31872ee8be91fa820a08" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strum" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7ac893c7d471c8a21f31cfe213ec4f6d9afeed25537c772e08ef3f005f8729e" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339f799d8b549e3744c7ac7feb216383e4005d94bdb22561b3ab8f3b808ae9fb" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "swift-rs" +version = "0.3.0" +source = "git+https://github.com/Brendonovich/swift-rs?rev=eb6de914ad57501da5019154d476d45660559999#eb6de914ad57501da5019154d476d45660559999" +dependencies = [ + "base64 0.13.1", + "serde", + "serde_json", +] + +[[package]] +name = "syn" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-deps" +version = "6.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff" +dependencies = [ + "cfg-expr", + "heck 0.4.1", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d16138f5d521fcde40580e1a34df784b063dd9ac05c7cbe344bf01f02a23be" +dependencies = [ + "bitflags", + "cairo-rs", + "cc", + "cocoa", + "core-foundation", + "core-graphics", + "crossbeam-channel", + "dispatch", + "gdk", + "gdk-pixbuf", + "gdk-sys", + "gdkwayland-sys", + "gdkx11-sys", + "gio", + "glib", + "glib-sys", + "gtk", + "image", + "instant", + "jni", + "lazy_static", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "once_cell", + "parking_lot", + "png", + "raw-window-handle", + "scopeguard", + "serde", + "tao-macros", + "unicode-segmentation", + "uuid", + "windows 0.44.0", + "windows-implement", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b27a4bcc5eb524658234589bdffc7e7bfb996dbae6ce9393bfd39cb4159b445" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tar" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tauri" +version = "2.0.0-alpha.3" +source = "git+https://github.com/tauri-apps/tauri?branch=next#dffd8eb5a87558afc2fca21870643ff7dad9eb04" +dependencies = [ + "anyhow", + "attohttpc", + "cocoa", + "dirs-next", + "embed_plist", + "encoding_rs", + "flate2", + "futures-util", + "glib", + "glob", + "gtk", + "heck 0.4.1", + "http", + "ignore", + "jni", + "libc", + "log", + "notify-rust", + "objc", + "once_cell", + "open", + "os_info", + "os_pipe", + "percent-encoding", + "rand 0.8.5", + "raw-window-handle", + "regex", + "rfd", + "semver 1.0.16", + "serde", + "serde_json", + "serde_repr", + "serialize-to-javascript", + "shared_child", + "state", + "swift-rs", + "tar", + "tauri-build", + "tauri-macros", + "tauri-runtime", + "tauri-runtime-wry", + "tauri-utils", + "tempfile", + "thiserror", + "tokio", + "url", + "uuid", + "webkit2gtk", + "webview2-com", + "windows 0.44.0", +] + +[[package]] +name = "tauri-build" +version = "2.0.0-alpha.1" +source = "git+https://github.com/tauri-apps/tauri?branch=next#dffd8eb5a87558afc2fca21870643ff7dad9eb04" +dependencies = [ + "anyhow", + "cargo_toml", + "filetime", + "heck 0.4.1", + "json-patch", + "semver 1.0.16", + "serde", + "serde_json", + "swift-rs", + "tauri-utils", + "tauri-winres", + "walkdir", +] + +[[package]] +name = "tauri-codegen" +version = "2.0.0-alpha.1" +source = "git+https://github.com/tauri-apps/tauri?branch=next#dffd8eb5a87558afc2fca21870643ff7dad9eb04" +dependencies = [ + "base64 0.21.0", + "brotli", + "ico", + "json-patch", + "plist", + "png", + "proc-macro2", + "quote", + "regex", + "semver 1.0.16", + "serde", + "serde_json", + "sha2", + "tauri-utils", + "thiserror", + "time", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-macros" +version = "2.0.0-alpha.1" +source = "git+https://github.com/tauri-apps/tauri?branch=next#dffd8eb5a87558afc2fca21870643ff7dad9eb04" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn", + "tauri-codegen", + "tauri-utils", +] + +[[package]] +name = "tauri-plugin-camera" +version = "0.0.0" +dependencies = [ + "serde", + "serde_json", + "tauri", + "tauri-build", + "thiserror", +] + +[[package]] +name = "tauri-runtime" +version = "0.13.0-alpha.1" +source = "git+https://github.com/tauri-apps/tauri?branch=next#dffd8eb5a87558afc2fca21870643ff7dad9eb04" +dependencies = [ + "gtk", + "http", + "http-range", + "jni", + "rand 0.8.5", + "raw-window-handle", + "serde", + "serde_json", + "tauri-utils", + "thiserror", + "url", + "uuid", + "webview2-com", + "windows 0.44.0", +] + +[[package]] +name = "tauri-runtime-wry" +version = "0.13.0-alpha.1" +source = "git+https://github.com/tauri-apps/tauri?branch=next#dffd8eb5a87558afc2fca21870643ff7dad9eb04" +dependencies = [ + "cocoa", + "gtk", + "jni", + "percent-encoding", + "rand 0.8.5", + "raw-window-handle", + "tauri-runtime", + "tauri-utils", + "uuid", + "webkit2gtk", + "webview2-com", + "windows 0.44.0", + "wry", +] + +[[package]] +name = "tauri-utils" +version = "2.0.0-alpha.1" +source = "git+https://github.com/tauri-apps/tauri?branch=next#dffd8eb5a87558afc2fca21870643ff7dad9eb04" +dependencies = [ + "brotli", + "ctor", + "glob", + "heck 0.4.1", + "html5ever", + "infer", + "json-patch", + "kuchiki", + "memchr", + "phf 0.10.1", + "proc-macro2", + "quote", + "semver 1.0.16", + "serde", + "serde_json", + "serde_with", + "thiserror", + "url", + "walkdir", + "windows 0.44.0", +] + +[[package]] +name = "tauri-winres" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b7a78dc04f75fb5ab815e66ac561c81e92a968a40f29e7c21afd152d694fad8" +dependencies = [ + "toml", + "version_check", +] + +[[package]] +name = "tauri-winrt-notification" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c58de036c4d2e20717024de2a3c4bf56c301f07b21bc8ef9b57189fce06f1f3b" +dependencies = [ + "quick-xml 0.23.1", + "strum", + "windows 0.39.0", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "thin-slice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" + +[[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53250a3b3fed8ff8fd988587d8925d26a83ac3845d9e03b220b37f34c2b8d6c2" +dependencies = [ + "itoa 1.0.5", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a460aeb8de6dcb0f381e1ee05f1cd56fcf5a5f6eb8187ff3d8f0b11078d38b7c" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +dependencies = [ + "autocfg", + "bytes", + "memchr", + "num_cpus", + "pin-project-lite", + "windows-sys 0.42.0", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" + +[[package]] +name = "toml_edit" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b" +dependencies = [ + "indexmap", + "nom8", + "toml_datetime", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "treediff" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52984d277bdf2a751072b5df30ec0377febdb02f7696d64c2d7d54630bac4303" +dependencies = [ + "serde_json", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "ucd-trie" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" + +[[package]] +name = "unicode-bidi" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "uuid" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" +dependencies = [ + "getrandom 0.2.8", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webkit2gtk" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8eea819afe15eb8dcdff4f19d8bfda540bae84d874c10e6f4b8faf2d6704bd1" +dependencies = [ + "bitflags", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ac7a95ddd3fdfcaf83d8e513b4b1ad101b95b413b6aa6662ed95f284fc3d5b" +dependencies = [ + "bitflags", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webview2-com" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11296e5daf3a653b79bf47d66c380e4143d5b9c975818871179a3bda79499562" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows 0.44.0", + "windows-implement", +] + +[[package]] +name = "webview2-com-macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaebe196c01691db62e9e4ca52c5ef1e4fd837dcae27dae3ada599b5a8fd05ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "webview2-com-sys" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde542bed28058a5b028d459689ee57f1d06685bb6c266da3b91b1be6703952f" +dependencies = [ + "regex", + "serde", + "serde_json", + "thiserror", + "windows 0.44.0", + "windows-bindgen", + "windows-metadata", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" +dependencies = [ + "windows_aarch64_msvc 0.39.0", + "windows_i686_gnu 0.39.0", + "windows_i686_msvc 0.39.0", + "windows_x86_64_gnu 0.39.0", + "windows_x86_64_msvc 0.39.0", +] + +[[package]] +name = "windows" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-targets", +] + +[[package]] +name = "windows-bindgen" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222204ecf46521382a4d88b4a1bbefca9f8855697b4ab7d20803901425e061a3" +dependencies = [ + "windows-metadata", + "windows-tokens", +] + +[[package]] +name = "windows-implement" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce87ca8e3417b02dc2a8a22769306658670ec92d78f1bd420d6310a67c245c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "853f69a591ecd4f810d29f17e902d40e349fb05b0b11fff63b08b826bfe39c7f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-metadata" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee78911e3f4ce32c1ad9d3c7b0bd95389662ad8d8f1a3155688fed70bd96e2b6" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.1", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.1", +] + +[[package]] +name = "windows-tokens" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4251900975a0d10841c5d4bde79c56681543367ef811f3fabb8d1803b0959b" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + +[[package]] +name = "wry" +version = "0.27.0" +source = "git+https://github.com/tauri-apps/wry?branch=dev#9975dda12a122051b375ec6485650546d2d91c26" +dependencies = [ + "base64 0.13.1", + "block", + "cocoa", + "core-graphics", + "crossbeam-channel", + "dunce", + "gdk", + "gio", + "glib", + "gtk", + "html5ever", + "http", + "kuchiki", + "libc", + "log", + "objc", + "objc_id", + "once_cell", + "serde", + "serde_json", + "sha2", + "soup3", + "tao", + "thiserror", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows 0.44.0", + "windows-implement", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "xattr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +dependencies = [ + "libc", +] diff --git a/plugins/camera/examples/tauri-app/src-tauri/Cargo.toml b/plugins/camera/examples/tauri-app/src-tauri/Cargo.toml new file mode 100644 index 00000000..aa341f89 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/Cargo.toml @@ -0,0 +1,27 @@ +workspace = {} + +[package] +name = "app" +version = "0.0.0" +description = "A Tauri App" +authors = ["you"] +license = "" +repository = "" +edition = "2021" +rust-version = "1.64" + +[lib] +crate-type = ["staticlib", "cdylib", "rlib"] + +[build-dependencies] +tauri-build = { git = "https://github.com/tauri-apps/tauri", branch = "next", features = [] } + +[dependencies] +serde_json = "1.0" +serde = { version = "1.0", features = ["derive"] } +tauri = { git = "https://github.com/tauri-apps/tauri", branch = "next", features = ["api-all"] } +tauri-plugin-camera = { path = "../../../" } + +[features] +# DO NOT remove this +custom-protocol = [ "tauri/custom-protocol" ] diff --git a/plugins/camera/examples/tauri-app/src-tauri/build.rs b/plugins/camera/examples/tauri-app/src-tauri/build.rs new file mode 100644 index 00000000..795b9b7c --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/.editorconfig b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/.editorconfig new file mode 100644 index 00000000..ebe51d3b --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/.gitignore b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/.gitignore new file mode 100644 index 00000000..671e5074 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/.gitignore @@ -0,0 +1,18 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +build +/captures +.externalNativeBuild +.cxx +local.properties + +/tauri-plugins +/tauri-api diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/.gitignore b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/.gitignore new file mode 100644 index 00000000..e897324f --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/.gitignore @@ -0,0 +1 @@ +/src/main/java/app/tauri/app/generated diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/build.gradle.kts b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/build.gradle.kts new file mode 100644 index 00000000..7e709df9 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/build.gradle.kts @@ -0,0 +1,117 @@ +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") + id("rustPlugin") +} + +android { + compileSdk = 33 + defaultConfig { + manifestPlaceholders["usesCleartextTraffic"] = "false" + applicationId = "app.tauri.app" + minSdk = 24 + targetSdk = 33 + versionCode = 1 + versionName = "1.0" + } + sourceSets.getByName("main") { + // Vulkan validation layers + val ndkHome = System.getenv("NDK_HOME") + jniLibs.srcDir("${ndkHome}/sources/third_party/vulkan/src/build-android/jniLibs") + } + buildTypes { + getByName("debug") { + manifestPlaceholders["usesCleartextTraffic"] = "true" + isDebuggable = true + isJniDebuggable = true + isMinifyEnabled = false + packagingOptions { + jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so") + + jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so") + + jniLibs.keepDebugSymbols.add("*/x86/*.so") + + jniLibs.keepDebugSymbols.add("*/x86_64/*.so") + } + } + getByName("release") { + isMinifyEnabled = true + val proguards = fileTree(".") { + include("*.pro") + } + proguardFiles(*proguards.toList().toTypedArray()) + } + } + flavorDimensions.add("abi") + productFlavors { + create("universal") { + val abiList = findProperty("abiList") as? String + + dimension = "abi" + ndk { + abiFilters += abiList?.split(",")?.map { it.trim() } ?: listOf( "arm64-v8a", "armeabi-v7a", "x86", "x86_64", + ) + } + } + create("arm64") { + dimension = "abi" + ndk { + abiFilters += listOf("arm64-v8a") + } + } + + create("arm") { + dimension = "abi" + ndk { + abiFilters += listOf("armeabi-v7a") + } + } + + create("x86") { + dimension = "abi" + ndk { + abiFilters += listOf("x86") + } + } + + create("x86_64") { + dimension = "abi" + ndk { + abiFilters += listOf("x86_64") + } + } + } + + assetPacks += mutableSetOf() + namespace = "app.tauri.app" +} + +rust { + rootDirRel = "../../../../" + targets = listOf("aarch64", "armv7", "i686", "x86_64") + arches = listOf("arm64", "arm", "x86", "x86_64") +} + +dependencies { + implementation("androidx.webkit:webkit:1.5.0") + implementation("androidx.appcompat:appcompat:1.5.1") + implementation("com.google.android.material:material:1.7.0") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.4") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0") + implementation(project(":tauri-android")) +} + +apply(from = "tauri.build.gradle.kts") + +afterEvaluate { + android.applicationVariants.all { + tasks["mergeUniversalReleaseJniLibFolders"].dependsOn(tasks["rustBuildRelease"]) + tasks["mergeUniversalDebugJniLibFolders"].dependsOn(tasks["rustBuildDebug"]) + productFlavors.filter{ it.name != "universal" }.forEach { _ -> + val archAndBuildType = name.capitalize() + tasks["merge${archAndBuildType}JniLibFolders"].dependsOn(tasks["rustBuild${archAndBuildType}"]) + } + } +} diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/proguard-rules.pro b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/proguard-tauri.pro b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/proguard-tauri.pro new file mode 100644 index 00000000..b4eb481e --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/proguard-tauri.pro @@ -0,0 +1,24 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. + +-keep class app.tauri.app.TauriActivity { + getAppClass(...); + getVersion(); +} + +-keep class app.tauri.app.RustWebView { + public (...); + loadUrlMainThread(...); +} + +-keep class app.tauri.app.Ipc { + public (...); + @android.webkit.JavascriptInterface public ; +} + +-keep class app.tauri.app.RustWebChromeClient,app.tauri.app.RustWebViewClient { + public (...); +} + +-keep class app.tauri.app.MainActivity { + public getPluginManager(); +} diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/AndroidManifest.xml b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..36df901f --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/AndroidManifest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/java/app/tauri/app/MainActivity.kt b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/java/app/tauri/app/MainActivity.kt new file mode 100644 index 00000000..3dff0081 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/java/app/tauri/app/MainActivity.kt @@ -0,0 +1,7 @@ +package app.tauri.app + +import app.tauri.plugin.PluginManager + +class MainActivity : TauriActivity() { + var pluginManager: PluginManager = PluginManager(this) +} diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/jniLibs/arm64-v8a/libapp.so b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/jniLibs/arm64-v8a/libapp.so new file mode 120000 index 00000000..9f1bb9d3 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/jniLibs/arm64-v8a/libapp.so @@ -0,0 +1 @@ +/home/lucas/projects/tauri/plugins-workspace/plugins/camera/examples/tauri-app/src-tauri/target/aarch64-linux-android/debug/libapp.so \ No newline at end of file diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/jniLibs/x86/libapp.so b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/jniLibs/x86/libapp.so new file mode 120000 index 00000000..da9e43a8 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/jniLibs/x86/libapp.so @@ -0,0 +1 @@ +/home/lucas/projects/tauri/plugins-workspace/plugins/camera/examples/tauri-app/src-tauri/target/i686-linux-android/debug/libapp.so \ No newline at end of file diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..2b068d11 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/drawable/ic_launcher_background.xml b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..07d5da9c --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/layout/activity_main.xml b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..4fc24441 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-hdpi/ic_launcher.png b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..28f1aa11 Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..85d0c88a Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 00000000..28f1aa11 Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-mdpi/ic_launcher.png b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..73e48dbf Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..13dd2147 Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 00000000..73e48dbf Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..1d98044f Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..a888b336 Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 00000000..1d98044f Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..08183246 Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..a2a838e7 Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..08183246 Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..b18bceb6 Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..3f8a57f3 Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..b18bceb6 Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/values-night/themes.xml b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/values-night/themes.xml new file mode 100644 index 00000000..7cd5d350 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/values/colors.xml b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..f8c6127d --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/values/strings.xml b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..f664d9a2 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + Tauri Camera + Camera + \ No newline at end of file diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/values/themes.xml b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/values/themes.xml new file mode 100644 index 00000000..04239cf4 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/xml/file_paths.xml b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/xml/file_paths.xml new file mode 100644 index 00000000..782d63b9 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/tauri.build.gradle.kts b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/tauri.build.gradle.kts new file mode 100644 index 00000000..27d35939 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/app/tauri.build.gradle.kts @@ -0,0 +1,8 @@ +// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. + +val implementation by configurations + +dependencies { + implementation(project(":tauri-plugin-camera")) + +} diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/build.gradle.kts b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/build.gradle.kts new file mode 100644 index 00000000..8c6fe584 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/build.gradle.kts @@ -0,0 +1,25 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath("com.android.tools.build:gradle:7.3.1") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10") + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +tasks.register("clean").configure { + delete("build") +} + diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/buildSrc/build.gradle.kts b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/buildSrc/build.gradle.kts new file mode 100644 index 00000000..2713738b --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/buildSrc/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + `kotlin-dsl` +} + +gradlePlugin { + plugins { + create("pluginsForCoolKids") { + id = "rustPlugin" + implementationClass = "app.tauri.RustPlugin" + } + } +} + +repositories { + google() + mavenCentral() +} + +dependencies { + compileOnly(gradleApi()) + implementation("com.android.tools.build:gradle:7.3.1") +} + diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/buildSrc/src/main/java/app/tauri/app/kotlin/BuildTask.kt b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/buildSrc/src/main/java/app/tauri/app/kotlin/BuildTask.kt new file mode 100644 index 00000000..c1f75733 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/buildSrc/src/main/java/app/tauri/app/kotlin/BuildTask.kt @@ -0,0 +1,43 @@ +package app.tauri + +import java.io.File +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.logging.LogLevel +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.TaskAction + +open class BuildTask : DefaultTask() { + @InputDirectory + @PathSensitive(PathSensitivity.RELATIVE) + var rootDirRel: File? = null + @Input + var target: String? = null + @Input + var release: Boolean? = null + + @TaskAction + fun build() { + val rootDirRel = rootDirRel ?: throw GradleException("rootDirRel cannot be null") + val target = target ?: throw GradleException("target cannot be null") + val release = release ?: throw GradleException("release cannot be null") + project.exec { + workingDir(File(project.projectDir, rootDirRel.path)) + executable("""pnpm""") + args(listOf("run", "tauri", "android", "android-studio-script")) + if (project.logger.isEnabled(LogLevel.DEBUG)) { + args("-vv") + } else if (project.logger.isEnabled(LogLevel.INFO)) { + args("-v") + } + if (release) { + args("--release") + } + args(listOf("--target", target)) + }.assertNormalExitValue() + } +} + diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/buildSrc/src/main/java/app/tauri/app/kotlin/RustPlugin.kt b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/buildSrc/src/main/java/app/tauri/app/kotlin/RustPlugin.kt new file mode 100644 index 00000000..f40ad054 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/buildSrc/src/main/java/app/tauri/app/kotlin/RustPlugin.kt @@ -0,0 +1,59 @@ +package app.tauri + +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.Plugin +import org.gradle.api.Project +import java.io.File +import java.util.* + +const val TASK_GROUP = "rust" + +open class Config { + var rootDirRel: String? = null + var targets: List? = null + var arches: List? = null +} + +open class RustPlugin : Plugin { + private lateinit var config: Config + + override fun apply(project: Project) { + config = project.extensions.create("rust", Config::class.java) + project.afterEvaluate { + if (config.targets == null) { + throw GradleException("targets cannot be null") + } + if (config.arches == null) { + throw GradleException("arches cannot be null") + } + for (profile in listOf("debug", "release")) { + val profileCapitalized = profile.capitalize(Locale.ROOT) + val buildTask = project.tasks.maybeCreate( + "rustBuild$profileCapitalized", + DefaultTask::class.java + ).apply { + group = TASK_GROUP + description = "Build dynamic library in $profile mode for all targets" + } + for (targetPair in config.targets!!.withIndex()) { + val targetName = targetPair.value + val targetArch = config.arches!![targetPair.index] + val targetArchCapitalized = targetArch.capitalize(Locale.ROOT) + val targetBuildTask = project.tasks.maybeCreate( + "rustBuild$targetArchCapitalized$profileCapitalized", + BuildTask::class.java + ).apply { + group = TASK_GROUP + description = "Build dynamic library in $profile mode for $targetArch" + rootDirRel = config.rootDirRel?.let { File(it) } + target = targetName + release = profile == "release" + } + buildTask.dependsOn(targetBuildTask) + project.tasks.findByName("preBuild")?.mustRunAfter(targetBuildTask) + } + } + } + } +} diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/gradle.properties b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/gradle.properties new file mode 100644 index 00000000..cd0519bb --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/gradle.properties @@ -0,0 +1,23 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/gradle/wrapper/gradle-wrapper.jar b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..e708b1c0 Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/gradle/wrapper/gradle-wrapper.jar differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/gradle/wrapper/gradle-wrapper.properties b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..de8c362b --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue May 10 19:22:52 CST 2022 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/gradlew b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/gradlew new file mode 100755 index 00000000..4f906e0c --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# 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 +# +# https://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. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/gradlew.bat b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/gradlew.bat new file mode 100644 index 00000000..ac1b06f9 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/settings.gradle b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/settings.gradle new file mode 100644 index 00000000..a1a06b10 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/settings.gradle @@ -0,0 +1,6 @@ +include ':app' + +include ':tauri-android' +project(':tauri-android').projectDir = new File('./tauri-api') + +apply from: 'tauri.settings.gradle' diff --git a/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/tauri.settings.gradle b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/tauri.settings.gradle new file mode 100644 index 00000000..d07d8e7f --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/gen/android/app/tauri.settings.gradle @@ -0,0 +1,3 @@ +// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +include ':tauri-plugin-camera' +project(':tauri-plugin-camera').projectDir = new File('./tauri-plugins/tauri-plugin-camera') \ No newline at end of file diff --git a/plugins/camera/examples/tauri-app/src-tauri/icons/128x128.png b/plugins/camera/examples/tauri-app/src-tauri/icons/128x128.png new file mode 100644 index 00000000..77e7d233 Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/icons/128x128.png differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/icons/128x128@2x.png b/plugins/camera/examples/tauri-app/src-tauri/icons/128x128@2x.png new file mode 100644 index 00000000..0f7976f1 Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/icons/128x128@2x.png differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/icons/32x32.png b/plugins/camera/examples/tauri-app/src-tauri/icons/32x32.png new file mode 100644 index 00000000..98fda06f Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/icons/32x32.png differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/icons/icon.icns b/plugins/camera/examples/tauri-app/src-tauri/icons/icon.icns new file mode 100644 index 00000000..29d6685a Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/icons/icon.icns differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/icons/icon.ico b/plugins/camera/examples/tauri-app/src-tauri/icons/icon.ico new file mode 100644 index 00000000..06c23c82 Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/icons/icon.ico differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/icons/icon.png b/plugins/camera/examples/tauri-app/src-tauri/icons/icon.png new file mode 100644 index 00000000..d1756ce4 Binary files /dev/null and b/plugins/camera/examples/tauri-app/src-tauri/icons/icon.png differ diff --git a/plugins/camera/examples/tauri-app/src-tauri/src/lib.rs b/plugins/camera/examples/tauri-app/src-tauri/src/lib.rs new file mode 100644 index 00000000..498c6a4b --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/src/lib.rs @@ -0,0 +1,11 @@ +// Copyright 2019-2022 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .plugin(tauri_plugin_camera::init()) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} diff --git a/plugins/camera/examples/tauri-app/src-tauri/tauri.conf.json b/plugins/camera/examples/tauri-app/src-tauri/tauri.conf.json new file mode 100644 index 00000000..94c3ac24 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src-tauri/tauri.conf.json @@ -0,0 +1,66 @@ +{ + "build": { + "beforeDevCommand": "pnpm dev", + "beforeBuildCommand": "pnpm build", + "devPath": "http://localhost:5173", + "distDir": "../dist", + "withGlobalTauri": false + }, + "package": { + "productName": "app", + "version": "0.0.0" + }, + "tauri": { + "allowlist": { + "all": true + }, + "bundle": { + "active": true, + "category": "DeveloperTool", + "copyright": "", + "deb": { + "depends": [] + }, + "externalBin": [], + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ], + "identifier": "app.tauri.camera-example", + "longDescription": "", + "macOS": { + "entitlements": null, + "exceptionDomain": "", + "frameworks": [], + "providerShortName": null, + "signingIdentity": null + }, + "resources": [], + "shortDescription": "", + "targets": "all", + "windows": { + "certificateThumbprint": null, + "digestAlgorithm": "sha256", + "timestampUrl": "" + } + }, + "security": { + "csp": null + }, + "updater": { + "active": false + }, + "windows": [ + { + "fullscreen": false, + "height": 600, + "resizable": true, + "title": "tauri-app", + "width": 800 + } + ] + } +} \ No newline at end of file diff --git a/plugins/camera/examples/tauri-app/src/App.svelte b/plugins/camera/examples/tauri-app/src/App.svelte new file mode 100644 index 00000000..1a554bf9 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src/App.svelte @@ -0,0 +1,9 @@ + + +
+
+ +
+
diff --git a/plugins/camera/examples/tauri-app/src/lib/GetPhoto.svelte b/plugins/camera/examples/tauri-app/src/lib/GetPhoto.svelte new file mode 100644 index 00000000..709e49a2 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src/lib/GetPhoto.svelte @@ -0,0 +1,31 @@ + + +
+ {#if imageSrc} + Selected + {/if} + +
+ + diff --git a/plugins/camera/examples/tauri-app/src/main.js b/plugins/camera/examples/tauri-app/src/main.js new file mode 100644 index 00000000..6b4e1a96 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src/main.js @@ -0,0 +1,8 @@ +import "./style.css"; +import App from "./App.svelte"; + +const app = new App({ + target: document.getElementById("app"), +}); + +export default app; diff --git a/plugins/camera/examples/tauri-app/src/style.css b/plugins/camera/examples/tauri-app/src/style.css new file mode 100644 index 00000000..c0f9e3bc --- /dev/null +++ b/plugins/camera/examples/tauri-app/src/style.css @@ -0,0 +1,102 @@ +: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; +} + +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; + } +} diff --git a/plugins/camera/examples/tauri-app/src/vite-env.d.ts b/plugins/camera/examples/tauri-app/src/vite-env.d.ts new file mode 100644 index 00000000..4078e747 --- /dev/null +++ b/plugins/camera/examples/tauri-app/src/vite-env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/plugins/camera/examples/tauri-app/vite.config.js b/plugins/camera/examples/tauri-app/vite.config.js new file mode 100644 index 00000000..b7427654 --- /dev/null +++ b/plugins/camera/examples/tauri-app/vite.config.js @@ -0,0 +1,40 @@ +import { defineConfig } from "vite"; +import { svelte } from "@sveltejs/vite-plugin-svelte"; +import { internalIpV4 } from "internal-ip"; + +// https://vitejs.dev/config/ +export default defineConfig(async () => { + const host = process.env.TAURI_PLATFORM === 'android' || process.env.TAURI_PLATFORM === 'ios' ? (await internalIpV4()) : 'localhost' + return { + plugins: [svelte()], + + // Vite optons tailored for Tauri development and only applied in `tauri dev` or `tauri build` + // prevent vite from obscuring rust errors + clearScreen: false, + // tauri expects a fixed port, fail if that port is not available + server: { + host: '0.0.0.0', + port: 5173, + strictPort: true, + hmr: { + protocol: 'ws', + host, + port: 5183 + }, + fs: { + allow: ['.', '../../tooling/api/dist'] + } + }, + // to make use of `TAURI_DEBUG` and other env variables + // https://tauri.studio/v1/api/config#buildconfig.beforedevcommand + envPrefix: ["VITE_", "TAURI_"], + build: { + // Tauri supports es2021 + target: ["es2021", "chrome100", "safari13"], + // don't minify for debug builds + minify: !process.env.TAURI_DEBUG ? "esbuild" : false, + // produce sourcemaps for debug builds + sourcemap: !!process.env.TAURI_DEBUG, + }, + } +}); diff --git a/plugins/camera/guest-js/index.ts b/plugins/camera/guest-js/index.ts new file mode 100644 index 00000000..be4fc462 --- /dev/null +++ b/plugins/camera/guest-js/index.ts @@ -0,0 +1,50 @@ +// Copyright 2019-2022 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT +import { invoke } from '@tauri-apps/api/tauri' + +export enum Source { + Prompt = 'PROMPT', + Camera = 'CAMERA', + Photos = 'PHOTOS' +} + +export enum ResultType { + Uri = 'uri', + Base64 = 'base64', + DataUrl = 'dataUrl' +} + +export enum CameraDirection { + Rear = 'REAR', + Front = 'FRONT' +} + +export interface ImageOptions { + quality?: number + allowEditing?: boolean + resultType?: ResultType + saveToGallery?: boolean + width?: number + height?: number + correctOrientation?: boolean + source?: Source + direction?: CameraDirection + presentationStyle?: 'fullscreen' | 'popover' + promptLabelHeader?: string + promptLabelCancel?: string + promptLabelPhoto?: string + promptLabelPicture?: string +} + +export interface Image { + data: string + assetUrl?: string + format: string + saved: boolean + exif: unknown +} + +export async function getPhoto(options?: ImageOptions): Promise { + return await invoke('plugin:camera|getPhoto', { ...options }) +} diff --git a/plugins/camera/ios/.gitignore b/plugins/camera/ios/.gitignore new file mode 100644 index 00000000..7d98e3ad --- /dev/null +++ b/plugins/camera/ios/.gitignore @@ -0,0 +1,11 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/config/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc +Package.resolved +/tauri-api diff --git a/plugins/camera/ios/Package.swift b/plugins/camera/ios/Package.swift new file mode 100644 index 00000000..45b30dc2 --- /dev/null +++ b/plugins/camera/ios/Package.swift @@ -0,0 +1,31 @@ +// swift-tools-version:5.7 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "tauri-plugin-camera", + platforms: [ + .iOS(.v11), + ], + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .library( + name: "tauri-plugin-camera", + type: .static, + targets: ["tauri-plugin-camera"]), + ], + dependencies: [ + .package(name: "Tauri", path: "../../../../../core/tauri/mobile/ios-api") + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "tauri-plugin-camera", + dependencies: [ + .byName(name: "Tauri") + ], + path: "Sources") + ] +) diff --git a/plugins/camera/ios/README.md b/plugins/camera/ios/README.md new file mode 100644 index 00000000..5293ed5b --- /dev/null +++ b/plugins/camera/ios/README.md @@ -0,0 +1,3 @@ +# Tauri Plugin camera + +A description of this package. diff --git a/plugins/camera/ios/Sources/CameraExtensions.swift b/plugins/camera/ios/Sources/CameraExtensions.swift new file mode 100644 index 00000000..938b61c0 --- /dev/null +++ b/plugins/camera/ios/Sources/CameraExtensions.swift @@ -0,0 +1,105 @@ +import UIKit +import Photos + +internal protocol CameraAuthorizationState { + var authorizationState: String { get } +} + +extension AVAuthorizationStatus: CameraAuthorizationState { + var authorizationState: String { + switch self { + case .denied, .restricted: + return "denied" + case .authorized: + return "granted" + case .notDetermined: + fallthrough + @unknown default: + return "prompt" + } + } +} + +extension PHAuthorizationStatus: CameraAuthorizationState { + var authorizationState: String { + switch self { + case .denied, .restricted: + return "denied" + case .authorized: + return "granted" + #if swift(>=5.3) + // poor proxy for Xcode 12/iOS 14, should be removed once building with Xcode 12 is required + case .limited: + return "limited" + #endif + case .notDetermined: + fallthrough + @unknown default: + return "prompt" + } + } +} + +internal extension PHAsset { + /** + Retrieves the image metadata for the asset. + */ + var imageData: [String: Any] { + let options = PHImageRequestOptions() + options.isSynchronous = true + options.resizeMode = .none + options.isNetworkAccessAllowed = false + options.version = .current + + var result: [String: Any] = [:] + _ = PHCachingImageManager().requestImageDataAndOrientation(for: self, options: options) { (data, _, _, _) in + if let data = data as NSData? { + let options = [kCGImageSourceShouldCache as String: kCFBooleanFalse] as CFDictionary + if let imgSrc = CGImageSourceCreateWithData(data, options), + let metadata = CGImageSourceCopyPropertiesAtIndex(imgSrc, 0, options) as? [String: Any] { + result = metadata + } + } + } + return result + } +} + +internal extension UIImage { + /** + Generates a new image from the existing one, implicitly resetting any orientation. + Dimensions greater than 0 will resize the image while preserving the aspect ratio. + */ + func reformat(to size: CGSize? = nil) -> UIImage { + let imageHeight = self.size.height + let imageWidth = self.size.width + // determine the max dimensions, 0 is treated as 'no restriction' + var maxWidth: CGFloat + if let size = size, size.width > 0 { + maxWidth = size.width + } else { + maxWidth = imageWidth + } + let maxHeight: CGFloat + if let size = size, size.height > 0 { + maxHeight = size.height + } else { + maxHeight = imageHeight + } + // adjust to preserve aspect ratio + var targetWidth = min(imageWidth, maxWidth) + var targetHeight = (imageHeight * targetWidth) / imageWidth + if targetHeight > maxHeight { + targetWidth = (imageWidth * maxHeight) / imageHeight + targetHeight = maxHeight + } + // generate the new image and return + let format: UIGraphicsImageRendererFormat = UIGraphicsImageRendererFormat.default() + format.scale = 1.0 + format.opaque = false + let renderer = UIGraphicsImageRenderer(size: CGSize(width: targetWidth, height: targetHeight), format: format) + return renderer.image { (_) in + self.draw(in: CGRect(origin: .zero, size: CGSize(width: targetWidth, height: targetHeight))) + } + } +} \ No newline at end of file diff --git a/plugins/camera/ios/Sources/CameraPlugin.swift b/plugins/camera/ios/Sources/CameraPlugin.swift new file mode 100644 index 00000000..aca9ad2d --- /dev/null +++ b/plugins/camera/ios/Sources/CameraPlugin.swift @@ -0,0 +1,576 @@ +import UIKit +import WebKit +import Tauri +import Photos +import PhotosUI + +public class CameraPlugin: Plugin { + private var invoke: Invoke? + private var settings = CameraSettings() + private let defaultSource = CameraSource.prompt + private let defaultDirection = CameraDirection.rear + private var multiple = false + + private var imageCounter = 0 + + @objc override public func checkPermissions(_ invoke: Invoke) { + var result: [String: Any] = [:] + for permission in CameraPermissionType.allCases { + let state: String + switch permission { + case .camera: + state = AVCaptureDevice.authorizationStatus(for: .video).authorizationState + case .photos: + if #available(iOS 14, *) { + state = PHPhotoLibrary.authorizationStatus(for: .readWrite).authorizationState + } else { + state = PHPhotoLibrary.authorizationStatus().authorizationState + } + } + result[permission.rawValue] = state + } + invoke.resolve(result) + } + + @objc override public func requestPermissions(_ invoke: Invoke) { + // get the list of desired types, if passed + let typeList = invoke.getArray("permissions", String.self)?.compactMap({ (type) -> CameraPermissionType? in + return CameraPermissionType(rawValue: type) + }) ?? [] + // otherwise check everything + let permissions: [CameraPermissionType] = (typeList.count > 0) ? typeList : CameraPermissionType.allCases + // request the permissions + let group = DispatchGroup() + for permission in permissions { + switch permission { + case .camera: + group.enter() + AVCaptureDevice.requestAccess(for: .video) { _ in + group.leave() + } + case .photos: + group.enter() + if #available(iOS 14, *) { + PHPhotoLibrary.requestAuthorization(for: .readWrite) { (_) in + group.leave() + } + } else { + PHPhotoLibrary.requestAuthorization({ (_) in + group.leave() + }) + } + } + } + group.notify(queue: DispatchQueue.main) { [weak self] in + self?.checkPermissions(invoke) + } + } + + @objc func pickLimitedLibraryPhotos(_ invoke: Invoke) { + if #available(iOS 14, *) { + PHPhotoLibrary.requestAuthorization(for: .readWrite) { (granted) in + if granted == .limited { + if let viewController = self.manager.viewController { + if #available(iOS 15, *) { + PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: viewController) { _ in + self.getLimitedLibraryPhotos(invoke) + } + } else { + PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: viewController) + invoke.resolve([ + "photos": [] + ]) + } + } + } else { + invoke.resolve([ + "photos": [] + ]) + } + } + } else { + invoke.unavailable("Not available on iOS 13") + } + } + + @objc func getLimitedLibraryPhotos(_ invoke: Invoke) { + if #available(iOS 14, *) { + PHPhotoLibrary.requestAuthorization(for: .readWrite) { (granted) in + if granted == .limited { + + self.invoke = invoke + + DispatchQueue.global(qos: .utility).async { + let assets = PHAsset.fetchAssets(with: .image, options: nil) + var processedImages: [ProcessedImage] = [] + + let imageManager = PHImageManager.default() + let options = PHImageRequestOptions() + options.deliveryMode = .highQualityFormat + + let group = DispatchGroup() + + for index in 0...(assets.count - 1) { + let asset = assets.object(at: index) + let fullSize = CGSize(width: asset.pixelWidth, height: asset.pixelHeight) + + group.enter() + imageManager.requestImage(for: asset, targetSize: fullSize, contentMode: .default, options: options) { image, _ in + guard let image = image else { + group.leave() + return + } + processedImages.append(self.processedImage(from: image, with: asset.imageData)) + group.leave() + } + } + + group.notify(queue: .global(qos: .utility)) { [weak self] in + self?.returnImages(processedImages) + } + } + } else { + invoke.resolve([ + "photos": [] + ]) + } + } + } else { + invoke.unavailable("Not available on iOS 13") + } + } + + @objc func getPhoto(_ invoke: Invoke) { + self.multiple = false + self.invoke = invoke + self.settings = cameraSettings(from: invoke) + + // Make sure they have all the necessary info.plist settings + if let missingUsageDescription = checkUsageDescriptions() { + Logger.error("[PLUGIN]", "Camera", "-", missingUsageDescription) + invoke.reject(missingUsageDescription) + return + } + + DispatchQueue.main.async { + switch self.settings.source { + case .prompt: + self.showPrompt() + case .camera: + self.showCamera() + case .photos: + self.showPhotos() + } + } + } + + @objc func pickImages(_ invoke: Invoke) { + self.multiple = true + self.invoke = invoke + self.settings = cameraSettings(from: invoke) + DispatchQueue.main.async { + self.showPhotos() + } + } + + private func checkUsageDescriptions() -> String? { + if let dict = Bundle.main.infoDictionary { + for key in CameraPropertyListKeys.allCases where dict[key.rawValue] == nil { + return key.missingMessage + } + } + return nil + } + + private func cameraSettings(from invoke: Invoke) -> CameraSettings { + var settings = CameraSettings() + settings.jpegQuality = min(abs(CGFloat(invoke.getFloat("quality") ?? 100.0)) / 100.0, 1.0) + settings.allowEditing = invoke.getBool("allowEditing") ?? false + settings.source = CameraSource(rawValue: invoke.getString("source") ?? defaultSource.rawValue) ?? defaultSource + settings.direction = CameraDirection(rawValue: invoke.getString("direction") ?? defaultDirection.rawValue) ?? defaultDirection + if let typeString = invoke.getString("resultType"), let type = CameraResultType(rawValue: typeString) { + settings.resultType = type + } + settings.saveToGallery = invoke.getBool("saveToGallery") ?? false + + // Get the new image dimensions if provided + settings.width = CGFloat(invoke.getInt("width") ?? 0) + settings.height = CGFloat(invoke.getInt("height") ?? 0) + if settings.width > 0 || settings.height > 0 { + // We resize only if a dimension was provided + settings.shouldResize = true + } + settings.shouldCorrectOrientation = invoke.getBool("correctOrientation") ?? true + settings.userPromptText = CameraPromptText(title: invoke.getString("promptLabelHeader"), + photoAction: invoke.getString("promptLabelPhoto"), + cameraAction: invoke.getString("promptLabelPicture"), + cancelAction: invoke.getString("promptLabelCancel")) + if let styleString = invoke.getString("presentationStyle"), styleString == "popover" { + settings.presentationStyle = .popover + } else { + settings.presentationStyle = .fullScreen + } + + return settings + } +} + +// public delegate methods +extension CameraPlugin: UIImagePickerControllerDelegate, UINavigationControllerDelegate, UIPopoverPresentationControllerDelegate { + public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { + picker.dismiss(animated: true) + self.invoke?.reject("User cancelled photos app") + } + + public func popoverPresentationControllerDidDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) { + self.invoke?.reject("User cancelled photos app") + } + + public func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { + self.invoke?.reject("User cancelled photos app") + } + + public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) { + picker.dismiss(animated: true) { + if let processedImage = self.processImage(from: info) { + self.returnProcessedImage(processedImage) + } else { + self.invoke?.reject("Error processing image") + } + } + } +} + +@available(iOS 14, *) +extension CameraPlugin: PHPickerViewControllerDelegate { + public func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { + picker.dismiss(animated: true, completion: nil) + guard let result = results.first else { + self.invoke?.reject("User cancelled photos app") + return + } + if multiple { + var images: [ProcessedImage] = [] + var processedCount = 0 + for img in results { + guard img.itemProvider.canLoadObject(ofClass: UIImage.self) else { + self.invoke?.reject("Error loading image") + return + } + // extract the image + img.itemProvider.loadObject(ofClass: UIImage.self) { [weak self] (reading, _) in + if let image = reading as? UIImage { + var asset: PHAsset? + if let assetId = img.assetIdentifier { + asset = PHAsset.fetchAssets(withLocalIdentifiers: [assetId], options: nil).firstObject + } + if let processedImage = self?.processedImage(from: image, with: asset?.imageData) { + images.append(processedImage) + } + processedCount += 1 + if processedCount == results.count { + self?.returnImages(images) + } + } else { + self?.invoke?.reject("Error loading image") + } + } + } + + } else { + guard result.itemProvider.canLoadObject(ofClass: UIImage.self) else { + self.invoke?.reject("Error loading image") + return + } + // extract the image + result.itemProvider.loadObject(ofClass: UIImage.self) { [weak self] (reading, _) in + if let image = reading as? UIImage { + var asset: PHAsset? + if let assetId = result.assetIdentifier { + asset = PHAsset.fetchAssets(withLocalIdentifiers: [assetId], options: nil).firstObject + } + if var processedImage = self?.processedImage(from: image, with: asset?.imageData) { + processedImage.flags = .gallery + self?.returnProcessedImage(processedImage) + return + } + } + self?.invoke?.reject("Error loading image") + } + } + } +} + +private extension CameraPlugin { + func returnImage(_ processedImage: ProcessedImage, isSaved: Bool) { + guard let jpeg = processedImage.generateJPEG(with: settings.jpegQuality) else { + self.invoke?.reject("Unable to convert image to jpeg") + return + } + + if settings.resultType == CameraResultType.uri || multiple { + guard let fileURL = try? saveTemporaryImage(jpeg), + let webURL = manager.assetUrl(fromLocalURL: fileURL) else { + invoke?.reject("Unable to get asset URL to file") + return + } + if self.multiple { + invoke?.resolve([ + "photos": [[ + "data": fileURL.absoluteString, + "exif": processedImage.exifData, + "assetUrl": webURL.absoluteString, + "format": "jpeg" + ]] + ]) + return + } + invoke?.resolve([ + "data": fileURL.absoluteString, + "exif": processedImage.exifData, + "assetUrl": webURL.absoluteString, + "format": "jpeg", + "saved": isSaved + ]) + } else if settings.resultType == CameraResultType.base64 { + self.invoke?.resolve([ + "data": jpeg.base64EncodedString(), + "exif": processedImage.exifData, + "format": "jpeg", + "saved": isSaved + ]) + } else if settings.resultType == CameraResultType.dataURL { + invoke?.resolve([ + "data": "data:image/jpeg;base64," + jpeg.base64EncodedString(), + "exif": processedImage.exifData, + "format": "jpeg", + "saved": isSaved + ]) + } + } + + func returnImages(_ processedImages: [ProcessedImage]) { + var photos: [JsonObject] = [] + for processedImage in processedImages { + guard let jpeg = processedImage.generateJPEG(with: settings.jpegQuality) else { + self.invoke?.reject("Unable to convert image to jpeg") + return + } + + guard let fileURL = try? saveTemporaryImage(jpeg), + let webURL = manager.assetUrl(fromLocalURL: fileURL) else { + invoke?.reject("Unable to get asset URL to file") + return + } + + photos.append([ + "path": fileURL.absoluteString, + "exif": processedImage.exifData, + "assetUrl": webURL.absoluteString, + "format": "jpeg" + ]) + } + invoke?.resolve([ + "photos": photos + ]) + } + + func returnProcessedImage(_ processedImage: ProcessedImage) { + // conditionally save the image + if settings.saveToGallery && (processedImage.flags.contains(.edited) == true || processedImage.flags.contains(.gallery) == false) { + _ = ImageSaver(image: processedImage.image) { error in + var isSaved = false + if error == nil { + isSaved = true + } + self.returnImage(processedImage, isSaved: isSaved) + } + } else { + self.returnImage(processedImage, isSaved: false) + } + } + + func showPrompt() { + // Build the action sheet + let alert = UIAlertController(title: settings.userPromptText.title, message: nil, preferredStyle: UIAlertController.Style.actionSheet) + alert.addAction(UIAlertAction(title: settings.userPromptText.photoAction, style: .default, handler: { [weak self] (_: UIAlertAction) in + self?.showPhotos() + })) + + alert.addAction(UIAlertAction(title: settings.userPromptText.cameraAction, style: .default, handler: { [weak self] (_: UIAlertAction) in + self?.showCamera() + })) + + alert.addAction(UIAlertAction(title: settings.userPromptText.cancelAction, style: .cancel, handler: { [weak self] (_: UIAlertAction) in + self?.invoke?.reject("User cancelled photos app prompt") + })) + UIUtils.centerPopover(rootViewController: manager.viewController, popoverController: alert) + self.manager.viewController?.present(alert, animated: true, completion: nil) + } + + func showCamera() { + // check if we have a camera + if manager.isSimEnvironment || !UIImagePickerController.isSourceTypeAvailable(UIImagePickerController.SourceType.camera) { + Logger.error("[PLUGIN]", "Camera", "-", "Camera not available in simulator") + invoke?.reject("Camera not available while running in Simulator") + return + } + // check for permission + let authStatus = AVCaptureDevice.authorizationStatus(for: .video) + if authStatus == .restricted || authStatus == .denied { + invoke?.reject("User denied access to camera") + return + } + // we either already have permission or can prompt + AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in + if granted { + DispatchQueue.main.async { + self?.presentCameraPicker() + } + } else { + self?.invoke?.reject("User denied access to camera") + } + } + } + + func showPhotos() { + // check for permission + let authStatus = PHPhotoLibrary.authorizationStatus() + if authStatus == .restricted || authStatus == .denied { + invoke?.reject("User denied access to photos") + return + } + // we either already have permission or can prompt + if authStatus == .authorized { + presentSystemAppropriateImagePicker() + } else { + PHPhotoLibrary.requestAuthorization({ [weak self] (status) in + if status == PHAuthorizationStatus.authorized { + DispatchQueue.main.async { [weak self] in + self?.presentSystemAppropriateImagePicker() + } + } else { + self?.invoke?.reject("User denied access to photos") + } + }) + } + } + + func presentCameraPicker() { + let picker = UIImagePickerController() + picker.delegate = self + picker.allowsEditing = self.settings.allowEditing + // select the input + picker.sourceType = .camera + if settings.direction == .rear, UIImagePickerController.isCameraDeviceAvailable(.rear) { + picker.cameraDevice = .rear + } else if settings.direction == .front, UIImagePickerController.isCameraDeviceAvailable(.front) { + picker.cameraDevice = .front + } + // present + picker.modalPresentationStyle = settings.presentationStyle + if settings.presentationStyle == .popover { + picker.popoverPresentationController?.delegate = self + UIUtils.centerPopover(rootViewController: manager.viewController, popoverController: picker) + } + manager.viewController?.present(picker, animated: true, completion: nil) + } + + func presentSystemAppropriateImagePicker() { + if #available(iOS 14, *) { + presentPhotoPicker() + } else { + presentImagePicker() + } + } + + func presentImagePicker() { + let picker = UIImagePickerController() + picker.delegate = self + picker.allowsEditing = self.settings.allowEditing + // select the input + picker.sourceType = .photoLibrary + // present + picker.modalPresentationStyle = settings.presentationStyle + if settings.presentationStyle == .popover { + picker.popoverPresentationController?.delegate = self + UIUtils.centerPopover(rootViewController: manager.viewController, popoverController: picker) + } + manager.viewController?.present(picker, animated: true, completion: nil) + } + + @available(iOS 14, *) + func presentPhotoPicker() { + var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared()) + configuration.selectionLimit = self.multiple ? (self.invoke?.getInt("limit") ?? 0) : 1 + configuration.filter = .images + let picker = PHPickerViewController(configuration: configuration) + picker.delegate = self + // present + picker.modalPresentationStyle = settings.presentationStyle + if settings.presentationStyle == .popover { + picker.popoverPresentationController?.delegate = self + UIUtils.centerPopover(rootViewController: manager.viewController, popoverController: picker) + } + manager.viewController?.present(picker, animated: true, completion: nil) + } + + func saveTemporaryImage(_ data: Data) throws -> URL { + var url: URL + repeat { + imageCounter += 1 + url = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("photo-\(imageCounter).jpg") + } while FileManager.default.fileExists(atPath: url.path) + + try data.write(to: url, options: .atomic) + return url + } + + func processImage(from info: [UIImagePickerController.InfoKey: Any]) -> ProcessedImage? { + var selectedImage: UIImage? + var flags: PhotoFlags = [] + // get the image + if let edited = info[UIImagePickerController.InfoKey.editedImage] as? UIImage { + selectedImage = edited // use the edited version + flags = flags.union([.edited]) + } else if let original = info[UIImagePickerController.InfoKey.originalImage] as? UIImage { + selectedImage = original // use the original version + } + guard let image = selectedImage else { + return nil + } + var metadata: [String: Any] = [:] + // get the image's metadata from the picker or from the photo album + if let photoMetadata = info[UIImagePickerController.InfoKey.mediaMetadata] as? [String: Any] { + metadata = photoMetadata + } else { + flags = flags.union([.gallery]) + } + if let asset = info[UIImagePickerController.InfoKey.phAsset] as? PHAsset { + metadata = asset.imageData + } + // get the result + var result = processedImage(from: image, with: metadata) + result.flags = flags + return result + } + + func processedImage(from image: UIImage, with metadata: [String: Any]?) -> ProcessedImage { + var result = ProcessedImage(image: image, metadata: metadata ?? [:]) + // resizing the image only makes sense if we have real values to which to constrain it + if settings.shouldResize, settings.width > 0 || settings.height > 0 { + result.image = result.image.reformat(to: CGSize(width: settings.width, height: settings.height)) + result.overwriteMetadataOrientation(to: 1) + } else if settings.shouldCorrectOrientation { + // resizing implicitly reformats the image so this is only needed if we aren't resizing + result.image = result.image.reformat() + result.overwriteMetadataOrientation(to: 1) + } + return result + } +} + +@_cdecl("init_plugin_camera") +func initCameraPlugin(webview: WKWebView?) { + Tauri.registerPlugin(webview: webview, name: "camera", plugin: CameraPlugin()) +} diff --git a/plugins/camera/ios/Sources/CameraTypes.swift b/plugins/camera/ios/Sources/CameraTypes.swift new file mode 100644 index 00000000..adfb5640 --- /dev/null +++ b/plugins/camera/ios/Sources/CameraTypes.swift @@ -0,0 +1,142 @@ +import UIKit + +// MARK: - Public + +public enum CameraSource: String { + case prompt = "PROMPT" + case camera = "CAMERA" + case photos = "PHOTOS" +} + +public enum CameraDirection: String { + case rear = "REAR" + case front = "FRONT" +} + +public enum CameraResultType: String { + case base64 + case uri + case dataURL = "dataUrl" +} + +struct CameraPromptText { + let title: String + let photoAction: String + let cameraAction: String + let cancelAction: String + + init(title: String? = nil, photoAction: String? = nil, cameraAction: String? = nil, cancelAction: String? = nil) { + self.title = title ?? "Photo" + self.photoAction = photoAction ?? "From Photos" + self.cameraAction = cameraAction ?? "Take Picture" + self.cancelAction = cancelAction ?? "Cancel" + } +} + +public struct CameraSettings { + var source: CameraSource = CameraSource.prompt + var direction: CameraDirection = CameraDirection.rear + var resultType = CameraResultType.base64 + var userPromptText = CameraPromptText() + var jpegQuality: CGFloat = 1.0 + var width: CGFloat = 0 + var height: CGFloat = 0 + var allowEditing = false + var shouldResize = false + var shouldCorrectOrientation = true + var saveToGallery = false + var presentationStyle = UIModalPresentationStyle.fullScreen +} + +public struct CameraResult { + let image: UIImage? + let metadata: [AnyHashable: Any] +} + +// MARK: - Internal + +internal enum CameraPermissionType: String, CaseIterable { + case camera + case photos +} + +internal enum CameraPropertyListKeys: String, CaseIterable { + case photoLibraryAddUsage = "NSPhotoLibraryAddUsageDescription" + case photoLibraryUsage = "NSPhotoLibraryUsageDescription" + case cameraUsage = "NSCameraUsageDescription" + + var link: String { + switch self { + case .photoLibraryAddUsage: + return "https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW73" + case .photoLibraryUsage: + return "https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW17" + case .cameraUsage: + return "https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW24" + } + } + + var missingMessage: String { + return "You are missing \(self.rawValue) in your Info.plist file." + + " Camera will not function without it. Learn more: \(self.link)" + } +} + +internal struct PhotoFlags: OptionSet { + let rawValue: Int + + static let edited = PhotoFlags(rawValue: 1 << 0) + static let gallery = PhotoFlags(rawValue: 1 << 1) + + static let all: PhotoFlags = [.edited, .gallery] +} + +internal struct ProcessedImage { + var image: UIImage + var metadata: [String: Any] + var flags: PhotoFlags = [] + + var exifData: [String: Any] { + var exifData = metadata["{Exif}"] as? [String: Any] + exifData?["Orientation"] = metadata["Orientation"] + exifData?["GPS"] = metadata["{GPS}"] + return exifData ?? [:] + } + + mutating func overwriteMetadataOrientation(to orientation: Int) { + replaceDictionaryOrientation(atNode: &metadata, to: orientation) + } + + func replaceDictionaryOrientation(atNode node: inout [String: Any], to orientation: Int) { + for key in node.keys { + if key == "Orientation", (node[key] as? Int) != nil { + node[key] = orientation + } else if var child = node[key] as? [String: Any] { + replaceDictionaryOrientation(atNode: &child, to: orientation) + node[key] = child + } + } + } + + func generateJPEG(with quality: CGFloat) -> Data? { + // convert the UIImage to a jpeg + guard let data = self.image.jpegData(compressionQuality: quality) else { + return nil + } + // define our jpeg data as an image source and get its type + guard let source = CGImageSourceCreateWithData(data as CFData, nil), let type = CGImageSourceGetType(source) else { + return data + } + // allocate an output buffer and create the destination to receive the new data + guard let output = NSMutableData(capacity: data.count), let destination = CGImageDestinationCreateWithData(output, type, 1, nil) else { + return data + } + // pipe the source into the destination while overwriting the metadata, this encodes the metadata information into the image + CGImageDestinationAddImageFromSource(destination, source, 0, self.metadata as CFDictionary) + // finish + guard CGImageDestinationFinalize(destination) else { + return data + } + return output as Data + } +} \ No newline at end of file diff --git a/plugins/camera/ios/Sources/ImageSaver.swift b/plugins/camera/ios/Sources/ImageSaver.swift new file mode 100644 index 00000000..a33b43b0 --- /dev/null +++ b/plugins/camera/ios/Sources/ImageSaver.swift @@ -0,0 +1,20 @@ +import UIKit + +class ImageSaver: NSObject { + + var onResult: ((Error?) -> Void) = {_ in } + + init(image: UIImage, onResult:@escaping ((Error?) -> Void)) { + self.onResult = onResult + super.init() + UIImageWriteToSavedPhotosAlbum(image, self, #selector(saveResult), nil) + } + + @objc func saveResult(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) { + if let error = error { + onResult(error) + } else { + onResult(nil) + } + } +} \ No newline at end of file diff --git a/plugins/camera/ios/Tests/PluginTests/PluginTests.swift b/plugins/camera/ios/Tests/PluginTests/PluginTests.swift new file mode 100644 index 00000000..4f8e9ace --- /dev/null +++ b/plugins/camera/ios/Tests/PluginTests/PluginTests.swift @@ -0,0 +1,8 @@ +import XCTest +@testable import ExamplePlugin + +final class ExamplePluginTests: XCTestCase { + func testExample() throws { + let plugin = ExamplePlugin() + } +} diff --git a/plugins/camera/package.json b/plugins/camera/package.json new file mode 100644 index 00000000..e252ed1b --- /dev/null +++ b/plugins/camera/package.json @@ -0,0 +1,33 @@ +{ + "name": "tauri-plugin-camera-api", + "version": "0.0.0", + "description": "Ask the user take a photo with the camera or select an image from the gallery.", + "license": "MIT or APACHE-2.0", + "authors": [ + "Tauri Programme within The Commons Conservancy" + ], + "type": "module", + "browser": "dist-js/index.min.js", + "module": "dist-js/index.mjs", + "types": "dist-js/index.d.ts", + "exports": { + "import": "./dist-js/index.mjs", + "types": "./dist-js/index.d.ts", + "browser": "./dist-js/index.min.js" + }, + "scripts": { + "build": "rollup -c" + }, + "files": [ + "dist-js", + "!dist-js/**/*.map", + "README.md", + "LICENSE" + ], + "devDependencies": { + "tslib": "^2.4.1" + }, + "dependencies": { + "@tauri-apps/api": "^2.0.0-alpha.0" + } +} diff --git a/plugins/camera/rollup.config.mjs b/plugins/camera/rollup.config.mjs new file mode 100644 index 00000000..6555e98b --- /dev/null +++ b/plugins/camera/rollup.config.mjs @@ -0,0 +1,11 @@ +import { readFileSync } from "fs"; + +import { createConfig } from "../../shared/rollup.config.mjs"; + +export default createConfig({ + input: "guest-js/index.ts", + pkg: JSON.parse( + readFileSync(new URL("./package.json", import.meta.url), "utf8") + ), + external: [/^@tauri-apps\/api/], +}); diff --git a/plugins/camera/src/error.rs b/plugins/camera/src/error.rs new file mode 100644 index 00000000..393cc95e --- /dev/null +++ b/plugins/camera/src/error.rs @@ -0,0 +1,24 @@ +// Copyright 2019-2022 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use serde::{ser::Serializer, Serialize}; + +pub type Result = std::result::Result; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError), +} + +impl Serialize for Error { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: Serializer, + { + serializer.serialize_str(self.to_string().as_ref()) + } +} diff --git a/plugins/camera/src/lib.rs b/plugins/camera/src/lib.rs new file mode 100644 index 00000000..b598d523 --- /dev/null +++ b/plugins/camera/src/lib.rs @@ -0,0 +1,60 @@ +// Copyright 2019-2022 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +#![cfg(mobile)] + +use tauri::{ + plugin::{Builder, PluginHandle, TauriPlugin}, + Manager, Runtime, +}; + +pub use models::*; +mod error; +pub mod models; +pub use error::*; + +#[cfg(target_os = "android")] +const PLUGIN_IDENTIFIER: &str = "app.tauri.camera"; + +#[cfg(target_os = "ios")] +extern "C" { + fn init_plugin_camera(webview: tauri::cocoa::base::id); +} + +/// A helper class to access the mobile camera APIs. +pub struct Camera(PluginHandle); + +impl Camera { + pub fn get_photo(&self, options: ImageOptions) -> Result { + self + .0 + .run_mobile_plugin("getPhoto", options) + .map_err(Into::into) + } +} + +/// Extensions to [`tauri::App`], [`tauri::AppHandle`] and [`tauri::Window`] to access the camera APIs. +pub trait CameraExt { + fn camera(&self) -> &Camera; +} + +impl> CameraExt for T { + fn camera(&self) -> &Camera { + self.state::>().inner() + } +} + +/// Initializes the plugin. +pub fn init() -> TauriPlugin { + Builder::new("camera") + .setup(|app, api| { + #[cfg(target_os = "android")] + let handle = api.register_android_plugin(PLUGIN_IDENTIFIER, "CameraPlugin")?; + #[cfg(target_os = "ios")] + let handle = api.register_ios_plugin(init_plugin_camera)?; + app.manage(Camera(handle)); + Ok(()) + }) + .build() +} diff --git a/plugins/camera/src/models.rs b/plugins/camera/src/models.rs new file mode 100644 index 00000000..65294857 --- /dev/null +++ b/plugins/camera/src/models.rs @@ -0,0 +1,38 @@ +// Copyright 2019-2022 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Default, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ImageOptions { + pub quality: Option, + #[serde(default)] + pub allow_editing: bool, + pub result_type: Option, + #[serde(default)] + pub save_to_gallery: bool, + pub width: Option, + pub height: Option, + #[serde(default)] + pub correct_orientation: bool, + pub source: Option, + pub direction: Option, + pub presentation_style: Option, + pub prompt_label_header: Option, + pub prompt_label_cancel: Option, + pub prompt_label_photo: Option, + pub prompt_label_picture: Option, +} + +#[derive(Debug, Clone, Default, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Image { + pub data: String, + pub asset_url: Option, + pub format: String, + #[serde(default)] + pub saved: bool, + pub exif: serde_json::Value, +} diff --git a/plugins/camera/tsconfig.json b/plugins/camera/tsconfig.json new file mode 100644 index 00000000..5098169a --- /dev/null +++ b/plugins/camera/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["guest-js/*.ts"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a8fb00f2..9f3afc22 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -52,6 +52,34 @@ importers: devDependencies: tslib: 2.4.1 + plugins/camera: + specifiers: + '@tauri-apps/api': ^2.0.0-alpha.0 + tslib: ^2.4.1 + dependencies: + '@tauri-apps/api': 2.0.0-alpha.0 + devDependencies: + tslib: 2.4.1 + + plugins/camera/examples/tauri-app: + specifiers: + '@sveltejs/vite-plugin-svelte': ^1.0.1 + '@tauri-apps/api': ^1.1.0 + '@tauri-apps/cli': ^2.0.0-alpha.0 + internal-ip: ^7.0.0 + svelte: ^3.49.0 + tauri-plugin-camera-api: link:../../ + vite: ^3.0.2 + dependencies: + '@tauri-apps/api': 1.2.0 + tauri-plugin-camera-api: link:../.. + devDependencies: + '@sveltejs/vite-plugin-svelte': 1.4.0_svelte@3.55.1+vite@3.2.5 + '@tauri-apps/cli': 2.0.0-alpha.2 + internal-ip: 7.0.0 + svelte: 3.55.1 + vite: 3.2.5 + plugins/fs-extra: specifiers: '@tauri-apps/api': ^1.2.0 @@ -164,6 +192,15 @@ importers: packages: + /@esbuild/android-arm/0.15.18: + resolution: {integrity: sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-arm/0.16.16: resolution: {integrity: sha512-BUuWMlt4WSXod1HSl7aGK8fJOsi+Tab/M0IDK1V1/GstzoOpqc/v3DqmN8MkuapPKQ9Br1WtLAN4uEgWR8x64A==} engines: {node: '>=12'} @@ -254,6 +291,15 @@ packages: dev: true optional: true + /@esbuild/linux-loong64/0.15.18: + resolution: {integrity: sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-loong64/0.16.16: resolution: {integrity: sha512-MdUFggHjRiCCwNE9+1AibewoNq6wf94GLB9Q9aXwl+a75UlRmbRK3h6WJyrSGA6ZstDJgaD2wiTSP7tQNUYxwA==} engines: {node: '>=12'} @@ -565,6 +611,25 @@ packages: - supports-color dev: true + /@sveltejs/vite-plugin-svelte/1.4.0_svelte@3.55.1+vite@3.2.5: + resolution: {integrity: sha512-6QupI/jemMfK+yI2pMtJcu5iO2gtgTfcBdGwMZZt+lgbFELhszbDl6Qjh000HgAV8+XUA+8EY8DusOFk8WhOIg==} + engines: {node: ^14.18.0 || >= 16} + peerDependencies: + svelte: ^3.44.0 + vite: ^3.0.0 + dependencies: + debug: 4.3.4 + deepmerge: 4.2.2 + kleur: 4.1.5 + magic-string: 0.26.7 + svelte: 3.55.1 + svelte-hmr: 0.15.1_svelte@3.55.1 + vite: 3.2.5 + vitefu: 0.2.4_vite@3.2.5 + transitivePeerDependencies: + - supports-color + dev: true + /@sveltejs/vite-plugin-svelte/2.0.2_svelte@3.55.1+vite@4.0.4: resolution: {integrity: sha512-xCEan0/NNpQuL0l5aS42FjwQ6wwskdxC3pW1OeFtEKNZwRg7Evro9lac9HesGP6TdFsTv2xMes5ASQVKbCacxg==} engines: {node: ^14.18.0 || >= 16} @@ -589,6 +654,11 @@ packages: engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'} dev: false + /@tauri-apps/api/2.0.0-alpha.0: + resolution: {integrity: sha512-PQdy1Ao6JwKwW2/C11nP+IqnrWHB7+UgbM71zbzA1W3+1yyd9Zg+K7rzZ7f3yhvD7kdxmXUN3KgSfGeiDFzZ2A==} + engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'} + dev: false + /@tauri-apps/cli-darwin-arm64/1.2.2: resolution: {integrity: sha512-W+Cp2weUMlvmGkRJeUjypbz9Lpl6o98xkgKAtobZSum5SNwpsBQfawJTESakNoD+FXyVg/snIk5sRdHge+tAaA==} engines: {node: '>= 10'} @@ -598,6 +668,15 @@ packages: dev: false optional: true + /@tauri-apps/cli-darwin-arm64/2.0.0-alpha.2: + resolution: {integrity: sha512-Wu5QdZUgh0DEE0b3EKdJRkZzFoVngezxgvncQlMdXNaiKjdT767K2fB0XvQps+ycbtVLbUlG15jAwPZbWqRYGw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@tauri-apps/cli-darwin-x64/1.2.2: resolution: {integrity: sha512-vmVAqt+ECH2d6cbcGJ7ddcCAZgmKe5xmxlL5r4xoaphu7OqU4gnv4VFURYkVltOfwzIFQVOPVSqwYyIDToCYNQ==} engines: {node: '>= 10'} @@ -607,6 +686,15 @@ packages: dev: false optional: true + /@tauri-apps/cli-darwin-x64/2.0.0-alpha.2: + resolution: {integrity: sha512-e5VLsT/exSW1swUWkhCEAQ/fM8mZaUMoGeyESYtO7VfTNVglS0j+VfQ9a8taRxtOkajDZmqMDvmii4tA5I1Bbw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@tauri-apps/cli-linux-arm-gnueabihf/1.2.2: resolution: {integrity: sha512-yYTdQurgi4QZR8z+fANjl522jdQz/VtesFpw+C/A0+zXg7tiRjicsywBDdPsvNzCqFeGKKkmTR+Lny5qxhGaeQ==} engines: {node: '>= 10'} @@ -616,6 +704,15 @@ packages: dev: false optional: true + /@tauri-apps/cli-linux-arm-gnueabihf/2.0.0-alpha.2: + resolution: {integrity: sha512-+/emaFpDPuqnTIyh+WcDqCbzc/SoREFfLDyumqdnFjRU1Uvc2Z9Eo/sMVnfuUw5vDMc2EPzYtT3uiZGez65ZTA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@tauri-apps/cli-linux-arm64-gnu/1.2.2: resolution: {integrity: sha512-ZSOVT6Eq1ay2+27B8KfA0MnpO7KYzONU6TjenH7DNcQki6eWGG5JoNu8QQ9Mdn3dAzY0XBP9i1ZHQOFu4iPtEg==} engines: {node: '>= 10'} @@ -625,6 +722,15 @@ packages: dev: false optional: true + /@tauri-apps/cli-linux-arm64-gnu/2.0.0-alpha.2: + resolution: {integrity: sha512-UHAyqt8fFbp9MEbUHSiEKjJC98w/Dta3r9auE70K+/uNpt9pnP/lGturDWWAJagRIFwYKPyqSEqL5qFcKadYqw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@tauri-apps/cli-linux-arm64-musl/1.2.2: resolution: {integrity: sha512-/NHSkqNQ+Pr4PshvyD1CeNFaPCaCpe1OeuAQgVi0rboSecC9fXN96G5dQbSBoxOUcCo6f8aTVE7zkZ4WchFVog==} engines: {node: '>= 10'} @@ -634,6 +740,15 @@ packages: dev: false optional: true + /@tauri-apps/cli-linux-arm64-musl/2.0.0-alpha.2: + resolution: {integrity: sha512-3euwm11RWvmTX+ISR/Y+N0TaWTJCRIj1pDgB+r2ZptRKlVTMNTJZDTXQlyJKcWEpi1azlbdxAzRvhN8NrgDmyA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@tauri-apps/cli-linux-x64-gnu/1.2.2: resolution: {integrity: sha512-4YTmfPuyvlHsvCkATDMwhklfuQm3HKxYXv/IOW9H0ra6pS9efVhrFYIC9Vfv6XaKN85Vnn/FYTEGMJLwCxZw2Q==} engines: {node: '>= 10'} @@ -643,6 +758,15 @@ packages: dev: false optional: true + /@tauri-apps/cli-linux-x64-gnu/2.0.0-alpha.2: + resolution: {integrity: sha512-ijJ8Wij5mVd9p6lXQ+pXoFlx3Iv1JS1KQTeySICds43xzE8esGp5+HXRXDwWqQLdVmtI77P5VRIe2ssXiaeDUg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@tauri-apps/cli-linux-x64-musl/1.2.2: resolution: {integrity: sha512-wr46tbscwFuCcA931R+ItOiUTT0djMmgKLd1HFCmFF82V9BKE2reIjr6O9l0NCXCo2WeD4pe3jA/Pt1dxDu+JA==} engines: {node: '>= 10'} @@ -652,6 +776,15 @@ packages: dev: false optional: true + /@tauri-apps/cli-linux-x64-musl/2.0.0-alpha.2: + resolution: {integrity: sha512-sg8OTQfG/zJ4+6MA/+hk08hVb57iJn5VZDzBb3o6IpJ0cwtM8YDNv5C+6HWttBuxsn4oEoYxGml/FvowMfsOCg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@tauri-apps/cli-win32-ia32-msvc/1.2.2: resolution: {integrity: sha512-6VmbVJOWUZJK5/JKhb3mNFKrKGfq0KV7lJGumfN95WJgkHeyL61p8bZit+o6ZgUGUhrOabkAawhDkrRY+ZQhIw==} engines: {node: '>= 10'} @@ -661,6 +794,15 @@ packages: dev: false optional: true + /@tauri-apps/cli-win32-ia32-msvc/2.0.0-alpha.2: + resolution: {integrity: sha512-R1AmO3GEm97ptM0tjxZjZ1fLnxzN3ZeOEKc85nR7ayqVqKVhMu+dhq5lKa/Y3GdMUR6Yj9GoCnaLp2xy4bV6JQ==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@tauri-apps/cli-win32-x64-msvc/1.2.2: resolution: {integrity: sha512-YRPJguJma+zSuRZpFoSZqls6+laggG1vqG0FPQWQTi+ywATgMpai2b2RZnffDlpHKp9mt4V/s2dtqOy6bpGZHg==} engines: {node: '>= 10'} @@ -670,6 +812,15 @@ packages: dev: false optional: true + /@tauri-apps/cli-win32-x64-msvc/2.0.0-alpha.2: + resolution: {integrity: sha512-cLJJWxCdvvQP+I0B4h6h0TMMNYISoatQu57QVxPqypbkC/lK/ljjrbD5nu7M9wTFBkLkCTGyMC7N99esCmgIBQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@tauri-apps/cli/1.2.2: resolution: {integrity: sha512-D8zib3A0vWCvPPSyYLxww/OdDlVcY7fpcDVBH6qUvheOjj2aCyU7H9AYMRBwpgCfz8zY5+vomee+laLeB0H13w==} engines: {node: '>= 10'} @@ -686,6 +837,22 @@ packages: '@tauri-apps/cli-win32-x64-msvc': 1.2.2 dev: false + /@tauri-apps/cli/2.0.0-alpha.2: + resolution: {integrity: sha512-M5o2ESOv9jGr7oIDl3sR3Q5++DXSW4xyfxzKCyu1JVGlOc+C9Q4y0dbKhlpd0wPCAxRa0ikbfu7z8qfEhHSpVQ==} + engines: {node: '>= 10'} + hasBin: true + optionalDependencies: + '@tauri-apps/cli-darwin-arm64': 2.0.0-alpha.2 + '@tauri-apps/cli-darwin-x64': 2.0.0-alpha.2 + '@tauri-apps/cli-linux-arm-gnueabihf': 2.0.0-alpha.2 + '@tauri-apps/cli-linux-arm64-gnu': 2.0.0-alpha.2 + '@tauri-apps/cli-linux-arm64-musl': 2.0.0-alpha.2 + '@tauri-apps/cli-linux-x64-gnu': 2.0.0-alpha.2 + '@tauri-apps/cli-linux-x64-musl': 2.0.0-alpha.2 + '@tauri-apps/cli-win32-ia32-msvc': 2.0.0-alpha.2 + '@tauri-apps/cli-win32-x64-msvc': 2.0.0-alpha.2 + dev: true + /@types/cookie/0.5.1: resolution: {integrity: sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==} dev: true @@ -1086,6 +1253,13 @@ packages: engines: {node: '>=0.10.0'} dev: true + /default-gateway/6.0.3: + resolution: {integrity: sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==} + engines: {node: '>= 10'} + dependencies: + execa: 5.1.1 + dev: true + /define-properties/1.1.4: resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} engines: {node: '>= 0.4'} @@ -1174,6 +1348,216 @@ packages: resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} dev: true + /esbuild-android-64/0.15.18: + resolution: {integrity: sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /esbuild-android-arm64/0.15.18: + resolution: {integrity: sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /esbuild-darwin-64/0.15.18: + resolution: {integrity: sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /esbuild-darwin-arm64/0.15.18: + resolution: {integrity: sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /esbuild-freebsd-64/0.15.18: + resolution: {integrity: sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-freebsd-arm64/0.15.18: + resolution: {integrity: sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-32/0.15.18: + resolution: {integrity: sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-64/0.15.18: + resolution: {integrity: sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-arm/0.15.18: + resolution: {integrity: sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-arm64/0.15.18: + resolution: {integrity: sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-mips64le/0.15.18: + resolution: {integrity: sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-ppc64le/0.15.18: + resolution: {integrity: sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-riscv64/0.15.18: + resolution: {integrity: sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-s390x/0.15.18: + resolution: {integrity: sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-netbsd-64/0.15.18: + resolution: {integrity: sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-openbsd-64/0.15.18: + resolution: {integrity: sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-sunos-64/0.15.18: + resolution: {integrity: sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-32/0.15.18: + resolution: {integrity: sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-64/0.15.18: + resolution: {integrity: sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-arm64/0.15.18: + resolution: {integrity: sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild/0.15.18: + resolution: {integrity: sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.15.18 + '@esbuild/linux-loong64': 0.15.18 + esbuild-android-64: 0.15.18 + esbuild-android-arm64: 0.15.18 + esbuild-darwin-64: 0.15.18 + esbuild-darwin-arm64: 0.15.18 + esbuild-freebsd-64: 0.15.18 + esbuild-freebsd-arm64: 0.15.18 + esbuild-linux-32: 0.15.18 + esbuild-linux-64: 0.15.18 + esbuild-linux-arm: 0.15.18 + esbuild-linux-arm64: 0.15.18 + esbuild-linux-mips64le: 0.15.18 + esbuild-linux-ppc64le: 0.15.18 + esbuild-linux-riscv64: 0.15.18 + esbuild-linux-s390x: 0.15.18 + esbuild-netbsd-64: 0.15.18 + esbuild-openbsd-64: 0.15.18 + esbuild-sunos-64: 0.15.18 + esbuild-windows-32: 0.15.18 + esbuild-windows-64: 0.15.18 + esbuild-windows-arm64: 0.15.18 + dev: true + /esbuild/0.16.16: resolution: {integrity: sha512-24JyKq10KXM5EBIgPotYIJ2fInNWVVqflv3gicIyQqfmUqi4HvDW1VR790cBgLJHCl96Syy7lhoz7tLFcmuRmg==} engines: {node: '>=12'} @@ -1502,6 +1886,21 @@ packages: engines: {node: '>=0.10.0'} dev: true + /execa/5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + /fast-deep-equal/3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true @@ -1603,6 +2002,11 @@ packages: has-symbols: 1.0.3 dev: true + /get-stream/6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true + /get-symbol-description/1.0.0: resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} engines: {node: '>= 0.4'} @@ -1711,6 +2115,11 @@ packages: function-bind: 1.1.1 dev: true + /human-signals/2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + dev: true + /ignore/5.2.1: resolution: {integrity: sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==} engines: {node: '>= 4'} @@ -1744,6 +2153,16 @@ packages: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} dev: true + /internal-ip/7.0.0: + resolution: {integrity: sha512-qE4TeD4brqC45Vq/+VASeMiS1KRyfBkR6HT2sh9pZVVCzSjPkaCEfKFU+dL0PRv7NHJtvoKN2r82G6wTfzorkw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + default-gateway: 6.0.3 + ipaddr.js: 2.0.1 + is-ip: 3.1.0 + p-event: 4.2.0 + dev: true + /internal-slot/1.0.4: resolution: {integrity: sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==} engines: {node: '>= 0.4'} @@ -1753,6 +2172,16 @@ packages: side-channel: 1.0.4 dev: true + /ip-regex/4.3.0: + resolution: {integrity: sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==} + engines: {node: '>=8'} + dev: true + + /ipaddr.js/2.0.1: + resolution: {integrity: sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==} + engines: {node: '>= 10'} + dev: true + /is-bigint/1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} dependencies: @@ -1811,6 +2240,13 @@ packages: is-extglob: 2.1.1 dev: true + /is-ip/3.1.0: + resolution: {integrity: sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==} + engines: {node: '>=8'} + dependencies: + ip-regex: 4.3.0 + dev: true + /is-module/1.0.0: resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} dev: true @@ -1851,6 +2287,11 @@ packages: call-bind: 1.0.2 dev: true + /is-stream/2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: true + /is-string/1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} @@ -1938,6 +2379,13 @@ packages: sourcemap-codec: 1.4.8 dev: true + /magic-string/0.26.7: + resolution: {integrity: sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==} + engines: {node: '>=12'} + dependencies: + sourcemap-codec: 1.4.8 + dev: true + /magic-string/0.27.0: resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} engines: {node: '>=12'} @@ -1945,6 +2393,10 @@ packages: '@jridgewell/sourcemap-codec': 1.4.14 dev: true + /merge-stream/2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + /merge2/1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1964,6 +2416,11 @@ packages: hasBin: true dev: true + /mimic-fn/2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true + /min-indent/1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} @@ -2027,6 +2484,13 @@ packages: engines: {node: '>=0.10.0'} dev: true + /npm-run-path/4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + dependencies: + path-key: 3.1.1 + dev: true + /object-inspect/1.12.2: resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==} dev: true @@ -2061,6 +2525,13 @@ packages: wrappy: 1.0.2 dev: true + /onetime/5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + /optionator/0.9.1: resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} engines: {node: '>= 0.8.0'} @@ -2073,6 +2544,18 @@ packages: word-wrap: 1.2.3 dev: true + /p-event/4.2.0: + resolution: {integrity: sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==} + engines: {node: '>=8'} + dependencies: + p-timeout: 3.2.0 + dev: true + + /p-finally/1.0.0: + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} + dev: true + /p-limit/3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -2087,6 +2570,13 @@ packages: p-limit: 3.1.0 dev: true + /p-timeout/3.2.0: + resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} + engines: {node: '>=8'} + dependencies: + p-finally: 1.0.0 + dev: true + /parent-module/1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -2216,6 +2706,14 @@ packages: glob: 7.2.3 dev: true + /rollup/2.79.1: + resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==} + engines: {node: '>=10.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + /rollup/3.7.4: resolution: {integrity: sha512-jN9rx3k5pfg9H9al0r0y1EYKSeiRANZRYX32SuNXAnKzh6cVyf4LZVto1KAuDnbHT03E1CpsgqDKaqQ8FZtgxw==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} @@ -2296,6 +2794,10 @@ packages: object-inspect: 1.12.2 dev: true + /signal-exit/3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true + /sirv/2.0.2: resolution: {integrity: sha512-4Qog6aE29nIjAOKe/wowFTxOdmbEZKb+3tsLljaBRzJwtqto0BChD2zzH0LhgCSXiI+V7X+Y45v14wBZQ1TK3w==} engines: {node: '>= 10'} @@ -2379,6 +2881,11 @@ packages: engines: {node: '>=4'} dev: true + /strip-final-newline/2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + dev: true + /strip-indent/3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -2597,6 +3104,39 @@ packages: punycode: 2.1.1 dev: true + /vite/3.2.5: + resolution: {integrity: sha512-4mVEpXpSOgrssFZAOmGIr85wPHKvaDAcXqxVxVRZhljkJOMZi1ibLibzjLHzJvcok8BMguLc7g1W6W/GqZbLdQ==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + esbuild: 0.15.18 + postcss: 8.4.21 + resolve: 1.22.1 + rollup: 2.79.1 + optionalDependencies: + fsevents: 2.3.2 + dev: true + /vite/4.0.4: resolution: {integrity: sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==} engines: {node: ^14.18.0 || >=16.0.0} @@ -2630,6 +3170,17 @@ packages: fsevents: 2.3.2 dev: true + /vitefu/0.2.4_vite@3.2.5: + resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 + peerDependenciesMeta: + vite: + optional: true + dependencies: + vite: 3.2.5 + dev: true + /vitefu/0.2.4_vite@4.0.4: resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==} peerDependencies: