mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-07-20 17:00:56 +02:00
Compare commits
156 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 03afae6d72 | |||
| 57ac986453 | |||
| 2ed6d6c1de | |||
| cdfd462955 | |||
| d6e0b6bbb1 | |||
| 40ae0a7fa0 | |||
| edc52ea056 | |||
| a0d949d93e | |||
| 13c63af965 | |||
| cad301fcc1 | |||
| 254f222e0e | |||
| b1439be667 | |||
| d157387722 | |||
| 41f6274270 | |||
| f08980f123 | |||
| 78df86f810 | |||
| aecec42d5d | |||
| 0767dcbd5c | |||
| 4be7690085 | |||
| c2b3981248 | |||
| 0c23b8ecfe | |||
| 4350ca652d | |||
| 1953f00188 | |||
| 9883f396dc | |||
| d8645ab3e5 | |||
| cb863d57c6 | |||
| d67e4f8cdc | |||
| e571e0be9d | |||
| 83ac944abf | |||
| 0139c54d0e | |||
| cad34b73fa | |||
| c0d64bf7d9 | |||
| e919bab3eb | |||
| 95c9291cee | |||
| 114e902537 | |||
| 6972acdca6 | |||
| 8bbc7a0d1d | |||
| 2d013294ef | |||
| 5c7668b6bb | |||
| ec0540138b | |||
| b86e999beb | |||
| c463d8ab14 | |||
| 1bb7beb307 | |||
| 3412fa2741 | |||
| af81fdac98 | |||
| c1fd33b3a2 | |||
| 250857b7b7 | |||
| 964e13f124 | |||
| e6cdc9f52e | |||
| 9228419e13 | |||
| 04d92139c8 | |||
| 5260fc90b0 | |||
| ea5c70060b | |||
| d016a8ab1e | |||
| d6a3898001 | |||
| 2e5bcdf202 | |||
| 4374b4fc1a | |||
| f75d21db33 | |||
| 4b95f5e079 | |||
| 99c3e37b54 | |||
| eaac19a5b7 | |||
| 5183e314cb | |||
| 2c0883e64e | |||
| 024ec0c29c | |||
| 35aad24773 | |||
| ab037b70d6 | |||
| 1198ad4fb0 | |||
| ea033fe3b8 | |||
| 2e432f70c9 | |||
| b19dcd25cb | |||
| 995e76436f | |||
| 1dc3612862 | |||
| f5f68063e4 | |||
| 36d3d19247 | |||
| 31ab6f8d24 | |||
| 21ae430ea3 | |||
| 015e817cf2 | |||
| 2a6d4b42bb | |||
| 550d137c64 | |||
| 3f21f39584 | |||
| 759c22758e | |||
| 6816dd1960 | |||
| 24154472a6 | |||
| 2574ec89e7 | |||
| 2971289252 | |||
| 6c3da6d290 | |||
| e97a4dedab | |||
| 05045f9a72 | |||
| 4f53e36a50 | |||
| 8374e997b8 | |||
| 35aa52f45e | |||
| bbc177150f | |||
| e6e2edca11 | |||
| 7ecd19da51 | |||
| a0b6c8ff3b | |||
| c12ea9306a | |||
| 98e2c11eef | |||
| 50b159f668 | |||
| 305c4f6b4e | |||
| 89f3e17952 | |||
| d4613ff002 | |||
| 69a9d5771c | |||
| 61e9b0ab64 | |||
| 25ad21beff | |||
| 2dc3f3f039 | |||
| c27af9128d | |||
| b60dd88702 | |||
| a97033bcd9 | |||
| 4375c98bed | |||
| 82fbb0c790 | |||
| de6bf68585 | |||
| 734c627084 | |||
| 05c5da072b | |||
| 0d126ff0ad | |||
| f122ee98c6 | |||
| 2308f2299d | |||
| 82c404635b | |||
| b1dbee2c55 | |||
| d7a0bb325d | |||
| f3d75f7abb | |||
| 6e2e7e48c3 | |||
| 9a2c98f450 | |||
| 4a2ecb6287 | |||
| 31415effdf | |||
| 04b33ea0b0 | |||
| 54e21f142b | |||
| d528c88b4f | |||
| 69146fa852 | |||
| 9f68f2d827 | |||
| 3d0d2e041b | |||
| 1e3d7ef16e | |||
| ce6835d50f | |||
| 1d9f47c882 | |||
| e221a04ef4 | |||
| eebfd2ed3e | |||
| 521cd8b372 | |||
| e4a40f4423 | |||
| fa8b11f19d | |||
| baefd761e0 | |||
| 2804803949 | |||
| d9d51eb8ea | |||
| 1483c63101 | |||
| 26ed78989a | |||
| f1564e58b6 | |||
| f2d4abb9e2 | |||
| 3dcf7522b1 | |||
| 02068550e8 | |||
| 66a75ece27 | |||
| fa601e8754 | |||
| d8bfe61f20 | |||
| e8915f17e4 | |||
| 6de61f854b | |||
| ad7a8d3e42 | |||
| d5509a9ac8 | |||
| 06727b19c1 | |||
| 2f9fa79747 |
+14
-8
@@ -1,11 +1,17 @@
|
|||||||
[advisories]
|
[advisories]
|
||||||
ignore = [
|
ignore = [
|
||||||
# time 0.1
|
# rsa Marvin Attack
|
||||||
"RUSTSEC-2020-0071",
|
"RUSTSEC-2023-0071",
|
||||||
# needs sqlx 0.7 (still in alpha)
|
# time crate can't be updated in the repo because of MSRV, users are unaffected
|
||||||
"RUSTSEC-2022-0090",
|
"RUSTSEC-2026-0009",
|
||||||
# wry needs kuchiki on Android
|
# libflate crates can't be updated in the repo because of MSRV, users are unaffected
|
||||||
"RUSTSEC-2023-0019",
|
"RUSTSEC-2026-0105",
|
||||||
# atty is only used when the `colored` feature is enabled on tauri-plugin-log
|
# quick-xml, need an update in plist, can't be updated in the repo because of MSRV, users are unaffected
|
||||||
"RUSTSEC-2021-0145",
|
# Also needs on update in wayland-scanner > arboard > tauri-plugin-clipboard-manager
|
||||||
|
"RUSTSEC-2026-0194",
|
||||||
|
# quick-xml, need an update in plist, can't be updated in the repo because of MSRV, users are unaffected
|
||||||
|
# Also needs on update in wayland-scanner > arboard > tauri-plugin-clipboard-manager
|
||||||
|
"RUSTSEC-2026-0195",
|
||||||
|
# quinn-proto, can't be updated in the repo because of MSRV, users are unaffected
|
||||||
|
"RUSTSEC-2026-0185",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
"dialog-js": minor:feat
|
|
||||||
"dialog": minor:feat
|
|
||||||
---
|
|
||||||
|
|
||||||
Add `xdg-portal` as an optional feature for `rfd`
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
"log": "minor"
|
|
||||||
"log-js": "minor"
|
|
||||||
---
|
|
||||||
|
|
||||||
Allow specifying a log formatter per target using the `format` method on `Target`.
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
"dialog": minor
|
|
||||||
"dialog-js": minor
|
|
||||||
---
|
|
||||||
|
|
||||||
Add `pickerMode` option to file picker (currently only used on iOS)
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
"localhost": patch
|
|
||||||
---
|
|
||||||
|
|
||||||
Disable caching on responses.
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
"barcode-scanner": patch
|
|
||||||
"barcode-scanner-js": patch
|
|
||||||
---
|
|
||||||
|
|
||||||
Fix the `cameraView` is not removed after scanning in iOS.
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
"log": patch
|
|
||||||
"log-js": patch
|
|
||||||
---
|
|
||||||
|
|
||||||
Fix log file rotation when exceeding `max_file_size`.
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
"nfc": "patch"
|
|
||||||
"nfc-js": "patch"
|
|
||||||
---
|
|
||||||
|
|
||||||
Update return value of `isAvailable` to match TypeScript function signature
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
"updater": minor
|
|
||||||
"updater-js": minor
|
|
||||||
---
|
|
||||||
|
|
||||||
Updater plugin now supports all bundle types: Deb, Rpm and AppImage for Linux; NSiS, MSI for Windows. This was added in https://github.com/tauri-apps/plugins-workspace/pull/2624
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
"upload": minor
|
|
||||||
"upload-js": minor
|
|
||||||
---
|
|
||||||
|
|
||||||
Upload plugin now supports specifying an HTTP method i.e. POST, PUT etc.
|
|
||||||
@@ -5,6 +5,7 @@ Hi! We, the maintainers, are really excited that you are interested in contribut
|
|||||||
- [Issue Reporting Guidelines](#issue-reporting-guidelines)
|
- [Issue Reporting Guidelines](#issue-reporting-guidelines)
|
||||||
- [Pull Request Guidelines](#pull-request-guidelines)
|
- [Pull Request Guidelines](#pull-request-guidelines)
|
||||||
- [Development Guide](#development-guide)
|
- [Development Guide](#development-guide)
|
||||||
|
- [AI Tool Policy](#ai-tool-policy)
|
||||||
|
|
||||||
## Issue Reporting Guidelines
|
## Issue Reporting Guidelines
|
||||||
|
|
||||||
@@ -60,3 +61,13 @@ The easiest way to test your changes is to use the [example app](https://github.
|
|||||||
To test local changes against your own application simply point the plugin create to your local repository, for example:
|
To test local changes against your own application simply point the plugin create to your local repository, for example:
|
||||||
|
|
||||||
`tauri-plugin-sample = { path = "path/to/local/tauri-plugin-sample/" }`
|
`tauri-plugin-sample = { path = "path/to/local/tauri-plugin-sample/" }`
|
||||||
|
|
||||||
|
## AI Tool Policy
|
||||||
|
|
||||||
|
It takes a lot of time to review a Pull Request while it's very easy to make a nonsensical but plausible looking one using AI tools.
|
||||||
|
It is unfair for other contributors and the reviewers to spend much of the time dealing with this, hence these rules:
|
||||||
|
|
||||||
|
1. Review and test all LLM-generated content before submitting, you're the one responsible for it, not the AI.
|
||||||
|
2. Don't use AI to respond to review comments (except for translations).
|
||||||
|
|
||||||
|
We will close the Pull Request with a `ai-slop` tag if you failed to do so.
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
name: 🐞 Bug Report
|
||||||
|
description: Report a bug
|
||||||
|
labels: ['type: bug']
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
## First of all
|
||||||
|
1. Please search for [existing issues](https://github.com/tauri-apps/plugins-workspace/issues?q=is%3Aissue) about this problem first.
|
||||||
|
2. Make sure `rustc` and all relevant Tauri packages are up to date.
|
||||||
|
3. Make sure it's an issue with a tauri plugin and not something else you are using.
|
||||||
|
4. Remember to follow our community guidelines and be friendly.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Describe the bug
|
||||||
|
description: A clear description of what the bug is. Include screenshots if applicable.
|
||||||
|
placeholder: Bug description
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: reproduction
|
||||||
|
attributes:
|
||||||
|
label: Reproduction
|
||||||
|
description: A link to a reproduction repo or steps to reproduce the behaviour.
|
||||||
|
placeholder: |
|
||||||
|
Please provide a minimal reproduction or steps to reproduce, see this guide https://stackoverflow.com/help/minimal-reproducible-example
|
||||||
|
Why reproduction is required? see this article https://antfu.me/posts/why-reproductions-are-required
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: expected-behavior
|
||||||
|
attributes:
|
||||||
|
label: Expected behavior
|
||||||
|
description: A clear description of what you expected to happen.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: info
|
||||||
|
attributes:
|
||||||
|
label: Full `tauri info` output
|
||||||
|
description: 'Output of `npm run tauri info` or `cargo tauri info`. Issues without this will be closed!'
|
||||||
|
render: text
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: Stack trace
|
||||||
|
description: A stack trace or ANY error messages you may see.
|
||||||
|
render: text
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
description: Add any other context about the problem here. For example a link to a Discord discussion.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: 💡 Request a new plugin
|
||||||
|
url: https://github.com/orgs/tauri-apps/discussions/new?category=plugin-requests
|
||||||
|
about: Propose a new Plugin to the community or the Tauri org.
|
||||||
|
- name: 💬 Discord Chat
|
||||||
|
url: https://discord.com/invite/tauri
|
||||||
|
about: Ask questions and talk to other Tauri users and the maintainers
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
name: 📚 Docs Report
|
||||||
|
about: Create a report to help us improve the docs
|
||||||
|
labels: 'type: documentation'
|
||||||
|
---
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
name: 💡 Feature Request
|
||||||
|
description: Request a feature or enhancement for an existing plugin
|
||||||
|
labels: ['type: feature request']
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
## First of all
|
||||||
|
- Please search for [existing issues](https://github.com/tauri-apps/plugins-workspace/issues?q=is%3Aissue) for this request first.
|
||||||
|
- Only requests for plugins that exist in this repo are allowed.
|
||||||
|
- You can request new plugins [here](https://github.com/orgs/tauri-apps/discussions/new?category=plugin-requests)
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: problem
|
||||||
|
attributes:
|
||||||
|
label: Describe the problem
|
||||||
|
description: A clear description of the problem this feature would solve
|
||||||
|
placeholder: "I'm always frustrated when..."
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: solution
|
||||||
|
attributes:
|
||||||
|
label: "Describe the solution you'd like"
|
||||||
|
description: A clear description of what change you would like
|
||||||
|
placeholder: 'I would like to...'
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: alternatives
|
||||||
|
attributes:
|
||||||
|
label: Alternatives considered
|
||||||
|
description: "Any alternative solutions you've considered"
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
description: Add any other context about the problem here.
|
||||||
@@ -33,20 +33,19 @@ jobs:
|
|||||||
audit-js:
|
audit-js:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- name: Cache pnpm modules
|
- name: Cache pnpm modules
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v6
|
||||||
with:
|
with:
|
||||||
path: ~/.pnpm-store
|
path: ~/.pnpm-store
|
||||||
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-
|
${{ runner.os }}-
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: 'lts/*'
|
node-version: 'lts/*'
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v6
|
||||||
with:
|
with:
|
||||||
version: 10.x.x
|
|
||||||
run_install: true
|
run_install: true
|
||||||
- name: audit
|
- name: audit
|
||||||
run: pnpm audit
|
run: pnpm audit
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ jobs:
|
|||||||
audit-rust:
|
audit-rust:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: rustsec/audit-check@v1
|
- uses: rustsec/audit-check@v2
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
# https://github.com/tauri-apps/plugins-workspace/issues/774
|
# https://github.com/tauri-apps/plugins-workspace/issues/774
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ jobs:
|
|||||||
check:
|
check:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: check change files end with .md
|
- name: check change files end with .md
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
packages: ${{ steps.filter.outputs.changes }}
|
packages: ${{ steps.filter.outputs.changes }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: dorny/paths-filter@v2
|
- uses: dorny/paths-filter@v2
|
||||||
id: filter
|
id: filter
|
||||||
with:
|
with:
|
||||||
@@ -153,21 +153,20 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: Cache pnpm modules
|
- name: Cache pnpm modules
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v6
|
||||||
with:
|
with:
|
||||||
path: ~/.pnpm-store
|
path: ~/.pnpm-store
|
||||||
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-
|
${{ runner.os }}-
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: 'lts/*'
|
node-version: 'lts/*'
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v6
|
||||||
with:
|
with:
|
||||||
version: 10.x.x
|
|
||||||
run_install: true
|
run_install: true
|
||||||
|
|
||||||
- name: build api
|
- name: build api
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ jobs:
|
|||||||
check:
|
check:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: dorny/paths-filter@v2
|
- uses: dorny/paths-filter@v2
|
||||||
id: filter
|
id: filter
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0 # required for use of git history
|
fetch-depth: 0 # required for use of git history
|
||||||
- name: covector status
|
- name: covector status
|
||||||
|
|||||||
@@ -28,18 +28,17 @@ jobs:
|
|||||||
successfulPublish: ${{ steps.covector.outputs.successfulPublish }}
|
successfulPublish: ${{ steps.covector.outputs.successfulPublish }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0 # required for use of git history
|
fetch-depth: 0 # required for use of git history
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: 'lts/*'
|
node-version: 'lts/*'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v6
|
||||||
with:
|
with:
|
||||||
version: 10.x.x
|
|
||||||
run_install: true
|
run_install: true
|
||||||
|
|
||||||
- name: cargo login
|
- name: cargo login
|
||||||
@@ -74,7 +73,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create Pull Request With Versions Bumped
|
- name: Create Pull Request With Versions Bumped
|
||||||
id: cpr
|
id: cpr
|
||||||
uses: peter-evans/create-pull-request@dd2324fc52d5d43c699a5636bcf19fceaa70c284 # 7.0.7
|
uses: peter-evans/create-pull-request@v8.1.1
|
||||||
if: steps.covector.outputs.commandRan == 'version'
|
if: steps.covector.outputs.commandRan == 'version'
|
||||||
with:
|
with:
|
||||||
title: 'Publish New Versions (${{ github.ref_name }})'
|
title: 'Publish New Versions (${{ github.ref_name }})'
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: install Rust stable and rustfmt
|
- name: install Rust stable and rustfmt
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
@@ -25,20 +25,19 @@ jobs:
|
|||||||
prettier:
|
prettier:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- name: Cache pnpm modules
|
- name: Cache pnpm modules
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v6
|
||||||
with:
|
with:
|
||||||
path: ~/.pnpm-store
|
path: ~/.pnpm-store
|
||||||
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-
|
${{ runner.os }}-
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: 'lts/*'
|
node-version: 'lts/*'
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v6
|
||||||
with:
|
with:
|
||||||
version: 10.x.x
|
|
||||||
run_install: true
|
run_install: true
|
||||||
- run: pnpm format:check
|
- run: pnpm format:check
|
||||||
|
|
||||||
@@ -46,7 +45,7 @@ jobs:
|
|||||||
name: taplo (.toml files)
|
name: taplo (.toml files)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: install Rust stable
|
- name: install Rust stable
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
name: integration tests
|
name: integration tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- v1
|
- v1
|
||||||
@@ -30,7 +31,7 @@ jobs:
|
|||||||
platform: [ubuntu-22.04, macos-latest, windows-latest]
|
platform: [ubuntu-22.04, macos-latest, windows-latest]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
|||||||
@@ -36,20 +36,19 @@ jobs:
|
|||||||
eslint:
|
eslint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- name: Cache pnpm modules
|
- name: Cache pnpm modules
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v6
|
||||||
with:
|
with:
|
||||||
path: ~/.pnpm-store
|
path: ~/.pnpm-store
|
||||||
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-
|
${{ runner.os }}-
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: 'lts/*'
|
node-version: 'lts/*'
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v6
|
||||||
with:
|
with:
|
||||||
version: 10.x.x
|
|
||||||
run_install: true
|
run_install: true
|
||||||
- name: eslint
|
- name: eslint
|
||||||
run: pnpm lint
|
run: pnpm lint
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
packages: ${{ steps.filter.outputs.changes }}
|
packages: ${{ steps.filter.outputs.changes }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: dorny/paths-filter@v2
|
- uses: dorny/paths-filter@v2
|
||||||
id: filter
|
id: filter
|
||||||
with:
|
with:
|
||||||
@@ -136,7 +136,7 @@ jobs:
|
|||||||
package: ${{ fromJSON(needs.changes.outputs.packages) }}
|
package: ${{ fromJSON(needs.changes.outputs.packages) }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: install webkit2gtk
|
- name: install webkit2gtk
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -19,26 +19,25 @@ jobs:
|
|||||||
sync-to-mirrors:
|
sync-to-mirrors:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: Fetch git tags
|
- name: Fetch git tags
|
||||||
run: git fetch origin 'refs/tags/*:refs/tags/*'
|
run: git fetch origin 'refs/tags/*:refs/tags/*'
|
||||||
|
|
||||||
- name: Cache pnpm modules
|
- name: Cache pnpm modules
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v6
|
||||||
with:
|
with:
|
||||||
path: ~/.pnpm-store
|
path: ~/.pnpm-store
|
||||||
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-
|
${{ runner.os }}-
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: 'lts/*'
|
node-version: 'lts/*'
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v6
|
||||||
with:
|
with:
|
||||||
version: 10.x.x
|
|
||||||
run_install: true
|
run_install: true
|
||||||
|
|
||||||
- name: Build packages
|
- name: Build packages
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
packages: ${{ steps.filter.outputs.changes }}
|
packages: ${{ steps.filter.outputs.changes }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: dorny/paths-filter@v2
|
- uses: dorny/paths-filter@v2
|
||||||
id: filter
|
id: filter
|
||||||
with:
|
with:
|
||||||
@@ -225,7 +225,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.platform.os }}
|
runs-on: ${{ matrix.platform.os }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: install webkit2gtk
|
- name: install webkit2gtk
|
||||||
if: contains(matrix.platform.target, 'unknown-linux')
|
if: contains(matrix.platform.target, 'unknown-linux')
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ target/
|
|||||||
package-lock.json
|
package-lock.json
|
||||||
yarn.lock
|
yarn.lock
|
||||||
bun.lockb
|
bun.lockb
|
||||||
|
bun.lock
|
||||||
|
|
||||||
# rust compiled folders
|
# rust compiled folders
|
||||||
target/
|
target/
|
||||||
|
|||||||
Generated
+619
-677
File diff suppressed because it is too large
Load Diff
+5
-5
@@ -11,11 +11,11 @@ resolver = "2"
|
|||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
log = "0.4"
|
log = "0.4.21"
|
||||||
tauri = { version = "2.8.2", default-features = false }
|
tauri = { version = "2.10", default-features = false }
|
||||||
tauri-build = "2.4"
|
tauri-build = "2.5"
|
||||||
tauri-plugin = "2.4"
|
tauri-plugin = "2.5"
|
||||||
tauri-utils = "2.7"
|
tauri-utils = "2.8"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
url = "2"
|
url = "2"
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ This repo and all plugins require a Rust version of at least **1.77.2**
|
|||||||
| [log](plugins/log) | Configurable logging. | ✅ | ✅ | ✅ | ✅ | ✅ |
|
| [log](plugins/log) | Configurable logging. | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
| [nfc](plugins/nfc) | Read and write NFC tags on Android and iOS. | ? | ? | ? | ✅ | ✅ |
|
| [nfc](plugins/nfc) | Read and write NFC tags on Android and iOS. | ? | ? | ? | ✅ | ✅ |
|
||||||
| [notification](plugins/notification) | Send message notifications (brief auto-expiring OS window element) to your user. Can also be used with the Notification Web API. | ✅ | ✅ | ✅ | ✅ | ✅ |
|
| [notification](plugins/notification) | Send message notifications (brief auto-expiring OS window element) to your user. Can also be used with the Notification Web API. | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
| [opener](plugins/opener) | Open files and URLs using their default application. | ✅ | ✅ | ✅ | ? | ? |
|
| [opener](plugins/opener) | Open files and URLs using their default application. | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
| [os](plugins/os) | Read information about the operating system. | ✅ | ✅ | ✅ | ✅ | ✅ |
|
| [os](plugins/os) | Read information about the operating system. | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
| [persisted-scope](plugins/persisted-scope) | Persist runtime scope changes on the filesystem. | ✅ | ✅ | ✅ | ? | ? |
|
| [persisted-scope](plugins/persisted-scope) | Persist runtime scope changes on the filesystem. | ✅ | ✅ | ✅ | ? | ? |
|
||||||
| [positioner](plugins/positioner) | Move windows to common locations. | ✅ | ✅ | ✅ | ❌ | ❌ |
|
| [positioner](plugins/positioner) | Move windows to common locations. | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||||
|
|||||||
@@ -1,5 +1,75 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## \[2.0.42]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `dialog-js@2.7.2`
|
||||||
|
- Upgraded to `store-js@2.4.4`
|
||||||
|
|
||||||
|
## \[2.0.41]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `log-js@2.9.0`
|
||||||
|
|
||||||
|
## \[2.0.40]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `barcode-scanner-js@2.4.5`
|
||||||
|
- Upgraded to `global-shortcut-js@2.3.2`
|
||||||
|
|
||||||
|
## \[2.0.39]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `fs-js@2.5.1`
|
||||||
|
- Upgraded to `opener-js@2.5.4`
|
||||||
|
- Upgraded to `store-js@2.4.3`
|
||||||
|
- Upgraded to `dialog-js@2.7.1`
|
||||||
|
- Upgraded to `http-js@2.5.9`
|
||||||
|
|
||||||
|
## \[2.0.38]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `fs-js@2.5.0`
|
||||||
|
- Upgraded to `http-js@2.5.8`
|
||||||
|
- Upgraded to `updater-js@2.10.1`
|
||||||
|
- Upgraded to `nfc-js@2.3.5`
|
||||||
|
- Upgraded to `dialog-js@2.7.0`
|
||||||
|
|
||||||
|
## \[2.0.37]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `updater-js@2.10.0`
|
||||||
|
- Upgraded to `barcode-scanner-js@2.4.4`
|
||||||
|
- Upgraded to `http-js@2.5.7`
|
||||||
|
- Upgraded to `shell-js@2.3.5`
|
||||||
|
|
||||||
|
## \[2.0.36]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `http-js@2.5.6`
|
||||||
|
- Upgraded to `dialog-js@2.6.0`
|
||||||
|
|
||||||
|
## \[2.0.35]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `dialog-js@2.5.0`
|
||||||
|
- Upgraded to `log-js@2.8.0`
|
||||||
|
- Upgraded to `shell-js@2.3.4`
|
||||||
|
- Upgraded to `barcode-scanner-js@2.4.3`
|
||||||
|
- Upgraded to `fs-js@2.4.5`
|
||||||
|
- Upgraded to `http-js@2.5.5`
|
||||||
|
- Upgraded to `nfc-js@2.3.4`
|
||||||
|
- Upgraded to `opener-js@2.5.3`
|
||||||
|
- Upgraded to `store-js@2.4.2`
|
||||||
|
|
||||||
## \[2.0.34]
|
## \[2.0.34]
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en" theme="dark">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta
|
<meta
|
||||||
name="viewport"
|
name="viewport"
|
||||||
content="width=device-width, initial-scale=1.0, viewport-fit=cover, user-scalable=0"
|
content="width=device-width, initial-scale=1.0, viewport-fit=cover, user-scalable=0"
|
||||||
/>
|
/>
|
||||||
<title>Svelte + Vite App</title>
|
<meta name="color-scheme" content="dark light" />
|
||||||
|
<title>API Example App</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
+28
-28
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "api",
|
"name": "api",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "2.0.34",
|
"version": "2.0.42",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --clearScreen false",
|
"dev": "vite --clearScreen false",
|
||||||
@@ -10,36 +10,36 @@
|
|||||||
"tauri": "tauri"
|
"tauri": "tauri"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "2.9.0",
|
"@tauri-apps/api": "^2.11.0",
|
||||||
"@tauri-apps/plugin-barcode-scanner": "^2.4.2",
|
"@tauri-apps/plugin-barcode-scanner": "workspace:*",
|
||||||
"@tauri-apps/plugin-biometric": "^2.3.2",
|
"@tauri-apps/plugin-biometric": "workspace:*",
|
||||||
"@tauri-apps/plugin-cli": "^2.4.1",
|
"@tauri-apps/plugin-cli": "workspace:*",
|
||||||
"@tauri-apps/plugin-clipboard-manager": "^2.3.2",
|
"@tauri-apps/plugin-clipboard-manager": "workspace:*",
|
||||||
"@tauri-apps/plugin-dialog": "^2.4.2",
|
"@tauri-apps/plugin-dialog": "2",
|
||||||
"@tauri-apps/plugin-fs": "^2.4.4",
|
"@tauri-apps/plugin-fs": "workspace:*",
|
||||||
"@tauri-apps/plugin-geolocation": "^2.2.0",
|
"@tauri-apps/plugin-geolocation": "workspace:*",
|
||||||
"@tauri-apps/plugin-global-shortcut": "^2.3.1",
|
"@tauri-apps/plugin-global-shortcut": "workspace:*",
|
||||||
"@tauri-apps/plugin-haptics": "^2.2.0",
|
"@tauri-apps/plugin-haptics": "workspace:*",
|
||||||
"@tauri-apps/plugin-http": "^2.5.4",
|
"@tauri-apps/plugin-http": "workspace:*",
|
||||||
"@tauri-apps/plugin-nfc": "^2.3.3",
|
"@tauri-apps/plugin-nfc": "workspace:*",
|
||||||
"@tauri-apps/plugin-notification": "^2.3.3",
|
"@tauri-apps/plugin-notification": "workspace:*",
|
||||||
"@tauri-apps/plugin-opener": "^2.5.2",
|
"@tauri-apps/plugin-opener": "workspace:*",
|
||||||
"@tauri-apps/plugin-os": "^2.3.2",
|
"@tauri-apps/plugin-os": "workspace:*",
|
||||||
"@tauri-apps/plugin-process": "^2.3.1",
|
"@tauri-apps/plugin-process": "workspace:*",
|
||||||
"@tauri-apps/plugin-shell": "^2.3.3",
|
"@tauri-apps/plugin-shell": "workspace:*",
|
||||||
"@tauri-apps/plugin-store": "^2.4.1",
|
"@tauri-apps/plugin-store": "2",
|
||||||
"@tauri-apps/plugin-updater": "^2.9.0",
|
"@tauri-apps/plugin-updater": "workspace:*",
|
||||||
"@tauri-apps/plugin-upload": "^2.3.0",
|
"@tauri-apps/plugin-upload": "workspace:*",
|
||||||
"@zerodevx/svelte-json-view": "1.0.11"
|
"@zerodevx/svelte-json-view": "1.0.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify-json/codicon": "^1.2.12",
|
"@iconify-json/codicon": "^1.2.49",
|
||||||
"@iconify-json/ph": "^1.2.2",
|
"@iconify-json/ph": "^1.2.2",
|
||||||
"@sveltejs/vite-plugin-svelte": "^6.0.0",
|
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
||||||
"@tauri-apps/cli": "2.9.4",
|
"@tauri-apps/cli": "2.11.4",
|
||||||
"@unocss/extractor-svelte": "^66.3.3",
|
"@unocss/extractor-svelte": "^66.6.7",
|
||||||
"svelte": "^5.20.4",
|
"svelte": "^5.54.0",
|
||||||
"unocss": "^66.3.3",
|
"unocss": "^66.6.7",
|
||||||
"vite": "^7.0.7"
|
"vite": "^8.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,75 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## \[2.0.46]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `dialog@2.7.2`
|
||||||
|
- Upgraded to `store@2.4.4`
|
||||||
|
|
||||||
|
## \[2.0.45]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `log@2.9.0`
|
||||||
|
|
||||||
|
## \[2.0.44]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `barcode-scanner@2.4.5`
|
||||||
|
- Upgraded to `global-shortcut@2.3.2`
|
||||||
|
|
||||||
|
## \[2.0.43]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `fs@2.5.1`
|
||||||
|
- Upgraded to `opener@2.5.4`
|
||||||
|
- Upgraded to `store@2.4.3`
|
||||||
|
- Upgraded to `dialog@2.7.1`
|
||||||
|
- Upgraded to `http@2.5.9`
|
||||||
|
|
||||||
|
## \[2.0.42]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `fs@2.5.0`
|
||||||
|
- Upgraded to `http@2.5.8`
|
||||||
|
- Upgraded to `updater@2.10.1`
|
||||||
|
- Upgraded to `nfc@2.3.5`
|
||||||
|
- Upgraded to `dialog@2.7.0`
|
||||||
|
|
||||||
|
## \[2.0.41]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `updater@2.10.0`
|
||||||
|
- Upgraded to `barcode-scanner@2.4.4`
|
||||||
|
- Upgraded to `http@2.5.7`
|
||||||
|
- Upgraded to `shell@2.3.5`
|
||||||
|
|
||||||
|
## \[2.0.40]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `http@2.5.6`
|
||||||
|
- Upgraded to `dialog@2.6.0`
|
||||||
|
|
||||||
|
## \[2.0.39]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `dialog@2.5.0`
|
||||||
|
- Upgraded to `log@2.8.0`
|
||||||
|
- Upgraded to `shell@2.3.4`
|
||||||
|
- Upgraded to `barcode-scanner@2.4.3`
|
||||||
|
- Upgraded to `fs@2.4.5`
|
||||||
|
- Upgraded to `http@2.5.5`
|
||||||
|
- Upgraded to `nfc@2.3.4`
|
||||||
|
- Upgraded to `opener@2.5.3`
|
||||||
|
- Upgraded to `store@2.4.2`
|
||||||
|
|
||||||
## \[2.0.38]
|
## \[2.0.38]
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "api"
|
name = "api"
|
||||||
publish = false
|
publish = false
|
||||||
version = "2.0.38"
|
version = "2.0.46"
|
||||||
description = "An example Tauri Application showcasing the api"
|
description = "An example Tauri Application showcasing the api"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
@@ -20,24 +20,24 @@ serde = { workspace = true }
|
|||||||
tiny_http = "0.12"
|
tiny_http = "0.12"
|
||||||
time = "0.3"
|
time = "0.3"
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
tauri-plugin-log = { path = "../../../plugins/log", version = "2.7.1" }
|
tauri-plugin-log = { path = "../../../plugins/log", version = "2.9.0" }
|
||||||
tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.4.4", features = [
|
tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.5.1", features = [
|
||||||
"watch",
|
"watch",
|
||||||
] }
|
] }
|
||||||
tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", version = "2.3.2" }
|
tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", version = "2.3.2" }
|
||||||
tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.4.2" }
|
tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.7.2" }
|
||||||
tauri-plugin-http = { path = "../../../plugins/http", features = [
|
tauri-plugin-http = { path = "../../../plugins/http", features = [
|
||||||
"multipart",
|
"multipart",
|
||||||
"cookies",
|
"cookies",
|
||||||
], version = "2.5.4" }
|
], version = "2.5.9" }
|
||||||
tauri-plugin-notification = { path = "../../../plugins/notification", version = "2.3.3", features = [
|
tauri-plugin-notification = { path = "../../../plugins/notification", version = "2.3.3", features = [
|
||||||
"windows7-compat",
|
"windows7-compat",
|
||||||
] }
|
] }
|
||||||
tauri-plugin-os = { path = "../../../plugins/os", version = "2.3.2" }
|
tauri-plugin-os = { path = "../../../plugins/os", version = "2.3.2" }
|
||||||
tauri-plugin-process = { path = "../../../plugins/process", version = "2.3.1" }
|
tauri-plugin-process = { path = "../../../plugins/process", version = "2.3.1" }
|
||||||
tauri-plugin-opener = { path = "../../../plugins/opener", version = "2.5.2" }
|
tauri-plugin-opener = { path = "../../../plugins/opener", version = "2.5.4" }
|
||||||
tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.3.3" }
|
tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.3.5" }
|
||||||
tauri-plugin-store = { path = "../../../plugins/store", version = "2.4.1" }
|
tauri-plugin-store = { path = "../../../plugins/store", version = "2.4.4" }
|
||||||
tauri-plugin-upload = { path = "../../../plugins/upload", version = "2.3.0" }
|
tauri-plugin-upload = { path = "../../../plugins/upload", version = "2.3.0" }
|
||||||
|
|
||||||
[dependencies.tauri]
|
[dependencies.tauri]
|
||||||
@@ -56,13 +56,13 @@ features = [
|
|||||||
|
|
||||||
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
|
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
|
||||||
tauri-plugin-cli = { path = "../../../plugins/cli", version = "2.4.1" }
|
tauri-plugin-cli = { path = "../../../plugins/cli", version = "2.4.1" }
|
||||||
tauri-plugin-global-shortcut = { path = "../../../plugins/global-shortcut", version = "2.3.1" }
|
tauri-plugin-global-shortcut = { path = "../../../plugins/global-shortcut", version = "2.3.2" }
|
||||||
tauri-plugin-updater = { path = "../../../plugins/updater", version = "2.9.0" }
|
tauri-plugin-updater = { path = "../../../plugins/updater", version = "2.10.1" }
|
||||||
tauri-plugin-window-state = { path = "../../../plugins/window-state", version = "2.2.0" }
|
tauri-plugin-window-state = { path = "../../../plugins/window-state", version = "2.2.0" }
|
||||||
|
|
||||||
[target."cfg(any(target_os = \"android\", target_os = \"ios\"))".dependencies]
|
[target."cfg(any(target_os = \"android\", target_os = \"ios\"))".dependencies]
|
||||||
tauri-plugin-barcode-scanner = { path = "../../../plugins/barcode-scanner/", version = "2.4.2" }
|
tauri-plugin-barcode-scanner = { path = "../../../plugins/barcode-scanner/", version = "2.4.5" }
|
||||||
tauri-plugin-nfc = { path = "../../../plugins/nfc", version = "2.3.3" }
|
tauri-plugin-nfc = { path = "../../../plugins/nfc", version = "2.3.5" }
|
||||||
tauri-plugin-biometric = { path = "../../../plugins/biometric/", version = "2.3.2" }
|
tauri-plugin-biometric = { path = "../../../plugins/biometric/", version = "2.3.2" }
|
||||||
tauri-plugin-geolocation = { path = "../../../plugins/geolocation/", version = "2.3.2" }
|
tauri-plugin-geolocation = { path = "../../../plugins/geolocation/", version = "2.3.2" }
|
||||||
tauri-plugin-haptics = { path = "../../../plugins/haptics/", version = "2.3.2" }
|
tauri-plugin-haptics = { path = "../../../plugins/haptics/", version = "2.3.2" }
|
||||||
|
|||||||
@@ -15,19 +15,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"core:default",
|
"core:default",
|
||||||
|
"core:app:allow-set-app-theme",
|
||||||
"fs:default",
|
"fs:default",
|
||||||
"core:window:allow-minimize",
|
"core:window:allow-minimize",
|
||||||
"core:window:allow-maximize",
|
"core:window:allow-toggle-maximize",
|
||||||
"core:window:allow-unmaximize",
|
|
||||||
"core:window:allow-close",
|
"core:window:allow-close",
|
||||||
"core:window:allow-start-dragging",
|
"core:window:allow-start-dragging",
|
||||||
"notification:default",
|
"notification:default",
|
||||||
"os:allow-platform",
|
"os:allow-platform",
|
||||||
"dialog:allow-open",
|
"dialog:default",
|
||||||
"dialog:allow-ask",
|
|
||||||
"dialog:allow-save",
|
|
||||||
"dialog:allow-confirm",
|
|
||||||
"dialog:allow-message",
|
|
||||||
{
|
{
|
||||||
"identifier": "shell:allow-spawn",
|
"identifier": "shell:allow-spawn",
|
||||||
"allow": [
|
"allow": [
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
package com.tauri.api
|
package com.tauri.api
|
||||||
|
|
||||||
class MainActivity : TauriActivity()
|
import android.os.Bundle
|
||||||
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
|
||||||
|
class MainActivity : TauriActivity() {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
enableEdgeToEdge()
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="Theme.api" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
<style name="Theme.api" parent="Theme.Material3.DayNight.NoActionBar">
|
||||||
<!-- Customize your theme here. -->
|
<!-- Customize your theme here. -->
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="Theme.api" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
<style name="Theme.api" parent="Theme.Material3.DayNight.NoActionBar">
|
||||||
<!-- Customize your theme here. -->
|
<!-- Customize your theme here. -->
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
+17
-1
@@ -21,7 +21,23 @@ open class BuildTask : DefaultTask() {
|
|||||||
runTauriCli(executable)
|
runTauriCli(executable)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||||
runTauriCli("$executable.cmd")
|
// Try different Windows-specific extensions
|
||||||
|
val fallbacks = listOf(
|
||||||
|
"$executable.exe",
|
||||||
|
"$executable.cmd",
|
||||||
|
"$executable.bat",
|
||||||
|
)
|
||||||
|
|
||||||
|
var lastException: Exception = e
|
||||||
|
for (fallback in fallbacks) {
|
||||||
|
try {
|
||||||
|
runTauriCli(fallback)
|
||||||
|
return
|
||||||
|
} catch (fallbackException: Exception) {
|
||||||
|
lastException = fallbackException
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw lastException
|
||||||
} else {
|
} else {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
#Tue May 10 19:22:52 CST 2022
|
#Tue May 10 19:22:52 CST 2022
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
+128
-195
@@ -1,7 +1,16 @@
|
|||||||
<script>
|
<script module lang="ts">
|
||||||
|
export type ViewProps = {
|
||||||
|
onMessage: (value: unknown) => void
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { onMount, tick } from 'svelte'
|
||||||
import { writable } from 'svelte/store'
|
import { writable } from 'svelte/store'
|
||||||
import { getCurrentWindow } from '@tauri-apps/api/window'
|
import { MediaQuery } from 'svelte/reactivity'
|
||||||
|
import { getCurrentWindow, type Theme } from '@tauri-apps/api/window'
|
||||||
import { getCurrentWebview } from '@tauri-apps/api/webview'
|
import { getCurrentWebview } from '@tauri-apps/api/webview'
|
||||||
|
import { setTheme } from '@tauri-apps/api/app'
|
||||||
import * as os from '@tauri-apps/plugin-os'
|
import * as os from '@tauri-apps/plugin-os'
|
||||||
|
|
||||||
import Welcome from './views/Welcome.svelte'
|
import Welcome from './views/Welcome.svelte'
|
||||||
@@ -23,11 +32,10 @@
|
|||||||
import Biometric from './views/Biometric.svelte'
|
import Biometric from './views/Biometric.svelte'
|
||||||
import Geolocation from './views/Geolocation.svelte'
|
import Geolocation from './views/Geolocation.svelte'
|
||||||
import Haptics from './views/Haptics.svelte'
|
import Haptics from './views/Haptics.svelte'
|
||||||
|
|
||||||
import { onMount, tick } from 'svelte'
|
|
||||||
import { ask } from '@tauri-apps/plugin-dialog'
|
|
||||||
import Nfc from './views/Nfc.svelte'
|
import Nfc from './views/Nfc.svelte'
|
||||||
|
|
||||||
|
import TitleBar from './lib/TitleBar.svelte'
|
||||||
|
|
||||||
const appWindow = getCurrentWindow()
|
const appWindow = getCurrentWindow()
|
||||||
|
|
||||||
if (appWindow.label !== 'main') {
|
if (appWindow.label !== 'main') {
|
||||||
@@ -148,114 +156,98 @@
|
|||||||
component: Haptics,
|
component: Haptics,
|
||||||
icon: 'i-ph-vibrate'
|
icon: 'i-ph-vibrate'
|
||||||
}
|
}
|
||||||
]
|
].filter(Boolean) as {
|
||||||
|
label: string
|
||||||
|
component: typeof Haptics
|
||||||
|
icon: string
|
||||||
|
}[]
|
||||||
|
let selected = $state.raw(views[0])
|
||||||
|
|
||||||
let selected = views[0]
|
// dark/light themes
|
||||||
function select(view) {
|
const preferDark = new MediaQuery('prefers-color-scheme: dark')
|
||||||
selected = view
|
let theme = $state<Theme | 'auto'>(
|
||||||
}
|
(localStorage.getItem('theme') as Theme | null) || 'auto'
|
||||||
|
)
|
||||||
|
|
||||||
// Window controls
|
async function switchTheme() {
|
||||||
let isWindowMaximized
|
switch (theme) {
|
||||||
onMount(async () => {
|
case 'dark':
|
||||||
isWindowMaximized = await appWindow.isMaximized()
|
theme = 'light'
|
||||||
appWindow.onResized(async () => {
|
break
|
||||||
isWindowMaximized = await appWindow.isMaximized()
|
case 'light':
|
||||||
})
|
theme = 'auto'
|
||||||
})
|
break
|
||||||
|
case 'auto':
|
||||||
function minimize() {
|
theme = 'dark'
|
||||||
appWindow.minimize()
|
break
|
||||||
}
|
|
||||||
|
|
||||||
async function toggleMaximize() {
|
|
||||||
;(await appWindow.isMaximized())
|
|
||||||
? appWindow.unmaximize()
|
|
||||||
: appWindow.maximize()
|
|
||||||
}
|
|
||||||
|
|
||||||
let confirmed_close = false
|
|
||||||
async function close() {
|
|
||||||
if (!confirmed_close) {
|
|
||||||
confirmed_close = await ask(
|
|
||||||
'Are you sure that you want to close this window?',
|
|
||||||
{
|
|
||||||
title: 'Tauri API'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if (confirmed_close) {
|
|
||||||
appWindow.close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
applyTheme()
|
||||||
}
|
}
|
||||||
|
|
||||||
// dark/light
|
function applyTheme() {
|
||||||
let isDark
|
const isDark = theme === 'auto' ? preferDark.current : theme === 'dark'
|
||||||
onMount(() => {
|
if (isDark) {
|
||||||
isDark = localStorage && localStorage.getItem('theme') == 'dark'
|
document.documentElement.classList.add('dark')
|
||||||
applyTheme(isDark)
|
document.documentElement.classList.remove('light')
|
||||||
|
} else {
|
||||||
|
document.documentElement.classList.remove('dark')
|
||||||
|
document.documentElement.classList.add('light')
|
||||||
|
}
|
||||||
|
setTheme(theme === 'auto' ? null : theme)
|
||||||
|
localStorage.setItem('theme', theme)
|
||||||
|
}
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
applyTheme()
|
||||||
})
|
})
|
||||||
function applyTheme(isDark) {
|
|
||||||
const html = document.querySelector('html')
|
|
||||||
isDark ? html.classList.add('dark') : html.classList.remove('dark')
|
|
||||||
localStorage && localStorage.setItem('theme', isDark ? 'dark' : '')
|
|
||||||
}
|
|
||||||
function toggleDark() {
|
|
||||||
isDark = !isDark
|
|
||||||
applyTheme(isDark)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Console
|
// Console
|
||||||
let messages = writable([])
|
const messages = writable<string[]>([])
|
||||||
let consoleTextEl
|
let consoleTextEl: HTMLDivElement
|
||||||
async function onMessage(value) {
|
|
||||||
|
// this function is renders HTML without sanitizing it so it's insecure
|
||||||
|
// we only use it with our own input data
|
||||||
|
async function insecureRenderHtml(html: string) {
|
||||||
messages.update((r) => [
|
messages.update((r) => [
|
||||||
...r,
|
...r,
|
||||||
{
|
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ${html}</pre>`
|
||||||
html:
|
|
||||||
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` +
|
|
||||||
(typeof value === 'string' ? value : JSON.stringify(value, null, 1)) +
|
|
||||||
'</pre>'
|
|
||||||
}
|
|
||||||
])
|
])
|
||||||
await tick()
|
await tick()
|
||||||
if (consoleTextEl) consoleTextEl.scrollTop = consoleTextEl.scrollHeight
|
consoleTextEl.scrollTop = consoleTextEl.scrollHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
// this function renders HTML without sanitizing it so it's insecure
|
async function onMessage(value: unknown) {
|
||||||
// we only use it with our own input data
|
const valueStr =
|
||||||
async function insecureRenderHtml(html) {
|
typeof value === 'string'
|
||||||
messages.update((r) => [
|
? value
|
||||||
...r,
|
: JSON.stringify(
|
||||||
{
|
value instanceof ArrayBuffer
|
||||||
html:
|
? Array.from(new Uint8Array(value))
|
||||||
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` +
|
: value,
|
||||||
html +
|
null,
|
||||||
'</pre>'
|
1
|
||||||
}
|
)
|
||||||
])
|
insecureRenderHtml(valueStr)
|
||||||
await tick()
|
|
||||||
if (consoleTextEl) consoleTextEl.scrollTop = consoleTextEl.scrollHeight
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function clear() {
|
function clear() {
|
||||||
messages.update(() => [])
|
messages.update(() => [])
|
||||||
}
|
}
|
||||||
|
|
||||||
let consoleEl, consoleH, cStartY
|
let consoleEl: HTMLDivElement
|
||||||
let minConsoleHeight = 50
|
let consoleH = 0
|
||||||
function startResizingConsole(e) {
|
let cStartY = 0
|
||||||
|
const minConsoleHeight = 50
|
||||||
|
function startResizingConsole(e: MouseEvent) {
|
||||||
cStartY = e.clientY
|
cStartY = e.clientY
|
||||||
|
|
||||||
const styles = window.getComputedStyle(consoleEl)
|
const styles = window.getComputedStyle(consoleEl)
|
||||||
consoleH = parseInt(styles.height, 10)
|
consoleH = parseInt(styles.height, 10)
|
||||||
|
|
||||||
const moveHandler = (e) => {
|
const moveHandler = (e: MouseEvent) => {
|
||||||
const dy = e.clientY - cStartY
|
const dy = e.clientY - cStartY
|
||||||
const newH = consoleH - dy
|
const newH = consoleH - dy
|
||||||
consoleEl.style.height = `${
|
consoleEl.style.height = `${newH < minConsoleHeight ? minConsoleHeight : newH}px`
|
||||||
newH < minConsoleHeight ? minConsoleHeight : newH
|
|
||||||
}px`
|
|
||||||
}
|
}
|
||||||
const upHandler = () => {
|
const upHandler = () => {
|
||||||
document.removeEventListener('mouseup', upHandler)
|
document.removeEventListener('mouseup', upHandler)
|
||||||
@@ -265,21 +257,19 @@
|
|||||||
document.addEventListener('mousemove', moveHandler)
|
document.addEventListener('mousemove', moveHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
let isWindows
|
let isWindows = os.platform() === 'windows'
|
||||||
onMount(async () => {
|
|
||||||
isWindows = (await os.platform()) === 'windows'
|
|
||||||
})
|
|
||||||
|
|
||||||
// mobile
|
// mobile
|
||||||
let isSideBarOpen = false
|
let isSideBarOpen = $state(false)
|
||||||
let sidebar
|
let sidebar: HTMLElement
|
||||||
let sidebarToggle
|
let sidebarToggle: HTMLElement
|
||||||
let isDraggingSideBar = false
|
let isDraggingSideBar = false
|
||||||
let draggingStartPosX = 0
|
let draggingStartPosX = 0
|
||||||
let draggingEndPosX = 0
|
let draggingEndPosX = 0
|
||||||
const clamp = (min, num, max) => Math.min(Math.max(num, min), max)
|
const clamp = (min: number, num: number, max: number) =>
|
||||||
|
Math.min(Math.max(num, min), max)
|
||||||
|
|
||||||
function toggleSidebar(sidebar, isSideBarOpen) {
|
function toggleSidebar() {
|
||||||
sidebar.style.setProperty(
|
sidebar.style.setProperty(
|
||||||
'--translate-x',
|
'--translate-x',
|
||||||
`${isSideBarOpen ? '0' : '-18.75'}rem`
|
`${isSideBarOpen ? '0' : '-18.75'}rem`
|
||||||
@@ -287,10 +277,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
sidebar = document.querySelector('#sidebar')
|
|
||||||
sidebarToggle = document.querySelector('#sidebarToggle')
|
|
||||||
|
|
||||||
document.addEventListener('click', (e) => {
|
document.addEventListener('click', (e) => {
|
||||||
|
if (!(e.target instanceof Node)) return
|
||||||
|
|
||||||
if (sidebarToggle.contains(e.target)) {
|
if (sidebarToggle.contains(e.target)) {
|
||||||
isSideBarOpen = !isSideBarOpen
|
isSideBarOpen = !isSideBarOpen
|
||||||
} else if (isSideBarOpen && !sidebar.contains(e.target)) {
|
} else if (isSideBarOpen && !sidebar.contains(e.target)) {
|
||||||
@@ -299,6 +288,7 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
document.addEventListener('touchstart', (e) => {
|
document.addEventListener('touchstart', (e) => {
|
||||||
|
if (!(e.target instanceof Node)) return
|
||||||
if (sidebarToggle.contains(e.target)) return
|
if (sidebarToggle.contains(e.target)) return
|
||||||
|
|
||||||
const x = e.touches[0].clientX
|
const x = e.touches[0].clientX
|
||||||
@@ -330,76 +320,22 @@
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
$: {
|
$effect(() => {
|
||||||
const sidebar = document.querySelector('#sidebar')
|
toggleSidebar()
|
||||||
if (sidebar) {
|
})
|
||||||
toggleSidebar(sidebar, isSideBarOpen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- custom titlebar for Windows -->
|
<!-- custom titlebar for Windows -->
|
||||||
{#if isWindows}
|
{#if isWindows}
|
||||||
<div
|
<TitleBar {appWindow} {theme} {switchTheme} />
|
||||||
class="w-screen select-none h-8 pl-2 flex justify-between items-center absolute text-primaryText dark:text-darkPrimaryText"
|
|
||||||
data-tauri-drag-region
|
|
||||||
>
|
|
||||||
<span class="lt-sm:pl-10 text-darkPrimaryText">Tauri API Validation</span>
|
|
||||||
<span
|
|
||||||
class="
|
|
||||||
h-100%
|
|
||||||
children:h-100% children:w-12 children:inline-flex
|
|
||||||
children:items-center children:justify-center"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
aria-label="Toggle dark mode"
|
|
||||||
title={isDark ? 'Switch to Light mode' : 'Switch to Dark mode'}
|
|
||||||
class="bg-inherit border-none hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"
|
|
||||||
on:click={toggleDark}
|
|
||||||
>
|
|
||||||
{#if isDark}
|
|
||||||
<div class="i-ph-sun"></div>
|
|
||||||
{:else}
|
|
||||||
<div class="i-ph-moon"></div>
|
|
||||||
{/if}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
aria-label="Minimize window"
|
|
||||||
title="Minimize"
|
|
||||||
class="bg-inherit border-none hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"
|
|
||||||
on:click={minimize}
|
|
||||||
>
|
|
||||||
<div class="i-codicon-chrome-minimize"></div>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
aria-label="Maximize window"
|
|
||||||
title={isWindowMaximized ? 'Restore' : 'Maximize'}
|
|
||||||
class="bg-inherit border-none hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"
|
|
||||||
on:click={toggleMaximize}
|
|
||||||
>
|
|
||||||
{#if isWindowMaximized}
|
|
||||||
<div class="i-codicon-chrome-restore"></div>
|
|
||||||
{:else}
|
|
||||||
<div class="i-codicon-chrome-maximize"></div>
|
|
||||||
{/if}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
aria-label="Close window"
|
|
||||||
title="Close"
|
|
||||||
class="bg-inherit border-none hover:bg-red-700 dark:hover:bg-red-700 hover:text-darkPrimaryText active:bg-red-700/90 dark:active:bg-red-700/90 active:text-darkPrimaryText"
|
|
||||||
on:click={close}
|
|
||||||
>
|
|
||||||
<div class="i-codicon-chrome-close"></div>
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- Sidebar toggle, only visible on small screens -->
|
<!-- Sidebar toggle, only visible on small screens -->
|
||||||
<div
|
<div
|
||||||
id="sidebarToggle"
|
id="sidebarToggle"
|
||||||
class="z-2000 sidebar-toggle hidden lt-sm:flex justify-center absolute items-center w-8 h-8 rd-8
|
bind:this={sidebarToggle}
|
||||||
bg-accent dark:bg-darkAccent active:bg-accentDark dark:active:bg-darkAccentDark"
|
class="z-2000 hidden lt-sm:flex justify-center absolute items-center w-8 h-8 rd-8
|
||||||
|
bg-accent dark:bg-darkAccent active:bg-accentDark dark:active:bg-darkAccentDark text-accentText dark:text-darkAccentText"
|
||||||
>
|
>
|
||||||
{#if isSideBarOpen}
|
{#if isSideBarOpen}
|
||||||
<span class="i-codicon-close animate-duration-300ms animate-fade-in"></span>
|
<span class="i-codicon-close animate-duration-300ms animate-fade-in"></span>
|
||||||
@@ -409,10 +345,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="flex h-screen w-screen overflow-hidden children-pt8 children-pb-2 text-primaryText dark:text-darkPrimaryText"
|
class="flex h-screen w-screen overflow-hidden text-primaryText dark:text-darkPrimaryText"
|
||||||
>
|
>
|
||||||
<aside
|
<aside
|
||||||
id="sidebar"
|
id="sidebar"
|
||||||
|
bind:this={sidebar}
|
||||||
class="lt-sm:h-screen lt-sm:shadow-lg lt-sm:shadow lt-sm:transition-transform lt-sm:absolute lt-sm:z-1999
|
class="lt-sm:h-screen lt-sm:shadow-lg lt-sm:shadow lt-sm:transition-transform lt-sm:absolute lt-sm:z-1999
|
||||||
bg-darkPrimaryLighter transition-colors-250 overflow-hidden grid select-none px-2"
|
bg-darkPrimaryLighter transition-colors-250 overflow-hidden grid select-none px-2"
|
||||||
>
|
>
|
||||||
@@ -420,13 +357,16 @@
|
|||||||
<img class="p-7" src="tauri_logo.png" alt="Tauri logo" />
|
<img class="p-7" src="tauri_logo.png" alt="Tauri logo" />
|
||||||
</a>
|
</a>
|
||||||
{#if !isWindows}
|
{#if !isWindows}
|
||||||
<a href="##" class="nv justify-between h-8" on:click={toggleDark}>
|
<a href="##" class="nv justify-between" onclick={switchTheme}>
|
||||||
{#if isDark}
|
{#if theme === 'auto'}
|
||||||
Switch to Light mode
|
|
||||||
<div class="i-ph-sun"></div>
|
|
||||||
{:else}
|
|
||||||
Switch to Dark mode
|
Switch to Dark mode
|
||||||
|
<div class="i-ph-circle-half-fill"></div>
|
||||||
|
{:else if theme === 'dark'}
|
||||||
|
Switch to Light mode
|
||||||
<div class="i-ph-moon"></div>
|
<div class="i-ph-moon"></div>
|
||||||
|
{:else if theme === 'light'}
|
||||||
|
Switch to Auto mode
|
||||||
|
<div class="i-ph-sun"></div>
|
||||||
{/if}
|
{/if}
|
||||||
</a>
|
</a>
|
||||||
<br />
|
<br />
|
||||||
@@ -435,7 +375,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<a
|
<a
|
||||||
class="nv justify-between h-8"
|
class="nv justify-between"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href="https://tauri.app/v1/guides/"
|
href="https://tauri.app/v1/guides/"
|
||||||
>
|
>
|
||||||
@@ -443,7 +383,7 @@
|
|||||||
<span class="i-codicon-link-external"></span>
|
<span class="i-codicon-link-external"></span>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
class="nv justify-between h-8"
|
class="nv justify-between"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href="https://github.com/tauri-apps/tauri"
|
href="https://github.com/tauri-apps/tauri"
|
||||||
>
|
>
|
||||||
@@ -451,7 +391,7 @@
|
|||||||
<span class="i-codicon-link-external"></span>
|
<span class="i-codicon-link-external"></span>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
class="nv justify-between h-8"
|
class="nv justify-between"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href="https://github.com/tauri-apps/tauri/tree/dev/examples/api"
|
href="https://github.com/tauri-apps/tauri/tree/dev/examples/api"
|
||||||
>
|
>
|
||||||
@@ -461,23 +401,19 @@
|
|||||||
<br />
|
<br />
|
||||||
<div class="bg-white/5 h-2px"></div>
|
<div class="bg-white/5 h-2px"></div>
|
||||||
<br />
|
<br />
|
||||||
<div
|
<div class="flex flex-col overflow-y-auto children-flex-none gap-1">
|
||||||
class="flex flex-col overflow-y-auto children-h-10 children-flex-none gap-1"
|
|
||||||
>
|
|
||||||
{#each views as view}
|
{#each views as view}
|
||||||
{#if view}
|
<a
|
||||||
<a
|
href="##"
|
||||||
href="##"
|
class="nv {selected === view ? 'nv_selected' : ''}"
|
||||||
class="nv {selected === view ? 'nv_selected' : ''}"
|
onclick={() => {
|
||||||
on:click={() => {
|
selected = view
|
||||||
select(view)
|
isSideBarOpen = false
|
||||||
isSideBarOpen = false
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<div class="{view.icon} mr-2"></div>
|
||||||
<div class="{view.icon} mr-2"></div>
|
<p>{view.label}</p></a
|
||||||
<p>{view.label}</p></a
|
>
|
||||||
>
|
|
||||||
{/if}
|
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
@@ -489,11 +425,7 @@
|
|||||||
<h1>{selected.label}</h1>
|
<h1>{selected.label}</h1>
|
||||||
<div class="overflow-y-auto">
|
<div class="overflow-y-auto">
|
||||||
<div class="mr-2">
|
<div class="mr-2">
|
||||||
<svelte:component
|
<selected.component {onMessage} />
|
||||||
this={selected.component}
|
|
||||||
{onMessage}
|
|
||||||
{insecureRenderHtml}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -503,20 +435,21 @@
|
|||||||
id="console"
|
id="console"
|
||||||
class="select-none h-15rem grid grid-rows-[2px_2rem_1fr] gap-1 overflow-hidden"
|
class="select-none h-15rem grid grid-rows-[2px_2rem_1fr] gap-1 overflow-hidden"
|
||||||
>
|
>
|
||||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
||||||
<div
|
<div
|
||||||
on:mousedown={startResizingConsole}
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
onmousedown={startResizingConsole}
|
||||||
class="bg-black/20 h-2px cursor-ns-resize"
|
class="bg-black/20 h-2px cursor-ns-resize"
|
||||||
></div>
|
></div>
|
||||||
<div class="flex justify-between items-center px-2">
|
<div class="flex justify-between items-center px-2">
|
||||||
<p class="font-semibold">Console</p>
|
<p class="font-semibold">Console</p>
|
||||||
<button
|
<button
|
||||||
aria-label="Clear Console"
|
aria-label="Clear Console"
|
||||||
class="cursor-pointer h-85% rd-1 p-1 flex justify-center items-center border-none bg-inherit
|
class="cursor-pointer h-85% rd-1 p-1 flex justify-center items-center border-none
|
||||||
hover:bg-hoverOverlay dark:hover:bg-darkHoverOverlay
|
hover:bg-hoverOverlay dark:hover:bg-darkHoverOverlay
|
||||||
active:bg-hoverOverlay/25 dark:active:bg-darkHoverOverlay/25
|
active:bg-hoverOverlay/25 dark:active:bg-darkHoverOverlay/25
|
||||||
"
|
"
|
||||||
on:click={clear}
|
onclick={clear}
|
||||||
>
|
>
|
||||||
<div class="i-codicon-clear-all"></div>
|
<div class="i-codicon-clear-all"></div>
|
||||||
</button>
|
</button>
|
||||||
@@ -525,8 +458,8 @@
|
|||||||
bind:this={consoleTextEl}
|
bind:this={consoleTextEl}
|
||||||
class="px-2 overflow-y-auto all:font-mono code-block all:text-xs select-text mr-2"
|
class="px-2 overflow-y-auto all:font-mono code-block all:text-xs select-text mr-2"
|
||||||
>
|
>
|
||||||
{#each $messages as r}
|
{#each $messages as messageHtml}
|
||||||
{@html r.html}
|
{@html messageHtml}
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
:root {
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.dark {
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
|
:root.light {
|
||||||
|
color-scheme: light;
|
||||||
|
}
|
||||||
|
|
||||||
*:not(h1, h2, h3, h4, h5, h6) {
|
*:not(h1, h2, h3, h4, h5, h6) {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -8,6 +19,11 @@
|
|||||||
font-family: 'Rubik', sans-serif;
|
font-family: 'Rubik', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
color: inherit;
|
||||||
|
background-color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 0.25rem;
|
width: 0.25rem;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
@@ -33,27 +49,34 @@ code.code-block {
|
|||||||
width: 18.75rem;
|
width: 18.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 640px) {
|
@media screen and (width < 640px) {
|
||||||
#sidebar {
|
#sidebar {
|
||||||
--translate-x: -18.75rem;
|
--translate-x: -18.75rem;
|
||||||
transform: translateX(var(--translate-x));
|
transform: translateX(var(--translate-x));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-toggle {
|
#sidebar,
|
||||||
margin-top: 0.5rem;
|
main {
|
||||||
margin-left: 0.5rem;
|
padding-top: calc(env(safe-area-inset-top) + 2rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
#sidebar {
|
||||||
overflow: hidden;
|
padding-left: calc(env(safe-area-inset-left) + 0.5rem);
|
||||||
padding: env(safe-area-inset-top) env(safe-area-inset-right)
|
}
|
||||||
env(safe-area-inset-bottom) env(safe-area-inset-left);
|
|
||||||
|
main {
|
||||||
|
padding-right: env(safe-area-inset-right);
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebarToggle {
|
||||||
|
margin-top: calc(env(safe-area-inset-top) + 0.5rem);
|
||||||
|
margin-left: calc(env(safe-area-inset-left) + 0.5rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar,
|
#sidebar,
|
||||||
#console {
|
#console {
|
||||||
padding-bottom: calc(env(safe-area-inset-bottom) + 24px);
|
padding-bottom: calc(env(safe-area-inset-bottom) + 1.5rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.transparent {
|
.transparent {
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { Theme, Window } from '@tauri-apps/api/window'
|
||||||
|
import { ask } from '@tauri-apps/plugin-dialog'
|
||||||
|
import { onMount } from 'svelte'
|
||||||
|
|
||||||
|
let {
|
||||||
|
appWindow,
|
||||||
|
switchTheme,
|
||||||
|
theme
|
||||||
|
}: { appWindow: Window; switchTheme: () => void; theme: Theme | 'auto' } =
|
||||||
|
$props()
|
||||||
|
|
||||||
|
// Window controls
|
||||||
|
let isWindowMaximized = $state(false)
|
||||||
|
onMount(async () => {
|
||||||
|
isWindowMaximized = await appWindow.isMaximized()
|
||||||
|
appWindow.onResized(async () => {
|
||||||
|
isWindowMaximized = await appWindow.isMaximized()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
async function minimize() {
|
||||||
|
await appWindow.minimize()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toggleMaximize() {
|
||||||
|
await appWindow.toggleMaximize()
|
||||||
|
}
|
||||||
|
|
||||||
|
let confirmedClose = false
|
||||||
|
async function close() {
|
||||||
|
if (!confirmedClose) {
|
||||||
|
confirmedClose = await ask(
|
||||||
|
'Are you sure that you want to close this window?',
|
||||||
|
{
|
||||||
|
title: 'Tauri API'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (confirmedClose) {
|
||||||
|
appWindow.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="w-screen select-none h-8 flex justify-between items-center absolute text-primaryText dark:text-darkPrimaryText"
|
||||||
|
data-tauri-drag-region
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="h-100% pl-2 flex-1 flex items-center lt-sm:pl-10 lt-lg:text-darkPrimaryText [app-region:drag]"
|
||||||
|
>Tauri API Validation</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="
|
||||||
|
h-100%
|
||||||
|
children:h-100% children:w-12 children:inline-flex
|
||||||
|
children:items-center children:justify-center"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
aria-label="Toggle dark mode"
|
||||||
|
title={theme ? 'Switch to Light mode' : 'Switch to Dark mode'}
|
||||||
|
class="border-none hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"
|
||||||
|
onclick={switchTheme}
|
||||||
|
>
|
||||||
|
{#if theme === 'auto'}
|
||||||
|
<div title="Switch to Dark mode" class="i-ph-circle-half-fill"></div>
|
||||||
|
{:else if theme === 'dark'}
|
||||||
|
<div title="Switch to Light mode" class="i-ph-moon"></div>
|
||||||
|
{:else if theme === 'light'}
|
||||||
|
<div title="Switch to Auto mode" class="i-ph-sun"></div>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
aria-label="Minimize window"
|
||||||
|
title="Minimize"
|
||||||
|
class="border-none hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"
|
||||||
|
onclick={minimize}
|
||||||
|
>
|
||||||
|
<div class="i-codicon-chrome-minimize"></div>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
aria-label="Maximize window"
|
||||||
|
title={isWindowMaximized ? 'Restore' : 'Maximize'}
|
||||||
|
class="border-none hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"
|
||||||
|
onclick={toggleMaximize}
|
||||||
|
>
|
||||||
|
{#if isWindowMaximized}
|
||||||
|
<div class="i-codicon-chrome-restore"></div>
|
||||||
|
{:else}
|
||||||
|
<div class="i-codicon-chrome-maximize"></div>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
aria-label="Close window"
|
||||||
|
title="Close"
|
||||||
|
class="border-none hover:bg-red-700 dark:hover:bg-red-700 hover:text-darkPrimaryText active:bg-red-700/90 dark:active:bg-red-700/90 active:text-darkPrimaryText"
|
||||||
|
onclick={close}
|
||||||
|
>
|
||||||
|
<div class="i-codicon-chrome-close"></div>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
import { readFile } from '@tauri-apps/plugin-fs'
|
import { readFile } from '@tauri-apps/plugin-fs'
|
||||||
|
|
||||||
export let onMessage
|
export let onMessage
|
||||||
export let insecureRenderHtml
|
|
||||||
let text = 'clipboard message'
|
let text = 'clipboard message'
|
||||||
|
|
||||||
function writeText() {
|
function writeText() {
|
||||||
@@ -41,7 +40,7 @@
|
|||||||
const image = await clipboard.readImage()
|
const image = await clipboard.readImage()
|
||||||
arrayBufferToBase64(await image.rgba(), function (base64) {
|
arrayBufferToBase64(await image.rgba(), function (base64) {
|
||||||
const src = 'data:image/png;base64,' + base64
|
const src = 'data:image/png;base64,' + base64
|
||||||
insecureRenderHtml('<img src="' + src + '"></img>')
|
onMessage('<img src="' + src + '"></img>')
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|||||||
@@ -1,122 +1,138 @@
|
|||||||
<script>
|
<script lang="ts">
|
||||||
import { open, save, confirm, message } from "@tauri-apps/plugin-dialog";
|
import {
|
||||||
import { readFile } from "@tauri-apps/plugin-fs";
|
open,
|
||||||
|
save,
|
||||||
|
confirm,
|
||||||
|
message,
|
||||||
|
type PickerMode,
|
||||||
|
type FileAccessMode
|
||||||
|
} from '@tauri-apps/plugin-dialog'
|
||||||
|
import { readFile } from '@tauri-apps/plugin-fs'
|
||||||
|
import type { ViewProps } from '../App.svelte'
|
||||||
|
|
||||||
export let onMessage;
|
let { onMessage }: ViewProps = $props()
|
||||||
export let insecureRenderHtml;
|
|
||||||
let defaultPath = null;
|
|
||||||
let filter = null;
|
|
||||||
let multiple = false;
|
|
||||||
let directory = false;
|
|
||||||
let pickerMode = "";
|
|
||||||
|
|
||||||
function arrayBufferToBase64(buffer, callback) {
|
let defaultPath: string | undefined = $state()
|
||||||
|
let filter = $state('')
|
||||||
|
let multiple = $state(false)
|
||||||
|
let directory = $state(false)
|
||||||
|
let pickerMode: PickerMode = $state('document')
|
||||||
|
let fileAccessMode: FileAccessMode = $state('scoped')
|
||||||
|
|
||||||
|
function arrayBufferToBase64(
|
||||||
|
buffer: ArrayBuffer,
|
||||||
|
callback: (base64: string) => void
|
||||||
|
) {
|
||||||
var blob = new Blob([buffer], {
|
var blob = new Blob([buffer], {
|
||||||
type: "application/octet-binary",
|
type: 'application/octet-binary'
|
||||||
});
|
})
|
||||||
var reader = new FileReader();
|
var reader = new FileReader()
|
||||||
reader.onload = function (evt) {
|
reader.onload = function (evt) {
|
||||||
var dataurl = evt.target.result;
|
var dataurl = evt.target!.result as string
|
||||||
callback(dataurl.substr(dataurl.indexOf(",") + 1));
|
callback(dataurl.split(',')[1])
|
||||||
};
|
}
|
||||||
reader.readAsDataURL(blob);
|
reader.readAsDataURL(blob)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function prompt() {
|
async function prompt() {
|
||||||
confirm("Do you want to do something?")
|
confirm('Do you want to do something?')
|
||||||
.then((res) => onMessage(res ? "Yes" : "No"))
|
.then((res) => onMessage(res ? 'Yes' : 'No'))
|
||||||
.catch(onMessage);
|
.catch(onMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function promptCustom() {
|
async function promptCustom() {
|
||||||
confirm("Is Tauri awesome?", {
|
confirm('Is Tauri awesome?', {
|
||||||
okLabel: "Absolutely",
|
okLabel: 'Absolutely',
|
||||||
cancelLabel: "Totally",
|
cancelLabel: 'Totally'
|
||||||
})
|
})
|
||||||
.then((res) =>
|
.then((res) =>
|
||||||
onMessage(
|
onMessage(
|
||||||
res ? "Tauri is absolutely awesome" : "Tauri is totally awesome"
|
res ? 'Tauri is absolutely awesome' : 'Tauri is totally awesome'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.catch(onMessage);
|
.catch(onMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function msg() {
|
async function msg() {
|
||||||
await message("Tauri is awesome!");
|
await message('Tauri is awesome!').then((res) => onMessage(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function msgCustom(result) {
|
async function msgCustom() {
|
||||||
const buttons = { yes: "awesome", no: "amazing", cancel: "stunning" };
|
const buttons = { yes: 'awesome', no: 'amazing', cancel: 'stunning' }
|
||||||
await message(`Tauri is: `, { buttons })
|
await message(`Tauri is: `, { buttons })
|
||||||
.then((res) => onMessage(`Tauri is ${res}`))
|
.then((res) => onMessage(`Tauri is ${res}`))
|
||||||
.catch(onMessage);
|
.catch(onMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
function openDialog() {
|
async function openDialog() {
|
||||||
open({
|
try {
|
||||||
title: "My wonderful open dialog",
|
const result = await open({
|
||||||
defaultPath,
|
title: 'My wonderful open dialog',
|
||||||
filters: filter
|
defaultPath,
|
||||||
? [
|
filters: filter
|
||||||
{
|
? [
|
||||||
name: "Tauri Example",
|
{
|
||||||
extensions: filter.split(",").map((f) => f.trim()),
|
name: 'Tauri Example',
|
||||||
},
|
extensions: filter.split(',').map((f) => f.trim())
|
||||||
]
|
|
||||||
: [],
|
|
||||||
multiple,
|
|
||||||
directory,
|
|
||||||
pickerMode: pickerMode === "" ? undefined : pickerMode,
|
|
||||||
})
|
|
||||||
.then(function (res) {
|
|
||||||
if (Array.isArray(res)) {
|
|
||||||
onMessage(res);
|
|
||||||
} else {
|
|
||||||
var pathToRead = res;
|
|
||||||
var isFile = pathToRead.match(/\S+\.\S+$/g);
|
|
||||||
readFile(pathToRead)
|
|
||||||
.then(function (response) {
|
|
||||||
if (isFile) {
|
|
||||||
if (
|
|
||||||
pathToRead.includes(".png") ||
|
|
||||||
pathToRead.includes(".jpg") ||
|
|
||||||
pathToRead.includes(".jpeg")
|
|
||||||
) {
|
|
||||||
arrayBufferToBase64(
|
|
||||||
new Uint8Array(response),
|
|
||||||
function (base64) {
|
|
||||||
var src = "data:image/png;base64," + base64;
|
|
||||||
insecureRenderHtml('<img src="' + src + '"></img>');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
onMessage(res);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
onMessage(res);
|
|
||||||
}
|
}
|
||||||
})
|
]
|
||||||
.catch(onMessage);
|
: [],
|
||||||
}
|
multiple,
|
||||||
|
directory,
|
||||||
|
pickerMode,
|
||||||
|
fileAccessMode
|
||||||
})
|
})
|
||||||
.catch(onMessage);
|
|
||||||
|
if (Array.isArray(result)) {
|
||||||
|
onMessage(result)
|
||||||
|
} else if (result === null) {
|
||||||
|
onMessage('user cancelled the selection')
|
||||||
|
} else {
|
||||||
|
const pathToRead = result
|
||||||
|
const isFile = pathToRead.match(/\S+\.\S+$/g)
|
||||||
|
|
||||||
|
await readFile(pathToRead).then(function (res) {
|
||||||
|
if (isFile) {
|
||||||
|
if (
|
||||||
|
pathToRead.includes('.png')
|
||||||
|
|| pathToRead.includes('.jpg')
|
||||||
|
|| pathToRead.includes('.jpeg')
|
||||||
|
) {
|
||||||
|
arrayBufferToBase64(res.buffer, function (base64) {
|
||||||
|
const src = 'data:image/png;base64,' + base64
|
||||||
|
onMessage('<img src="' + src + '"></img>')
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Convert byte array to UTF-8 string
|
||||||
|
const decoder = new TextDecoder('utf-8')
|
||||||
|
const text = decoder.decode(res)
|
||||||
|
onMessage(text)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onMessage(res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (exception) {
|
||||||
|
onMessage(exception)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveDialog() {
|
function saveDialog() {
|
||||||
save({
|
save({
|
||||||
title: "My wonderful save dialog",
|
title: 'My wonderful save dialog',
|
||||||
defaultPath,
|
defaultPath,
|
||||||
filters: filter
|
filters: filter
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
name: "Tauri Example",
|
name: 'Tauri Example',
|
||||||
extensions: filter.split(",").map((f) => f.trim()),
|
extensions: filter.split(',').map((f) => f.trim())
|
||||||
},
|
}
|
||||||
]
|
]
|
||||||
: [],
|
: []
|
||||||
})
|
})
|
||||||
.then(onMessage)
|
.then(onMessage)
|
||||||
.catch(onMessage);
|
.catch(onMessage)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -154,18 +170,26 @@
|
|||||||
<option value="document">Document</option>
|
<option value="document">Document</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="dialog-file-access-mode">File Access Mode:</label>
|
||||||
|
<select id="dialog-file-access-mode" bind:value={fileAccessMode}>
|
||||||
|
<option value="copy">Copy</option>
|
||||||
|
<option value="scoped">Scoped</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<div class="flex flex-wrap flex-col md:flex-row gap-2 children:flex-shrink-0">
|
<div class="flex flex-wrap flex-col md:flex-row gap-2 children:flex-shrink-0">
|
||||||
<button class="btn" id="open-dialog" on:click={openDialog}>Open dialog</button>
|
<button class="btn" id="open-dialog" onclick={openDialog}>Open dialog</button>
|
||||||
<button class="btn" id="save-dialog" on:click={saveDialog}
|
<button class="btn" id="save-dialog" onclick={saveDialog}
|
||||||
>Open save dialog</button
|
>Open save dialog</button
|
||||||
>
|
>
|
||||||
<button class="btn" id="prompt-dialog" on:click={prompt}>Prompt</button>
|
<button class="btn" id="prompt-dialog" onclick={prompt}>Prompt</button>
|
||||||
<button class="btn" id="custom-prompt-dialog" on:click={promptCustom}
|
<button class="btn" id="custom-prompt-dialog" onclick={promptCustom}
|
||||||
>Prompt (custom)</button
|
>Prompt (custom)</button
|
||||||
>
|
>
|
||||||
<button class="btn" id="message-dialog" on:click={msg}>Message</button>
|
<button class="btn" id="message-dialog" onclick={msg}>Message</button>
|
||||||
<button class="btn" id="message-dialog" on:click={msgCustom}>Message (custom)</button>
|
<button class="btn" id="message-dialog" onclick={msgCustom}
|
||||||
|
>Message (custom)</button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
import { arrayBufferToBase64 } from '../lib/utils'
|
import { arrayBufferToBase64 } from '../lib/utils'
|
||||||
import { onDestroy, onMount } from 'svelte'
|
import { onDestroy, onMount } from 'svelte'
|
||||||
|
|
||||||
const { onMessage, insecureRenderHtml } = $props()
|
const { onMessage } = $props()
|
||||||
|
|
||||||
let path = $state('')
|
let path = $state('')
|
||||||
let img
|
let img
|
||||||
@@ -118,12 +118,12 @@
|
|||||||
new Uint8Array(response),
|
new Uint8Array(response),
|
||||||
function (base64) {
|
function (base64) {
|
||||||
const src = 'data:image/png;base64,' + base64
|
const src = 'data:image/png;base64,' + base64
|
||||||
insecureRenderHtml('<img src="' + src + '"></img>')
|
onMessage('<img src="' + src + '"></img>')
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
const value = String.fromCharCode.apply(null, response)
|
const value = String.fromCharCode.apply(null, response)
|
||||||
insecureRenderHtml(
|
onMessage(
|
||||||
'<textarea id="file-response"></textarea><button id="file-save">Save</button>'
|
'<textarea id="file-response"></textarea><button id="file-save">Save</button>'
|
||||||
)
|
)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
+9
-20
@@ -11,30 +11,19 @@
|
|||||||
"example:api:dev": "pnpm run --filter \"api\" tauri dev"
|
"example:api:dev": "pnpm run --filter \"api\" tauri dev"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "9.39.1",
|
"@eslint/js": "10.0.1",
|
||||||
"@rollup/plugin-node-resolve": "16.0.3",
|
"@rollup/plugin-node-resolve": "16.0.3",
|
||||||
"@rollup/plugin-terser": "0.4.4",
|
"@rollup/plugin-terser": "1.0.0",
|
||||||
"@rollup/plugin-typescript": "12.3.0",
|
"@rollup/plugin-typescript": "12.3.0",
|
||||||
"covector": "^0.12.4",
|
"eslint": "10.4.0",
|
||||||
"eslint": "9.39.1",
|
|
||||||
"eslint-config-prettier": "10.1.8",
|
"eslint-config-prettier": "10.1.8",
|
||||||
"eslint-plugin-security": "3.0.1",
|
"eslint-plugin-security": "4.0.1",
|
||||||
"prettier": "3.6.2",
|
"prettier": "3.8.3",
|
||||||
"rollup": "4.53.2",
|
"rollup": "4.60.3",
|
||||||
"tslib": "2.8.1",
|
"tslib": "2.8.1",
|
||||||
"typescript": "5.9.3",
|
"typescript": "6.0.3",
|
||||||
"typescript-eslint": "8.47.0"
|
"typescript-eslint": "8.58.2"
|
||||||
},
|
},
|
||||||
"minimumReleaseAge": 4320,
|
"minimumReleaseAge": 4320,
|
||||||
"pnpm": {
|
"packageManager": "pnpm@11.13.0"
|
||||||
"overrides": {
|
|
||||||
"esbuild@<0.25.0": ">=0.25.0"
|
|
||||||
},
|
|
||||||
"onlyBuiltDependencies": [
|
|
||||||
"esbuild"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"pnpm": "^10.16.0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,6 @@
|
|||||||
"LICENSE"
|
"LICENSE"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^2.8.0"
|
"@tauri-apps/api": "^2.11.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "guest-js"
|
||||||
|
},
|
||||||
"include": ["guest-js/*.ts"]
|
"include": ["guest-js/*.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,20 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## \[2.4.5]
|
||||||
|
|
||||||
|
- [`d8645ab3`](https://github.com/tauri-apps/plugins-workspace/commit/d8645ab3e5b508456681eb53275c0837db25aeee) ([#3393](https://github.com/tauri-apps/plugins-workspace/pull/3393) by [@AlexisZankowitch](https://github.com/tauri-apps/plugins-workspace/../../AlexisZankowitch)) Fixed a crash on iOS when `cancel()` is invoked by running the cancel handler on the main thread.
|
||||||
|
|
||||||
|
## \[2.4.4]
|
||||||
|
|
||||||
|
- [`82fbb0c7`](https://github.com/tauri-apps/plugins-workspace/commit/82fbb0c790288eca72af9ade13828ded7700ff90) ([#3221](https://github.com/tauri-apps/plugins-workspace/pull/3221)) On iOS, fixed an application crash happening when the scanner was started when user denied permission before.
|
||||||
|
- [`b60dd887`](https://github.com/tauri-apps/plugins-workspace/commit/b60dd88702a2be2af942a3d104d728970a4c42d6) ([#3223](https://github.com/tauri-apps/plugins-workspace/pull/3223)) On iOS, start the scanning session on a separate thread to fix performance issues.
|
||||||
|
- [`c27af912`](https://github.com/tauri-apps/plugins-workspace/commit/c27af9128d6cc7a2424ec49e98005e68b2d0eb1a) ([#3222](https://github.com/tauri-apps/plugins-workspace/pull/3222)) On iOS, fixed an application crash happening when the scanner was started on the iOS Simulator (no camera available).
|
||||||
|
|
||||||
|
## \[2.4.3]
|
||||||
|
|
||||||
|
- [`d8bfe61f`](https://github.com/tauri-apps/plugins-workspace/commit/d8bfe61f20f235314bad93a9c50d8b7f3eade734) ([#3121](https://github.com/tauri-apps/plugins-workspace/pull/3121) by [@NKIPSC](https://github.com/tauri-apps/plugins-workspace/../../NKIPSC)) Remove unnecessary checks on Android when requesting camera permission.
|
||||||
|
- [`631d0e25`](https://github.com/tauri-apps/plugins-workspace/commit/631d0e256a37946b6a9102ca35511abfbebb92c5) ([#2440](https://github.com/tauri-apps/plugins-workspace/pull/2440) by [@kingsword09](https://github.com/tauri-apps/plugins-workspace/../../kingsword09)) Fix the `cameraView` is not removed after scanning in iOS.
|
||||||
|
|
||||||
## \[2.4.2]
|
## \[2.4.2]
|
||||||
|
|
||||||
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
|
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tauri-plugin-barcode-scanner"
|
name = "tauri-plugin-barcode-scanner"
|
||||||
version = "2.4.2"
|
version = "2.4.5"
|
||||||
description = "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS"
|
description = "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
authors = { workspace = true }
|
authors = { workspace = true }
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ import java.util.concurrent.ExecutionException
|
|||||||
|
|
||||||
private const val PERMISSION_ALIAS_CAMERA = "camera"
|
private const val PERMISSION_ALIAS_CAMERA = "camera"
|
||||||
private const val PERMISSION_NAME = Manifest.permission.CAMERA
|
private const val PERMISSION_NAME = Manifest.permission.CAMERA
|
||||||
private const val PREFS_PERMISSION_FIRST_TIME_ASKING = "PREFS_PERMISSION_FIRST_TIME_ASKING"
|
|
||||||
|
|
||||||
@InvokeArg
|
@InvokeArg
|
||||||
class ScanOptions {
|
class ScanOptions {
|
||||||
@@ -354,17 +353,6 @@ class BarcodeScannerPlugin(private val activity: Activity) : Plugin(activity),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun markFirstPermissionRequest() {
|
|
||||||
val sharedPreference: SharedPreferences =
|
|
||||||
activity.getSharedPreferences(PREFS_PERMISSION_FIRST_TIME_ASKING, MODE_PRIVATE)
|
|
||||||
sharedPreference.edit().putBoolean(PERMISSION_NAME, false).apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun firstPermissionRequest(): Boolean {
|
|
||||||
return activity.getSharedPreferences(PREFS_PERMISSION_FIRST_TIME_ASKING, MODE_PRIVATE)
|
|
||||||
.getBoolean(PERMISSION_NAME, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("ObsoleteSdkInt")
|
@SuppressLint("ObsoleteSdkInt")
|
||||||
@PermissionCallback
|
@PermissionCallback
|
||||||
fun cameraPermissionCallback(invoke: Invoke) {
|
fun cameraPermissionCallback(invoke: Invoke) {
|
||||||
@@ -380,9 +368,7 @@ class BarcodeScannerPlugin(private val activity: Activity) : Plugin(activity),
|
|||||||
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.GRANTED)
|
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.GRANTED)
|
||||||
} else {
|
} else {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
if (!activity.shouldShowRequestPermissionRationale(PERMISSION_NAME)) {
|
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.DENIED)
|
||||||
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.DENIED)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.GRANTED)
|
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.GRANTED)
|
||||||
}
|
}
|
||||||
@@ -401,20 +387,12 @@ class BarcodeScannerPlugin(private val activity: Activity) : Plugin(activity),
|
|||||||
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.GRANTED)
|
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.GRANTED)
|
||||||
} else {
|
} else {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
if (firstPermissionRequest() || activity.shouldShowRequestPermissionRationale(
|
requestPermissionForAlias(
|
||||||
PERMISSION_NAME
|
PERMISSION_ALIAS_CAMERA,
|
||||||
)
|
invoke,
|
||||||
) {
|
"cameraPermissionCallback"
|
||||||
markFirstPermissionRequest()
|
)
|
||||||
requestPermissionForAlias(
|
return
|
||||||
PERMISSION_ALIAS_CAMERA,
|
|
||||||
invoke,
|
|
||||||
"cameraPermissionCallback"
|
|
||||||
)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.DENIED)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.GRANTED)
|
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.GRANTED)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
if("__TAURI__"in window){var __TAURI_PLUGIN_BARCODE_SCANNER__=function(n){"use strict";async function a(n,a={},e){return window.__TAURI_INTERNALS__.invoke(n,a,e)}var e;return"function"==typeof SuppressedError&&SuppressedError,n.Format=void 0,(e=n.Format||(n.Format={})).QRCode="QR_CODE",e.UPC_A="UPC_A",e.UPC_E="UPC_E",e.EAN8="EAN_8",e.EAN13="EAN_13",e.Code39="CODE_39",e.Code93="CODE_93",e.Code128="CODE_128",e.Codabar="CODABAR",e.ITF="ITF",e.Aztec="AZTEC",e.DataMatrix="DATA_MATRIX",e.PDF417="PDF_417",e.GS1DataBar="GS1_DATA_BAR",e.GS1DataBarLimited="GS1_DATA_BAR_LIMITED",e.GS1DataBarExpanded="GS1_DATA_BAR_EXPANDED",n.cancel=async function(){await a("plugin:barcode-scanner|cancel")},n.checkPermissions=async function(){return await async function(n){return a(`plugin:${n}|check_permissions`)}("barcode-scanner").then((n=>n.camera))},n.openAppSettings=async function(){await a("plugin:barcode-scanner|open_app_settings")},n.requestPermissions=async function(){return await async function(n){return a(`plugin:${n}|request_permissions`)}("barcode-scanner").then((n=>n.camera))},n.scan=async function(n){return await a("plugin:barcode-scanner|scan",{...n})},n}({});Object.defineProperty(window.__TAURI__,"barcodeScanner",{value:__TAURI_PLUGIN_BARCODE_SCANNER__})}
|
if("__TAURI__"in window){var __TAURI_PLUGIN_BARCODE_SCANNER__=function(n){"use strict";async function a(n,a={},e){return window.__TAURI_INTERNALS__.invoke(n,a,e)}var e;return"function"==typeof SuppressedError&&SuppressedError,n.Format=void 0,(e=n.Format||(n.Format={})).QRCode="QR_CODE",e.UPC_A="UPC_A",e.UPC_E="UPC_E",e.EAN8="EAN_8",e.EAN13="EAN_13",e.Code39="CODE_39",e.Code93="CODE_93",e.Code128="CODE_128",e.Codabar="CODABAR",e.ITF="ITF",e.Aztec="AZTEC",e.DataMatrix="DATA_MATRIX",e.PDF417="PDF_417",e.GS1DataBar="GS1_DATA_BAR",e.GS1DataBarLimited="GS1_DATA_BAR_LIMITED",e.GS1DataBarExpanded="GS1_DATA_BAR_EXPANDED",n.cancel=async function(){await a("plugin:barcode-scanner|cancel")},n.checkPermissions=async function(){return await async function(n){return a(`plugin:${n}|check_permissions`)}("barcode-scanner").then(n=>n.camera)},n.openAppSettings=async function(){await a("plugin:barcode-scanner|open_app_settings")},n.requestPermissions=async function(){return await async function(n){return a(`plugin:${n}|request_permissions`)}("barcode-scanner").then(n=>n.camera)},n.scan=async function(n){return await a("plugin:barcode-scanner|scan",{...n})},n}({});Object.defineProperty(window.__TAURI__,"barcodeScanner",{value:__TAURI_PLUGIN_BARCODE_SCANNER__})}
|
||||||
|
|||||||
@@ -261,27 +261,34 @@ class BarcodeScannerPlugin: Plugin, AVCaptureMetadataOutputObjectsDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func runScanner(_ invoke: Invoke, args: ScanOptions) {
|
private func runScanner(_ invoke: Invoke, args: ScanOptions) {
|
||||||
|
if getPermissionState() != "granted" {
|
||||||
|
invoke.reject("Camera permission denied or not yet requested")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
scanFormats = [AVMetadataObject.ObjectType]()
|
scanFormats = [AVMetadataObject.ObjectType]()
|
||||||
|
|
||||||
(args.formats ?? []).forEach { format in
|
(args.formats ?? []).forEach { format in
|
||||||
if let formatValue = format.value {
|
if let formatValue = format.value {
|
||||||
scanFormats.append(formatValue)
|
scanFormats.append(formatValue)
|
||||||
} else {
|
} else {
|
||||||
invoke.reject("Unsupported barcode format on this iOS version: \(format)")
|
invoke.reject("Unsupported barcode format on this iOS version: \(format)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if scanFormats.isEmpty {
|
if scanFormats.isEmpty {
|
||||||
for supportedFormat in SupportedFormat.allCases {
|
for supportedFormat in SupportedFormat.allCases {
|
||||||
if let formatValue = supportedFormat.value {
|
if let formatValue = supportedFormat.value {
|
||||||
scanFormats.append(formatValue)
|
scanFormats.append(formatValue)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.metaOutput!.metadataObjectTypes = self.scanFormats
|
self.metaOutput!.metadataObjectTypes = self.scanFormats
|
||||||
self.captureSession!.startRunning()
|
DispatchQueue.main.async {
|
||||||
|
self.captureSession!.startRunning()
|
||||||
|
}
|
||||||
|
|
||||||
self.isScanning = true
|
self.isScanning = true
|
||||||
}
|
}
|
||||||
@@ -298,6 +305,13 @@ class BarcodeScannerPlugin: Plugin, AVCaptureMetadataOutputObjectsDelegate {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if camera is available on this platform (iOS simulator doesn't have cameras)
|
||||||
|
let availableVideoDevices = discoverCaptureDevices()
|
||||||
|
if availableVideoDevices.isEmpty {
|
||||||
|
invoke.reject("No camera available on this device (e.g., iOS Simulator)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var iOS14min: Bool = false
|
var iOS14min: Bool = false
|
||||||
if #available(iOS 14.0, *) { iOS14min = true }
|
if #available(iOS 14.0, *) { iOS14min = true }
|
||||||
if !iOS14min && self.getPermissionState() != "granted" {
|
if !iOS14min && self.getPermissionState() != "granted" {
|
||||||
@@ -323,10 +337,11 @@ class BarcodeScannerPlugin: Plugin, AVCaptureMetadataOutputObjectsDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc private func cancel(_ invoke: Invoke) {
|
@objc private func cancel(_ invoke: Invoke) {
|
||||||
self.invoke?.reject("cancelled")
|
DispatchQueue.main.async { [self] in
|
||||||
|
self.invoke?.reject("cancelled")
|
||||||
destroy()
|
self.destroy()
|
||||||
invoke.resolve()
|
invoke.resolve()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@tauri-apps/plugin-barcode-scanner",
|
"name": "@tauri-apps/plugin-barcode-scanner",
|
||||||
"version": "2.4.2",
|
"version": "2.4.5",
|
||||||
"description": "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS",
|
"description": "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"authors": [
|
"authors": [
|
||||||
@@ -25,6 +25,6 @@
|
|||||||
"LICENSE"
|
"LICENSE"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^2.8.0"
|
"@tauri-apps/api": "^2.11.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "guest-js"
|
||||||
|
},
|
||||||
"include": ["guest-js/*.ts"]
|
"include": ["guest-js/*.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,6 @@
|
|||||||
"LICENSE"
|
"LICENSE"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^2.8.0"
|
"@tauri-apps/api": "^2.11.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "guest-js"
|
||||||
|
},
|
||||||
"include": ["guest-js/*.ts"]
|
"include": ["guest-js/*.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,6 @@
|
|||||||
"LICENSE"
|
"LICENSE"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^2.8.0"
|
"@tauri-apps/api": "^2.11.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "guest-js"
|
||||||
|
},
|
||||||
"include": ["guest-js/*.ts"]
|
"include": ["guest-js/*.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
if("__TAURI__"in window){var __TAURI_PLUGIN_CLIPBOARD_MANAGER__=function(e){"use strict";var n;async function t(e,n={},t){return window.__TAURI_INTERNALS__.invoke(e,n,t)}"function"==typeof SuppressedError&&SuppressedError;class r{get rid(){return function(e,n,t,r){if("function"==typeof n?e!==n||!r:!n.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===t?r:"a"===t?r.call(e):r?r.value:n.get(e)}(this,n,"f")}constructor(e){n.set(this,void 0),function(e,n,t){if("function"==typeof n||!n.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");n.set(e,t)}(this,n,e)}async close(){return t("plugin:resources|close",{rid:this.rid})}}n=new WeakMap;class a extends r{constructor(e){super(e)}static async new(e,n,r){return t("plugin:image|new",{rgba:i(e),width:n,height:r}).then((e=>new a(e)))}static async fromBytes(e){return t("plugin:image|from_bytes",{bytes:i(e)}).then((e=>new a(e)))}static async fromPath(e){return t("plugin:image|from_path",{path:e}).then((e=>new a(e)))}async rgba(){return t("plugin:image|rgba",{rid:this.rid}).then((e=>new Uint8Array(e)))}async size(){return t("plugin:image|size",{rid:this.rid})}}function i(e){return null==e?null:"string"==typeof e?e:e instanceof a?e.rid:e}return e.clear=async function(){await t("plugin:clipboard-manager|clear")},e.readImage=async function(){return await t("plugin:clipboard-manager|read_image").then((e=>new a(e)))},e.readText=async function(){return await t("plugin:clipboard-manager|read_text")},e.writeHtml=async function(e,n){await t("plugin:clipboard-manager|write_html",{html:e,altText:n})},e.writeImage=async function(e){await t("plugin:clipboard-manager|write_image",{image:i(e)})},e.writeText=async function(e,n){await t("plugin:clipboard-manager|write_text",{label:n?.label,text:e})},e}({});Object.defineProperty(window.__TAURI__,"clipboardManager",{value:__TAURI_PLUGIN_CLIPBOARD_MANAGER__})}
|
if("__TAURI__"in window){var __TAURI_PLUGIN_CLIPBOARD_MANAGER__=function(e){"use strict";var n;async function t(e,n={},t){return window.__TAURI_INTERNALS__.invoke(e,n,t)}"function"==typeof SuppressedError&&SuppressedError;class r{get rid(){return function(e,n,t,r){if("function"==typeof n?e!==n||!r:!n.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===t?r:"a"===t?r.call(e):r?r.value:n.get(e)}(this,n,"f")}constructor(e){n.set(this,void 0),function(e,n,t){if("function"==typeof n||!n.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");n.set(e,t)}(this,n,e)}async close(){return t("plugin:resources|close",{rid:this.rid})}}n=new WeakMap;class a extends r{constructor(e){super(e)}static async new(e,n,r){return t("plugin:image|new",{rgba:i(e),width:n,height:r}).then(e=>new a(e))}static async fromBytes(e){return t("plugin:image|from_bytes",{bytes:i(e)}).then(e=>new a(e))}static async fromPath(e){return t("plugin:image|from_path",{path:e}).then(e=>new a(e))}async rgba(){return t("plugin:image|rgba",{rid:this.rid}).then(e=>new Uint8Array(e))}async size(){return t("plugin:image|size",{rid:this.rid})}}function i(e){return null==e?null:"string"==typeof e?e:e instanceof a?e.rid:e}return e.clear=async function(){await t("plugin:clipboard-manager|clear")},e.readImage=async function(){return await t("plugin:clipboard-manager|read_image").then(e=>new a(e))},e.readText=async function(){return await t("plugin:clipboard-manager|read_text")},e.writeHtml=async function(e,n){await t("plugin:clipboard-manager|write_html",{html:e,altText:n})},e.writeImage=async function(e){await t("plugin:clipboard-manager|write_image",{image:i(e)})},e.writeText=async function(e,n){await t("plugin:clipboard-manager|write_text",{label:n?.label,text:e})},e}({});Object.defineProperty(window.__TAURI__,"clipboardManager",{value:__TAURI_PLUGIN_CLIPBOARD_MANAGER__})}
|
||||||
|
|||||||
@@ -24,6 +24,6 @@
|
|||||||
"LICENSE"
|
"LICENSE"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^2.8.0"
|
"@tauri-apps/api": "^2.11.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "guest-js"
|
||||||
|
},
|
||||||
"include": ["guest-js/*.ts"]
|
"include": ["guest-js/*.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,22 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## \[2.4.9]
|
||||||
|
|
||||||
|
- [`e6cdc9f5`](https://github.com/tauri-apps/plugins-workspace/commit/e6cdc9f52e2cd975b11b8e4c12879d597f1f76c3) ([#3396](https://github.com/tauri-apps/plugins-workspace/pull/3396) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) Fix broken iOS custom URL schemes
|
||||||
|
|
||||||
|
## \[2.4.8]
|
||||||
|
|
||||||
|
- [`024ec0c2`](https://github.com/tauri-apps/plugins-workspace/commit/024ec0c29c20cf94579dab9b79d6be0da61a8daa) ([#3214](https://github.com/tauri-apps/plugins-workspace/pull/3214) by [@joshIsCoding](https://github.com/tauri-apps/plugins-workspace/../../joshIsCoding)) Account for differing Android VIEW intent in ChromeOS, fixing deep-link behaviour on Chromium platforms.
|
||||||
|
- [`015e817c`](https://github.com/tauri-apps/plugins-workspace/commit/015e817cf2d7f66c1b9268606af8318dfe0bc4ee) ([#3186](https://github.com/tauri-apps/plugins-workspace/pull/3186) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Validate Android new intent is actually a deep link before triggering the onOpenUrl event.
|
||||||
|
|
||||||
|
## \[2.4.7]
|
||||||
|
|
||||||
|
- [`8374e997`](https://github.com/tauri-apps/plugins-workspace/commit/8374e997b82c95516fc0c1f6d665d9fc3b52edf8) ([#3258](https://github.com/tauri-apps/plugins-workspace/pull/3258) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Fix runtime deep link registration failing on Linux when the app path has spaces.
|
||||||
|
|
||||||
|
## \[2.4.6]
|
||||||
|
|
||||||
|
- [`28048039`](https://github.com/tauri-apps/plugins-workspace/commit/28048039496e84b46847c008416d341f1349e30e) ([#3143](https://github.com/tauri-apps/plugins-workspace/pull/3143) by [@Tunglies](https://github.com/tauri-apps/plugins-workspace/../../Tunglies)) Fix clippy warnings. No user facing changes.
|
||||||
|
|
||||||
## \[2.4.5]
|
## \[2.4.5]
|
||||||
|
|
||||||
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
|
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tauri-plugin-deep-link"
|
name = "tauri-plugin-deep-link"
|
||||||
version = "2.4.5"
|
version = "2.4.9"
|
||||||
description = "Set your Tauri application as the default handler for an URL"
|
description = "Set your Tauri application as the default handler for an URL"
|
||||||
authors = { workspace = true }
|
authors = { workspace = true }
|
||||||
license = { workspace = true }
|
license = { workspace = true }
|
||||||
|
|||||||
@@ -6,9 +6,8 @@ package app.tauri.deep_link
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.PatternMatcher
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import app.tauri.Logger
|
|
||||||
import app.tauri.annotation.InvokeArg
|
import app.tauri.annotation.InvokeArg
|
||||||
import app.tauri.annotation.Command
|
import app.tauri.annotation.Command
|
||||||
import app.tauri.annotation.TauriPlugin
|
import app.tauri.annotation.TauriPlugin
|
||||||
@@ -16,23 +15,43 @@ import app.tauri.plugin.Channel
|
|||||||
import app.tauri.plugin.JSObject
|
import app.tauri.plugin.JSObject
|
||||||
import app.tauri.plugin.Plugin
|
import app.tauri.plugin.Plugin
|
||||||
import app.tauri.plugin.Invoke
|
import app.tauri.plugin.Invoke
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
|
||||||
@InvokeArg
|
@InvokeArg
|
||||||
class SetEventHandlerArgs {
|
class SetEventHandlerArgs {
|
||||||
lateinit var handler: Channel
|
lateinit var handler: Channel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@InvokeArg
|
||||||
|
class AssociatedDomain {
|
||||||
|
var scheme: List<String> = listOf("https", "http")
|
||||||
|
var host: String? = null
|
||||||
|
var path: List<String> = listOf()
|
||||||
|
var pathPattern: List<String> = listOf()
|
||||||
|
var pathPrefix: List<String> = listOf()
|
||||||
|
var pathSuffix: List<String> = listOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
@InvokeArg
|
||||||
|
class PluginConfig {
|
||||||
|
var mobile: List<AssociatedDomain> = listOf()
|
||||||
|
}
|
||||||
|
|
||||||
@TauriPlugin
|
@TauriPlugin
|
||||||
class DeepLinkPlugin(private val activity: Activity): Plugin(activity) {
|
class DeepLinkPlugin(private val activity: Activity): Plugin(activity) {
|
||||||
//private val implementation = Example()
|
//private val implementation = Example()
|
||||||
private var webView: WebView? = null
|
private var webView: WebView? = null
|
||||||
private var currentUrl: String? = null
|
private var currentUrl: String? = null
|
||||||
private var channel: Channel? = null
|
private var channel: Channel? = null
|
||||||
|
private var config: PluginConfig? = null
|
||||||
companion object {
|
companion object {
|
||||||
var instance: DeepLinkPlugin? = null
|
var instance: DeepLinkPlugin? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isViewIntent(action: String?): Boolean {
|
||||||
|
return action == Intent.ACTION_VIEW || action == "org.chromium.arc.intent.action.VIEW"
|
||||||
|
}
|
||||||
|
|
||||||
@Command
|
@Command
|
||||||
fun getCurrent(invoke: Invoke) {
|
fun getCurrent(invoke: Invoke) {
|
||||||
val ret = JSObject()
|
val ret = JSObject()
|
||||||
@@ -51,27 +70,105 @@ class DeepLinkPlugin(private val activity: Activity): Plugin(activity) {
|
|||||||
|
|
||||||
override fun load(webView: WebView) {
|
override fun load(webView: WebView) {
|
||||||
instance = this
|
instance = this
|
||||||
|
config = getConfig(PluginConfig::class.java)
|
||||||
val intent = activity.intent
|
|
||||||
|
|
||||||
if (intent.action == Intent.ACTION_VIEW) {
|
|
||||||
// TODO: check if it makes sense to split up init url and last url
|
|
||||||
this.currentUrl = intent.data.toString()
|
|
||||||
val event = JSObject()
|
|
||||||
event.put("url", this.currentUrl)
|
|
||||||
this.channel?.send(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
super.load(webView)
|
super.load(webView)
|
||||||
this.webView = webView
|
this.webView = webView
|
||||||
|
|
||||||
|
val intent = activity.intent
|
||||||
|
|
||||||
|
if (isViewIntent(intent.action) && intent.data != null) {
|
||||||
|
val url = intent.data.toString()
|
||||||
|
if (isDeepLink(url)) {
|
||||||
|
// TODO: check if it makes sense to split up init url and last url
|
||||||
|
this.currentUrl = url
|
||||||
|
val event = JSObject()
|
||||||
|
event.put("url", this.currentUrl)
|
||||||
|
this.channel?.send(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent) {
|
override fun onNewIntent(intent: Intent) {
|
||||||
if (intent.action == Intent.ACTION_VIEW) {
|
if (isViewIntent(intent.action) && intent.data != null) {
|
||||||
this.currentUrl = intent.data.toString()
|
val url = intent.data.toString()
|
||||||
val event = JSObject()
|
if (isDeepLink(url)) {
|
||||||
event.put("url", this.currentUrl)
|
this.currentUrl = url
|
||||||
this.channel?.send(event)
|
val event = JSObject()
|
||||||
|
event.put("url", this.currentUrl)
|
||||||
|
this.channel?.send(event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isDeepLink(url: String): Boolean {
|
||||||
|
val config = this.config ?: return false
|
||||||
|
|
||||||
|
if (config.mobile.isEmpty()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val uri = try {
|
||||||
|
url.toUri()
|
||||||
|
} catch (_: Exception) {
|
||||||
|
// not a URL
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val scheme = uri.scheme ?: return false
|
||||||
|
val host = uri.host
|
||||||
|
val path = uri.path ?: ""
|
||||||
|
|
||||||
|
// Check if URL matches any configured mobile deep link
|
||||||
|
for (domain in config.mobile) {
|
||||||
|
// Check scheme
|
||||||
|
if (!domain.scheme.any { it.equals(scheme, ignoreCase = true) }) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check host (if configured)
|
||||||
|
if (domain.host != null) {
|
||||||
|
if (!host.equals(domain.host, ignoreCase = true)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check path constraints
|
||||||
|
// According to Android docs:
|
||||||
|
// - path: exact match, must begin with /
|
||||||
|
// - pathPrefix: matches initial part of path
|
||||||
|
// - pathSuffix: matches ending part, doesn't need to begin with /
|
||||||
|
// - pathPattern: simple glob pattern (., *, .*)
|
||||||
|
val pathMatches = when {
|
||||||
|
// Exact path match (must begin with /)
|
||||||
|
domain.path.isNotEmpty() && domain.path.any { it == path } -> true
|
||||||
|
// Path pattern match (simple glob: ., *, .*)
|
||||||
|
domain.pathPattern.isNotEmpty() && domain.pathPattern.any { pattern ->
|
||||||
|
try {
|
||||||
|
PatternMatcher(pattern, PatternMatcher.PATTERN_SIMPLE_GLOB).match(path)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} -> true
|
||||||
|
// Path prefix match
|
||||||
|
domain.pathPrefix.isNotEmpty() && domain.pathPrefix.any { prefix ->
|
||||||
|
path.startsWith(prefix)
|
||||||
|
} -> true
|
||||||
|
// Path suffix match
|
||||||
|
domain.pathSuffix.isNotEmpty() && domain.pathSuffix.any { suffix ->
|
||||||
|
path.endsWith(suffix)
|
||||||
|
} -> true
|
||||||
|
// If no path constraints, any path is allowed
|
||||||
|
domain.path.isEmpty() && domain.pathPattern.isEmpty() &&
|
||||||
|
domain.pathPrefix.isEmpty() && domain.pathSuffix.isEmpty() -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pathMatches) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
if("__TAURI__"in window){var __TAURI_PLUGIN_DEEP_LINK__=function(e){"use strict";function n(e,n=!1){return window.__TAURI_INTERNALS__.transformCallback(e,n)}async function r(e,n={},r){return window.__TAURI_INTERNALS__.invoke(e,n,r)}var t;async function i(e,t,i){const _={kind:"Any"};return r("plugin:event|listen",{event:e,target:_,handler:n(t)}).then((n=>async()=>async function(e,n){window.__TAURI_EVENT_PLUGIN_INTERNALS__.unregisterListener(e,n),await r("plugin:event|unlisten",{event:e,eventId:n})}(e,n)))}return"function"==typeof SuppressedError&&SuppressedError,function(e){e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",e.WINDOW_DESTROYED="tauri://destroyed",e.WINDOW_FOCUS="tauri://focus",e.WINDOW_BLUR="tauri://blur",e.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",e.WINDOW_THEME_CHANGED="tauri://theme-changed",e.WINDOW_CREATED="tauri://window-created",e.WEBVIEW_CREATED="tauri://webview-created",e.DRAG_ENTER="tauri://drag-enter",e.DRAG_OVER="tauri://drag-over",e.DRAG_DROP="tauri://drag-drop",e.DRAG_LEAVE="tauri://drag-leave"}(t||(t={})),e.getCurrent=async function(){return await r("plugin:deep-link|get_current")},e.isRegistered=async function(e){return await r("plugin:deep-link|is_registered",{protocol:e})},e.onOpenUrl=async function(e){return await i("deep-link://new-url",(n=>{e(n.payload)}))},e.register=async function(e){return await r("plugin:deep-link|register",{protocol:e})},e.unregister=async function(e){return await r("plugin:deep-link|unregister",{protocol:e})},e}({});Object.defineProperty(window.__TAURI__,"deepLink",{value:__TAURI_PLUGIN_DEEP_LINK__})}
|
if("__TAURI__"in window){var __TAURI_PLUGIN_DEEP_LINK__=function(e){"use strict";function n(e,n=!1){return window.__TAURI_INTERNALS__.transformCallback(e,n)}async function r(e,n={},r){return window.__TAURI_INTERNALS__.invoke(e,n,r)}var t;async function i(e,t,i){const _={kind:"Any"};return r("plugin:event|listen",{event:e,target:_,handler:n(t)}).then(n=>async()=>async function(e,n){window.__TAURI_EVENT_PLUGIN_INTERNALS__.unregisterListener(e,n),await r("plugin:event|unlisten",{event:e,eventId:n})}(e,n))}return"function"==typeof SuppressedError&&SuppressedError,function(e){e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",e.WINDOW_DESTROYED="tauri://destroyed",e.WINDOW_FOCUS="tauri://focus",e.WINDOW_BLUR="tauri://blur",e.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",e.WINDOW_THEME_CHANGED="tauri://theme-changed",e.WINDOW_CREATED="tauri://window-created",e.WINDOW_SUSPENDED="tauri://suspended",e.WINDOW_RESUMED="tauri://resumed",e.WEBVIEW_CREATED="tauri://webview-created",e.DRAG_ENTER="tauri://drag-enter",e.DRAG_OVER="tauri://drag-over",e.DRAG_DROP="tauri://drag-drop",e.DRAG_LEAVE="tauri://drag-leave"}(t||(t={})),e.getCurrent=async function(){return await r("plugin:deep-link|get_current")},e.isRegistered=async function(e){return await r("plugin:deep-link|is_registered",{protocol:e})},e.onOpenUrl=async function(e){return await i("deep-link://new-url",n=>{e(n.payload)})},e.register=async function(e){return await r("plugin:deep-link|register",{protocol:e})},e.unregister=async function(e){return await r("plugin:deep-link|unregister",{protocol:e})},e}({});Object.defineProperty(window.__TAURI__,"deepLink",{value:__TAURI_PLUGIN_DEEP_LINK__})}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ fn intent_filter(domain: &AssociatedDomain) -> String {
|
|||||||
format!(
|
format!(
|
||||||
r#"<intent-filter {auto_verify}>
|
r#"<intent-filter {auto_verify}>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<!-- ChromeOS ARC++ uses a different action for deep links -->
|
||||||
|
<action android:name="org.chromium.arc.intent.action.VIEW" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
{schemes}
|
{schemes}
|
||||||
@@ -137,13 +139,7 @@ fn main() {
|
|||||||
let deep_link_domains = config
|
let deep_link_domains = config
|
||||||
.mobile
|
.mobile
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|domain| {
|
.filter(|domain| !domain.is_app_link())
|
||||||
if domain.is_app_link() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(domain)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if deep_link_domains.is_empty() {
|
if deep_link_domains.is_empty() {
|
||||||
@@ -177,7 +173,7 @@ fn main() {
|
|||||||
);
|
);
|
||||||
dict.insert(
|
dict.insert(
|
||||||
"CFBundleURLName".into(),
|
"CFBundleURLName".into(),
|
||||||
format!("{}", domain.scheme[0]).into(),
|
domain.scheme[0].clone().into(),
|
||||||
);
|
);
|
||||||
plist::Value::Dictionary(dict)
|
plist::Value::Dictionary(dict)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,29 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## \[2.2.12]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `deep-link-js@2.4.9`
|
||||||
|
|
||||||
|
## \[2.2.11]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `deep-link-js@2.4.8`
|
||||||
|
|
||||||
|
## \[2.2.10]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `deep-link-js@2.4.7`
|
||||||
|
|
||||||
|
## \[2.2.9]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `deep-link-js@2.4.6`
|
||||||
|
|
||||||
## \[2.2.8]
|
## \[2.2.8]
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "deep-link-example",
|
"name": "deep-link-example",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "2.2.8",
|
"version": "2.2.12",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
@@ -10,12 +10,12 @@
|
|||||||
"tauri": "tauri"
|
"tauri": "tauri"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "2.9.0",
|
"@tauri-apps/api": "^2.11.0",
|
||||||
"@tauri-apps/plugin-deep-link": "2.4.5"
|
"@tauri-apps/plugin-deep-link": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "2.9.4",
|
"@tauri-apps/cli": "2.11.4",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^6.0.0",
|
||||||
"vite": "^7.0.7"
|
"vite": "^8.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="Theme.deep_link_example" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
<style name="Theme.deep_link_example" parent="Theme.Material3.DayNight.NoActionBar">
|
||||||
<!-- Customize your theme here. -->
|
<!-- Customize your theme here. -->
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="Theme.deep_link_example" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
<style name="Theme.deep_link_example" parent="Theme.Material3.DayNight.NoActionBar">
|
||||||
<!-- Customize your theme here. -->
|
<!-- Customize your theme here. -->
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@tauri-apps/plugin-deep-link",
|
"name": "@tauri-apps/plugin-deep-link",
|
||||||
"version": "2.4.5",
|
"version": "2.4.9",
|
||||||
"description": "Set your Tauri application as the default handler for an URL",
|
"description": "Set your Tauri application as the default handler for an URL",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"authors": [
|
"authors": [
|
||||||
@@ -25,9 +25,9 @@
|
|||||||
"LICENSE"
|
"LICENSE"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^2.8.0"
|
"@tauri-apps/api": "^2.11.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "2.9.4"
|
"@tauri-apps/cli": "2.11.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -293,7 +293,7 @@ mod imp {
|
|||||||
.unwrap_or_else(|| bin.into_os_string())
|
.unwrap_or_else(|| bin.into_os_string())
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.to_string();
|
.to_string();
|
||||||
let qualified_exec = format!("{} %u", exec);
|
let qualified_exec = format!("\"{}\" %u", exec);
|
||||||
|
|
||||||
let target = self.app.path().data_dir()?.join("applications");
|
let target = self.app.path().data_dir()?.join("applications");
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "guest-js"
|
||||||
|
},
|
||||||
"include": ["guest-js/*.ts"]
|
"include": ["guest-js/*.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,39 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## \[2.7.2]
|
||||||
|
|
||||||
|
- [`40ae0a7f`](https://github.com/tauri-apps/plugins-workspace/commit/40ae0a7fa0ecc9b0cdbd952297f7a17f37308229) ([#3491](https://github.com/tauri-apps/plugins-workspace/pull/3491) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) Use `com.google.android.material.dialog.MaterialAlertDialogBuilder` instead of `AlertDialog` so the dialog follows the app's theme
|
||||||
|
|
||||||
|
## \[2.7.1]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `fs-js@2.5.1`
|
||||||
|
|
||||||
|
## \[2.7.0]
|
||||||
|
|
||||||
|
- [`24154472`](https://github.com/tauri-apps/plugins-workspace/commit/24154472a6710a690173df0a121125d1f1b871e8) ([#3287](https://github.com/tauri-apps/plugins-workspace/pull/3287) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) Re-use `message` command in Rust side for `ask` and `confirm` commands, `allow-ask` and `allow-confirm` permissions are now aliases to `allow-message`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `fs-js@2.5.0`
|
||||||
|
|
||||||
|
## \[2.6.0]
|
||||||
|
|
||||||
|
- [`d7a0bb32`](https://github.com/tauri-apps/plugins-workspace/commit/d7a0bb325dad919d6cc132eb3898c33540de77c4) ([#3136](https://github.com/tauri-apps/plugins-workspace/pull/3136) by [@onehumandev](https://github.com/tauri-apps/plugins-workspace/../../onehumandev)) Add `fileAccessMode` option to file picker.
|
||||||
|
|
||||||
|
## \[2.5.0]
|
||||||
|
|
||||||
|
- [`dff6fa98`](https://github.com/tauri-apps/plugins-workspace/commit/dff6fa986a9a05ba98b6ca660fea78ae97251fc2) ([#3034](https://github.com/tauri-apps/plugins-workspace/pull/3034) by [@onehumandev](https://github.com/tauri-apps/plugins-workspace/../../onehumandev)) Add `pickerMode` option to file picker (currently only used on iOS)
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Upgraded to `fs-js@2.4.5`
|
||||||
|
|
||||||
|
### feat
|
||||||
|
|
||||||
|
- [`c23fa03f`](https://github.com/tauri-apps/plugins-workspace/commit/c23fa03f07d5c1c220bcf0bca482364513e3f754) ([#3098](https://github.com/tauri-apps/plugins-workspace/pull/3098) by [@Lepidopteran](https://github.com/tauri-apps/plugins-workspace/../../Lepidopteran)) Add `xdg-portal` as an optional feature for `rfd`
|
||||||
|
|
||||||
## \[2.4.2]
|
## \[2.4.2]
|
||||||
|
|
||||||
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
|
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tauri-plugin-dialog"
|
name = "tauri-plugin-dialog"
|
||||||
version = "2.4.2"
|
version = "2.7.2"
|
||||||
description = "Native system dialogs for opening and saving files along with message dialogs on your Tauri application."
|
description = "Native system dialogs for opening and saving files along with message dialogs on your Tauri application."
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
authors = { workspace = true }
|
authors = { workspace = true }
|
||||||
@@ -11,7 +11,7 @@ links = "tauri-plugin-dialog"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["gtk3"]
|
default = ["gtk3"]
|
||||||
xdg-portal = ["rfd/xdg-portal"]
|
xdg-portal = ["rfd/xdg-portal", "rfd/tokio", "rfd/wayland"]
|
||||||
gtk3 = ["rfd/gtk3"]
|
gtk3 = ["rfd/gtk3"]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
@@ -37,14 +37,13 @@ tauri = { workspace = true }
|
|||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
tauri-plugin-fs = { path = "../fs", version = "2.4.4" }
|
tauri-plugin-fs = { path = "../fs", version = "2.5.1" }
|
||||||
|
|
||||||
[target.'cfg(target_os = "ios")'.dependencies]
|
[target.'cfg(target_os = "ios")'.dependencies]
|
||||||
tauri = { workspace = true, features = ["wry"] }
|
tauri = { workspace = true, features = ["wry"] }
|
||||||
|
|
||||||
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
|
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
|
||||||
rfd = { version = "0.15", default-features = false, features = [
|
rfd = { version = "0.16", default-features = false, features = [
|
||||||
"tokio",
|
|
||||||
"common-controls-v6",
|
"common-controls-v6",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation("androidx.core:core-ktx:1.9.0")
|
implementation("androidx.core:core-ktx:1.9.0")
|
||||||
implementation("androidx.appcompat:appcompat:1.6.0")
|
implementation("androidx.appcompat:appcompat:1.7.1")
|
||||||
implementation("com.google.android.material:material:1.7.0")
|
implementation("com.google.android.material:material:1.13.0")
|
||||||
testImplementation("junit:junit:4.13.2")
|
testImplementation("junit:junit:4.13.2")
|
||||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
package app.tauri.dialog
|
package app.tauri.dialog
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.AlertDialog
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
@@ -21,6 +20,7 @@ import app.tauri.plugin.Invoke
|
|||||||
import app.tauri.plugin.JSArray
|
import app.tauri.plugin.JSArray
|
||||||
import app.tauri.plugin.JSObject
|
import app.tauri.plugin.JSObject
|
||||||
import app.tauri.plugin.Plugin
|
import app.tauri.plugin.Plugin
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
|
||||||
@InvokeArg
|
@InvokeArg
|
||||||
class Filter {
|
class Filter {
|
||||||
@@ -158,7 +158,7 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
|
|||||||
|
|
||||||
Handler(Looper.getMainLooper())
|
Handler(Looper.getMainLooper())
|
||||||
.post {
|
.post {
|
||||||
val builder = AlertDialog.Builder(activity)
|
val builder = MaterialAlertDialogBuilder(activity)
|
||||||
|
|
||||||
if (args.title != null) {
|
if (args.title != null) {
|
||||||
builder.setTitle(args.title)
|
builder.setTitle(args.title)
|
||||||
@@ -191,8 +191,7 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val dialog = builder.create()
|
builder.show()
|
||||||
dialog.show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
if("__TAURI__"in window){var __TAURI_PLUGIN_DIALOG__=function(t){"use strict";async function n(t,n={},e){return window.__TAURI_INTERNALS__.invoke(t,n,e)}function e(t){if(void 0!==t)return"string"==typeof t?t:"ok"in t&&"cancel"in t?{OkCancelCustom:[t.ok,t.cancel]}:"yes"in t&&"no"in t&&"cancel"in t?{YesNoCancelCustom:[t.yes,t.no,t.cancel]}:"ok"in t?{OkCustom:t.ok}:void 0}return"function"==typeof SuppressedError&&SuppressedError,t.ask=async function(t,e){const o="string"==typeof e?{title:e}:e;return await n("plugin:dialog|ask",{message:t.toString(),title:o?.title?.toString(),kind:o?.kind,yesButtonLabel:o?.okLabel?.toString(),noButtonLabel:o?.cancelLabel?.toString()})},t.confirm=async function(t,e){const o="string"==typeof e?{title:e}:e;return await n("plugin:dialog|confirm",{message:t.toString(),title:o?.title?.toString(),kind:o?.kind,okButtonLabel:o?.okLabel?.toString(),cancelButtonLabel:o?.cancelLabel?.toString()})},t.message=async function(t,o){const i="string"==typeof o?{title:o}:o;return n("plugin:dialog|message",{message:t.toString(),title:i?.title?.toString(),kind:i?.kind,okButtonLabel:i?.okLabel?.toString(),buttons:e(i?.buttons)})},t.open=async function(t={}){return"object"==typeof t&&Object.freeze(t),await n("plugin:dialog|open",{options:t})},t.save=async function(t={}){return"object"==typeof t&&Object.freeze(t),await n("plugin:dialog|save",{options:t})},t}({});Object.defineProperty(window.__TAURI__,"dialog",{value:__TAURI_PLUGIN_DIALOG__})}
|
if("__TAURI__"in window){var __TAURI_PLUGIN_DIALOG__=function(n){"use strict";async function e(n,e={},t){return window.__TAURI_INTERNALS__.invoke(n,e,t)}function t(n){if(void 0!==n)return"string"==typeof n?n:"ok"in n&&"cancel"in n?{OkCancelCustom:[n.ok,n.cancel]}:"yes"in n&&"no"in n&&"cancel"in n?{YesNoCancelCustom:[n.yes,n.no,n.cancel]}:"ok"in n?{OkCustom:n.ok}:void 0}async function o(n,o){return await e("plugin:dialog|message",{message:n,title:o?.title,kind:o?.kind,buttons:t(o?.buttons)})}return"function"==typeof SuppressedError&&SuppressedError,n.ask=async function(n,e){const t="string"==typeof e?{title:e}:e,i=t?.okLabel||t?.cancelLabel,a=t?.okLabel??"Yes";return await o(n,{title:t?.title,kind:t?.kind,buttons:i?{ok:a,cancel:t.cancelLabel??"No"}:"YesNo"})===a},n.confirm=async function(n,e){const t="string"==typeof e?{title:e}:e,i=t?.okLabel||t?.cancelLabel,a=t?.okLabel??"Ok";return await o(n,{title:t?.title,kind:t?.kind,buttons:i?{ok:a,cancel:t.cancelLabel??"Cancel"}:"OkCancel"})===a},n.message=async function(n,e){const t="string"==typeof e?{title:e}:e;return t&&!t.buttons&&t.okLabel&&(t.buttons={ok:t.okLabel}),o(n,t)},n.open=async function(n={}){return"object"==typeof n&&Object.freeze(n),await e("plugin:dialog|open",{options:n})},n.save=async function(n={}){return"object"==typeof n&&Object.freeze(n),await e("plugin:dialog|save",{options:n})},n}({});Object.defineProperty(window.__TAURI__,"dialog",{value:__TAURI_PLUGIN_DIALOG__})}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
const COMMANDS: &[&str] = &["open", "save", "message", "ask", "confirm"];
|
const COMMANDS: &[&str] = &["open", "save", "message"];
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let result = tauri_plugin::Builder::new(COMMANDS)
|
let result = tauri_plugin::Builder::new(COMMANDS)
|
||||||
|
|||||||
@@ -76,6 +76,31 @@ interface OpenDialogOptions {
|
|||||||
* On desktop, this option is ignored.
|
* On desktop, this option is ignored.
|
||||||
*/
|
*/
|
||||||
pickerMode?: PickerMode
|
pickerMode?: PickerMode
|
||||||
|
/**
|
||||||
|
* The file access mode of the dialog.
|
||||||
|
* If not provided, `copy` is used, which matches the behavior of the {@linkcode open} method before the introduction of this option.
|
||||||
|
*
|
||||||
|
* **Usage**
|
||||||
|
* If a file is opened with {@linkcode fileAccessMode: 'copy'}, it will be copied to the app's sandbox.
|
||||||
|
* This means the file can be read, edited, deleted, copied, or any other operation without any issues, since the file
|
||||||
|
* now belongs to the app.
|
||||||
|
* This also means that the caller has responsibility of deleting the file if this file is not meant to be retained
|
||||||
|
* in the app sandbox.
|
||||||
|
*
|
||||||
|
* If a file is opened with {@linkcode fileAccessMode: 'scoped'}, the file will remain in its original location
|
||||||
|
* and security-scoped access will be automatically managed by the system.
|
||||||
|
*
|
||||||
|
* **Note**
|
||||||
|
* This is specifically meant for document pickers on iOS or MacOS, in conjunction with [security scoped resources](https://developer.apple.com/documentation/foundation/nsurl/startaccessingsecurityscopedresource()).
|
||||||
|
*
|
||||||
|
* Why only document pickers, and not image or video pickers?
|
||||||
|
* The image and video pickers on iOS behave differently from the document pickers, and return [NSItemProvider](https://developer.apple.com/documentation/foundation/nsitemprovider) objects instead of file URLs.
|
||||||
|
* These are meant to be ephemeral (only available within the callback of the picker), and are not accessible outside of the callback.
|
||||||
|
* So for image and video pickers, the only way to access the file is to copy it to the app's sandbox, and this is the URL that is returned from this API.
|
||||||
|
* This means there is no provision for using `scoped` mode with image or video pickers.
|
||||||
|
* If an image or video picker is used, `copy` is always used.
|
||||||
|
*/
|
||||||
|
fileAccessMode?: FileAccessMode
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -111,6 +136,16 @@ interface SaveDialogOptions {
|
|||||||
*/
|
*/
|
||||||
export type PickerMode = 'document' | 'media' | 'image' | 'video'
|
export type PickerMode = 'document' | 'media' | 'image' | 'video'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The file access mode of the dialog.
|
||||||
|
*
|
||||||
|
* - `copy`: copy/move the picked file to the app sandbox; no scoped access required.
|
||||||
|
* - `scoped`: keep file in place; security-scoped access is automatically managed.
|
||||||
|
*
|
||||||
|
* **Note:** This option is only supported on iOS 14 and above. This parameter is ignored on iOS 13 and below.
|
||||||
|
*/
|
||||||
|
export type FileAccessMode = 'copy' | 'scoped'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default buttons for a message dialog.
|
* Default buttons for a message dialog.
|
||||||
*
|
*
|
||||||
@@ -370,6 +405,18 @@ async function save(options: SaveDialogOptions = {}): Promise<string | null> {
|
|||||||
*/
|
*/
|
||||||
export type MessageDialogResult = 'Yes' | 'No' | 'Ok' | 'Cancel' | (string & {})
|
export type MessageDialogResult = 'Yes' | 'No' | 'Ok' | 'Cancel' | (string & {})
|
||||||
|
|
||||||
|
async function messageCommand(
|
||||||
|
message: string,
|
||||||
|
options?: Omit<MessageDialogOptions, 'okLabel'>
|
||||||
|
) {
|
||||||
|
return await invoke<MessageDialogResult>('plugin:dialog|message', {
|
||||||
|
message,
|
||||||
|
title: options?.title,
|
||||||
|
kind: options?.kind,
|
||||||
|
buttons: buttonsToRust(options?.buttons)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows a message dialog with an `Ok` button.
|
* Shows a message dialog with an `Ok` button.
|
||||||
* @example
|
* @example
|
||||||
@@ -392,18 +439,17 @@ async function message(
|
|||||||
options?: string | MessageDialogOptions
|
options?: string | MessageDialogOptions
|
||||||
): Promise<MessageDialogResult> {
|
): Promise<MessageDialogResult> {
|
||||||
const opts = typeof options === 'string' ? { title: options } : options
|
const opts = typeof options === 'string' ? { title: options } : options
|
||||||
|
if (opts && !opts.buttons && opts.okLabel) {
|
||||||
return invoke<MessageDialogResult>('plugin:dialog|message', {
|
opts.buttons = { ok: opts.okLabel }
|
||||||
message: message.toString(),
|
}
|
||||||
title: opts?.title?.toString(),
|
return messageCommand(message, opts)
|
||||||
kind: opts?.kind,
|
|
||||||
okButtonLabel: opts?.okLabel?.toString(),
|
|
||||||
buttons: buttonsToRust(opts?.buttons)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows a question dialog with `Yes` and `No` buttons.
|
* Shows a question dialog with `Yes` and `No` buttons.
|
||||||
|
*
|
||||||
|
* Convenient wrapper for `await message('msg', { buttons: 'YesNo' }) === 'Yes'`
|
||||||
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```typescript
|
* ```typescript
|
||||||
* import { ask } from '@tauri-apps/plugin-dialog';
|
* import { ask } from '@tauri-apps/plugin-dialog';
|
||||||
@@ -423,17 +469,24 @@ async function ask(
|
|||||||
options?: string | ConfirmDialogOptions
|
options?: string | ConfirmDialogOptions
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const opts = typeof options === 'string' ? { title: options } : options
|
const opts = typeof options === 'string' ? { title: options } : options
|
||||||
return await invoke('plugin:dialog|ask', {
|
const customButtons = opts?.okLabel || opts?.cancelLabel
|
||||||
message: message.toString(),
|
const okLabel = opts?.okLabel ?? 'Yes'
|
||||||
title: opts?.title?.toString(),
|
return (
|
||||||
kind: opts?.kind,
|
(await messageCommand(message, {
|
||||||
yesButtonLabel: opts?.okLabel?.toString(),
|
title: opts?.title,
|
||||||
noButtonLabel: opts?.cancelLabel?.toString()
|
kind: opts?.kind,
|
||||||
})
|
buttons: customButtons
|
||||||
|
? { ok: okLabel, cancel: opts.cancelLabel ?? 'No' }
|
||||||
|
: 'YesNo'
|
||||||
|
})) === okLabel
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows a question dialog with `Ok` and `Cancel` buttons.
|
* Shows a question dialog with `Ok` and `Cancel` buttons.
|
||||||
|
*
|
||||||
|
* Convenient wrapper for `await message('msg', { buttons: 'OkCancel' }) === 'Ok'`
|
||||||
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```typescript
|
* ```typescript
|
||||||
* import { confirm } from '@tauri-apps/plugin-dialog';
|
* import { confirm } from '@tauri-apps/plugin-dialog';
|
||||||
@@ -453,13 +506,17 @@ async function confirm(
|
|||||||
options?: string | ConfirmDialogOptions
|
options?: string | ConfirmDialogOptions
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const opts = typeof options === 'string' ? { title: options } : options
|
const opts = typeof options === 'string' ? { title: options } : options
|
||||||
return await invoke('plugin:dialog|confirm', {
|
const customButtons = opts?.okLabel || opts?.cancelLabel
|
||||||
message: message.toString(),
|
const okLabel = opts?.okLabel ?? 'Ok'
|
||||||
title: opts?.title?.toString(),
|
return (
|
||||||
kind: opts?.kind,
|
(await messageCommand(message, {
|
||||||
okButtonLabel: opts?.okLabel?.toString(),
|
title: opts?.title,
|
||||||
cancelButtonLabel: opts?.cancelLabel?.toString()
|
kind: opts?.kind,
|
||||||
})
|
buttons: customButtons
|
||||||
|
? { ok: okLabel, cancel: opts.cancelLabel ?? 'Cancel' }
|
||||||
|
: 'OkCancel'
|
||||||
|
})) === okLabel
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ struct FilePickerOptions: Decodable {
|
|||||||
var filters: [Filter]?
|
var filters: [Filter]?
|
||||||
var defaultPath: String?
|
var defaultPath: String?
|
||||||
var pickerMode: PickerMode?
|
var pickerMode: PickerMode?
|
||||||
|
var fileAccessMode: FileAccessMode?
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SaveFileDialogOptions: Decodable {
|
struct SaveFileDialogOptions: Decodable {
|
||||||
@@ -41,6 +42,11 @@ struct SaveFileDialogOptions: Decodable {
|
|||||||
var defaultPath: String?
|
var defaultPath: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum FileAccessMode: String, Decodable {
|
||||||
|
case copy
|
||||||
|
case scoped
|
||||||
|
}
|
||||||
|
|
||||||
enum PickerMode: String, Decodable {
|
enum PickerMode: String, Decodable {
|
||||||
case document
|
case document
|
||||||
case media
|
case media
|
||||||
@@ -56,6 +62,7 @@ class DialogPlugin: Plugin {
|
|||||||
override init() {
|
override init() {
|
||||||
super.init()
|
super.init()
|
||||||
filePickerController = FilePickerController(self)
|
filePickerController = FilePickerController(self)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public func showFilePicker(_ invoke: Invoke) throws {
|
@objc public func showFilePicker(_ invoke: Invoke) throws {
|
||||||
@@ -70,12 +77,13 @@ class DialogPlugin: Plugin {
|
|||||||
case .error(let error):
|
case .error(let error):
|
||||||
invoke.reject(error)
|
invoke.reject(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if #available(iOS 14, *) {
|
if #available(iOS 14, *) {
|
||||||
let parsedTypes = parseFiltersOption(args.filters ?? [])
|
let parsedTypes = parseFiltersOption(args.filters ?? [])
|
||||||
|
|
||||||
let mimeKinds = Set(parsedTypes.compactMap { $0.preferredMIMEType?.components(separatedBy: "/")[0] })
|
let mimeKinds = Set(
|
||||||
|
parsedTypes.compactMap { $0.preferredMIMEType?.components(separatedBy: "/")[0] })
|
||||||
let filtersIncludeImage = mimeKinds.contains("image")
|
let filtersIncludeImage = mimeKinds.contains("image")
|
||||||
let filtersIncludeVideo = mimeKinds.contains("video")
|
let filtersIncludeVideo = mimeKinds.contains("video")
|
||||||
let filtersIncludeNonMedia = mimeKinds.contains(where: { $0 != "image" && $0 != "video" })
|
let filtersIncludeNonMedia = mimeKinds.contains(where: { $0 != "image" && $0 != "video" })
|
||||||
@@ -85,7 +93,8 @@ class DialogPlugin: Plugin {
|
|||||||
if args.pickerMode == .media
|
if args.pickerMode == .media
|
||||||
|| args.pickerMode == .image
|
|| args.pickerMode == .image
|
||||||
|| args.pickerMode == .video
|
|| args.pickerMode == .video
|
||||||
|| (!filtersIncludeNonMedia && (filtersIncludeImage || filtersIncludeVideo)) {
|
|| (!filtersIncludeNonMedia && (filtersIncludeImage || filtersIncludeVideo))
|
||||||
|
{
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
|
var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
|
||||||
configuration.selectionLimit = (args.multiple ?? false) ? 0 : 1
|
configuration.selectionLimit = (args.multiple ?? false) ? 0 : 1
|
||||||
@@ -107,8 +116,10 @@ class DialogPlugin: Plugin {
|
|||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
// The UTType.item is the catch-all, allowing for any file type to be selected.
|
// The UTType.item is the catch-all, allowing for any file type to be selected.
|
||||||
let contentTypes = parsedTypes.isEmpty ? [UTType.item] : parsedTypes
|
let contentTypes = parsedTypes.isEmpty ? [UTType.item] : parsedTypes
|
||||||
let picker: UIDocumentPickerViewController = UIDocumentPickerViewController(forOpeningContentTypes: contentTypes, asCopy: true)
|
let picker: UIDocumentPickerViewController = UIDocumentPickerViewController(
|
||||||
|
forOpeningContentTypes: contentTypes,
|
||||||
|
asCopy: args.fileAccessMode == .scoped ? false : true)
|
||||||
|
|
||||||
if let defaultPath = args.defaultPath {
|
if let defaultPath = args.defaultPath {
|
||||||
picker.directoryURL = URL(string: defaultPath)
|
picker.directoryURL = URL(string: defaultPath)
|
||||||
}
|
}
|
||||||
@@ -181,7 +192,7 @@ class DialogPlugin: Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsedTypes
|
return parsedTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,14 +214,14 @@ class DialogPlugin: Plugin {
|
|||||||
if !filtersIncludeNonMedia && (filtersIncludeImage || filtersIncludeVideo) {
|
if !filtersIncludeNonMedia && (filtersIncludeImage || filtersIncludeVideo) {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
let picker = UIImagePickerController()
|
let picker = UIImagePickerController()
|
||||||
picker.delegate = self.filePickerController
|
picker.delegate = self.filePickerController
|
||||||
|
|
||||||
if filtersIncludeImage && !filtersIncludeVideo {
|
if filtersIncludeImage && !filtersIncludeVideo {
|
||||||
picker.sourceType = .photoLibrary
|
picker.sourceType = .photoLibrary
|
||||||
}
|
}
|
||||||
|
|
||||||
picker.modalPresentationStyle = .fullScreen
|
picker.modalPresentationStyle = .fullScreen
|
||||||
self.presentViewController(picker)
|
self.presentViewController(picker)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let documentTypes = parsedTypes.isEmpty ? ["public.data"] : parsedTypes
|
let documentTypes = parsedTypes.isEmpty ? ["public.data"] : parsedTypes
|
||||||
@@ -234,7 +245,8 @@ class DialogPlugin: Plugin {
|
|||||||
for filter in filters {
|
for filter in filters {
|
||||||
for ext in filter.extensions ?? [] {
|
for ext in filter.extensions ?? [] {
|
||||||
guard
|
guard
|
||||||
let utType: String = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, ext as CFString, nil)?.takeRetainedValue() as String?
|
let utType: String = UTTypeCreatePreferredIdentifierForTag(
|
||||||
|
kUTTagClassMIMEType, ext as CFString, nil)?.takeRetainedValue() as String?
|
||||||
else {
|
else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -292,6 +304,7 @@ class DialogPlugin: Plugin {
|
|||||||
manager.viewController?.present(alert, animated: true, completion: nil)
|
manager.viewController?.present(alert, animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@_cdecl("init_plugin_dialog")
|
@_cdecl("init_plugin_dialog")
|
||||||
|
|||||||
@@ -95,16 +95,33 @@ public class FilePickerController: NSObject {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ## In which cases do we need to save a copy of a file selected by a user to the app sandbox?
|
||||||
|
/// In short, only when the file is **not** selected using UIDocumentPickerDelegate.
|
||||||
|
/// For the rest of the cases, we need to write a copy of the file to the app sandbox.
|
||||||
|
///
|
||||||
|
/// For PHPicker (used for photos and videos), `NSItemProvider.loadFileRepresentation` returns a temporary file URL that is deleted after the completion handler.
|
||||||
|
/// The recommendation is to [Persist](https://developer.apple.com/documentation/foundation/nsitemprovider/2888338-loadfilerepresentation) the file by moving/copying
|
||||||
|
/// it to your app’s directory within the completion handler.
|
||||||
|
///
|
||||||
|
/// If available, `loadInPlaceFileRepresentation` can open a file in place; Photos assets typically do not support true in-place access,
|
||||||
|
/// so fall back to persisting a local file.
|
||||||
|
/// Ref: https://developer.apple.com/documentation/foundation/nsitemprovider/2888335-loadinplacefilerepresentation
|
||||||
|
///
|
||||||
|
/// For UIDocumentPicker, prefer "open in place" and avoid copying when possible.
|
||||||
|
/// Ref: https://developer.apple.com/documentation/uikit/uidocumentpickerviewcontroller
|
||||||
private func saveTemporaryFile(_ sourceUrl: URL) throws -> URL {
|
private func saveTemporaryFile(_ sourceUrl: URL) throws -> URL {
|
||||||
|
|
||||||
var directory = URL(fileURLWithPath: NSTemporaryDirectory())
|
var directory = URL(fileURLWithPath: NSTemporaryDirectory())
|
||||||
if let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first {
|
if let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first {
|
||||||
directory = cachesDirectory
|
directory = cachesDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
let targetUrl = directory.appendingPathComponent(sourceUrl.lastPathComponent)
|
let targetUrl = directory.appendingPathComponent(sourceUrl.lastPathComponent)
|
||||||
do {
|
do {
|
||||||
try deleteFile(targetUrl)
|
try deleteFile(targetUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
try FileManager.default.copyItem(at: sourceUrl, to: targetUrl)
|
try FileManager.default.copyItem(at: sourceUrl, to: targetUrl)
|
||||||
return targetUrl
|
return targetUrl
|
||||||
}
|
}
|
||||||
@@ -119,8 +136,7 @@ public class FilePickerController: NSObject {
|
|||||||
extension FilePickerController: UIDocumentPickerDelegate {
|
extension FilePickerController: UIDocumentPickerDelegate {
|
||||||
public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
|
public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
|
||||||
do {
|
do {
|
||||||
let temporaryUrls = try urls.map { try saveTemporaryFile($0) }
|
self.plugin.onFilePickerEvent(.selected(urls))
|
||||||
self.plugin.onFilePickerEvent(.selected(temporaryUrls))
|
|
||||||
} catch {
|
} catch {
|
||||||
self.plugin.onFilePickerEvent(.error("Failed to create a temporary copy of the file"))
|
self.plugin.onFilePickerEvent(.error("Failed to create a temporary copy of the file"))
|
||||||
}
|
}
|
||||||
@@ -191,6 +207,8 @@ extension FilePickerController: PHPickerViewControllerDelegate {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
// We have to make a copy of the file to the app sandbox here, as PHPicker returns an NSItemProvider with either an ephemeral file URL or content that is deleted after the completion handler.
|
||||||
|
// This is a different behavior from UIDocumentPicker, where the file can either be copied to the app sandbox or opened in place, and then accessed with `startAccessingSecurityScopedResource`.
|
||||||
let temporaryUrl = try self.saveTemporaryFile(url)
|
let temporaryUrl = try self.saveTemporaryFile(url)
|
||||||
temporaryUrls.append(temporaryUrl)
|
temporaryUrls.append(temporaryUrl)
|
||||||
} catch {
|
} catch {
|
||||||
@@ -212,6 +230,8 @@ extension FilePickerController: PHPickerViewControllerDelegate {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
// We have to make a copy of the file to the app sandbox here, as PHPicker returns an NSItemProvider with either an ephemeral file URL or content that is deleted after the completion handler.
|
||||||
|
// This is a different behavior from UIDocumentPicker, where the file can either be copied to the app sandbox or opened in place, and then accessed with `startAccessingSecurityScopedResource`.
|
||||||
let temporaryUrl = try self.saveTemporaryFile(url)
|
let temporaryUrl = try self.saveTemporaryFile(url)
|
||||||
temporaryUrls.append(temporaryUrl)
|
temporaryUrls.append(temporaryUrl)
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@tauri-apps/plugin-dialog",
|
"name": "@tauri-apps/plugin-dialog",
|
||||||
"version": "2.4.2",
|
"version": "2.7.2",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"authors": [
|
"authors": [
|
||||||
"Tauri Programme within The Commons Conservancy"
|
"Tauri Programme within The Commons Conservancy"
|
||||||
@@ -24,6 +24,6 @@
|
|||||||
"LICENSE"
|
"LICENSE"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^2.8.0"
|
"@tauri-apps/api": "^2.11.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
"$schema" = "schemas/schema.json"
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "allow-ask"
|
||||||
|
description = "Enables the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)"
|
||||||
|
commands.allow = ["message"]
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "deny-ask"
|
||||||
|
description = "Denies the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)"
|
||||||
|
commands.deny = ["message"]
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
# Automatically generated - DO NOT EDIT!
|
|
||||||
|
|
||||||
"$schema" = "../../schemas/schema.json"
|
|
||||||
|
|
||||||
[[permission]]
|
|
||||||
identifier = "allow-ask"
|
|
||||||
description = "Enables the ask command without any pre-configured scope."
|
|
||||||
commands.allow = ["ask"]
|
|
||||||
|
|
||||||
[[permission]]
|
|
||||||
identifier = "deny-ask"
|
|
||||||
description = "Denies the ask command without any pre-configured scope."
|
|
||||||
commands.deny = ["ask"]
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
# Automatically generated - DO NOT EDIT!
|
|
||||||
|
|
||||||
"$schema" = "../../schemas/schema.json"
|
|
||||||
|
|
||||||
[[permission]]
|
|
||||||
identifier = "allow-confirm"
|
|
||||||
description = "Enables the confirm command without any pre-configured scope."
|
|
||||||
commands.allow = ["confirm"]
|
|
||||||
|
|
||||||
[[permission]]
|
|
||||||
identifier = "deny-confirm"
|
|
||||||
description = "Denies the confirm command without any pre-configured scope."
|
|
||||||
commands.deny = ["confirm"]
|
|
||||||
@@ -9,8 +9,6 @@ All dialog types are enabled.
|
|||||||
|
|
||||||
#### This default permission set includes the following:
|
#### This default permission set includes the following:
|
||||||
|
|
||||||
- `allow-ask`
|
|
||||||
- `allow-confirm`
|
|
||||||
- `allow-message`
|
- `allow-message`
|
||||||
- `allow-save`
|
- `allow-save`
|
||||||
- `allow-open`
|
- `allow-open`
|
||||||
@@ -32,7 +30,7 @@ All dialog types are enabled.
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
Enables the ask command without any pre-configured scope.
|
Enables the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -45,33 +43,7 @@ Enables the ask command without any pre-configured scope.
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
Denies the ask command without any pre-configured scope.
|
Denies the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
`dialog:allow-confirm`
|
|
||||||
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
Enables the confirm command without any pre-configured scope.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
`dialog:deny-confirm`
|
|
||||||
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
Denies the confirm command without any pre-configured scope.
|
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -151,6 +123,32 @@ Enables the save command without any pre-configured scope.
|
|||||||
|
|
||||||
Denies the save command without any pre-configured scope.
|
Denies the save command without any pre-configured scope.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
`dialog:allow-confirm`
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Enables the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
`dialog:deny-confirm`
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Denies the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
"$schema" = "schemas/schema.json"
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "allow-confirm"
|
||||||
|
description = "Enables the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)"
|
||||||
|
commands.allow = ["message"]
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "deny-confirm"
|
||||||
|
description = "Denies the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)"
|
||||||
|
commands.deny = ["message"]
|
||||||
@@ -11,10 +11,4 @@ All dialog types are enabled.
|
|||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
permissions = [
|
permissions = ["allow-message", "allow-save", "allow-open"]
|
||||||
"allow-ask",
|
|
||||||
"allow-confirm",
|
|
||||||
"allow-message",
|
|
||||||
"allow-save",
|
|
||||||
"allow-open",
|
|
||||||
]
|
|
||||||
|
|||||||
@@ -295,28 +295,16 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
"description": "Enables the ask command without any pre-configured scope.",
|
"description": "Enables the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "allow-ask",
|
"const": "allow-ask",
|
||||||
"markdownDescription": "Enables the ask command without any pre-configured scope."
|
"markdownDescription": "Enables the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Denies the ask command without any pre-configured scope.",
|
"description": "Denies the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "deny-ask",
|
"const": "deny-ask",
|
||||||
"markdownDescription": "Denies the ask command without any pre-configured scope."
|
"markdownDescription": "Denies the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)"
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Enables the confirm command without any pre-configured scope.",
|
|
||||||
"type": "string",
|
|
||||||
"const": "allow-confirm",
|
|
||||||
"markdownDescription": "Enables the confirm command without any pre-configured scope."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Denies the confirm command without any pre-configured scope.",
|
|
||||||
"type": "string",
|
|
||||||
"const": "deny-confirm",
|
|
||||||
"markdownDescription": "Denies the confirm command without any pre-configured scope."
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the message command without any pre-configured scope.",
|
"description": "Enables the message command without any pre-configured scope.",
|
||||||
@@ -355,10 +343,22 @@
|
|||||||
"markdownDescription": "Denies the save command without any pre-configured scope."
|
"markdownDescription": "Denies the save command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-ask`\n- `allow-confirm`\n- `allow-message`\n- `allow-save`\n- `allow-open`",
|
"description": "Enables the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)",
|
||||||
|
"type": "string",
|
||||||
|
"const": "allow-confirm",
|
||||||
|
"markdownDescription": "Enables the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)",
|
||||||
|
"type": "string",
|
||||||
|
"const": "deny-confirm",
|
||||||
|
"markdownDescription": "Denies the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-message`\n- `allow-save`\n- `allow-open`",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "default",
|
"const": "default",
|
||||||
"markdownDescription": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-ask`\n- `allow-confirm`\n- `allow-message`\n- `allow-save`\n- `allow-open`"
|
"markdownDescription": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-message`\n- `allow-save`\n- `allow-open`"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ use tauri::{command, Manager, Runtime, State, Window};
|
|||||||
use tauri_plugin_fs::FsExt;
|
use tauri_plugin_fs::FsExt;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Dialog, FileDialogBuilder, FilePath, MessageDialogBuilder, MessageDialogButtons,
|
Dialog, FileAccessMode, FileDialogBuilder, FilePath, MessageDialogButtons, MessageDialogKind,
|
||||||
MessageDialogKind, MessageDialogResult, PickerMode, Result, CANCEL, NO, OK, YES,
|
MessageDialogResult, PickerMode, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
@@ -63,6 +63,10 @@ pub struct OpenDialogOptions {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[cfg_attr(mobile, allow(dead_code))]
|
#[cfg_attr(mobile, allow(dead_code))]
|
||||||
picker_mode: Option<PickerMode>,
|
picker_mode: Option<PickerMode>,
|
||||||
|
/// The file access mode of the dialog.
|
||||||
|
#[serde(default)]
|
||||||
|
#[cfg_attr(mobile, allow(dead_code))]
|
||||||
|
file_access_mode: Option<FileAccessMode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The options for the save dialog API.
|
/// The options for the save dialog API.
|
||||||
@@ -141,6 +145,9 @@ pub(crate) async fn open<R: Runtime>(
|
|||||||
let extensions: Vec<&str> = filter.extensions.iter().map(|s| &**s).collect();
|
let extensions: Vec<&str> = filter.extensions.iter().map(|s| &**s).collect();
|
||||||
dialog_builder = dialog_builder.add_filter(filter.name, &extensions);
|
dialog_builder = dialog_builder.add_filter(filter.name, &extensions);
|
||||||
}
|
}
|
||||||
|
if let Some(file_access_mode) = options.file_access_mode {
|
||||||
|
dialog_builder = dialog_builder.set_file_access_mode(file_access_mode);
|
||||||
|
}
|
||||||
|
|
||||||
let res = if options.directory {
|
let res = if options.directory {
|
||||||
#[cfg(desktop)]
|
#[cfg(desktop)]
|
||||||
@@ -251,17 +258,20 @@ pub(crate) async fn save<R: Runtime>(
|
|||||||
Ok(path.map(|p| p.simplified()))
|
Ok(path.map(|p| p.simplified()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn message_dialog<R: Runtime>(
|
#[command]
|
||||||
#[allow(unused_variables)] window: Window<R>,
|
pub(crate) async fn message<R: Runtime>(
|
||||||
|
#[allow(unused)] window: Window<R>,
|
||||||
dialog: State<'_, Dialog<R>>,
|
dialog: State<'_, Dialog<R>>,
|
||||||
title: Option<String>,
|
title: Option<String>,
|
||||||
message: String,
|
message: String,
|
||||||
kind: Option<MessageDialogKind>,
|
kind: Option<MessageDialogKind>,
|
||||||
buttons: MessageDialogButtons,
|
buttons: Option<MessageDialogButtons>,
|
||||||
) -> MessageDialogBuilder<R> {
|
) -> Result<MessageDialogResult> {
|
||||||
let mut builder = dialog.message(message);
|
let mut builder = dialog.message(message);
|
||||||
|
|
||||||
builder = builder.buttons(buttons);
|
if let Some(buttons) = buttons {
|
||||||
|
builder = builder.buttons(buttons);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(title) = title {
|
if let Some(title) = title {
|
||||||
builder = builder.title(title);
|
builder = builder.title(title);
|
||||||
@@ -276,86 +286,5 @@ fn message_dialog<R: Runtime>(
|
|||||||
builder = builder.kind(kind);
|
builder = builder.kind(kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder
|
Ok(builder.blocking_show_with_result())
|
||||||
}
|
|
||||||
|
|
||||||
#[command]
|
|
||||||
pub(crate) async fn message<R: Runtime>(
|
|
||||||
window: Window<R>,
|
|
||||||
dialog: State<'_, Dialog<R>>,
|
|
||||||
title: Option<String>,
|
|
||||||
message: String,
|
|
||||||
kind: Option<MessageDialogKind>,
|
|
||||||
ok_button_label: Option<String>,
|
|
||||||
buttons: Option<MessageDialogButtons>,
|
|
||||||
) -> Result<MessageDialogResult> {
|
|
||||||
let buttons = buttons.unwrap_or(if let Some(ok_button_label) = ok_button_label {
|
|
||||||
MessageDialogButtons::OkCustom(ok_button_label)
|
|
||||||
} else {
|
|
||||||
MessageDialogButtons::Ok
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(message_dialog(window, dialog, title, message, kind, buttons).blocking_show_with_result())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[command]
|
|
||||||
pub(crate) async fn ask<R: Runtime>(
|
|
||||||
window: Window<R>,
|
|
||||||
dialog: State<'_, Dialog<R>>,
|
|
||||||
title: Option<String>,
|
|
||||||
message: String,
|
|
||||||
kind: Option<MessageDialogKind>,
|
|
||||||
yes_button_label: Option<String>,
|
|
||||||
no_button_label: Option<String>,
|
|
||||||
) -> Result<bool> {
|
|
||||||
let dialog = message_dialog(
|
|
||||||
window,
|
|
||||||
dialog,
|
|
||||||
title,
|
|
||||||
message,
|
|
||||||
kind,
|
|
||||||
if let Some(yes_button_label) = yes_button_label {
|
|
||||||
MessageDialogButtons::OkCancelCustom(
|
|
||||||
yes_button_label,
|
|
||||||
no_button_label.unwrap_or(NO.to_string()),
|
|
||||||
)
|
|
||||||
} else if let Some(no_button_label) = no_button_label {
|
|
||||||
MessageDialogButtons::OkCancelCustom(YES.to_string(), no_button_label)
|
|
||||||
} else {
|
|
||||||
MessageDialogButtons::YesNo
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(dialog.blocking_show())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[command]
|
|
||||||
pub(crate) async fn confirm<R: Runtime>(
|
|
||||||
window: Window<R>,
|
|
||||||
dialog: State<'_, Dialog<R>>,
|
|
||||||
title: Option<String>,
|
|
||||||
message: String,
|
|
||||||
kind: Option<MessageDialogKind>,
|
|
||||||
ok_button_label: Option<String>,
|
|
||||||
cancel_button_label: Option<String>,
|
|
||||||
) -> Result<bool> {
|
|
||||||
let dialog = message_dialog(
|
|
||||||
window,
|
|
||||||
dialog,
|
|
||||||
title,
|
|
||||||
message,
|
|
||||||
kind,
|
|
||||||
if let Some(ok_button_label) = ok_button_label {
|
|
||||||
MessageDialogButtons::OkCancelCustom(
|
|
||||||
ok_button_label,
|
|
||||||
cancel_button_label.unwrap_or(CANCEL.to_string()),
|
|
||||||
)
|
|
||||||
} else if let Some(cancel_button_label) = cancel_button_label {
|
|
||||||
MessageDialogButtons::OkCancelCustom(OK.to_string(), cancel_button_label)
|
|
||||||
} else {
|
|
||||||
MessageDialogButtons::OkCancel
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(dialog.blocking_show())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,9 +53,19 @@ pub enum PickerMode {
|
|||||||
Video,
|
Video,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum FileAccessMode {
|
||||||
|
Copy,
|
||||||
|
Scoped,
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) const OK: &str = "Ok";
|
pub(crate) const OK: &str = "Ok";
|
||||||
|
#[cfg(mobile)]
|
||||||
pub(crate) const CANCEL: &str = "Cancel";
|
pub(crate) const CANCEL: &str = "Cancel";
|
||||||
|
#[cfg(mobile)]
|
||||||
pub(crate) const YES: &str = "Yes";
|
pub(crate) const YES: &str = "Yes";
|
||||||
|
#[cfg(mobile)]
|
||||||
pub(crate) const NO: &str = "No";
|
pub(crate) const NO: &str = "No";
|
||||||
|
|
||||||
macro_rules! blocking_fn {
|
macro_rules! blocking_fn {
|
||||||
@@ -190,8 +200,6 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
|||||||
commands::open,
|
commands::open,
|
||||||
commands::save,
|
commands::save,
|
||||||
commands::message,
|
commands::message,
|
||||||
commands::ask,
|
|
||||||
commands::confirm
|
|
||||||
])
|
])
|
||||||
.setup(|app, api| {
|
.setup(|app, api| {
|
||||||
#[cfg(mobile)]
|
#[cfg(mobile)]
|
||||||
@@ -239,8 +247,8 @@ impl<R: Runtime> MessageDialogBuilder<R> {
|
|||||||
dialog,
|
dialog,
|
||||||
title: title.into(),
|
title: title.into(),
|
||||||
message: message.into(),
|
message: message.into(),
|
||||||
kind: Default::default(),
|
kind: MessageDialogKind::default(),
|
||||||
buttons: Default::default(),
|
buttons: MessageDialogButtons::default(),
|
||||||
#[cfg(desktop)]
|
#[cfg(desktop)]
|
||||||
parent: None,
|
parent: None,
|
||||||
}
|
}
|
||||||
@@ -379,6 +387,7 @@ pub struct FileDialogBuilder<R: Runtime> {
|
|||||||
pub(crate) title: Option<String>,
|
pub(crate) title: Option<String>,
|
||||||
pub(crate) can_create_directories: Option<bool>,
|
pub(crate) can_create_directories: Option<bool>,
|
||||||
pub(crate) picker_mode: Option<PickerMode>,
|
pub(crate) picker_mode: Option<PickerMode>,
|
||||||
|
pub(crate) file_access_mode: Option<FileAccessMode>,
|
||||||
#[cfg(desktop)]
|
#[cfg(desktop)]
|
||||||
pub(crate) parent: Option<crate::desktop::WindowHandle>,
|
pub(crate) parent: Option<crate::desktop::WindowHandle>,
|
||||||
}
|
}
|
||||||
@@ -391,6 +400,7 @@ pub(crate) struct FileDialogPayload<'a> {
|
|||||||
filters: &'a Vec<Filter>,
|
filters: &'a Vec<Filter>,
|
||||||
multiple: bool,
|
multiple: bool,
|
||||||
picker_mode: &'a Option<PickerMode>,
|
picker_mode: &'a Option<PickerMode>,
|
||||||
|
file_access_mode: &'a Option<FileAccessMode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// raw window handle :(
|
// raw window handle :(
|
||||||
@@ -407,6 +417,7 @@ impl<R: Runtime> FileDialogBuilder<R> {
|
|||||||
title: None,
|
title: None,
|
||||||
can_create_directories: None,
|
can_create_directories: None,
|
||||||
picker_mode: None,
|
picker_mode: None,
|
||||||
|
file_access_mode: None,
|
||||||
#[cfg(desktop)]
|
#[cfg(desktop)]
|
||||||
parent: None,
|
parent: None,
|
||||||
}
|
}
|
||||||
@@ -419,6 +430,7 @@ impl<R: Runtime> FileDialogBuilder<R> {
|
|||||||
filters: &self.filters,
|
filters: &self.filters,
|
||||||
multiple,
|
multiple,
|
||||||
picker_mode: &self.picker_mode,
|
picker_mode: &self.picker_mode,
|
||||||
|
file_access_mode: &self.file_access_mode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,6 +500,14 @@ impl<R: Runtime> FileDialogBuilder<R> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the file access mode of the dialog.
|
||||||
|
/// This is only used on iOS.
|
||||||
|
/// On desktop and Android, this option is ignored.
|
||||||
|
pub fn set_file_access_mode(mut self, mode: FileAccessMode) -> Self {
|
||||||
|
self.file_access_mode.replace(mode);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Shows the dialog to select a single file.
|
/// Shows the dialog to select a single file.
|
||||||
///
|
///
|
||||||
/// This is not a blocking operation,
|
/// This is not a blocking operation,
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "guest-js"
|
||||||
|
},
|
||||||
"include": ["guest-js/*.ts"]
|
"include": ["guest-js/*.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,21 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## \[2.5.1]
|
||||||
|
|
||||||
|
- [`ec054013`](https://github.com/tauri-apps/plugins-workspace/commit/ec0540138bece081e9a87982091947360e61987a) ([#3323](https://github.com/tauri-apps/plugins-workspace/pull/3323) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated dependency `toml` from 0.9 to 1
|
||||||
|
|
||||||
|
## \[2.5.0]
|
||||||
|
|
||||||
|
- [`e97a4ded`](https://github.com/tauri-apps/plugins-workspace/commit/e97a4dedab8608d9d57029784ee8a2607ca3a996) ([#3244](https://github.com/tauri-apps/plugins-workspace/pull/3244) by [@SeijiOkuda](https://github.com/tauri-apps/plugins-workspace/../../SeijiOkuda)) Add `encoding` option for `readTextFile` and `readTextFileLines`
|
||||||
|
- [`f75d21db`](https://github.com/tauri-apps/plugins-workspace/commit/f75d21db3351d6f12adf585c2c797c20ece94f7f) ([#3360](https://github.com/tauri-apps/plugins-workspace/pull/3360) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) Removed the dependency on `tauri-utils`'s `build` feature
|
||||||
|
- [`f5f68063`](https://github.com/tauri-apps/plugins-workspace/commit/f5f68063e459c33522f44a04df3120d9d039ec13) ([#3185](https://github.com/tauri-apps/plugins-workspace/pull/3185) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Enable access for security-scoped resources on iOS by automatically calling `NSURL::startAccessingSecurityScopedResource` on resource access and adding the `stopAccessingSecurityScopedResource` API.
|
||||||
|
|
||||||
|
## \[2.4.5]
|
||||||
|
|
||||||
|
- [`521cd8b3`](https://github.com/tauri-apps/plugins-workspace/commit/521cd8b372c862d96b1637775710e4d7cf2443e2) ([#3155](https://github.com/tauri-apps/plugins-workspace/pull/3155) by [@EliasStar](https://github.com/tauri-apps/plugins-workspace/../../EliasStar)) Fix off by one error in the implementation of readTextFileLines causing all lines to end with an (additional) null byte.
|
||||||
|
Issue: [#3154](https://github.com/tauri-apps/plugins-workspace/issues/3154)
|
||||||
|
PR: [#3155](https://github.com/tauri-apps/plugins-workspace/pull/3155)
|
||||||
|
|
||||||
## \[2.4.4]
|
## \[2.4.4]
|
||||||
|
|
||||||
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
|
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tauri-plugin-fs"
|
name = "tauri-plugin-fs"
|
||||||
version = "2.4.4"
|
version = "2.5.1"
|
||||||
description = "Access the file system."
|
description = "Access the file system."
|
||||||
authors = { workspace = true }
|
authors = { workspace = true }
|
||||||
license = { workspace = true }
|
license = { workspace = true }
|
||||||
@@ -20,8 +20,8 @@ ios = { level = "partial", notes = "Access is restricted to Application folder b
|
|||||||
tauri-plugin = { workspace = true, features = ["build"] }
|
tauri-plugin = { workspace = true, features = ["build"] }
|
||||||
schemars = { workspace = true }
|
schemars = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
toml = "0.9"
|
toml = "1.0"
|
||||||
tauri-utils = { workspace = true, features = ["build"] }
|
tauri-utils = { workspace = true }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
@@ -30,6 +30,7 @@ serde_repr = "0.1"
|
|||||||
tauri = { workspace = true }
|
tauri = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
|
log = { workspace = true }
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
glob = { workspace = true }
|
glob = { workspace = true }
|
||||||
# TODO: Remove `serialization-compat-6` in v3
|
# TODO: Remove `serialization-compat-6` in v3
|
||||||
@@ -41,5 +42,8 @@ notify-debouncer-full = { version = "0.6", optional = true }
|
|||||||
dunce = { workspace = true }
|
dunce = { workspace = true }
|
||||||
percent-encoding = "2"
|
percent-encoding = "2"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "ios")'.dependencies]
|
||||||
|
objc2-foundation = { version = "0.3", features = ["NSURL", "NSString"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
watch = ["notify", "notify-debouncer-full"]
|
watch = ["notify", "notify-debouncer-full"]
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user