@deeprespond/im-tauri 0.0.0-stage → 1.0.0-alpha

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.
@@ -0,0 +1,32 @@
1
+ import * as _deeprespond_im_web_platform from '@deeprespond/im-web/platform';
2
+ import * as _deeprespond_im_web from '@deeprespond/im-web';
3
+ export * from '@deeprespond/im-web';
4
+ import { B as Bridge, e as DRTauriClient } from './types-CVlFmFSn.js';
5
+ export { a as DREngine, b as DRTauriEngineClient, D as DRTauriEngineOptions, f as DesktopEventMethods, c as DesktopEvents, g as DesktopInfo, d as DesktopModule, h as DesktopNotification, i as DownloadParams, j as DownloadTask, E as EngineAlert, k as EngineState, N as NotificationClickEvent } from './types-CVlFmFSn.js';
6
+
7
+ /** 本包的版本,必须与 Rust 插件 tauri-plugin-deeprespond-im 相同(《2.功能概述文档》第 13 节)。 */
8
+ declare const TAURI_SDK_VERSION: string;
9
+
10
+ /**
11
+ * 创建界面窗口的客户端(《3.接口设计》第 4 节):对 App 来说就是 Web SDK 的 DRClient,另有 desktop 和 engineState。
12
+ * 只需要 AppKey:服务端地址等由引擎决定;读本地存储由插件的只读连接直接返回,其他调用都转给引擎。
13
+ */
14
+ declare function createDRTauriClient(options: {
15
+ appKey: string;
16
+ logger?: {
17
+ level?: 'error' | 'warn' | 'info' | 'debug';
18
+ sink?: (entry: _deeprespond_im_web.LogEntry) => void;
19
+ };
20
+ }, env?: {
21
+ bridge: Bridge;
22
+ overrides?: Partial<_deeprespond_im_web_platform.PlatformAdapter>;
23
+ }): DRTauriClient;
24
+ /**
25
+ * 接收拖入本窗口的文件(《3.接口设计》第 6 节):插件在 Rust 中登记路径后才交给窗口,只交回可读的普通文件。
26
+ * 返回取消监听的函数。
27
+ */
28
+ declare function onFilesDropped(handler: (paths: string[]) => void, env?: {
29
+ bridge: Bridge;
30
+ }): () => void;
31
+
32
+ export { DRTauriClient, TAURI_SDK_VERSION, createDRTauriClient, onFilesDropped };
package/dist/index.js ADDED
@@ -0,0 +1,93 @@
1
+ import { currentLabel, unsupported, ENGINE_LABEL, Bridge, makeLogger, Desktop, createTauriPlatform, osFromUserAgent, clickEvent, invalidState } from './chunk-DMEEI2YZ.js';
2
+ export { TAURI_SDK_VERSION } from './chunk-DMEEI2YZ.js';
3
+ import { DRError, createDRClient } from '@deeprespond/im-web';
4
+ export * from '@deeprespond/im-web';
5
+
6
+ function createDRTauriClient(options, env) {
7
+ const label = env ? env.bridge.label : currentLabel();
8
+ if (label === null) throw unsupported("not_tauri", "createDRTauriClient 只能在 Tauri 应用的窗口中调用");
9
+ if (label === ENGINE_LABEL) throw unsupported("not_ui_window", "引擎页中用 createDRTauriEngine");
10
+ if (!options || typeof options.appKey !== "string" || !/^[^#\s]+#[^#\s]+$/.test(options.appKey)) {
11
+ throw new DRError({ code: "local_validation", message: "appKey", details: { reason: "invalid_format", field: "appKey" }, source: "local" });
12
+ }
13
+ const bridge = env?.bridge ?? Bridge.get();
14
+ const logger = makeLogger(bridge, options.logger);
15
+ const warn = (message, err) => logger.sink({ level: "warn", time: Date.now(), module: "tauri", message, code: err instanceof DRError ? err.code : void 0, reason: err instanceof DRError ? err.reason : void 0 });
16
+ let client;
17
+ let engineState = "starting";
18
+ let bridgeError = null;
19
+ const desktop = new Desktop(bridge, () => client);
20
+ const tp = createTauriPlatform({
21
+ role: "follower",
22
+ appKey: options.appKey,
23
+ bridge,
24
+ onWarn: warn,
25
+ extend: () => ({
26
+ desktop: { value: desktop, enumerable: true },
27
+ engineState: { get: () => engineState, enumerable: true }
28
+ })
29
+ });
30
+ if (env?.overrides) Object.assign(tp.platform, env.overrides);
31
+ tp.platform.fatalError = () => bridgeError ? { code: bridgeError.code, details: bridgeError.details } : null;
32
+ tp.platform.leaderUnavailable = () => {
33
+ if (bridgeError) return { code: bridgeError.code, details: bridgeError.details };
34
+ if (bridge.info?.another_instance) return { code: "invalid_state", details: { reason: "another_instance" } };
35
+ return { code: "engine_unavailable", details: { reason: engineState === "ready" ? "starting" : engineState } };
36
+ };
37
+ client = createDRClient({
38
+ appKey: options.appKey,
39
+ // 界面窗口不直接访问服务端(全部转给引擎),只为满足选项的格式
40
+ apiUrl: "http://engine.invalid",
41
+ platform: osFromUserAgent(typeof navigator !== "undefined" ? navigator.userAgent : ""),
42
+ logger,
43
+ platformAdapter: tp.platform
44
+ });
45
+ const host = tp.host();
46
+ const setState = (state, appKeys) => {
47
+ const next = state === "ready" && Array.isArray(appKeys) && !appKeys.includes(options.appKey) ? "starting" : state;
48
+ if (next === engineState) return;
49
+ engineState = next;
50
+ tp.setEngineState(next);
51
+ if (next === "restarting" || next === "failed") host.forgetLeader();
52
+ if (next === "restarting" || next === "failed") void client.channels.current?.leave().catch(() => {
53
+ });
54
+ host.emit("engine.stateChanged", { state: next });
55
+ };
56
+ let eventSeen = false;
57
+ void bridge.ready.then(
58
+ (info) => {
59
+ if (!eventSeen) setState(info.engine_state, info.app_keys);
60
+ if (info.another_instance) setState("failed", []);
61
+ if (info.launch_click) host.emit("desktop.notificationClicked", clickEvent(bridge, { t: "notification_click", ...info.launch_click, launched: true }));
62
+ },
63
+ (err) => {
64
+ bridgeError = err instanceof DRError ? err : invalidState("plugin");
65
+ warn("与插件的连接失败", err);
66
+ setState("failed", []);
67
+ }
68
+ );
69
+ bridge.on("engine", (event) => {
70
+ eventSeen = true;
71
+ setState(event.state, event.app_keys);
72
+ });
73
+ bridge.on("notification_click", (event) => host.emit("desktop.notificationClicked", clickEvent(bridge, event)));
74
+ bridge.on("exit", () => {
75
+ void client.channels.current?.leave().catch(() => {
76
+ });
77
+ });
78
+ bridge.on("resume", () => {
79
+ void client.channels.current?.refresh().catch(() => {
80
+ });
81
+ });
82
+ return client;
83
+ }
84
+ function onFilesDropped(handler, env) {
85
+ if (!env && currentLabel() === null) throw unsupported("not_tauri", "onFilesDropped 只能在 Tauri 应用的窗口中调用");
86
+ return (env?.bridge ?? Bridge.get()).on("files_dropped", (event) => {
87
+ if (Array.isArray(event.paths)) handler(event.paths.filter((p) => typeof p === "string"));
88
+ });
89
+ }
90
+
91
+ export { createDRTauriClient, onFilesDropped };
92
+ //# sourceMappingURL=index.js.map
93
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { DRError, createDRClient, type DRClient } from '@deeprespond/im-web';\nimport { createTauriPlatform, osFromUserAgent } from './adapter';\nimport { Bridge, ENGINE_LABEL, currentLabel, type EngineState } from './adapter/ipc';\nimport { clickEvent, invalidState, makeLogger, unsupported } from './common';\nimport { Desktop } from './desktop/module';\nimport type { DRTauriClient } from './types';\n\nexport * from '@deeprespond/im-web';\nexport type { DesktopModule, DesktopNotification, DownloadParams, DownloadTask, DesktopInfo, DRTauriClient, DRTauriEngineClient, DesktopEventMethods, EngineState, NotificationClickEvent, DesktopEvents, EngineAlert, DRTauriEngineOptions, DREngine } from './types';\nexport { TAURI_SDK_VERSION } from './version';\n\n/**\n * 创建界面窗口的客户端(《3.接口设计》第 4 节):对 App 来说就是 Web SDK 的 DRClient,另有 desktop 和 engineState。\n * 只需要 AppKey:服务端地址等由引擎决定;读本地存储由插件的只读连接直接返回,其他调用都转给引擎。\n */\nexport function createDRTauriClient(options: { appKey: string; logger?: { level?: 'error' | 'warn' | 'info' | 'debug'; sink?: (entry: import('@deeprespond/im-web').LogEntry) => void } }, env?: { bridge: Bridge; overrides?: Partial<import('@deeprespond/im-web/platform').PlatformAdapter> }): DRTauriClient {\n const label = env ? env.bridge.label : currentLabel();\n if (label === null) throw unsupported('not_tauri', 'createDRTauriClient 只能在 Tauri 应用的窗口中调用');\n if (label === ENGINE_LABEL) throw unsupported('not_ui_window', '引擎页中用 createDRTauriEngine');\n if (!options || typeof options.appKey !== 'string' || !/^[^#\\s]+#[^#\\s]+$/.test(options.appKey)) {\n throw new DRError({ code: 'local_validation', message: 'appKey', details: { reason: 'invalid_format', field: 'appKey' }, source: 'local' });\n }\n const bridge = env?.bridge ?? Bridge.get();\n const logger = makeLogger(bridge, options.logger);\n const warn = (message: string, err?: unknown) =>\n logger.sink({ level: 'warn', time: Date.now(), module: 'tauri', message, code: err instanceof DRError ? err.code : undefined, reason: err instanceof DRError ? err.reason : undefined });\n let client!: DRClient;\n let engineState: EngineState = 'starting';\n let bridgeError: DRError | null = null;\n const desktop = new Desktop(bridge, () => client);\n const tp = createTauriPlatform({\n role: 'follower',\n appKey: options.appKey,\n bridge,\n onWarn: warn,\n extend: () => ({\n desktop: { value: desktop, enumerable: true },\n engineState: { get: () => engineState, enumerable: true },\n }),\n });\n if (env?.overrides) Object.assign(tp.platform, env.overrides);\n // 界面窗口不等插件的初始化(登录状态要同步可用),版本不同等致命错误在之后的调用中报告\n tp.platform.fatalError = () => (bridgeError ? { code: bridgeError.code, details: bridgeError.details } : null);\n // 转发的调用在引擎不可用时的错误:版本不同、另一个进程、引擎启动中或重建中(《3.接口设计》第 7 节)\n tp.platform.leaderUnavailable = () => {\n if (bridgeError) return { code: bridgeError.code, details: bridgeError.details };\n if (bridge.info?.another_instance) return { code: 'invalid_state', details: { reason: 'another_instance' } };\n return { code: 'engine_unavailable', details: { reason: engineState === 'ready' ? 'starting' : engineState } };\n };\n client = createDRClient({\n appKey: options.appKey,\n // 界面窗口不直接访问服务端(全部转给引擎),只为满足选项的格式\n apiUrl: 'http://engine.invalid',\n platform: osFromUserAgent(typeof navigator !== 'undefined' ? navigator.userAgent : ''),\n logger,\n platformAdapter: tp.platform,\n });\n const host = tp.host();\n const setState = (state: EngineState, appKeys: unknown) => {\n // 引擎就绪但还没有创建这个 AppKey 的客户端:仍为 starting\n const next: EngineState = state === 'ready' && Array.isArray(appKeys) && !appKeys.includes(options.appKey) ? 'starting' : state;\n if (next === engineState) return;\n engineState = next;\n tp.setEngineState(next);\n if (next === 'restarting' || next === 'failed') host.forgetLeader();\n if (next === 'restarting' || next === 'failed') void client.channels.current?.leave().catch(() => {});\n host.emit('engine.stateChanged', { state: next });\n };\n // hello 的结果是那一刻的快照:之后经 Channel 到达的 engine 事件更新(事件可能先于 hello 的结果到达,不能被旧快照覆盖)\n let eventSeen = false;\n void bridge.ready.then(\n (info) => {\n if (!eventSeen) setState(info.engine_state, info.app_keys);\n if (info.another_instance) setState('failed', []);\n if (info.launch_click) host.emit('desktop.notificationClicked', clickEvent(bridge, { t: 'notification_click', ...info.launch_click, launched: true }));\n },\n (err) => {\n bridgeError = err instanceof DRError ? err : invalidState('plugin');\n warn('与插件的连接失败', err);\n setState('failed', []);\n },\n );\n bridge.on('engine', (event) => {\n eventSeen = true;\n setState(event.state as EngineState, event.app_keys);\n });\n bridge.on('notification_click', (event) => host.emit('desktop.notificationClicked', clickEvent(bridge, event)));\n bridge.on('exit', () => { void client.channels.current?.leave().catch(() => {}); });\n bridge.on('resume', () => { void client.channels.current?.refresh().catch(() => {}); });\n return client as unknown as DRTauriClient;\n}\n\n/**\n * 接收拖入本窗口的文件(《3.接口设计》第 6 节):插件在 Rust 中登记路径后才交给窗口,只交回可读的普通文件。\n * 返回取消监听的函数。\n */\nexport function onFilesDropped(handler: (paths: string[]) => void, env?: { bridge: Bridge }): () => void {\n if (!env && currentLabel() === null) throw unsupported('not_tauri', 'onFilesDropped 只能在 Tauri 应用的窗口中调用');\n return (env?.bridge ?? Bridge.get()).on('files_dropped', (event) => {\n if (Array.isArray(event.paths)) handler(event.paths.filter((p): p is string => typeof p === 'string'));\n });\n}\n"]}
@@ -0,0 +1,277 @@
1
+ import { DRClient, DREvents, MessageView, NotifyDecision, CallHandle, TicketContext, DRClientOptions } from '@deeprespond/im-web';
2
+
3
+ /** hello 的结果:本页面在插件中的身份、系统信息和引擎状态。 */
4
+ interface HelloInfo {
5
+ instance: number;
6
+ label: string;
7
+ engine: boolean;
8
+ plugin_version: string;
9
+ protocol: number;
10
+ os: 'windows' | 'macos' | 'linux';
11
+ os_version: string;
12
+ arch: string;
13
+ computer_name: string;
14
+ webview_version: string;
15
+ device_id: string;
16
+ data_dir: string;
17
+ temp_dir: string;
18
+ another_instance: boolean;
19
+ engine_state: EngineState$1;
20
+ app_keys: string[];
21
+ launch_click: Record<string, unknown> | null;
22
+ main_window: string;
23
+ log_file?: boolean;
24
+ window: {
25
+ visible: boolean;
26
+ focused: boolean;
27
+ minimized: boolean;
28
+ screen_locked: boolean;
29
+ } | null;
30
+ }
31
+ type EngineState$1 = 'starting' | 'ready' | 'restarting' | 'failed';
32
+ /** 插件经 Channel 交来的事件(《4.实现与测试》第 4 节)。 */
33
+ interface PluginEvent {
34
+ t: string;
35
+ [key: string]: unknown;
36
+ }
37
+ type Listener = (event: PluginEvent) => void;
38
+ /** 调用插件的方式(测试中换成假的插件)。 */
39
+ interface Transport {
40
+ invoke<T>(cmd: string, args?: unknown, options?: {
41
+ headers: Record<string, string>;
42
+ }): Promise<T>;
43
+ /** 建立事件通道:返回交给 hello 的参数值。 */
44
+ channel(onEvent: (event: PluginEvent) => void): unknown;
45
+ }
46
+ /**
47
+ * 与插件的连接(每个页面一个):第一个命令 hello 交来 Channel,插件之后的事件都经它按顺序到达。
48
+ * 版本检查:JavaScript 包与插件的版本、协议不同的以 unsupported(version_mismatch)拒绝(《2.功能概述文档》第 13 节)。
49
+ */
50
+ declare class Bridge {
51
+ #private;
52
+ static get(): Bridge;
53
+ /** 测试用:指定本页面的连接(假的插件)。 */
54
+ static use(bridge: Bridge | null): void;
55
+ readonly label: string;
56
+ readonly isEngine: boolean;
57
+ readonly ready: Promise<HelloInfo>;
58
+ constructor(label: string, transport?: Transport);
59
+ /** hello 之后的信息;之前为 null。 */
60
+ get info(): HelloInfo | null;
61
+ /** 调用插件命令(等 hello 完成)。 */
62
+ call<T>(command: string, args?: Record<string, unknown>): Promise<T>;
63
+ /** 原始字节的请求体(Blob 落盘),参数放在请求头中。 */
64
+ callRaw<T>(command: string, body: Uint8Array, headers: Record<string, string>): Promise<T>;
65
+ on(type: string, listener: Listener): () => void;
66
+ }
67
+
68
+ /** 桌面模块(《3.接口设计》第 5 节):界面窗口的客户端和引擎(engine.client)都有。 */
69
+ interface DesktopModule {
70
+ /** 显示系统通知(manual 模式下使用;auto 模式下也可以显示 App 自己的通知)。id 不能以 dr: 开头。 */
71
+ notify(n: DesktopNotification): Promise<void>;
72
+ clearNotifications(p?: {
73
+ conversation_id?: string;
74
+ }): Promise<void>;
75
+ /** macOS 为系统授权;Windows 读取用户或组策略是否关闭了本应用的通知;Linux 总是 granted。 */
76
+ notificationPermission(): Promise<'granted' | 'denied' | 'default'>;
77
+ requestNotificationPermission(): Promise<'granted' | 'denied'>;
78
+ /** badge 为 manual 时使用;null 清除。 */
79
+ setBadge(count: number | null): Promise<void>;
80
+ /** critical:Windows 持续闪烁、macOS 持续跳动直到获得焦点。 */
81
+ requestAttention(p?: {
82
+ critical?: boolean;
83
+ }): Promise<void>;
84
+ /** 托盘图标闪烁的辅助:trayId 为 App 创建托盘时的 ID,icons 为应用资源中的图标路径;null 停止。 */
85
+ trayBlink(p: {
86
+ trayId: string;
87
+ icons: [string, string];
88
+ intervalMs?: number;
89
+ } | null): Promise<void>;
90
+ /** 系统的打开对话框;返回的路径登记为允许读取,取消时为空数组。 */
91
+ pickFiles(p?: {
92
+ multiple?: boolean;
93
+ filters?: Array<{
94
+ name: string;
95
+ extensions: string[];
96
+ }>;
97
+ }): Promise<string[]>;
98
+ /** 系统的保存对话框,返回的路径登记为允许写入。 */
99
+ pickSavePath(p?: {
100
+ defaultName?: string;
101
+ }): Promise<string | null>;
102
+ /** 允许读取的路径的预览地址(drim-file 协议),用于 <img>、<video>。 */
103
+ fileUrl(path: string): string;
104
+ download(source: string | {
105
+ message: MessageView;
106
+ }, p?: DownloadParams): DownloadTask;
107
+ /** 可执行和脚本类文件以 permission_denied(executable_file)失败。 */
108
+ openPath(path: string): Promise<void>;
109
+ revealPath(path: string): Promise<void>;
110
+ /** 返回压缩包的路径。 */
111
+ exportDiagnostics(p?: {
112
+ saveTo?: string;
113
+ }): Promise<string>;
114
+ readonly info: DesktopInfo;
115
+ }
116
+ interface DesktopNotification {
117
+ /** 同一 id 替换旧通知;SDK 自动显示的通知以 dr: 开头,App 的 id 不能以 dr: 开头。 */
118
+ id?: string;
119
+ title: string;
120
+ body: string;
121
+ /** 点击时随事件交回。 */
122
+ conversation_id?: string;
123
+ seq?: number;
124
+ /** 点击时原样交回,最多 1 KB。 */
125
+ data?: Record<string, string>;
126
+ sound?: boolean;
127
+ /** 最多 2 个;Linux 视通知服务而定。 */
128
+ actions?: Array<{
129
+ id: string;
130
+ title: string;
131
+ }>;
132
+ }
133
+ interface DownloadParams {
134
+ /** 目录或完整路径;默认系统的“下载”目录。 */
135
+ saveTo?: string;
136
+ /** 默认 rename。 */
137
+ conflict?: 'rename' | 'overwrite';
138
+ onProgress?: (loaded: number, total: number | null) => void;
139
+ signal?: AbortSignal;
140
+ }
141
+ interface DownloadTask extends Promise<{
142
+ path: string;
143
+ size: number;
144
+ }> {
145
+ cancel(): void;
146
+ }
147
+ interface DesktopInfo {
148
+ os: 'windows' | 'macos' | 'linux';
149
+ os_version: string;
150
+ webview_version: string;
151
+ sdk_version: string;
152
+ /** 同 calls.supported。 */
153
+ calls_supported: boolean;
154
+ }
155
+ type EngineState = 'starting' | 'ready' | 'restarting' | 'failed';
156
+ /** 带桌面事件类型的 on、once:桌面事件的载荷有确切的类型,其他事件同 Web SDK。 */
157
+ interface DesktopEventMethods {
158
+ on<E extends keyof DesktopEvents>(event: E, listener: (payload: DesktopEvents[E]) => void): () => void;
159
+ on<E extends keyof DREvents & string>(event: E, listener: (payload: DREvents[E]) => void): () => void;
160
+ once<E extends keyof DesktopEvents>(event: E, listener: (payload: DesktopEvents[E]) => void): () => void;
161
+ once<E extends keyof DREvents & string>(event: E, listener: (payload: DREvents[E]) => void): () => void;
162
+ }
163
+ /** 界面窗口的客户端(《3.接口设计》第 4 节)。 */
164
+ interface DRTauriClient extends DRClient, DesktopEventMethods {
165
+ readonly desktop: DesktopModule;
166
+ readonly engineState: EngineState;
167
+ on: DesktopEventMethods['on'];
168
+ once: DesktopEventMethods['once'];
169
+ }
170
+ /** 引擎客户端(引擎页中的 engine.client)。 */
171
+ interface DRTauriEngineClient extends DRClient, DesktopEventMethods {
172
+ readonly desktop: DesktopModule;
173
+ on: DesktopEventMethods['on'];
174
+ once: DesktopEventMethods['once'];
175
+ }
176
+ /** 通知或通知上的按钮被点击(desktop.notificationClicked)。 */
177
+ interface NotificationClickEvent {
178
+ id: string;
179
+ conversation_id?: string;
180
+ seq?: number;
181
+ kind: 'message' | 'notice' | 'call' | 'session' | 'custom';
182
+ action?: string;
183
+ data?: Record<string, string>;
184
+ /** 应用是被这次点击启动的。 */
185
+ launched: boolean;
186
+ /** 先认领的窗口调用,表示已处理(插件不再调用 Rust 的处理函数,也不显示主窗口)。 */
187
+ claim(): void;
188
+ }
189
+ /** 桌面相关的事件(《3.接口设计》第 8 节),在 client.on 中使用。 */
190
+ interface DesktopEvents {
191
+ 'engine.stateChanged': {
192
+ state: EngineState;
193
+ };
194
+ 'desktop.notificationClicked': NotificationClickEvent;
195
+ 'desktop.unreadChanged': {
196
+ total: number;
197
+ badge: number;
198
+ };
199
+ 'desktop.systemResumed': {
200
+ slept_ms: number;
201
+ };
202
+ 'desktop.networkChanged': {
203
+ online: boolean;
204
+ };
205
+ 'window.roleChanged': {
206
+ isLeader: boolean;
207
+ isAlertTab: boolean;
208
+ };
209
+ }
210
+ /** 引擎提醒(auto 模式的通知文字、manual 模式交给 App 的提醒)。 */
211
+ type EngineAlert = {
212
+ kind: 'message';
213
+ message: MessageView;
214
+ notify: NotifyDecision;
215
+ } | {
216
+ kind: 'notice';
217
+ type: 'friend_request' | 'group_request';
218
+ title: string;
219
+ body: string;
220
+ user?: string;
221
+ group_id?: string;
222
+ };
223
+ interface DRTauriEngineOptions {
224
+ appKey: string;
225
+ apiUrl: string;
226
+ wsUrl?: string;
227
+ /** 默认取计算机名。 */
228
+ deviceName?: string;
229
+ appVersion?: string;
230
+ ticketProvider?: (ctx: TicketContext) => Promise<string>;
231
+ /** 需要插件的 sqlcipher 特性。 */
232
+ encryptDatabase?: boolean;
233
+ autoMarkRead?: boolean;
234
+ conversationWindow?: number;
235
+ userCacheTtlMs?: number;
236
+ /** 默认 48、209715200。 */
237
+ outbox?: {
238
+ maxAgeHours?: number;
239
+ persistAttachmentMaxBytes?: number;
240
+ };
241
+ /** 上传前的处理:图片的压缩参数,或 App 自己的处理(路径文件只在设置了 preprocess 时才读入内存交给它)。 */
242
+ media?: DRClientOptions['media'];
243
+ notifications?: {
244
+ /** 默认 auto(《2.功能概述文档》第 8.2 节)。 */
245
+ mode?: 'auto' | 'manual';
246
+ /** 默认 true。 */
247
+ sound?: boolean;
248
+ /** 好友申请、入群申请等是否提醒,默认 true。 */
249
+ notices?: boolean;
250
+ /** 没有界面窗口时的来电,默认 auto。 */
251
+ calls?: 'auto' | 'manual';
252
+ /** 来电铃声的资源路径(引擎页可以访问的地址),默认用 SDK 内置的。 */
253
+ ringtone?: string;
254
+ /** 自定义 auto 模式通知的文字(如其他语言);返回 null 表示这条不提醒。 */
255
+ format?: (alert: EngineAlert) => {
256
+ title: string;
257
+ body: string;
258
+ } | null;
259
+ };
260
+ /** 默认 auto。 */
261
+ badge?: 'auto' | 'manual';
262
+ /** 默认 false。 */
263
+ badgeIncludesMuted?: boolean;
264
+ /** 默认 true。 */
265
+ attention?: boolean;
266
+ logger?: DRClientOptions['logger'];
267
+ }
268
+ interface DREngine {
269
+ /** 引擎客户端,引擎页中可以直接使用。 */
270
+ readonly client: DRTauriEngineClient;
271
+ /** manual 模式下处理提醒(只在负责提醒的是引擎时调用)。 */
272
+ onAlert(handler: (alert: EngineAlert) => void | Promise<void>): () => void;
273
+ /** calls 为 manual 时处理没有界面窗口时的来电。 */
274
+ onIncomingCall(handler: (call: CallHandle) => void | Promise<void>): () => void;
275
+ }
276
+
277
+ export { Bridge as B, type DRTauriEngineOptions as D, type EngineAlert as E, type NotificationClickEvent as N, type DREngine as a, type DRTauriEngineClient as b, type DesktopEvents as c, type DesktopModule as d, type DRTauriClient as e, type DesktopEventMethods as f, type DesktopInfo as g, type DesktopNotification as h, type DownloadParams as i, type DownloadTask as j, type EngineState as k };
package/package.json CHANGED
@@ -1,6 +1,54 @@
1
1
  {
2
2
  "name": "@deeprespond/im-tauri",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0-alpha",
4
+ "description": "深应智能 IM 的 Tauri SDK:Web SDK 的核心加 Rust 插件(tauri-plugin-deeprespond-im)",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ },
16
+ "./engine": {
17
+ "types": "./dist/engine.d.ts",
18
+ "import": "./dist/engine.js"
19
+ },
20
+ "./templates/engine.html": "./src/templates/engine.html"
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "src/templates"
25
+ ],
26
+ "scripts": {
27
+ "build": "tsup",
28
+ "typecheck": "tsc -p tsconfig.json --noEmit",
29
+ "test": "vitest run"
30
+ },
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "peerDependencies": {
35
+ "@deeprespond/im-web": "~1.0.0-alpha",
36
+ "@tauri-apps/api": "^2.12.0"
37
+ },
38
+ "devDependencies": {
39
+ "@deeprespond/im-web": "link:../web",
40
+ "@tauri-apps/api": "^2.12.1",
41
+ "@types/node": "^20.19.43",
42
+ "tsup": "^8.5.0",
43
+ "typescript": "^5.9.3",
44
+ "vitest": "^3.2.4"
45
+ },
46
+ "engines": {
47
+ "node": ">=20.19"
48
+ },
49
+ "pnpm": {
50
+ "onlyBuiltDependencies": [
51
+ "esbuild"
52
+ ]
53
+ }
54
+ }
@@ -0,0 +1,20 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>deeprespond-im-engine</title>
6
+ </head>
7
+ <body>
8
+ <!--
9
+ 引擎页模板(《sdk-tauri/3.接口设计》第 3 节):复制到前端项目,加入打包配置的入口(如 Vite 的 build.rollupOptions.input),
10
+ 在 Rust 中以 engine_url("engine.html") 指向它。引擎页不显示,只创建引擎客户端,不引入 App 的界面框架。
11
+ im-config.js 导出 { appKey, apiUrl };需要定制提醒、来电的,在这里注册 engine.onAlert、engine.onIncomingCall。
12
+ -->
13
+ <script type="module">
14
+ import { createDRTauriEngine } from '@deeprespond/im-tauri/engine';
15
+ import config from './im-config.js';
16
+
17
+ createDRTauriEngine({ ...config });
18
+ </script>
19
+ </body>
20
+ </html>