mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-09-11 07:55:34 +03:00
Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com> Co-authored-by: Lucas Nogueira <lucas@tauri.studio> Co-authored-by: Raphii <iam@raphii.co> Co-authored-by: Fabian-Lars <fabianlars@fabianlars.de> Co-authored-by: Lucas Nogueira <lucas@tauri.app>
This commit is contained in:
parent
84c4159754
commit
3b98141aa2
5
.changes/file-associations-config.md
Normal file
5
.changes/file-associations-config.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri-utils": minor:feat
|
||||
---
|
||||
|
||||
Add a configuration object for file associations under `BundleConfig`.
|
5
.changes/file-associations.md
Normal file
5
.changes/file-associations.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri": minor:feat
|
||||
---
|
||||
|
||||
Added support to file associations.
|
6
.changes/runtime-opened-event.md
Normal file
6
.changes/runtime-opened-event.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
"tauri-runtime": minor:feat
|
||||
"tauri-runtime-wry": minor:feat
|
||||
---
|
||||
|
||||
Added the `Opened` variant to `RunEvent`.
|
@ -121,7 +121,7 @@ jobs:
|
||||
steps.covector.outputs.successfulPublish == 'true' &&
|
||||
contains(steps.covector.outputs.packagesPublished, '@tauri-apps/cli')
|
||||
run: |
|
||||
echo '${{ steps.covector.outputs }}' > output.json
|
||||
echo '${{ toJSON(steps.covector.outputs) }}' > output.json
|
||||
id=$(jq '.["-tauri-apps-cli-releaseId"]' < output.json)
|
||||
rm output.json
|
||||
echo "cliReleaseId=$id" >> "$GITHUB_OUTPUT"
|
||||
|
@ -20,6 +20,7 @@ exclude = [
|
||||
"examples/resources/src-tauri",
|
||||
"examples/sidecar/src-tauri",
|
||||
"examples/web/core",
|
||||
"examples/file-associations/src-tauri",
|
||||
"examples/workspace",
|
||||
]
|
||||
|
||||
|
@ -925,6 +925,16 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"fileAssociations": {
|
||||
"description": "File associations to application.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/FileAssociation"
|
||||
}
|
||||
},
|
||||
"shortDescription": {
|
||||
"description": "A short description of your application.",
|
||||
"type": [
|
||||
@ -1122,6 +1132,97 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"FileAssociation": {
|
||||
"description": "File association",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ext"
|
||||
],
|
||||
"properties": {
|
||||
"ext": {
|
||||
"description": "File extensions to associate with this app. e.g. 'png'",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AssociationExt"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"description": "The name. Maps to `CFBundleTypeName` on macOS. Default to ext[0]",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"description": {
|
||||
"description": "The association description. Windows-only. It is displayed on the `Type` column on Windows Explorer.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"role": {
|
||||
"description": "The app’s role with respect to the type. Maps to `CFBundleTypeRole` on macOS.",
|
||||
"default": "Editor",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BundleTypeRole"
|
||||
}
|
||||
]
|
||||
},
|
||||
"mimeType": {
|
||||
"description": "The mime-type e.g. 'image/png' or 'text/plain'. Linux-only.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"AssociationExt": {
|
||||
"description": "An extension for a [`FileAssociation`].\n\nA leading `.` is automatically stripped.",
|
||||
"type": "string"
|
||||
},
|
||||
"BundleTypeRole": {
|
||||
"description": "macOS-only. Corresponds to CFBundleTypeRole",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "CFBundleTypeRole.Editor. Files can be read and edited.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Editor"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "CFBundleTypeRole.Viewer. Files can be read.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Viewer"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "CFBundleTypeRole.Shell",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Shell"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "CFBundleTypeRole.QLGenerator",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"QLGenerator"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "CFBundleTypeRole.None",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"None"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"AppImageConfig": {
|
||||
"description": "Configuration for AppImage bundles.\n\nSee more: https://tauri.app/v1/api/config#appimageconfig",
|
||||
"type": "object",
|
||||
|
@ -13,7 +13,7 @@ edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
wry = { version = "0.28.3", default-features = false, features = [ "file-drop", "protocol" ] }
|
||||
wry = { version = "0.30", default-features = false, features = [ "file-drop", "protocol" ] }
|
||||
tauri-runtime = { version = "0.13.0-alpha.6", path = "../tauri-runtime" }
|
||||
tauri-utils = { version = "2.0.0-alpha.6", path = "../tauri-utils" }
|
||||
uuid = { version = "1", features = [ "v4" ] }
|
||||
@ -21,15 +21,15 @@ rand = "0.8"
|
||||
raw-window-handle = "0.5"
|
||||
|
||||
[target."cfg(windows)".dependencies]
|
||||
webview2-com = "0.22"
|
||||
webview2-com = "0.25"
|
||||
|
||||
[target."cfg(windows)".dependencies.windows]
|
||||
version = "0.44"
|
||||
version = "0.48"
|
||||
features = [ "Win32_Foundation" ]
|
||||
|
||||
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
|
||||
gtk = { version = "0.16", features = [ "v3_24" ] }
|
||||
webkit2gtk = { version = "0.19.1", features = [ "v2_38" ] }
|
||||
webkit2gtk = { version = "1.1", features = [ "v2_38" ] }
|
||||
percent-encoding = "2.1"
|
||||
|
||||
[target."cfg(any(target_os = \"ios\", target_os = \"macos\"))".dependencies]
|
||||
@ -48,4 +48,4 @@ macos-private-api = [
|
||||
"tauri-runtime/macos-private-api"
|
||||
]
|
||||
objc-exception = [ "wry/objc-exception" ]
|
||||
linux-headers = [ "wry/linux-headers", "webkit2gtk/v2_36" ]
|
||||
linux-headers = [ ]
|
||||
|
@ -2952,6 +2952,10 @@ fn handle_event_loop<T: UserEvent>(
|
||||
);
|
||||
}
|
||||
},
|
||||
#[cfg(target_os = "macos")]
|
||||
Event::Opened { urls } => {
|
||||
callback(RunEvent::Opened { urls });
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ rand = "0.8"
|
||||
url = { version = "2" }
|
||||
|
||||
[target."cfg(windows)".dependencies.windows]
|
||||
version = "0.44"
|
||||
version = "0.48"
|
||||
features = [ "Win32_Foundation" ]
|
||||
|
||||
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
|
||||
@ -44,6 +44,9 @@ gtk = { version = "0.16", features = [ "v3_24" ] }
|
||||
[target."cfg(target_os = \"android\")".dependencies]
|
||||
jni = "0.20"
|
||||
|
||||
[target."cfg(target_os = \"macos\")".dependencies]
|
||||
url = "2"
|
||||
|
||||
[features]
|
||||
devtools = [ ]
|
||||
system-tray = [ ]
|
||||
|
@ -290,6 +290,7 @@ pub trait UserEvent: Debug + Clone + Send + 'static {}
|
||||
impl<T: Debug + Clone + Send + 'static> UserEvent for T {}
|
||||
|
||||
/// Event triggered on the event loop run.
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum RunEvent<T: UserEvent> {
|
||||
/// Event loop is exiting.
|
||||
@ -313,6 +314,9 @@ pub enum RunEvent<T: UserEvent> {
|
||||
///
|
||||
/// This event is useful as a place to put your code that should be run after all state-changing events have been handled and you want to do stuff (updating state, performing calculations, etc) that happens as the “main body” of your event loop.
|
||||
MainEventsCleared,
|
||||
/// Emitted when the user wants to open the specified resource with the app.
|
||||
#[cfg(target_os = "macos")]
|
||||
Opened { urls: Vec<url::Url> },
|
||||
/// A custom event defined by the user.
|
||||
UserEvent(T),
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ dunce = "1"
|
||||
heck = "0.4"
|
||||
|
||||
[target."cfg(windows)".dependencies.windows]
|
||||
version = "0.44.0"
|
||||
version = "0.48.0"
|
||||
features = [
|
||||
"implement",
|
||||
"Win32_Foundation",
|
||||
|
@ -624,6 +624,78 @@ impl Default for WindowsConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// macOS-only. Corresponds to CFBundleTypeRole
|
||||
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "schema", derive(JsonSchema))]
|
||||
pub enum BundleTypeRole {
|
||||
/// CFBundleTypeRole.Editor. Files can be read and edited.
|
||||
#[default]
|
||||
Editor,
|
||||
/// CFBundleTypeRole.Viewer. Files can be read.
|
||||
Viewer,
|
||||
/// CFBundleTypeRole.Shell
|
||||
Shell,
|
||||
/// CFBundleTypeRole.QLGenerator
|
||||
QLGenerator,
|
||||
/// CFBundleTypeRole.None
|
||||
None,
|
||||
}
|
||||
|
||||
impl Display for BundleTypeRole {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Editor => write!(f, "Editor"),
|
||||
Self::Viewer => write!(f, "Viewer"),
|
||||
Self::Shell => write!(f, "Shell"),
|
||||
Self::QLGenerator => write!(f, "QLGenerator"),
|
||||
Self::None => write!(f, "None"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An extension for a [`FileAssociation`].
|
||||
///
|
||||
/// A leading `.` is automatically stripped.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
|
||||
#[cfg_attr(feature = "schema", derive(JsonSchema))]
|
||||
pub struct AssociationExt(pub String);
|
||||
|
||||
impl fmt::Display for AssociationExt {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> serde::Deserialize<'d> for AssociationExt {
|
||||
fn deserialize<D: Deserializer<'d>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let ext = String::deserialize(deserializer)?;
|
||||
if let Some(ext) = ext.strip_prefix('.') {
|
||||
Ok(AssociationExt(ext.into()))
|
||||
} else {
|
||||
Ok(AssociationExt(ext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// File association
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "schema", derive(JsonSchema))]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
pub struct FileAssociation {
|
||||
/// File extensions to associate with this app. e.g. 'png'
|
||||
pub ext: Vec<AssociationExt>,
|
||||
/// The name. Maps to `CFBundleTypeName` on macOS. Default to ext[0]
|
||||
pub name: Option<String>,
|
||||
/// The association description. Windows-only. It is displayed on the `Type` column on Windows Explorer.
|
||||
pub description: Option<String>,
|
||||
/// The app’s role with respect to the type. Maps to `CFBundleTypeRole` on macOS.
|
||||
#[serde(default)]
|
||||
pub role: BundleTypeRole,
|
||||
/// The mime-type e.g. 'image/png' or 'text/plain'. Linux-only.
|
||||
#[serde(alias = "mime-type")]
|
||||
pub mime_type: Option<String>,
|
||||
}
|
||||
|
||||
/// The Updater configuration object.
|
||||
///
|
||||
/// See more: https://tauri.app/v1/api/config#updaterconfig
|
||||
@ -720,6 +792,8 @@ pub struct BundleConfig {
|
||||
/// Should be one of the following:
|
||||
/// Business, DeveloperTool, Education, Entertainment, Finance, Game, ActionGame, AdventureGame, ArcadeGame, BoardGame, CardGame, CasinoGame, DiceGame, EducationalGame, FamilyGame, KidsGame, MusicGame, PuzzleGame, RacingGame, RolePlayingGame, SimulationGame, SportsGame, StrategyGame, TriviaGame, WordGame, GraphicsAndDesign, HealthcareAndFitness, Lifestyle, Medical, Music, News, Photography, Productivity, Reference, SocialNetworking, Sports, Travel, Utility, Video, Weather.
|
||||
pub category: Option<String>,
|
||||
/// File associations to application.
|
||||
pub file_associations: Option<Vec<FileAssociation>>,
|
||||
/// A short description of your application.
|
||||
#[serde(alias = "short-description")]
|
||||
pub short_description: Option<String>,
|
||||
@ -1668,7 +1742,7 @@ fn default_dist_dir() -> AppUrl {
|
||||
struct PackageVersion(String);
|
||||
|
||||
impl<'d> serde::Deserialize<'d> for PackageVersion {
|
||||
fn deserialize<D: Deserializer<'d>>(deserializer: D) -> Result<PackageVersion, D::Error> {
|
||||
fn deserialize<D: Deserializer<'d>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
struct PackageVersionVisitor;
|
||||
|
||||
impl<'d> Visitor<'d> for PackageVersionVisitor {
|
||||
@ -2291,6 +2365,7 @@ mod build {
|
||||
let resources = quote!(None);
|
||||
let copyright = quote!(None);
|
||||
let category = quote!(None);
|
||||
let file_associations = quote!(None);
|
||||
let short_description = quote!(None);
|
||||
let long_description = quote!(None);
|
||||
let appimage = quote!(Default::default());
|
||||
@ -2313,6 +2388,7 @@ mod build {
|
||||
resources,
|
||||
copyright,
|
||||
category,
|
||||
file_associations,
|
||||
short_description,
|
||||
long_description,
|
||||
appimage,
|
||||
@ -2616,6 +2692,7 @@ mod test {
|
||||
resources: None,
|
||||
copyright: None,
|
||||
category: None,
|
||||
file_associations: None,
|
||||
short_description: None,
|
||||
long_description: None,
|
||||
appimage: Default::default(),
|
||||
|
@ -70,7 +70,7 @@ ico = { version = "0.2.0", optional = true }
|
||||
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
|
||||
gtk = { version = "0.16", features = [ "v3_24" ] }
|
||||
glib = "0.16"
|
||||
webkit2gtk = { version = "0.19.1", features = [ "v2_38" ] }
|
||||
webkit2gtk = { version = "1.1", features = [ "v2_38" ] }
|
||||
|
||||
[target."cfg(target_os = \"macos\")".dependencies]
|
||||
embed_plist = "1.2"
|
||||
@ -78,10 +78,10 @@ cocoa = "0.24"
|
||||
objc = "0.2"
|
||||
|
||||
[target."cfg(windows)".dependencies]
|
||||
webview2-com = "0.22"
|
||||
webview2-com = "0.25"
|
||||
|
||||
[target."cfg(windows)".dependencies.windows]
|
||||
version = "0.44"
|
||||
version = "0.48"
|
||||
features = [ "Win32_Foundation" ]
|
||||
|
||||
[target."cfg(any(target_os = \"android\", target_os = \"ios\"))".dependencies]
|
||||
|
@ -177,6 +177,12 @@ pub enum RunEvent {
|
||||
///
|
||||
/// This event is useful as a place to put your code that should be run after all state-changing events have been handled and you want to do stuff (updating state, performing calculations, etc) that happens as the “main body” of your event loop.
|
||||
MainEventsCleared,
|
||||
/// Emitted when the user wants to open the specified resource with the app.
|
||||
#[cfg(target_os = "macos")]
|
||||
Opened {
|
||||
/// The URL of the resources that is being open.
|
||||
urls: Vec<url::Url>,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<EventLoopMessage> for RunEvent {
|
||||
@ -1008,6 +1014,7 @@ impl<R: Runtime> Builder<R> {
|
||||
/// [`State`](crate::State) guard. In particular, if a value of type `T`
|
||||
/// is managed by Tauri, adding `State<T>` to the list of arguments in a
|
||||
/// command handler instructs Tauri to retrieve the managed value.
|
||||
/// Additionally, [`state`](crate::Manager#method.state) can be used to retrieve the value manually.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
@ -1516,6 +1523,8 @@ fn on_event_loop_event<R: Runtime, F: FnMut(&AppHandle<R>, RunEvent) + 'static>(
|
||||
RuntimeRunEvent::Resumed => RunEvent::Resumed,
|
||||
RuntimeRunEvent::MainEventsCleared => RunEvent::MainEventsCleared,
|
||||
RuntimeRunEvent::UserEvent(t) => t.into(),
|
||||
#[cfg(target_os = "macos")]
|
||||
RuntimeRunEvent::Opened { urls } => RunEvent::Opened { urls },
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
|
||||
|
@ -680,18 +680,13 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
|
||||
|
||||
/// Add `state` to the state managed by the application.
|
||||
///
|
||||
/// This method can be called any number of times as long as each call
|
||||
/// refers to a different `T`.
|
||||
/// If a state for `T` is already managed, the function returns false and the value is ignored.
|
||||
/// If the state for the `T` type has previously been set, the state is unchanged and false is returned. Otherwise true is returned.
|
||||
///
|
||||
/// Managed state can be retrieved by any command handler via the
|
||||
/// [`State`](crate::State) guard. In particular, if a value of type `T`
|
||||
/// is managed by Tauri, adding `State<T>` to the list of arguments in a
|
||||
/// command handler instructs Tauri to retrieve the managed value.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if state of type `T` is already being managed.
|
||||
/// Additionally, [`state`](Self#method.state) can be used to retrieve the value manually.
|
||||
///
|
||||
/// # Mutability
|
||||
///
|
||||
|
@ -1054,8 +1054,8 @@ impl<R: Runtime> Window<R> {
|
||||
/// });
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(all(feature = "wry"))]
|
||||
#[cfg_attr(doc_cfg, doc(all(feature = "wry")))]
|
||||
#[cfg(feature = "wry")]
|
||||
#[cfg_attr(doc_cfg, doc(feature = "wry"))]
|
||||
pub fn with_webview<F: FnOnce(PlatformWebview) + Send + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
|
657
examples/api/src-tauri/Cargo.lock
generated
657
examples/api/src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
11
examples/file-associations/README.md
Normal file
11
examples/file-associations/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# File Associations Example
|
||||
|
||||
This example demonstrates how to make associations between the application and certain file types.
|
||||
|
||||
This feature is commonly used for functionality such as previewing or editing files.
|
||||
|
||||
## Running the example
|
||||
|
||||
```
|
||||
cargo build --example file-associations
|
||||
```
|
26
examples/file-associations/index.html
Normal file
26
examples/file-associations/index.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>File Associations</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>File Associations</h1>
|
||||
<div id="files"></div>
|
||||
|
||||
<script>
|
||||
const d = document.getElementById('files')
|
||||
d.textContent = window.openedUrls
|
||||
|
||||
window.onFileOpen = (files) => {
|
||||
console.log(files)
|
||||
d.textContent = files
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
15
examples/file-associations/package.json
Normal file
15
examples/file-associations/package.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "file-associations",
|
||||
"version": "1.0.0",
|
||||
"pkg": {
|
||||
"assets": [
|
||||
"src/**/*"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"tauri": "node ../../tooling/cli/node/tauri.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
}
|
||||
}
|
4
examples/file-associations/src-tauri/.gitignore
vendored
Normal file
4
examples/file-associations/src-tauri/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
WixTools
|
3
examples/file-associations/src-tauri/.license_template
Normal file
3
examples/file-associations/src-tauri/.license_template
Normal file
@ -0,0 +1,3 @@
|
||||
// Copyright {20\d{2}(-20\d{2})?} Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
4069
examples/file-associations/src-tauri/Cargo.lock
generated
Normal file
4069
examples/file-associations/src-tauri/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
19
examples/file-associations/src-tauri/Cargo.toml
Normal file
19
examples/file-associations/src-tauri/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "tauri-file-associations-demo"
|
||||
version = "0.1.0"
|
||||
description = "A Tauri application that associate file types"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { path = "../../../core/tauri-build", features = ["codegen"] }
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = [ "derive" ] }
|
||||
tauri = { path = "../../../core/tauri", features = [] }
|
||||
url = "2"
|
||||
|
||||
[features]
|
||||
default = [ "custom-protocol" ]
|
||||
custom-protocol = [ "tauri/custom-protocol" ]
|
21
examples/file-associations/src-tauri/Info.plist
Normal file
21
examples/file-associations/src-tauri/Info.plist
Normal file
@ -0,0 +1,21 @@
|
||||
<!-- Add this file next to your tauri.conf.json file -->
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<!-- Obviously needs to be replaced with your app's bundle identifier -->
|
||||
<string>com.tauri.dev-file-associations-demo</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<!-- register the myapp:// and myscheme:// schemes -->
|
||||
<string>myapp</string>
|
||||
<string>myscheme</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
7
examples/file-associations/src-tauri/build.rs
Normal file
7
examples/file-associations/src-tauri/build.rs
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
69
examples/file-associations/src-tauri/src/main.rs
Normal file
69
examples/file-associations/src-tauri/src/main.rs
Normal file
@ -0,0 +1,69 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#![cfg_attr(
|
||||
all(not(debug_assertions), target_os = "windows"),
|
||||
windows_subsystem = "windows"
|
||||
)]
|
||||
|
||||
use std::{env, sync::Mutex};
|
||||
use tauri::Manager;
|
||||
|
||||
struct OpenedUrls(Mutex<Option<Vec<url::Url>>>);
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.manage(OpenedUrls(Default::default()))
|
||||
.setup(|app| {
|
||||
#[cfg(any(windows, target_os = "linux"))]
|
||||
{
|
||||
// NOTICE: `args` may include URL protocol (`your-app-protocol://`) or arguments (`--`) if app supports them.
|
||||
let mut urls = Vec::new();
|
||||
for arg in env::args().skip(1) {
|
||||
if let Ok(url) = url::Url::parse(&arg) {
|
||||
urls.push(url);
|
||||
}
|
||||
}
|
||||
|
||||
if !urls.is_empty() {
|
||||
app.state::<OpenedUrls>().0.lock().unwrap().replace(urls);
|
||||
}
|
||||
}
|
||||
|
||||
let opened_urls = if let Some(urls) = &*app.state::<OpenedUrls>().0.lock().unwrap() {
|
||||
urls
|
||||
.iter()
|
||||
.map(|u| u.as_str().replace("\\", "\\\\"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
} else {
|
||||
"".into()
|
||||
};
|
||||
|
||||
tauri::WindowBuilder::new(app, "main", Default::default())
|
||||
.initialization_script(&format!("window.openedUrls = `{opened_urls}`"))
|
||||
.initialization_script(&format!("console.log(`{opened_urls}`)"))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.build(tauri::generate_context!())
|
||||
.expect("error while running tauri application")
|
||||
.run(|app, event| {
|
||||
#[cfg(target_os = "macos")]
|
||||
if let tauri::RunEvent::Opened { urls } = event {
|
||||
if let Some(w) = app.get_window("main") {
|
||||
let urls = urls
|
||||
.iter()
|
||||
.map(|u| u.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
let _ = w.eval(&format!("window.onFileOpen(`{urls}`)"));
|
||||
}
|
||||
|
||||
app.state::<OpenedUrls>().0.lock().unwrap().replace(urls);
|
||||
}
|
||||
});
|
||||
}
|
58
examples/file-associations/src-tauri/tauri.conf.json
Normal file
58
examples/file-associations/src-tauri/tauri.conf.json
Normal file
@ -0,0 +1,58 @@
|
||||
{
|
||||
"$schema": "../../../tooling/cli/schema.json",
|
||||
"build": {
|
||||
"distDir": [
|
||||
"../index.html"
|
||||
],
|
||||
"devPath": [
|
||||
"../index.html"
|
||||
],
|
||||
"beforeDevCommand": "",
|
||||
"beforeBuildCommand": ""
|
||||
},
|
||||
"tauri": {
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"identifier": "com.tauri.dev-file-associations-demo",
|
||||
"icon": [
|
||||
"../../.icons/32x32.png",
|
||||
"../../.icons/128x128.png",
|
||||
"../../.icons/128x128@2x.png",
|
||||
"../../.icons/icon.icns",
|
||||
"../../.icons/icon.ico"
|
||||
],
|
||||
"resources": [],
|
||||
"externalBin": [],
|
||||
"copyright": "",
|
||||
"category": "DeveloperTool",
|
||||
"fileAssociations": [
|
||||
{
|
||||
"ext": ["png"],
|
||||
"mimeType": "image/png"
|
||||
},
|
||||
{
|
||||
"ext": ["jpg", "jpeg"],
|
||||
"mimeType": "image/jpeg"
|
||||
},
|
||||
{
|
||||
"ext": ["gif"],
|
||||
"mimeType": "image/gif"
|
||||
}
|
||||
],
|
||||
"shortDescription": "",
|
||||
"longDescription": "",
|
||||
"deb": {
|
||||
"depends": []
|
||||
},
|
||||
"macOS": {
|
||||
"frameworks": [],
|
||||
"exceptionDomain": ""
|
||||
}
|
||||
},
|
||||
"windows": [],
|
||||
"security": {
|
||||
"csp": "default-src 'self'"
|
||||
}
|
||||
}
|
||||
}
|
4
examples/file-associations/yarn.lock
Normal file
4
examples/file-associations/yarn.lock
Normal file
@ -0,0 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
@ -14,7 +14,7 @@ tauri-build = { path = "../../../../core/tauri-build", features = [] }
|
||||
|
||||
[dependencies]
|
||||
api = { path = "../api" }
|
||||
tauri = { path = "../../../../core/tauri", features = ["dialog"] }
|
||||
tauri = { path = "../../../../core/tauri" }
|
||||
|
||||
[features]
|
||||
custom-protocol = [ "tauri/custom-protocol" ]
|
||||
|
@ -167,8 +167,20 @@ fn generate_desktop_file(settings: &Settings, data_dir: &Path) -> crate::Result<
|
||||
exec: &'a str,
|
||||
icon: &'a str,
|
||||
name: &'a str,
|
||||
mime_type: Option<String>,
|
||||
}
|
||||
|
||||
let mime_type = if let Some(associations) = settings.file_associations() {
|
||||
let mime_types: Vec<&str> = associations
|
||||
.iter()
|
||||
.filter_map(|association| association.mime_type.as_ref())
|
||||
.map(|s| s.as_str())
|
||||
.collect();
|
||||
Some(mime_types.join(";"))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
handlebars.render_to_write(
|
||||
"main.desktop",
|
||||
&DesktopTemplateParams {
|
||||
@ -184,6 +196,7 @@ fn generate_desktop_file(settings: &Settings, data_dir: &Path) -> crate::Result<
|
||||
exec: bin_name,
|
||||
icon: bin_name,
|
||||
name: settings.product_name(),
|
||||
mime_type,
|
||||
},
|
||||
file,
|
||||
)?;
|
||||
|
@ -8,3 +8,6 @@ Icon={{icon}}
|
||||
Name={{name}}
|
||||
Terminal=false
|
||||
Type=Application
|
||||
{{#if mime_type}}
|
||||
MimeType={{mime_type}}
|
||||
{{/if}}
|
||||
|
@ -31,6 +31,7 @@ use crate::Settings;
|
||||
|
||||
use anyhow::Context;
|
||||
use log::{info, warn};
|
||||
use tauri_utils::config::BundleTypeRole;
|
||||
|
||||
use std::{
|
||||
fs,
|
||||
@ -163,6 +164,45 @@ fn create_info_plist(
|
||||
if let Some(version) = settings.macos().minimum_system_version.clone() {
|
||||
plist.insert("LSMinimumSystemVersion".into(), version.into());
|
||||
}
|
||||
|
||||
if let Some(associations) = settings.file_associations() {
|
||||
plist.insert(
|
||||
"CFBundleDocumentTypes".into(),
|
||||
plist::Value::Array(
|
||||
associations
|
||||
.iter()
|
||||
.map(|association| {
|
||||
let mut dict = plist::Dictionary::new();
|
||||
dict.insert(
|
||||
"CFBundleTypeExtensions".into(),
|
||||
plist::Value::Array(
|
||||
association
|
||||
.ext
|
||||
.iter()
|
||||
.map(|ext| ext.to_string().into())
|
||||
.collect(),
|
||||
),
|
||||
);
|
||||
dict.insert(
|
||||
"CFBundleTypeName".into(),
|
||||
association
|
||||
.name
|
||||
.as_ref()
|
||||
.unwrap_or(&association.ext[0].0)
|
||||
.to_string()
|
||||
.into(),
|
||||
);
|
||||
dict.insert(
|
||||
"CFBundleTypeRole".into(),
|
||||
association.role.to_string().into(),
|
||||
);
|
||||
plist::Value::Dictionary(dict)
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
plist.insert("LSRequiresCarbon".into(), true.into());
|
||||
plist.insert("NSHighResolutionCapable".into(), true.into());
|
||||
if let Some(copyright) = settings.copyright_string() {
|
||||
|
@ -7,7 +7,7 @@ use super::category::AppCategory;
|
||||
use crate::bundle::{common, platform::target_triple};
|
||||
pub use tauri_utils::config::WebviewInstallMode;
|
||||
use tauri_utils::{
|
||||
config::{BundleType, NSISInstallerMode},
|
||||
config::{BundleType, FileAssociation, NSISInstallerMode},
|
||||
resources::{external_binaries, ResourcePaths},
|
||||
};
|
||||
|
||||
@ -362,6 +362,8 @@ pub struct BundleSettings {
|
||||
pub copyright: Option<String>,
|
||||
/// the app's category.
|
||||
pub category: Option<AppCategory>,
|
||||
/// the file associations
|
||||
pub file_associations: Option<Vec<FileAssociation>>,
|
||||
/// the app's short description.
|
||||
pub short_description: Option<String>,
|
||||
/// the app's long description.
|
||||
@ -786,6 +788,11 @@ impl Settings {
|
||||
self.bundle_settings.category
|
||||
}
|
||||
|
||||
/// Return file associations.
|
||||
pub fn file_associations(&self) -> &Option<Vec<FileAssociation>> {
|
||||
&self.bundle_settings.file_associations
|
||||
}
|
||||
|
||||
/// Returns the app's short description.
|
||||
pub fn short_description(&self) -> &str {
|
||||
self
|
||||
|
@ -646,6 +646,10 @@ pub fn build_wix_app_installer(
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(file_associations) = &settings.file_associations() {
|
||||
data.insert("file_associations", to_json(file_associations));
|
||||
}
|
||||
|
||||
if let Some(path) = custom_template_path {
|
||||
handlebars
|
||||
.register_template_string("main.wxs", read_to_string(path)?)
|
||||
|
@ -301,6 +301,10 @@ fn build_nsis_app_installer(
|
||||
let binaries = generate_binaries_data(settings)?;
|
||||
data.insert("binaries", to_json(binaries));
|
||||
|
||||
if let Some(file_associations) = &settings.file_associations() {
|
||||
data.insert("file_associations", to_json(file_associations));
|
||||
}
|
||||
|
||||
let silent_webview2_install = if let WebviewInstallMode::DownloadBootstrapper { silent }
|
||||
| WebviewInstallMode::EmbedBootstrapper { silent }
|
||||
| WebviewInstallMode::OfflineInstaller { silent } =
|
||||
@ -388,6 +392,8 @@ fn build_nsis_app_installer(
|
||||
}
|
||||
|
||||
let mut handlebars = Handlebars::new();
|
||||
handlebars.register_helper("or", Box::new(handlebars_or));
|
||||
handlebars.register_helper("association-description", Box::new(association_description));
|
||||
handlebars.register_escape_fn(|s| {
|
||||
let mut output = String::new();
|
||||
for c in s.chars() {
|
||||
@ -414,6 +420,12 @@ fn build_nsis_app_installer(
|
||||
.map_err(|e| e.to_string())
|
||||
.expect("Failed to setup handlebar template");
|
||||
}
|
||||
|
||||
write_ut16_le_with_bom(
|
||||
&output_path.join("FileAssociation.nsh"),
|
||||
include_str!("./templates/FileAssociation.nsh"),
|
||||
)?;
|
||||
|
||||
let installer_nsi_path = output_path.join("installer.nsi");
|
||||
write_ut16_le_with_bom(
|
||||
&installer_nsi_path,
|
||||
@ -473,6 +485,42 @@ fn build_nsis_app_installer(
|
||||
Ok(vec![nsis_installer_path])
|
||||
}
|
||||
|
||||
fn handlebars_or(
|
||||
h: &handlebars::Helper<'_, '_>,
|
||||
_: &Handlebars<'_>,
|
||||
_: &handlebars::Context,
|
||||
_: &mut handlebars::RenderContext<'_, '_>,
|
||||
out: &mut dyn handlebars::Output,
|
||||
) -> handlebars::HelperResult {
|
||||
let param1 = h.param(0).unwrap().render();
|
||||
let param2 = h.param(1).unwrap();
|
||||
|
||||
out.write(&if param1.is_empty() {
|
||||
param2.render()
|
||||
} else {
|
||||
param1
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn association_description(
|
||||
h: &handlebars::Helper<'_, '_>,
|
||||
_: &Handlebars<'_>,
|
||||
_: &handlebars::Context,
|
||||
_: &mut handlebars::RenderContext<'_, '_>,
|
||||
out: &mut dyn handlebars::Output,
|
||||
) -> handlebars::HelperResult {
|
||||
let description = h.param(0).unwrap().render();
|
||||
let ext = h.param(1).unwrap();
|
||||
|
||||
out.write(&if description.is_empty() {
|
||||
format!("{} File", ext.render().to_uppercase())
|
||||
} else {
|
||||
description
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// BTreeMap<OriginalPath, (ParentOfTargetPath, TargetPath)>
|
||||
type ResourcesMap = BTreeMap<PathBuf, (String, PathBuf)>;
|
||||
fn generate_resource_data(settings: &Settings) -> crate::Result<ResourcesMap> {
|
||||
|
135
tooling/bundler/src/bundle/windows/templates/FileAssociation.nsh
Normal file
135
tooling/bundler/src/bundle/windows/templates/FileAssociation.nsh
Normal file
@ -0,0 +1,135 @@
|
||||
; from https://gist.github.com/nikku/281d0ef126dbc215dd58bfd5b3a5cd5b
|
||||
; fileassoc.nsh
|
||||
; File association helper macros
|
||||
; Written by Saivert
|
||||
;
|
||||
; Improved by Nikku<https://github.com/nikku>.
|
||||
;
|
||||
; Features automatic backup system and UPDATEFILEASSOC macro for
|
||||
; shell change notification.
|
||||
;
|
||||
; |> How to use <|
|
||||
; To associate a file with an application so you can double-click it in explorer, use
|
||||
; the APP_ASSOCIATE macro like this:
|
||||
;
|
||||
; Example:
|
||||
; !insertmacro APP_ASSOCIATE "txt" "myapp.textfile" "Description of txt files" \
|
||||
; "$INSTDIR\myapp.exe,0" "Open with myapp" "$INSTDIR\myapp.exe $\"%1$\""
|
||||
;
|
||||
; Never insert the APP_ASSOCIATE macro multiple times, it is only ment
|
||||
; to associate an application with a single file and using the
|
||||
; the "open" verb as default. To add more verbs (actions) to a file
|
||||
; use the APP_ASSOCIATE_ADDVERB macro.
|
||||
;
|
||||
; Example:
|
||||
; !insertmacro APP_ASSOCIATE_ADDVERB "myapp.textfile" "edit" "Edit with myapp" \
|
||||
; "$INSTDIR\myapp.exe /edit $\"%1$\""
|
||||
;
|
||||
; To have access to more options when registering the file association use the
|
||||
; APP_ASSOCIATE_EX macro. Here you can specify the verb and what verb is to be the
|
||||
; standard action (default verb).
|
||||
;
|
||||
; Note, that this script takes into account user versus global installs.
|
||||
; To properly work you must initialize the SHELL_CONTEXT variable via SetShellVarContext.
|
||||
;
|
||||
; And finally: To remove the association from the registry use the APP_UNASSOCIATE
|
||||
; macro. Here is another example just to wrap it up:
|
||||
; !insertmacro APP_UNASSOCIATE "txt" "myapp.textfile"
|
||||
;
|
||||
; |> Note <|
|
||||
; When defining your file class string always use the short form of your application title
|
||||
; then a period (dot) and the type of file. This keeps the file class sort of unique.
|
||||
; Examples:
|
||||
; Winamp.Playlist
|
||||
; NSIS.Script
|
||||
; Photoshop.JPEGFile
|
||||
;
|
||||
; |> Tech info <|
|
||||
; The registry key layout for a global file association is:
|
||||
;
|
||||
; HKEY_LOCAL_MACHINE\Software\Classes
|
||||
; <".ext"> = <applicationID>
|
||||
; <applicationID> = <"description">
|
||||
; shell
|
||||
; <verb> = <"menu-item text">
|
||||
; command = <"command string">
|
||||
;
|
||||
;
|
||||
; The registry key layout for a per-user file association is:
|
||||
;
|
||||
; HKEY_CURRENT_USER\Software\Classes
|
||||
; <".ext"> = <applicationID>
|
||||
; <applicationID> = <"description">
|
||||
; shell
|
||||
; <verb> = <"menu-item text">
|
||||
; command = <"command string">
|
||||
;
|
||||
|
||||
!macro APP_ASSOCIATE EXT FILECLASS DESCRIPTION ICON COMMANDTEXT COMMAND
|
||||
; Backup the previously associated file class
|
||||
ReadRegStr $R0 SHELL_CONTEXT "Software\Classes\.${EXT}" ""
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "${FILECLASS}_backup" "$R0"
|
||||
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "" "${FILECLASS}"
|
||||
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}" "" `${DESCRIPTION}`
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\DefaultIcon" "" `${ICON}`
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell" "" "open"
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell\open" "" `${COMMANDTEXT}`
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell\open\command" "" `${COMMAND}`
|
||||
!macroend
|
||||
|
||||
!macro APP_ASSOCIATE_EX EXT FILECLASS DESCRIPTION ICON VERB DEFAULTVERB SHELLNEW COMMANDTEXT COMMAND
|
||||
; Backup the previously associated file class
|
||||
ReadRegStr $R0 SHELL_CONTEXT "Software\Classes\.${EXT}" ""
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "${FILECLASS}_backup" "$R0"
|
||||
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "" "${FILECLASS}"
|
||||
StrCmp "${SHELLNEW}" "0" +2
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}\ShellNew" "NullFile" ""
|
||||
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}" "" `${DESCRIPTION}`
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\DefaultIcon" "" `${ICON}`
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell" "" `${DEFAULTVERB}`
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell\${VERB}" "" `${COMMANDTEXT}`
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell\${VERB}\command" "" `${COMMAND}`
|
||||
!macroend
|
||||
|
||||
!macro APP_ASSOCIATE_ADDVERB FILECLASS VERB COMMANDTEXT COMMAND
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell\${VERB}" "" `${COMMANDTEXT}`
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell\${VERB}\command" "" `${COMMAND}`
|
||||
!macroend
|
||||
|
||||
!macro APP_ASSOCIATE_REMOVEVERB FILECLASS VERB
|
||||
DeleteRegKey SHELL_CONTEXT `Software\Classes\${FILECLASS}\shell\${VERB}`
|
||||
!macroend
|
||||
|
||||
|
||||
!macro APP_UNASSOCIATE EXT FILECLASS
|
||||
; Backup the previously associated file class
|
||||
ReadRegStr $R0 SHELL_CONTEXT "Software\Classes\.${EXT}" `${FILECLASS}_backup`
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "" "$R0"
|
||||
|
||||
DeleteRegKey SHELL_CONTEXT `Software\Classes\${FILECLASS}`
|
||||
!macroend
|
||||
|
||||
!macro APP_ASSOCIATE_GETFILECLASS OUTPUT EXT
|
||||
ReadRegStr ${OUTPUT} SHELL_CONTEXT "Software\Classes\.${EXT}" ""
|
||||
!macroend
|
||||
|
||||
|
||||
; !defines for use with SHChangeNotify
|
||||
!ifdef SHCNE_ASSOCCHANGED
|
||||
!undef SHCNE_ASSOCCHANGED
|
||||
!endif
|
||||
!define SHCNE_ASSOCCHANGED 0x08000000
|
||||
!ifdef SHCNF_FLUSH
|
||||
!undef SHCNF_FLUSH
|
||||
!endif
|
||||
!define SHCNF_FLUSH 0x1000
|
||||
|
||||
!macro UPDATEFILEASSOC
|
||||
; Using the system.dll plugin to call the SHChangeNotify Win32 API function so we
|
||||
; can update the shell.
|
||||
System::Call "shell32::SHChangeNotify(i,i,i,i) (${SHCNE_ASSOCCHANGED}, ${SHCNF_FLUSH}, 0, 0)"
|
||||
!macroend
|
@ -2,6 +2,7 @@
|
||||
!include FileFunc.nsh
|
||||
!include x64.nsh
|
||||
!include WordFunc.nsh
|
||||
!include "FileAssociation.nsh"
|
||||
|
||||
!define MANUFACTURER "{{manufacturer}}"
|
||||
!define PRODUCTNAME "{{product_name}}"
|
||||
@ -527,6 +528,13 @@ Section Install
|
||||
IntOp $AppSize $AppSize + $0
|
||||
{{/each}}
|
||||
|
||||
; Create file associations
|
||||
{{#each file_associations as |association| ~}}
|
||||
{{#each association.ext as |ext| ~}}
|
||||
!insertmacro APP_ASSOCIATE "{{ext}}" "{{or association.name ext}}" "{{association-description association.description ext}}" "$INSTDIR\${MAINBINARYNAME}.exe,0" "Open with ${PRODUCTNAME}" "$INSTDIR\${MAINBINARYNAME}.exe $\"%1$\""
|
||||
{{/each}}
|
||||
{{/each}}
|
||||
|
||||
; Create uninstaller
|
||||
WriteUninstaller "$INSTDIR\uninstall.exe"
|
||||
|
||||
@ -623,6 +631,13 @@ Section Uninstall
|
||||
Delete "$INSTDIR\\{{this}}"
|
||||
{{/each}}
|
||||
|
||||
; Delete app associations
|
||||
{{#each file_associations as |association| ~}}
|
||||
{{#each association.ext as |ext| ~}}
|
||||
!insertmacro APP_UNASSOCIATE "{{ext}}" "{{or association.name ext}}"
|
||||
{{/each}}
|
||||
{{/each}}
|
||||
|
||||
; Delete uninstaller
|
||||
Delete "$INSTDIR\uninstall.exe"
|
||||
|
||||
|
@ -114,6 +114,15 @@
|
||||
</Component>
|
||||
<Component Id="Path" Guid="{{path_component_guid}}" Win64="$(var.Win64)">
|
||||
<File Id="Path" Source="{{app_exe_source}}" KeyPath="yes" Checksum="yes"/>
|
||||
{{#each file_associations as |association| ~}}
|
||||
{{#each association.ext as |ext| ~}}
|
||||
<ProgId Id="{{../../product_name}}.{{ext}}" Advertise="yes" Description="{{association.description}}">
|
||||
<Extension Id="{{ext}}" Advertise="yes">
|
||||
<Verb Id="open" Command="Open with {{../../product_name}}" Argument=""%1"" />
|
||||
</Extension>
|
||||
</ProgId>
|
||||
{{/each~}}
|
||||
{{/each~}}
|
||||
</Component>
|
||||
{{#each binaries as |bin| ~}}
|
||||
<Component Id="{{ bin.id }}" Guid="{{bin.guid}}" Win64="$(var.Win64)">
|
||||
|
130
tooling/cli/Cargo.lock
generated
130
tooling/cli/Cargo.lock
generated
@ -613,6 +613,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core2"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.7"
|
||||
@ -798,6 +807,12 @@ dependencies = [
|
||||
"syn 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dary_heap"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7762d17f1241643615821a8455a0b2c3e803784b058693d990b11f2dce25a0ca"
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.17"
|
||||
@ -1437,6 +1452,15 @@ version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
@ -1502,7 +1526,21 @@ checksum = "e5c13fb08e5d4dfc151ee5e88bae63f7773d61852f3bdc73c9f4b9e1bde03148"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mac",
|
||||
"markup5ever",
|
||||
"markup5ever 0.10.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "html5ever"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mac",
|
||||
"markup5ever 0.11.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
@ -1679,7 +1717,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
"hashbrown 0.12.3",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@ -2002,7 +2040,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ea8e9c6e031377cff82ee3001dc8026cdf431ed4e2e6b51f98ab8c73484a358"
|
||||
dependencies = [
|
||||
"cssparser",
|
||||
"html5ever",
|
||||
"html5ever 0.25.2",
|
||||
"matches",
|
||||
"selectors",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kuchikiki"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8"
|
||||
dependencies = [
|
||||
"cssparser",
|
||||
"html5ever 0.26.0",
|
||||
"indexmap",
|
||||
"matches",
|
||||
"selectors",
|
||||
]
|
||||
@ -2040,21 +2091,25 @@ checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
|
||||
|
||||
[[package]]
|
||||
name = "libflate"
|
||||
version = "1.4.0"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ff4ae71b685bbad2f2f391fe74f6b7659a34871c08b210fdc039e43bee07d18"
|
||||
checksum = "9f7d5654ae1795afc7ff76f4365c2c8791b0feb18e8996a96adad8ffd7c3b2bf"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
"core2",
|
||||
"crc32fast",
|
||||
"dary_heap",
|
||||
"libflate_lz77",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libflate_lz77"
|
||||
version = "1.2.0"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a52d3a8bfc85f250440e4424db7d857e241a3aebbbe301f3eb606ab15c39acbf"
|
||||
checksum = "be5f52fb8c451576ec6b79d3f4deb327398bc05bbdbd99021a6e77a4c855d524"
|
||||
dependencies = [
|
||||
"core2",
|
||||
"hashbrown 0.13.2",
|
||||
"rle-decode-fast",
|
||||
]
|
||||
|
||||
@ -2137,7 +2192,21 @@ checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd"
|
||||
dependencies = [
|
||||
"log",
|
||||
"phf 0.8.0",
|
||||
"phf_codegen",
|
||||
"phf_codegen 0.8.0",
|
||||
"string_cache",
|
||||
"string_cache_codegen",
|
||||
"tendril",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markup5ever"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
|
||||
dependencies = [
|
||||
"log",
|
||||
"phf 0.10.1",
|
||||
"phf_codegen 0.10.0",
|
||||
"string_cache",
|
||||
"string_cache_codegen",
|
||||
"tendril",
|
||||
@ -2729,6 +2798,16 @@ dependencies = [
|
||||
"phf_shared 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
|
||||
dependencies = [
|
||||
"phf_generator 0.10.0",
|
||||
"phf_shared 0.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.8.0"
|
||||
@ -3335,7 +3414,7 @@ dependencies = [
|
||||
"log",
|
||||
"matches",
|
||||
"phf 0.8.0",
|
||||
"phf_codegen",
|
||||
"phf_codegen 0.8.0",
|
||||
"precomputed-hash",
|
||||
"servo_arc",
|
||||
"smallvec",
|
||||
@ -3896,7 +3975,7 @@ dependencies = [
|
||||
"env_logger",
|
||||
"handlebars 4.3.7",
|
||||
"heck",
|
||||
"html5ever",
|
||||
"html5ever 0.26.0",
|
||||
"ignore",
|
||||
"image",
|
||||
"include_dir",
|
||||
@ -3907,7 +3986,7 @@ dependencies = [
|
||||
"jsonrpsee-core",
|
||||
"jsonrpsee-ws-client",
|
||||
"jsonschema",
|
||||
"kuchiki",
|
||||
"kuchikiki",
|
||||
"libc",
|
||||
"local-ip-address",
|
||||
"log",
|
||||
@ -4012,7 +4091,7 @@ dependencies = [
|
||||
"ctor 0.1.26",
|
||||
"getrandom 0.2.9",
|
||||
"heck",
|
||||
"html5ever",
|
||||
"html5ever 0.25.2",
|
||||
"infer",
|
||||
"json-patch",
|
||||
"json5",
|
||||
@ -4041,11 +4120,11 @@ dependencies = [
|
||||
"getrandom 0.2.9",
|
||||
"glob",
|
||||
"heck",
|
||||
"html5ever",
|
||||
"html5ever 0.26.0",
|
||||
"infer",
|
||||
"json-patch",
|
||||
"json5",
|
||||
"kuchiki",
|
||||
"kuchikiki",
|
||||
"memchr",
|
||||
"phf 0.10.1",
|
||||
"schemars",
|
||||
@ -4058,7 +4137,7 @@ dependencies = [
|
||||
"toml",
|
||||
"url",
|
||||
"walkdir",
|
||||
"windows 0.44.0",
|
||||
"windows 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4773,23 +4852,14 @@ dependencies = [
|
||||
"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 0.44.0",
|
||||
"windows-interface",
|
||||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||
dependencies = [
|
||||
"windows-implement 0.48.0",
|
||||
"windows-interface",
|
||||
"windows-targets 0.48.0",
|
||||
]
|
||||
|
||||
@ -4805,9 +4875,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.44.0"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce87ca8e3417b02dc2a8a22769306658670ec92d78f1bd420d6310a67c245c6"
|
||||
checksum = "5e2ee588991b9e7e6c8338edf3333fbe4da35dc72092643958ebb43f0ab2c49c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -4816,9 +4886,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.44.0"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "853f69a591ecd4f810d29f17e902d40e349fb05b0b11fff63b08b826bfe39c7f"
|
||||
checksum = "e6fb8df20c9bcaa8ad6ab513f7b40104840c8867d5751126e4df3b08388d0cc7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -925,6 +925,16 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"fileAssociations": {
|
||||
"description": "File associations to application.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/FileAssociation"
|
||||
}
|
||||
},
|
||||
"shortDescription": {
|
||||
"description": "A short description of your application.",
|
||||
"type": [
|
||||
@ -1122,6 +1132,97 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"FileAssociation": {
|
||||
"description": "File association",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ext"
|
||||
],
|
||||
"properties": {
|
||||
"ext": {
|
||||
"description": "File extensions to associate with this app. e.g. 'png'",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AssociationExt"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"description": "The name. Maps to `CFBundleTypeName` on macOS. Default to ext[0]",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"description": {
|
||||
"description": "The association description. Windows-only. It is displayed on the `Type` column on Windows Explorer.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"role": {
|
||||
"description": "The app’s role with respect to the type. Maps to `CFBundleTypeRole` on macOS.",
|
||||
"default": "Editor",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BundleTypeRole"
|
||||
}
|
||||
]
|
||||
},
|
||||
"mimeType": {
|
||||
"description": "The mime-type e.g. 'image/png' or 'text/plain'. Linux-only.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"AssociationExt": {
|
||||
"description": "An extension for a [`FileAssociation`].\n\nA leading `.` is automatically stripped.",
|
||||
"type": "string"
|
||||
},
|
||||
"BundleTypeRole": {
|
||||
"description": "macOS-only. Corresponds to CFBundleTypeRole",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "CFBundleTypeRole.Editor. Files can be read and edited.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Editor"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "CFBundleTypeRole.Viewer. Files can be read.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Viewer"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "CFBundleTypeRole.Shell",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Shell"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "CFBundleTypeRole.QLGenerator",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"QLGenerator"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "CFBundleTypeRole.None",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"None"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"AppImageConfig": {
|
||||
"description": "Configuration for AppImage bundles.\n\nSee more: https://tauri.app/v1/api/config#appimageconfig",
|
||||
"type": "object",
|
||||
|
@ -79,8 +79,6 @@ pub fn command(options: Options) -> Result<()> {
|
||||
let r = command_internal(options);
|
||||
if r.is_err() {
|
||||
kill_before_dev_process();
|
||||
#[cfg(not(debug_assertions))]
|
||||
let _ = check_for_updates();
|
||||
}
|
||||
r
|
||||
}
|
||||
@ -262,8 +260,6 @@ pub fn setup(options: &mut Options, mobile: bool) -> Result<AppInterface> {
|
||||
|
||||
let _ = ctrlc::set_handler(move || {
|
||||
kill_before_dev_process();
|
||||
#[cfg(not(debug_assertions))]
|
||||
let _ = check_for_updates();
|
||||
exit(130);
|
||||
});
|
||||
}
|
||||
|
@ -1130,6 +1130,7 @@ fn tauri_config_to_bundle_settings(
|
||||
})?),
|
||||
None => None,
|
||||
},
|
||||
file_associations: config.file_associations,
|
||||
short_description: config.short_description,
|
||||
long_description: config.long_description,
|
||||
external_bin: config.external_bin,
|
||||
|
Loading…
Reference in New Issue
Block a user