@sosweetham/tauri-plugin-sharehub-api 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SoSweetHam
4
+ Portions Copyright (c) 2025 Vladimir Pankratov (the share-out implementation,
5
+ derived from tauri-plugin-sharekit, MIT)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,197 @@
1
+ [![Crates.io Version](https://img.shields.io/crates/v/tauri-plugin-sharehub)](https://crates.io/crates/tauri-plugin-sharehub)
2
+ [![NPM Version](https://img.shields.io/npm/v/@sosweetham%2Ftauri-plugin-sharehub-api)](https://www.npmjs.com/package/@sosweetham/tauri-plugin-sharehub-api)
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
4
+
5
+ # tauri-plugin-sharehub
6
+
7
+ Bidirectional native sharing for [Tauri 2](https://tauri.app) apps: share text and
8
+ files **out** to other apps, and receive text, links, images, and files shared **in**
9
+ from other apps as a share target.
10
+
11
+ ## Bidirectional
12
+
13
+ Most share plugins only push content out. `sharehub` does both:
14
+
15
+ - **Share OUT.** Open the system share sheet to hand text or a file to another app.
16
+ Backed by `UIActivityViewController` (iOS), `ACTION_SEND` (Android),
17
+ `NSSharingServicePicker` (macOS), and `DataTransferManager` (Windows).
18
+ - **Share IN.** Register your app as a share target so other apps can send it text,
19
+ links, images, and files. On iOS a copyable Share Extension writes the shared blobs
20
+ plus a `manifest.json` into a shared App Group container and opens your app via a
21
+ deep link. On Android the plugin copies the `ACTION_SEND` / `ACTION_SEND_MULTIPLE`
22
+ streams into the app's files directory. Either way your app reads the queue with
23
+ `getPendingShares()` and pulls bytes lazily with `readSharedItem(id)`.
24
+
25
+ ## Platform support
26
+
27
+ | Capability | iOS | Android | macOS | Windows | Linux |
28
+ | ----------------- | --- | ------- | ----- | ------- | ----- |
29
+ | Share text (OUT) | yes | yes | yes | yes | no |
30
+ | Share file (OUT) | yes | yes | yes | yes | no |
31
+ | Receive shares (IN) | yes | yes | no | no | no |
32
+
33
+ Desktop receive is a graceful no-op: `getPendingShares()` resolves to an empty list
34
+ rather than throwing, so the same code runs everywhere. On Linux the share-out calls
35
+ are no-ops.
36
+
37
+ ## Install
38
+
39
+ Add the Rust crate to your Tauri app:
40
+
41
+ ```sh
42
+ cargo add tauri-plugin-sharehub
43
+ ```
44
+
45
+ Add the JavaScript guest bindings with your package manager of choice:
46
+
47
+ ```sh
48
+ pnpm add @sosweetham/tauri-plugin-sharehub-api
49
+ # or: npm add @sosweetham/tauri-plugin-sharehub-api
50
+ # or: yarn add @sosweetham/tauri-plugin-sharehub-api
51
+ ```
52
+
53
+ Register the plugin in `src-tauri/src/lib.rs`:
54
+
55
+ ```rust
56
+ // src-tauri/src/lib.rs
57
+ tauri::Builder::default()
58
+ .plugin(tauri_plugin_sharehub::init())
59
+ // ...
60
+ ```
61
+
62
+ Grant the default permission set in your capability file, e.g.
63
+ `src-tauri/capabilities/default.json`:
64
+
65
+ ```json
66
+ {
67
+ "permissions": ["sharehub:default"]
68
+ }
69
+ ```
70
+
71
+ `sharehub:default` allows every command: `share_text`, `share_file`,
72
+ `get_pending_shares`, `read_shared_item`, and `clear_pending_shares`.
73
+
74
+ ## Usage: share OUT
75
+
76
+ ```ts
77
+ import { shareText, shareFile } from "@sosweetham/tauri-plugin-sharehub-api";
78
+
79
+ // Share plain text.
80
+ await shareText("Pendi is great!");
81
+
82
+ // Share a file by file:// URL.
83
+ await shareFile("file:///path/to/document.pdf", {
84
+ mimeType: "application/pdf",
85
+ title: "My Document",
86
+ });
87
+
88
+ // Anchor the share sheet (iPad / macOS, in webview coordinates).
89
+ await shareText("Hello!", {
90
+ position: { x: 100, y: 200, preferredEdge: "bottom" },
91
+ });
92
+ ```
93
+
94
+ ## Usage: share IN
95
+
96
+ Your app receives shares by reading a queue. The reliable mechanism is to call
97
+ `getPendingShares()` on launch and from your deep-link handler, then read bytes for the
98
+ items you care about and clear the queue once handled.
99
+
100
+ ```ts
101
+ import {
102
+ getPendingShares,
103
+ readSharedItem,
104
+ clearPendingShares,
105
+ onShare,
106
+ } from "@sosweetham/tauri-plugin-sharehub-api";
107
+
108
+ async function consumeShares() {
109
+ const { items } = await getPendingShares();
110
+ if (items.length === 0) return;
111
+
112
+ for (const item of items) {
113
+ if (item.kind === "text" || item.kind === "url") {
114
+ console.log("shared text/link:", item.text ?? item.url);
115
+ } else {
116
+ // image / file: pull the bytes lazily and wrap them in a File.
117
+ const buf = await readSharedItem(item.id);
118
+ const file = new File([buf], item.name ?? "shared", {
119
+ type: item.mimeType,
120
+ });
121
+ // ...upload or process `file`
122
+ }
123
+ }
124
+
125
+ // Only clear once everything has been handled successfully. A mid-flow
126
+ // crash leaves the share for the next launch.
127
+ await clearPendingShares();
128
+ }
129
+
130
+ // Best-effort: fires when a share arrives while the app is already running.
131
+ // Silently no-ops where the platform has no live channel, so treat it as a
132
+ // nudge on top of the launch-time `getPendingShares()` path, not a replacement.
133
+ const unsubscribe = await onShare(() => {
134
+ void consumeShares();
135
+ });
136
+ ```
137
+
138
+ ### Deep-link + App Group model
139
+
140
+ iOS only lets a separate **Share Extension** target receive system shares, so it cannot
141
+ ship inside the plugin: it must be a target in *your* app. The extension copies the
142
+ shared blobs plus a `manifest.json` into a shared **App Group** container and opens your
143
+ app at `<yourScheme>://share`. Your app handles that deep link, navigates to a share
144
+ screen, and calls `getPendingShares()`.
145
+
146
+ A copyable extension template (`ShareViewController.swift`, `Info.plist`,
147
+ `ShareExt.entitlements`) and full setup steps live in
148
+ [`extensions/ios/README.md`](extensions/ios/README.md). The key gotchas: the same App
149
+ Group must be on both the host app and the extension entitlements and registered on your
150
+ Apple Developer account, and the extension's `appScheme` must match your
151
+ `tauri-plugin-deep-link` scheme.
152
+
153
+ On **Android**, add an `ACTION_SEND` / `ACTION_SEND_MULTIPLE` intent filter to your main
154
+ activity in `src-tauri/gen/android/app/src/main/AndroidManifest.xml` so the system lists
155
+ your app as a share target, for example:
156
+
157
+ ```xml
158
+ <intent-filter>
159
+ <action android:name="android.intent.action.SEND" />
160
+ <category android:name="android.intent.category.DEFAULT" />
161
+ <data android:mimeType="*/*" />
162
+ </intent-filter>
163
+ <intent-filter>
164
+ <action android:name="android.intent.action.SEND_MULTIPLE" />
165
+ <category android:name="android.intent.category.DEFAULT" />
166
+ <data android:mimeType="*/*" />
167
+ </intent-filter>
168
+ ```
169
+
170
+ The plugin copies the incoming streams into the app's files directory and exposes them
171
+ through the same `getPendingShares()` / `readSharedItem()` API.
172
+
173
+ ## Type generation
174
+
175
+ Every shape that crosses the JS bridge is defined once in Rust, in `src/models.rs`, and
176
+ generated outward:
177
+
178
+ ```
179
+ src/models.rs -> schemars JSON Schema -> Zod (guest-js/schemas.ts)
180
+ ```
181
+
182
+ Run `pnpm generate-types` after editing `src/models.rs` to regenerate
183
+ `guest-js/schemas.ts` (driven by the dev-only `gen` Cargo feature). Never edit
184
+ `guest-js/schemas.ts` by hand. Inbound payloads cross from arbitrary external apps
185
+ through messy native parsing, so `getPendingShares()` validates its result at runtime
186
+ against the generated Zod schema before returning it. Outbound option types are
187
+ hand-written in `guest-js/index.ts` because they are JS-constructed arguments and need
188
+ no runtime parse.
189
+
190
+ ## Credits
191
+
192
+ The share-OUT path is derived from Choochmeque's
193
+ [`tauri-plugin-sharekit`](https://github.com/Choochmeque/tauri-plugin-sharekit) (MIT).
194
+
195
+ ## License
196
+
197
+ MIT
@@ -0,0 +1,105 @@
1
+ 'use strict';
2
+
3
+ var core = require('@tauri-apps/api/core');
4
+ var zod = require('zod');
5
+
6
+ // GENERATED by `pnpm generate-types` (schemars JSON Schema -> Zod). Do not edit.
7
+ // Source of truth: src/models.rs (SharePayload). Validates inbound share payloads.
8
+ const sharePayloadSchema = zod.z.object({ "items": zod.z.array(zod.z.object({ "id": zod.z.string().describe("Stable id for this item within the current pending batch."), "kind": zod.z.enum(["text", "url", "image", "file"]).describe("Discriminates how to interpret the remaining fields."), "mimeType": zod.z.union([zod.z.string().describe("Best-effort MIME type for `image` / `file` items."), zod.z.null().describe("Best-effort MIME type for `image` / `file` items.")]).describe("Best-effort MIME type for `image` / `file` items.").optional(), "name": zod.z.union([zod.z.string().describe("Original display name for `image` / `file` items."), zod.z.null().describe("Original display name for `image` / `file` items.")]).describe("Original display name for `image` / `file` items.").optional(), "size": zod.z.union([zod.z.number().int().gte(0).describe("Size in bytes for `image` / `file` items, when known."), zod.z.null().describe("Size in bytes for `image` / `file` items, when known.")]).describe("Size in bytes for `image` / `file` items, when known.").optional(), "text": zod.z.union([zod.z.string().describe("Present for `text` / `url` items."), zod.z.null().describe("Present for `text` / `url` items.")]).describe("Present for `text` / `url` items.").optional(), "url": zod.z.union([zod.z.string().describe("Present for `url` items (the shared link)."), zod.z.null().describe("Present for `url` items (the shared link).")]).describe("Present for `url` items (the shared link).").optional() }).describe("One item handed to the app by an external app's share action. **Metadata only** — bytes are fetched lazily via `read_shared_item(id)`. This is the public wire shape crossing the JS bridge (the on-disk path stays behind the Rust boundary).")) }).describe("The set of items currently waiting to be consumed by the app.");
9
+
10
+ // ----- Share OUT -----
11
+ /**
12
+ * Opens the native sharing interface to share the specified text.
13
+ *
14
+ * ```javascript
15
+ * import { shareText } from "@sosweetham/tauri-plugin-sharehub-api";
16
+ * await shareText('I am a shared message');
17
+ * ```
18
+ */
19
+ async function shareText(text, options) {
20
+ await core.invoke("plugin:sharehub|share_text", {
21
+ text,
22
+ ...options,
23
+ });
24
+ }
25
+ /**
26
+ * Opens the native sharing interface to share a file.
27
+ *
28
+ * ```javascript
29
+ * import { shareFile } from "@sosweetham/tauri-plugin-sharehub-api";
30
+ * await shareFile('file:///path/to/file.pdf', {
31
+ * mimeType: 'application/pdf',
32
+ * title: 'Document.pdf'
33
+ * });
34
+ * ```
35
+ * @param url - The file URL to share (must be a file:// URL)
36
+ * @param options - Optional settings including MIME type and title
37
+ */
38
+ async function shareFile(url, options) {
39
+ await core.invoke("plugin:sharehub|share_file", {
40
+ url,
41
+ ...options,
42
+ });
43
+ }
44
+ // ----- Share IN (receive / share-target) -----
45
+ /**
46
+ * Returns the items currently waiting to be consumed by this app (metadata only).
47
+ * Does **not** clear the queue — call {@link clearPendingShares} once handled, so a
48
+ * mid-flow crash leaves the share for next launch.
49
+ *
50
+ * The result is validated against {@link sharePayloadSchema} (generated from the Rust
51
+ * models): this is the one boundary where data originates from arbitrary external apps
52
+ * via messy native parsing, so the runtime check earns its keep. On platforms without
53
+ * an in-app share target (desktop) this resolves to an empty list rather than throwing.
54
+ */
55
+ async function getPendingShares() {
56
+ const raw = await core.invoke("plugin:sharehub|get_pending_shares");
57
+ return sharePayloadSchema.parse(raw);
58
+ }
59
+ /**
60
+ * Reads the raw bytes of a pending `image`/`file` item by id, returned as an
61
+ * `ArrayBuffer`. Wrap it in a `File`/`Blob` to upload:
62
+ *
63
+ * ```javascript
64
+ * const buf = await readSharedItem(item.id);
65
+ * const file = new File([buf], item.name ?? "shared", { type: item.mimeType });
66
+ * ```
67
+ */
68
+ async function readSharedItem(id) {
69
+ return await core.invoke("plugin:sharehub|read_shared_item", { id });
70
+ }
71
+ /**
72
+ * Clears the pending queue and removes the copied blobs. Call only after the items
73
+ * have been successfully handled.
74
+ */
75
+ async function clearPendingShares() {
76
+ await core.invoke("plugin:sharehub|clear_pending_shares");
77
+ }
78
+ /**
79
+ * Best-effort subscription for shares that arrive while the app is already running.
80
+ *
81
+ * The **reliable** mechanism is {@link getPendingShares} on launch plus your app's
82
+ * deep-link handler (the share extension / intent opens your app, you navigate to a
83
+ * share screen and call `getPendingShares`). This listener is only a foreground
84
+ * nudge and silently no-ops where the platform has no live channel.
85
+ *
86
+ * @returns an unsubscribe function.
87
+ */
88
+ async function onShare(cb) {
89
+ try {
90
+ const listener = await core.addPluginListener("sharehub", "received", (payload) => cb(payload));
91
+ return () => {
92
+ void listener.unregister();
93
+ };
94
+ }
95
+ catch {
96
+ return () => { };
97
+ }
98
+ }
99
+
100
+ exports.clearPendingShares = clearPendingShares;
101
+ exports.getPendingShares = getPendingShares;
102
+ exports.onShare = onShare;
103
+ exports.readSharedItem = readSharedItem;
104
+ exports.shareFile = shareFile;
105
+ exports.shareText = shareText;
@@ -0,0 +1,82 @@
1
+ import { type SharePayload } from "./schemas";
2
+ export type { SharePayload };
3
+ export type SharedItem = SharePayload["items"][number];
4
+ export type SharedItemKind = SharedItem["kind"];
5
+ export interface SharePosition {
6
+ x: number;
7
+ y: number;
8
+ /** macOS only: which edge the picker appears from */
9
+ preferredEdge?: "top" | "bottom" | "left" | "right";
10
+ }
11
+ export interface ShareTextOptions {
12
+ /** Android only */
13
+ mimeType?: string;
14
+ /** Position for the share sheet (iPad/macOS only) */
15
+ position?: SharePosition;
16
+ }
17
+ export interface ShareFileOptions {
18
+ mimeType?: string;
19
+ title?: string;
20
+ /** Position for the share sheet (iPad/macOS only) */
21
+ position?: SharePosition;
22
+ }
23
+ /**
24
+ * Opens the native sharing interface to share the specified text.
25
+ *
26
+ * ```javascript
27
+ * import { shareText } from "@sosweetham/tauri-plugin-sharehub-api";
28
+ * await shareText('I am a shared message');
29
+ * ```
30
+ */
31
+ export declare function shareText(text: string, options?: ShareTextOptions): Promise<void>;
32
+ /**
33
+ * Opens the native sharing interface to share a file.
34
+ *
35
+ * ```javascript
36
+ * import { shareFile } from "@sosweetham/tauri-plugin-sharehub-api";
37
+ * await shareFile('file:///path/to/file.pdf', {
38
+ * mimeType: 'application/pdf',
39
+ * title: 'Document.pdf'
40
+ * });
41
+ * ```
42
+ * @param url - The file URL to share (must be a file:// URL)
43
+ * @param options - Optional settings including MIME type and title
44
+ */
45
+ export declare function shareFile(url: string, options?: ShareFileOptions): Promise<void>;
46
+ /**
47
+ * Returns the items currently waiting to be consumed by this app (metadata only).
48
+ * Does **not** clear the queue — call {@link clearPendingShares} once handled, so a
49
+ * mid-flow crash leaves the share for next launch.
50
+ *
51
+ * The result is validated against {@link sharePayloadSchema} (generated from the Rust
52
+ * models): this is the one boundary where data originates from arbitrary external apps
53
+ * via messy native parsing, so the runtime check earns its keep. On platforms without
54
+ * an in-app share target (desktop) this resolves to an empty list rather than throwing.
55
+ */
56
+ export declare function getPendingShares(): Promise<SharePayload>;
57
+ /**
58
+ * Reads the raw bytes of a pending `image`/`file` item by id, returned as an
59
+ * `ArrayBuffer`. Wrap it in a `File`/`Blob` to upload:
60
+ *
61
+ * ```javascript
62
+ * const buf = await readSharedItem(item.id);
63
+ * const file = new File([buf], item.name ?? "shared", { type: item.mimeType });
64
+ * ```
65
+ */
66
+ export declare function readSharedItem(id: string): Promise<ArrayBuffer>;
67
+ /**
68
+ * Clears the pending queue and removes the copied blobs. Call only after the items
69
+ * have been successfully handled.
70
+ */
71
+ export declare function clearPendingShares(): Promise<void>;
72
+ /**
73
+ * Best-effort subscription for shares that arrive while the app is already running.
74
+ *
75
+ * The **reliable** mechanism is {@link getPendingShares} on launch plus your app's
76
+ * deep-link handler (the share extension / intent opens your app, you navigate to a
77
+ * share screen and call `getPendingShares`). This listener is only a foreground
78
+ * nudge and silently no-ops where the platform has no live channel.
79
+ *
80
+ * @returns an unsubscribe function.
81
+ */
82
+ export declare function onShare(cb: (payload: SharePayload) => void): Promise<() => void>;
@@ -0,0 +1,98 @@
1
+ import { invoke, addPluginListener } from '@tauri-apps/api/core';
2
+ import { z } from 'zod';
3
+
4
+ // GENERATED by `pnpm generate-types` (schemars JSON Schema -> Zod). Do not edit.
5
+ // Source of truth: src/models.rs (SharePayload). Validates inbound share payloads.
6
+ const sharePayloadSchema = z.object({ "items": z.array(z.object({ "id": z.string().describe("Stable id for this item within the current pending batch."), "kind": z.enum(["text", "url", "image", "file"]).describe("Discriminates how to interpret the remaining fields."), "mimeType": z.union([z.string().describe("Best-effort MIME type for `image` / `file` items."), z.null().describe("Best-effort MIME type for `image` / `file` items.")]).describe("Best-effort MIME type for `image` / `file` items.").optional(), "name": z.union([z.string().describe("Original display name for `image` / `file` items."), z.null().describe("Original display name for `image` / `file` items.")]).describe("Original display name for `image` / `file` items.").optional(), "size": z.union([z.number().int().gte(0).describe("Size in bytes for `image` / `file` items, when known."), z.null().describe("Size in bytes for `image` / `file` items, when known.")]).describe("Size in bytes for `image` / `file` items, when known.").optional(), "text": z.union([z.string().describe("Present for `text` / `url` items."), z.null().describe("Present for `text` / `url` items.")]).describe("Present for `text` / `url` items.").optional(), "url": z.union([z.string().describe("Present for `url` items (the shared link)."), z.null().describe("Present for `url` items (the shared link).")]).describe("Present for `url` items (the shared link).").optional() }).describe("One item handed to the app by an external app's share action. **Metadata only** — bytes are fetched lazily via `read_shared_item(id)`. This is the public wire shape crossing the JS bridge (the on-disk path stays behind the Rust boundary).")) }).describe("The set of items currently waiting to be consumed by the app.");
7
+
8
+ // ----- Share OUT -----
9
+ /**
10
+ * Opens the native sharing interface to share the specified text.
11
+ *
12
+ * ```javascript
13
+ * import { shareText } from "@sosweetham/tauri-plugin-sharehub-api";
14
+ * await shareText('I am a shared message');
15
+ * ```
16
+ */
17
+ async function shareText(text, options) {
18
+ await invoke("plugin:sharehub|share_text", {
19
+ text,
20
+ ...options,
21
+ });
22
+ }
23
+ /**
24
+ * Opens the native sharing interface to share a file.
25
+ *
26
+ * ```javascript
27
+ * import { shareFile } from "@sosweetham/tauri-plugin-sharehub-api";
28
+ * await shareFile('file:///path/to/file.pdf', {
29
+ * mimeType: 'application/pdf',
30
+ * title: 'Document.pdf'
31
+ * });
32
+ * ```
33
+ * @param url - The file URL to share (must be a file:// URL)
34
+ * @param options - Optional settings including MIME type and title
35
+ */
36
+ async function shareFile(url, options) {
37
+ await invoke("plugin:sharehub|share_file", {
38
+ url,
39
+ ...options,
40
+ });
41
+ }
42
+ // ----- Share IN (receive / share-target) -----
43
+ /**
44
+ * Returns the items currently waiting to be consumed by this app (metadata only).
45
+ * Does **not** clear the queue — call {@link clearPendingShares} once handled, so a
46
+ * mid-flow crash leaves the share for next launch.
47
+ *
48
+ * The result is validated against {@link sharePayloadSchema} (generated from the Rust
49
+ * models): this is the one boundary where data originates from arbitrary external apps
50
+ * via messy native parsing, so the runtime check earns its keep. On platforms without
51
+ * an in-app share target (desktop) this resolves to an empty list rather than throwing.
52
+ */
53
+ async function getPendingShares() {
54
+ const raw = await invoke("plugin:sharehub|get_pending_shares");
55
+ return sharePayloadSchema.parse(raw);
56
+ }
57
+ /**
58
+ * Reads the raw bytes of a pending `image`/`file` item by id, returned as an
59
+ * `ArrayBuffer`. Wrap it in a `File`/`Blob` to upload:
60
+ *
61
+ * ```javascript
62
+ * const buf = await readSharedItem(item.id);
63
+ * const file = new File([buf], item.name ?? "shared", { type: item.mimeType });
64
+ * ```
65
+ */
66
+ async function readSharedItem(id) {
67
+ return await invoke("plugin:sharehub|read_shared_item", { id });
68
+ }
69
+ /**
70
+ * Clears the pending queue and removes the copied blobs. Call only after the items
71
+ * have been successfully handled.
72
+ */
73
+ async function clearPendingShares() {
74
+ await invoke("plugin:sharehub|clear_pending_shares");
75
+ }
76
+ /**
77
+ * Best-effort subscription for shares that arrive while the app is already running.
78
+ *
79
+ * The **reliable** mechanism is {@link getPendingShares} on launch plus your app's
80
+ * deep-link handler (the share extension / intent opens your app, you navigate to a
81
+ * share screen and call `getPendingShares`). This listener is only a foreground
82
+ * nudge and silently no-ops where the platform has no live channel.
83
+ *
84
+ * @returns an unsubscribe function.
85
+ */
86
+ async function onShare(cb) {
87
+ try {
88
+ const listener = await addPluginListener("sharehub", "received", (payload) => cb(payload));
89
+ return () => {
90
+ void listener.unregister();
91
+ };
92
+ }
93
+ catch {
94
+ return () => { };
95
+ }
96
+ }
97
+
98
+ export { clearPendingShares, getPendingShares, onShare, readSharedItem, shareFile, shareText };
@@ -0,0 +1,49 @@
1
+ import { z } from "zod";
2
+ export declare const sharePayloadSchema: z.ZodObject<{
3
+ items: z.ZodArray<z.ZodObject<{
4
+ id: z.ZodString;
5
+ kind: z.ZodEnum<["text", "url", "image", "file"]>;
6
+ mimeType: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNull]>>;
7
+ name: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNull]>>;
8
+ size: z.ZodOptional<z.ZodUnion<[z.ZodNumber, z.ZodNull]>>;
9
+ text: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNull]>>;
10
+ url: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNull]>>;
11
+ }, "strip", z.ZodTypeAny, {
12
+ id: string;
13
+ kind: "text" | "url" | "image" | "file";
14
+ text?: string | null | undefined;
15
+ url?: string | null | undefined;
16
+ mimeType?: string | null | undefined;
17
+ name?: string | null | undefined;
18
+ size?: number | null | undefined;
19
+ }, {
20
+ id: string;
21
+ kind: "text" | "url" | "image" | "file";
22
+ text?: string | null | undefined;
23
+ url?: string | null | undefined;
24
+ mimeType?: string | null | undefined;
25
+ name?: string | null | undefined;
26
+ size?: number | null | undefined;
27
+ }>, "many">;
28
+ }, "strip", z.ZodTypeAny, {
29
+ items: {
30
+ id: string;
31
+ kind: "text" | "url" | "image" | "file";
32
+ text?: string | null | undefined;
33
+ url?: string | null | undefined;
34
+ mimeType?: string | null | undefined;
35
+ name?: string | null | undefined;
36
+ size?: number | null | undefined;
37
+ }[];
38
+ }, {
39
+ items: {
40
+ id: string;
41
+ kind: "text" | "url" | "image" | "file";
42
+ text?: string | null | undefined;
43
+ url?: string | null | undefined;
44
+ mimeType?: string | null | undefined;
45
+ name?: string | null | undefined;
46
+ size?: number | null | undefined;
47
+ }[];
48
+ }>;
49
+ export type SharePayload = z.infer<typeof sharePayloadSchema>;
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@sosweetham/tauri-plugin-sharehub-api",
3
+ "version": "0.1.0",
4
+ "description": "Share content to and from Tauri 2 apps through the native OS share UI: share out (text/files) and receive shares in (text, links, images, files) as a share target. iOS, Android, macOS, Windows.",
5
+ "license": "MIT",
6
+ "author": "SoSweetHam <sosweetham@gmail.com>",
7
+ "type": "module",
8
+ "types": "./dist-js/index.d.ts",
9
+ "main": "./dist-js/index.cjs",
10
+ "module": "./dist-js/index.js",
11
+ "exports": {
12
+ "types": "./dist-js/index.d.ts",
13
+ "import": "./dist-js/index.js",
14
+ "require": "./dist-js/index.cjs"
15
+ },
16
+ "files": [
17
+ "dist-js",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/sosweetham/tauri-plugin-sharehub.git"
24
+ },
25
+ "homepage": "https://github.com/sosweetham/tauri-plugin-sharehub#readme",
26
+ "bugs": {
27
+ "url": "https://github.com/sosweetham/tauri-plugin-sharehub/issues"
28
+ },
29
+ "keywords": [
30
+ "tauri",
31
+ "tauri-plugin",
32
+ "share",
33
+ "share-target",
34
+ "share-sheet",
35
+ "ios",
36
+ "android",
37
+ "macos",
38
+ "windows"
39
+ ],
40
+ "engines": {
41
+ "node": ">=18"
42
+ },
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "dependencies": {
47
+ "@tauri-apps/api": ">=2.0.0",
48
+ "zod": "^3.23.0"
49
+ },
50
+ "devDependencies": {
51
+ "@rollup/plugin-typescript": "^11.1.6",
52
+ "json-schema-to-zod": "^2.6.0",
53
+ "rollup": "^4.9.6",
54
+ "tslib": "^2.6.2",
55
+ "typescript": "^5.3.3"
56
+ },
57
+ "scripts": {
58
+ "build": "rollup -c",
59
+ "check-types": "tsc --noEmit",
60
+ "generate-types": "node scripts/gen-zod.mjs"
61
+ }
62
+ }