@flighthq/host-tauri 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.
Files changed (64) hide show
  1. package/dist/index.d.ts +13 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +13 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/tauriApp.d.ts +4 -0
  6. package/dist/tauriApp.d.ts.map +1 -0
  7. package/dist/tauriApp.js +159 -0
  8. package/dist/tauriApp.js.map +1 -0
  9. package/dist/tauriClipboard.d.ts +4 -0
  10. package/dist/tauriClipboard.d.ts.map +1 -0
  11. package/dist/tauriClipboard.js +108 -0
  12. package/dist/tauriClipboard.js.map +1 -0
  13. package/dist/tauriDialog.d.ts +4 -0
  14. package/dist/tauriDialog.d.ts.map +1 -0
  15. package/dist/tauriDialog.js +83 -0
  16. package/dist/tauriDialog.js.map +1 -0
  17. package/dist/tauriMenu.d.ts +4 -0
  18. package/dist/tauriMenu.d.ts.map +1 -0
  19. package/dist/tauriMenu.js +68 -0
  20. package/dist/tauriMenu.js.map +1 -0
  21. package/dist/tauriModule.d.ts +213 -0
  22. package/dist/tauriModule.d.ts.map +1 -0
  23. package/dist/tauriModule.js +25 -0
  24. package/dist/tauriModule.js.map +1 -0
  25. package/dist/tauriNotification.d.ts +4 -0
  26. package/dist/tauriNotification.d.ts.map +1 -0
  27. package/dist/tauriNotification.js +102 -0
  28. package/dist/tauriNotification.js.map +1 -0
  29. package/dist/tauriPlatform.d.ts +4 -0
  30. package/dist/tauriPlatform.d.ts.map +1 -0
  31. package/dist/tauriPlatform.js +33 -0
  32. package/dist/tauriPlatform.js.map +1 -0
  33. package/dist/tauriRegister.d.ts +3 -0
  34. package/dist/tauriRegister.d.ts.map +1 -0
  35. package/dist/tauriRegister.js +44 -0
  36. package/dist/tauriRegister.js.map +1 -0
  37. package/dist/tauriShell.d.ts +4 -0
  38. package/dist/tauriShell.d.ts.map +1 -0
  39. package/dist/tauriShell.js +67 -0
  40. package/dist/tauriShell.js.map +1 -0
  41. package/dist/tauriShortcut.d.ts +4 -0
  42. package/dist/tauriShortcut.d.ts.map +1 -0
  43. package/dist/tauriShortcut.js +53 -0
  44. package/dist/tauriShortcut.js.map +1 -0
  45. package/dist/tauriTray.d.ts +4 -0
  46. package/dist/tauriTray.d.ts.map +1 -0
  47. package/dist/tauriTray.js +167 -0
  48. package/dist/tauriTray.js.map +1 -0
  49. package/dist/tauriWindow.d.ts +4 -0
  50. package/dist/tauriWindow.d.ts.map +1 -0
  51. package/dist/tauriWindow.js +166 -0
  52. package/dist/tauriWindow.js.map +1 -0
  53. package/package.json +48 -0
  54. package/src/tauriApp.test.ts +80 -0
  55. package/src/tauriClipboard.test.ts +69 -0
  56. package/src/tauriDialog.test.ts +83 -0
  57. package/src/tauriMenu.test.ts +129 -0
  58. package/src/tauriNotification.test.ts +59 -0
  59. package/src/tauriPlatform.test.ts +56 -0
  60. package/src/tauriRegister.test.ts +67 -0
  61. package/src/tauriShell.test.ts +63 -0
  62. package/src/tauriShortcut.test.ts +77 -0
  63. package/src/tauriTray.test.ts +123 -0
  64. package/src/tauriWindow.test.ts +144 -0
@@ -0,0 +1,167 @@
1
+ // Maps Flight's TrayBackend onto Tauri's `@tauri-apps/api/tray`. Tauri's `TrayIcon.new` is async, while
2
+ // TrayBackend.create is synchronous and must return a numeric id now — so create allocates the id
3
+ // immediately, records it, and adopts the resolved `TrayIcon` handle when the async build settles;
4
+ // method calls before the handle lands (setIcon/setTitle/…) queue nothing and simply no-op on the not-
5
+ // yet-ready record, matching the "no-op when absent" pattern. Click/double-click are delivered through
6
+ // the Tauri tray `action` callback and forwarded as TrayEventData to the single subscribed listener.
7
+ // Tauri exposes no Windows balloon, no tray bounds, and no template/pressed-icon distinction here, so
8
+ // those methods report the contract sentinels (null / no-op). Icons cross as string paths.
9
+ export function createTauriTrayBackend(tauri) {
10
+ const trayModule = tauri.tray;
11
+ const menuModule = tauri.menu;
12
+ const trays = new Map();
13
+ let nextId = 0;
14
+ // The single tray event listener, owned by this backend and set via subscribe.
15
+ let eventListener = null;
16
+ const emit = (id, type) => {
17
+ eventListener?.({
18
+ altKey: false,
19
+ bounds: null,
20
+ ctrlKey: false,
21
+ dropFiles: null,
22
+ dropText: null,
23
+ id,
24
+ metaKey: false,
25
+ position: null,
26
+ shiftKey: false,
27
+ type,
28
+ });
29
+ };
30
+ return {
31
+ create(options) {
32
+ const id = nextId++;
33
+ const record = { icon: null, title: options.title ?? '', tooltip: options.tooltip ?? '' };
34
+ trays.set(id, record);
35
+ trayModule.TrayIcon.new({
36
+ icon: options.icon,
37
+ title: options.title,
38
+ tooltip: options.tooltip,
39
+ action: (event) => {
40
+ const type = toTrayEventType(event);
41
+ if (type !== null)
42
+ emit(id, type);
43
+ },
44
+ })
45
+ .then((icon) => {
46
+ record.icon = icon;
47
+ })
48
+ .catch(() => {
49
+ /* tray creation failed — the record stays iconless and its methods no-op */
50
+ });
51
+ return id;
52
+ },
53
+ destroy(id) {
54
+ const record = trays.get(id);
55
+ if (!record)
56
+ return;
57
+ record.icon?.close().catch(() => { });
58
+ trays.delete(id);
59
+ },
60
+ displayBalloon() {
61
+ // Tauri has no Windows balloon API; no-op.
62
+ },
63
+ removeBalloon() {
64
+ // No balloon to remove.
65
+ },
66
+ getBounds() {
67
+ // Tauri does not report tray icon bounds; null sentinel.
68
+ return null;
69
+ },
70
+ getCapabilities() {
71
+ return { balloon: false, bounds: false, clickEvents: true, dropFiles: false, pressedIcon: false, title: true };
72
+ },
73
+ getTitle(id) {
74
+ return trays.get(id)?.title ?? '';
75
+ },
76
+ getTooltip(id) {
77
+ return trays.get(id)?.tooltip ?? '';
78
+ },
79
+ isDestroyed(id) {
80
+ return !trays.has(id);
81
+ },
82
+ listIds() {
83
+ return [...trays.keys()];
84
+ },
85
+ popUpContextMenu(id) {
86
+ // Tauri shows the tray menu on click via setMenu; it has no imperative popup here. No-op.
87
+ void id;
88
+ },
89
+ setContextMenu(id, items) {
90
+ const record = trays.get(id);
91
+ if (!record)
92
+ return;
93
+ void (async () => {
94
+ const built = await buildTrayItems(menuModule, items);
95
+ const menu = await menuModule.Menu.new({ items: built });
96
+ await record.icon?.setMenu(menu);
97
+ })().catch(() => { });
98
+ },
99
+ setIcon(id, icon) {
100
+ trays
101
+ .get(id)
102
+ ?.icon?.setIcon(icon)
103
+ .catch(() => { });
104
+ },
105
+ setIgnoreDoubleClickEvents() {
106
+ // Tauri has no double-click-ignore toggle; no-op.
107
+ },
108
+ setPressedIcon() {
109
+ // Tauri has no distinct pressed-state icon; no-op.
110
+ },
111
+ setTemplate() {
112
+ // Template-ness is set on the icon at creation in Tauri, not toggled here; no-op.
113
+ },
114
+ setTitle(id, title) {
115
+ const record = trays.get(id);
116
+ if (!record)
117
+ return;
118
+ record.title = title;
119
+ record.icon?.setTitle(title).catch(() => { });
120
+ },
121
+ setTooltip(id, tooltip) {
122
+ const record = trays.get(id);
123
+ if (!record)
124
+ return;
125
+ record.tooltip = tooltip;
126
+ record.icon?.setTooltip(tooltip).catch(() => { });
127
+ },
128
+ subscribe(listener) {
129
+ eventListener = listener;
130
+ return () => {
131
+ if (eventListener === listener)
132
+ eventListener = null;
133
+ };
134
+ },
135
+ };
136
+ }
137
+ // Maps a Tauri tray pointer event onto Flight's TrayEventType, or null for events with no Flight
138
+ // equivalent (enter/leave/move). A right-button click reports 'rightClick'.
139
+ function toTrayEventType(event) {
140
+ if (event.type === 'DoubleClick')
141
+ return 'doubleClick';
142
+ if (event.type === 'Click')
143
+ return event.button === 'Right' ? 'rightClick' : 'click';
144
+ return null;
145
+ }
146
+ // Builds Tauri tray-menu item handles from Flight templates. Tray-menu selection is delivered through
147
+ // the menu backend's listener, so these items carry no action callback (mirroring the electron seam).
148
+ async function buildTrayItems(menuModule, items) {
149
+ const built = [];
150
+ for (const item of items) {
151
+ if (item.type === 'separator') {
152
+ built.push(await menuModule.PredefinedMenuItem.new({ item: 'Separator' }));
153
+ }
154
+ else if (item.submenu) {
155
+ built.push(await menuModule.Submenu.new({
156
+ text: item.label,
157
+ enabled: item.enabled,
158
+ items: await buildTrayItems(menuModule, item.submenu),
159
+ }));
160
+ }
161
+ else {
162
+ built.push(await menuModule.MenuItem.new({ id: item.id, text: item.label, enabled: item.enabled }));
163
+ }
164
+ }
165
+ return built;
166
+ }
167
+ //# sourceMappingURL=tauriTray.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tauriTray.js","sourceRoot":"","sources":["../src/tauriTray.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { WindowBackend } from '@flighthq/types';
2
+ import type { TauriApi } from './tauriModule';
3
+ export declare function createTauriWindowBackend(tauri: TauriApi): WindowBackend;
4
+ //# sourceMappingURL=tauriWindow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tauriWindow.d.ts","sourceRoot":"","sources":["../src/tauriWindow.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAqB,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAExE,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,eAAe,CAAC;AAU3D,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,QAAQ,GAAG,aAAa,CAgJvE"}
@@ -0,0 +1,166 @@
1
+ import { emitSignal } from '@flighthq/signals';
2
+ // Maps Flight's WindowBackend onto Tauri's `@tauri-apps/api/window`. Every Tauri window call is async
3
+ // while WindowBackend's commands are synchronous (void), so the adapter fires each call and forgets,
4
+ // swallowing rejections at the seam. `open` adopts the webview's current OS window (`getCurrentWindow`),
5
+ // applies the WindowOptions, and wires Tauri's onMoved/onResized/onFocusChanged/onCloseRequested events
6
+ // back onto the entity + its signals — the same pattern the electron seam uses for user-driven state
7
+ // changes. `getBounds` cannot read Tauri's async position/size synchronously, so it reports the entity's
8
+ // mirrored fields. Scope: this is the single current window (the browser-page-window analogue); creating
9
+ // additional OS windows is a `WebviewWindow`-label concern left to the host and not modeled here.
10
+ export function createTauriWindowBackend(tauri) {
11
+ const windowModule = tauri.window;
12
+ const windows = new WeakMap();
13
+ const run = (win, fn) => {
14
+ const w = windows.get(win);
15
+ if (w === undefined)
16
+ return;
17
+ fn(w).catch(() => {
18
+ /* window closed or the call is unsupported on this platform */
19
+ });
20
+ };
21
+ return {
22
+ open(win, options) {
23
+ const w = windowModule.getCurrentWindow();
24
+ windows.set(win, w);
25
+ if (options.title !== undefined)
26
+ w.setTitle(options.title).catch(() => { });
27
+ if (options.width !== undefined && options.height !== undefined) {
28
+ w.setSize(new windowModule.LogicalSize(options.width, options.height)).catch(() => { });
29
+ }
30
+ if (options.x !== undefined && options.y !== undefined) {
31
+ w.setPosition(new windowModule.LogicalPosition(options.x, options.y)).catch(() => { });
32
+ }
33
+ if (options.resizable !== undefined)
34
+ w.setResizable(options.resizable).catch(() => { });
35
+ if (options.alwaysOnTop !== undefined)
36
+ w.setAlwaysOnTop(options.alwaysOnTop).catch(() => { });
37
+ if (options.fullscreen !== undefined)
38
+ w.setFullscreen(options.fullscreen).catch(() => { });
39
+ if (options.minWidth !== undefined && options.minHeight !== undefined) {
40
+ w.setMinSize(new windowModule.LogicalSize(options.minWidth, options.minHeight)).catch(() => { });
41
+ }
42
+ if (options.maxWidth !== undefined && options.maxWidth >= 0 && options.maxHeight !== undefined) {
43
+ w.setMaxSize(new windowModule.LogicalSize(options.maxWidth, options.maxHeight)).catch(() => { });
44
+ }
45
+ if (options.center)
46
+ w.center().catch(() => { });
47
+ if (options.maximized)
48
+ w.maximize().catch(() => { });
49
+ if (options.minimized)
50
+ w.minimize().catch(() => { });
51
+ if (options.visible === false)
52
+ w.hide().catch(() => { });
53
+ else
54
+ w.show().catch(() => { });
55
+ w.onMoved((event) => {
56
+ win.x = event.payload.x;
57
+ win.y = event.payload.y;
58
+ emitSignal(win.onMove);
59
+ }).catch(() => { });
60
+ w.onResized((event) => {
61
+ win.width = event.payload.width;
62
+ win.height = event.payload.height;
63
+ emitSignal(win.onResize);
64
+ }).catch(() => { });
65
+ w.onFocusChanged((event) => {
66
+ win.focused = event.payload;
67
+ emitSignal(event.payload ? win.onFocusIn : win.onFocusOut);
68
+ }).catch(() => { });
69
+ w.onCloseRequested(() => emitSignal(win.onClose)).catch(() => { });
70
+ return true;
71
+ },
72
+ close(win) {
73
+ const w = windows.get(win);
74
+ if (w === undefined)
75
+ return;
76
+ w.close().catch(() => { });
77
+ windows.delete(win);
78
+ },
79
+ setTitle(win, title) {
80
+ run(win, (w) => w.setTitle(title));
81
+ },
82
+ setPosition(win, x, y) {
83
+ run(win, (w) => w.setPosition(new windowModule.LogicalPosition(x, y)));
84
+ },
85
+ setSize(win, width, height) {
86
+ run(win, (w) => w.setSize(new windowModule.LogicalSize(width, height)));
87
+ },
88
+ getBounds(win, out) {
89
+ // Tauri's position/size are async; report the entity's mirrored bounds rather than block.
90
+ out.x = win.x;
91
+ out.y = win.y;
92
+ out.width = win.width;
93
+ out.height = win.height;
94
+ return out;
95
+ },
96
+ minimize(win) {
97
+ run(win, (w) => w.minimize());
98
+ },
99
+ maximize(win) {
100
+ run(win, (w) => w.maximize());
101
+ },
102
+ restore(win) {
103
+ run(win, (w) => w.unmaximize());
104
+ },
105
+ focus(win) {
106
+ run(win, (w) => w.setFocus());
107
+ },
108
+ show(win) {
109
+ run(win, (w) => w.show());
110
+ },
111
+ hide(win) {
112
+ run(win, (w) => w.hide());
113
+ },
114
+ center(win) {
115
+ run(win, (w) => w.center());
116
+ },
117
+ setResizable(win, resizable) {
118
+ run(win, (w) => w.setResizable(resizable));
119
+ },
120
+ setAlwaysOnTop(win, alwaysOnTop) {
121
+ run(win, (w) => w.setAlwaysOnTop(alwaysOnTop));
122
+ },
123
+ setMinimumSize(win, width, height) {
124
+ run(win, (w) => w.setMinSize(new windowModule.LogicalSize(width, height)));
125
+ },
126
+ setMaximumSize(win, width, height) {
127
+ run(win, (w) => w.setMaxSize(new windowModule.LogicalSize(width, height)));
128
+ },
129
+ setFullscreen(win, fullscreen) {
130
+ run(win, (w) => w.setFullscreen(fullscreen));
131
+ },
132
+ setIcon(win, icon) {
133
+ run(win, (w) => w.setIcon(icon));
134
+ },
135
+ setOpacity() {
136
+ // Tauri's window API exposes no opacity control; no-op.
137
+ },
138
+ setSkipTaskbar(win, skip) {
139
+ run(win, (w) => w.setSkipTaskbar(skip));
140
+ },
141
+ setMenuBarVisible() {
142
+ // Tauri menus are not a per-window menu bar toggled here; no-op.
143
+ },
144
+ setParent() {
145
+ // Parenting an already-created window is not modeled through the current-window seam; no-op.
146
+ },
147
+ setProgress() {
148
+ // Taskbar progress is available via Tauri's setProgressBar but not modeled here; no-op.
149
+ },
150
+ requestAttention(win, attention) {
151
+ // Tauri's requestUserAttention takes a UserAttentionType (1 = Critical) or null to cancel.
152
+ run(win, (w) => w.requestUserAttention(attention ? 1 : null));
153
+ },
154
+ setContentProtection(win, enabled) {
155
+ run(win, (w) => w.setContentProtected(enabled));
156
+ },
157
+ flashWindowFrame(win) {
158
+ // Map a one-shot frame flash to an informational (2) attention request.
159
+ run(win, (w) => w.requestUserAttention(2));
160
+ },
161
+ setHasShadow(win, hasShadow) {
162
+ run(win, (w) => w.setShadow(hasShadow));
163
+ },
164
+ };
165
+ }
166
+ //# sourceMappingURL=tauriWindow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tauriWindow.js","sourceRoot":"","sources":["../src/tauriWindow.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@flighthq/host-tauri",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist",
15
+ "src/**/*.test.ts",
16
+ "!dist/**/*.test.js",
17
+ "!dist/**/*.test.d.ts",
18
+ "!dist/**/*.test.js.map",
19
+ "!dist/**/*.test.d.ts.map"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsc -b",
23
+ "clean": "tsc -b --clean",
24
+ "test": "vitest run --config vitest.config.ts",
25
+ "test:watch": "vitest --watch --config vitest.config.ts",
26
+ "prepack": "npm run clean && npm run clean:dist && npm run build",
27
+ "clean:dist": "tsx ../../scripts/clean-package-dist.ts"
28
+ },
29
+ "dependencies": {
30
+ "@flighthq/app": "0.1.0",
31
+ "@flighthq/application": "0.1.0",
32
+ "@flighthq/clipboard": "0.1.0",
33
+ "@flighthq/dialog": "0.1.0",
34
+ "@flighthq/menu": "0.1.0",
35
+ "@flighthq/notification": "0.1.0",
36
+ "@flighthq/platform": "0.1.0",
37
+ "@flighthq/shell": "0.1.0",
38
+ "@flighthq/shortcut": "0.1.0",
39
+ "@flighthq/signals": "0.1.0",
40
+ "@flighthq/tray": "0.1.0",
41
+ "@flighthq/types": "0.1.0"
42
+ },
43
+ "devDependencies": {
44
+ "typescript": "^5.3.0"
45
+ },
46
+ "description": "Tauri (v2) host backend: registers Tauri JS API implementations of Flight's window/app/dialog/clipboard/menu/tray/shortcut/platform/notification/shell seams over an injected TauriApi",
47
+ "sideEffects": false
48
+ }
@@ -0,0 +1,80 @@
1
+ import { createTauriAppBackend } from './tauriApp';
2
+ import type { TauriApi } from './tauriModule';
3
+
4
+ function fakeTauri() {
5
+ const calls: string[] = [];
6
+ const tauri = {
7
+ app: {
8
+ async getName() {
9
+ return 'FlightApp';
10
+ },
11
+ async getVersion() {
12
+ return '2.3.4';
13
+ },
14
+ async hide() {
15
+ calls.push('hide');
16
+ },
17
+ async show() {
18
+ calls.push('show');
19
+ },
20
+ },
21
+ os: {
22
+ arch: () => 'x86_64',
23
+ locale: () => 'fr-FR',
24
+ platform: () => 'linux',
25
+ version: () => '',
26
+ },
27
+ process: {
28
+ async exit() {
29
+ calls.push('exit');
30
+ },
31
+ async relaunch() {
32
+ calls.push('relaunch');
33
+ },
34
+ },
35
+ } as unknown as TauriApi;
36
+ return { tauri, calls };
37
+ }
38
+
39
+ describe('createTauriAppBackend', () => {
40
+ it('serves name and version from the prefetch cache once it resolves', async () => {
41
+ const backend = createTauriAppBackend(fakeTauri().tauri);
42
+ // The sync getters read '' until the construction-time prefetch settles.
43
+ expect(backend.getName()).toBe('');
44
+ await Promise.resolve();
45
+ await Promise.resolve();
46
+ expect(backend.getName()).toBe('FlightApp');
47
+ expect(backend.getVersion()).toBe('2.3.4');
48
+ });
49
+
50
+ it('sources locale from the os plugin', () => {
51
+ const backend = createTauriAppBackend(fakeTauri().tauri);
52
+ expect(backend.getLocale()).toBe('fr-FR');
53
+ expect(backend.getSystemLocale()).toBe('fr-FR');
54
+ expect(backend.getPreferredSystemLanguages()).toEqual(['fr-FR']);
55
+ });
56
+
57
+ it('fires the process/app control methods', () => {
58
+ const { tauri, calls } = fakeTauri();
59
+ const backend = createTauriAppBackend(tauri);
60
+ backend.quit();
61
+ backend.relaunch();
62
+ expect(backend.hideApp()).toBe(true);
63
+ expect(backend.showApp()).toBe(true);
64
+ expect(calls).toContain('exit');
65
+ expect(calls).toContain('relaunch');
66
+ expect(calls).toContain('hide');
67
+ expect(calls).toContain('show');
68
+ });
69
+
70
+ it('reports sentinels for the unmapped surface', () => {
71
+ const backend = createTauriAppBackend(fakeTauri().tauri);
72
+ expect(backend.bounceDock()).toBe(-1);
73
+ expect(backend.setBadgeCount(3)).toBe(false);
74
+ expect(backend.setName('X')).toBe(false);
75
+ expect(backend.getAppPath()).toBe('');
76
+ expect(backend.getCommandLine()).toEqual([]);
77
+ expect(backend.isAppHidden()).toBe(false);
78
+ expect(typeof backend.subscribeReady(() => {})).toBe('function');
79
+ });
80
+ });
@@ -0,0 +1,69 @@
1
+ import { createTauriClipboardBackend } from './tauriClipboard';
2
+ import type { TauriApi } from './tauriModule';
3
+
4
+ function fakeTauri() {
5
+ const store = { text: '' };
6
+ const calls: string[] = [];
7
+ const tauri = {
8
+ clipboard: {
9
+ async readText() {
10
+ calls.push('readText');
11
+ return store.text;
12
+ },
13
+ async writeText(text: string) {
14
+ calls.push('writeText');
15
+ store.text = text;
16
+ },
17
+ async clear() {
18
+ calls.push('clear');
19
+ store.text = '';
20
+ },
21
+ },
22
+ } as unknown as TauriApi;
23
+ return { tauri, store, calls };
24
+ }
25
+
26
+ describe('createTauriClipboardBackend', () => {
27
+ it('round-trips text through the Tauri clipboard', async () => {
28
+ const { tauri, calls } = fakeTauri();
29
+ const backend = createTauriClipboardBackend(tauri);
30
+ expect(await backend.writeText('hi')).toBe(true);
31
+ expect(await backend.readText()).toBe('hi');
32
+ expect(await backend.hasText()).toBe(true);
33
+ expect(calls).toContain('writeText');
34
+ expect(calls).toContain('readText');
35
+ });
36
+
37
+ it('clears via the Tauri clipboard', async () => {
38
+ const { tauri, calls } = fakeTauri();
39
+ const backend = createTauriClipboardBackend(tauri);
40
+ await backend.writeText('x');
41
+ expect(await backend.clear()).toBe(true);
42
+ expect(calls).toContain('clear');
43
+ expect(await backend.hasText()).toBe(false);
44
+ });
45
+
46
+ it('reports sentinels for unsupported flavors', async () => {
47
+ const backend = createTauriClipboardBackend(fakeTauri().tauri);
48
+ expect(await backend.readHtml()).toBe('');
49
+ expect(await backend.writeHtml('<b/>')).toBe(false);
50
+ expect(await backend.readImage()).toBe('');
51
+ expect(await backend.readBookmark()).toBeNull();
52
+ expect(await backend.getFormats()).toEqual([]);
53
+ expect(await backend.readFiles()).toEqual([]);
54
+ expect(backend.getChangeCount()).toBe(-1);
55
+ });
56
+
57
+ it('resolves sentinels when the clipboard read throws', async () => {
58
+ const tauri = {
59
+ clipboard: {
60
+ async readText() {
61
+ throw new Error('denied');
62
+ },
63
+ },
64
+ } as unknown as TauriApi;
65
+ const backend = createTauriClipboardBackend(tauri);
66
+ expect(await backend.readText()).toBe('');
67
+ expect(await backend.hasText()).toBe(false);
68
+ });
69
+ });
@@ -0,0 +1,83 @@
1
+ import { createTauriDialogBackend } from './tauriDialog';
2
+ import type { TauriApi, TauriDialogOpenOptions } from './tauriModule';
3
+
4
+ interface DialogCalls {
5
+ open: TauriDialogOpenOptions[];
6
+ confirm: { message: string; kind?: string }[];
7
+ message: { message: string; kind?: string }[];
8
+ }
9
+
10
+ function fakeTauri(openResult: string | string[] | null, saveResult: string | null) {
11
+ const calls: DialogCalls = { open: [], confirm: [], message: [] };
12
+ const tauri = {
13
+ dialog: {
14
+ async open(options: TauriDialogOpenOptions) {
15
+ calls.open.push(options);
16
+ return openResult;
17
+ },
18
+ async save() {
19
+ return saveResult;
20
+ },
21
+ async message(message: string, options?: { kind?: string }) {
22
+ calls.message.push({ message, kind: options?.kind });
23
+ },
24
+ async confirm(message: string, options?: { kind?: string }) {
25
+ calls.confirm.push({ message, kind: options?.kind });
26
+ return true;
27
+ },
28
+ async ask() {
29
+ return true;
30
+ },
31
+ },
32
+ } as unknown as TauriApi;
33
+ return { tauri, calls };
34
+ }
35
+
36
+ describe('createTauriDialogBackend', () => {
37
+ it('maps openFile results to populated file handles', async () => {
38
+ const { tauri, calls } = fakeTauri('/home/u/a.txt', null);
39
+ const backend = createTauriDialogBackend(tauri);
40
+ const handles = await backend.openFile({ title: 'Open', filters: [{ name: 'Text', extensions: ['txt'] }] });
41
+ expect(handles).toHaveLength(1);
42
+ expect(handles[0]).toEqual({ kind: 'File', name: 'a.txt', path: '/home/u/a.txt' });
43
+ expect(calls.open[0].directory).toBeUndefined();
44
+ expect(calls.open[0].filters).toEqual([{ name: 'Text', extensions: ['txt'] }]);
45
+ });
46
+
47
+ it('normalizes a multi-select array and cancel (null) result', async () => {
48
+ const multi = await createTauriDialogBackend(fakeTauri(['/a', '/b'], null).tauri).openFile({ multiple: true });
49
+ expect(multi.map((h) => h.path)).toEqual(['/a', '/b']);
50
+ const cancelled = await createTauriDialogBackend(fakeTauri(null, null).tauri).openFile({});
51
+ expect(cancelled).toEqual([]);
52
+ });
53
+
54
+ it('opens directories with the directory flag and Directory kind', async () => {
55
+ const { tauri, calls } = fakeTauri('/home/u/dir', null);
56
+ const handles = await backend(tauri).openDirectory({});
57
+ expect(calls.open[0].directory).toBe(true);
58
+ expect(handles[0].kind).toBe('Directory');
59
+ });
60
+
61
+ it('maps saveFile to a single handle or null', async () => {
62
+ expect((await backend(fakeTauri(null, '/out.png').tauri).saveFile({}))?.path).toBe('/out.png');
63
+ expect(await backend(fakeTauri(null, null).tauri).saveFile({})).toBeNull();
64
+ });
65
+
66
+ it('maps message to an acknowledgement result and confirm to a boolean', async () => {
67
+ const { tauri, calls } = fakeTauri(null, null);
68
+ const result = await backend(tauri).message({ message: 'Hi', kind: 'question' });
69
+ expect(result).toEqual({ buttonIndex: 0, cancelled: false, checkboxChecked: false });
70
+ // 'question' has no Tauri glyph and falls back to 'info'.
71
+ expect(calls.message[0].kind).toBe('info');
72
+ expect(await backend(tauri).confirm({ message: 'Sure?', kind: 'warning' })).toBe(true);
73
+ expect(calls.confirm[0].kind).toBe('warning');
74
+ });
75
+
76
+ it('reports the null sentinel for prompt (no Tauri text dialog)', async () => {
77
+ expect(await backend(fakeTauri(null, null).tauri).prompt({ message: 'Name?' })).toBeNull();
78
+ });
79
+ });
80
+
81
+ function backend(tauri: TauriApi) {
82
+ return createTauriDialogBackend(tauri);
83
+ }