@flighthq/tray 0.5.0-next.1944.cd6b6e7 → 0.5.1-edge.638.d4dbd4e
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/README.md +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/tray.d.ts +25 -34
- package/dist/tray.d.ts.map +1 -1
- package/dist/tray.js +182 -234
- package/dist/tray.js.map +1 -1
- package/package.json +6 -4
- package/src/enableTrayGuards.test.ts +45 -45
- package/src/tray.test.ts +337 -775
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @flighthq/tray
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Capability-refined tray icon Entities over explicitly injected host facets
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -13,5 +13,5 @@ Import the supported application-facing API from `@flighthq/tray`. The `@flighth
|
|
|
13
13
|
This package is part of the locked-version Flight SDK graph. Applications may instead install and import `@flighthq/sdk` when package-level tree shaking is sufficient.
|
|
14
14
|
|
|
15
15
|
- [Flight project](https://github.com/flighthq/flight)
|
|
16
|
-
- [Source for @flighthq/tray@0.5.
|
|
17
|
-
- [License](https://github.com/flighthq/flight/blob/
|
|
16
|
+
- [Source for @flighthq/tray@0.5.1-edge.638.d4dbd4e](https://github.com/flighthq/flight/tree/d4dbd4e347dc8be10b180c85a766ab50333dcb51/packages/tray)
|
|
17
|
+
- [License](https://github.com/flighthq/flight/blob/d4dbd4e347dc8be10b180c85a766ab50333dcb51/LICENSE.md)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { disableTrayGuards, enableTrayGuards, createTrayIcon, destroyTrayIcon, displayTrayBalloon,
|
|
1
|
+
export { disableTrayGuards, enableTrayGuards, createTrayIcon, destroyTrayIcon, displayTrayBalloon, getTrayIconBounds, getTrayIconTitle, getTrayIconTooltip, getTrayIcons, isTrayDestroyed, isTrayIconAnimating, onTrayBalloonEvent, onTrayDrop, onTrayInteraction, onTrayMenuSelection, popupTrayContextMenu, removeTrayBalloon, setTrayIcon, setTrayIconContextMenu, setTrayIconTemplate, setTrayIconTitle, setTrayIconTooltip, setTrayIgnoreDoubleClickEvents, setTrayPressedIcon, startTrayIconAnimation, stopTrayIconAnimation, } from './contract';
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,UAAU,EACV,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,WAAW,EACX,sBAAsB,EACtB,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,8BAA8B,EAC9B,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { disableTrayGuards, enableTrayGuards, createTrayIcon, destroyTrayIcon, displayTrayBalloon,
|
|
1
|
+
export { disableTrayGuards, enableTrayGuards, createTrayIcon, destroyTrayIcon, displayTrayBalloon, getTrayIconBounds, getTrayIconTitle, getTrayIconTooltip, getTrayIcons, isTrayDestroyed, isTrayIconAnimating, onTrayBalloonEvent, onTrayDrop, onTrayInteraction, onTrayMenuSelection, popupTrayContextMenu, removeTrayBalloon, setTrayIcon, setTrayIconContextMenu, setTrayIconTemplate, setTrayIconTitle, setTrayIconTooltip, setTrayIgnoreDoubleClickEvents, setTrayPressedIcon, startTrayIconAnimation, stopTrayIconAnimation, } from './contract';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,UAAU,EACV,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,WAAW,EACX,sBAAsB,EACtB,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,8BAA8B,EAC9B,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
|
package/dist/tray.d.ts
CHANGED
|
@@ -1,36 +1,27 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export declare function
|
|
5
|
-
export declare function
|
|
6
|
-
export declare function
|
|
7
|
-
export declare function
|
|
8
|
-
export declare function
|
|
9
|
-
export declare function
|
|
10
|
-
export declare function
|
|
11
|
-
export declare function
|
|
12
|
-
export declare function
|
|
13
|
-
export declare function
|
|
14
|
-
export declare function
|
|
15
|
-
export declare function
|
|
16
|
-
export declare function
|
|
17
|
-
export declare function observeTrayHostResult(operation: string, succeeded: boolean): void;
|
|
18
|
-
export declare function onTrayEvent(listener: (event: Readonly<TrayEventData>) => void): () => void;
|
|
19
|
-
export declare function popupTrayContextMenu(tray: TrayIcon, position?: Readonly<Vector2Like>): void;
|
|
20
|
-
export declare function removeTrayBalloon(tray: TrayIcon): void;
|
|
21
|
-
export declare function resetTrayBackendForTest(): void;
|
|
22
|
-
/** Installs the tray guard, or clears it with `null`. The seam exists so the messages and the
|
|
23
|
-
* `@flighthq/log` dependency live in the separately-importable guard module rather than here; not
|
|
24
|
-
* importing that module costs production nothing. Called by `enableTrayGuards`, not directly. */
|
|
1
|
+
import type { HasTrayLifecycle, MenuItemTemplate, TrayAnimationStartResult, TrayAnimationStopResult, TrayBalloonDisplayResult, TrayBalloonEvent, TrayBalloonOptions, TrayBalloonRemoveResult, TrayBoundsResult, TrayCreateResult, TrayDestroyResult, TrayDoubleClickPolicyUpdateResult, TrayDropEvent, TrayEventAttachResult, TrayIcon, TrayIconForHost, TrayIconOptions, TrayIconSource, TrayImageUpdateResult, TrayInteractionEvent, TrayMenuSelectionEvent, TrayMenuUpdateResult, TrayPopupMenuResult, TrayPressedImageUpdateResult, TrayTemplateImageUpdateResult, TrayTitleReadResult, TrayTitleUpdateResult, TrayTooltipReadResult, TrayTooltipUpdateResult, TrayWithBalloon, TrayWithBalloonEvents, TrayWithBounds, TrayWithDoubleClickPolicy, TrayWithDropEvents, TrayWithImage, TrayWithInteractionEvents, TrayWithMenu, TrayWithMenuSelectionEvents, TrayWithPopupMenu, TrayWithPressedImage, TrayWithTemplateImage, TrayWithTitle, TrayWithTooltip, Vector2Like } from '@flighthq/types/contract';
|
|
2
|
+
export declare function createTrayIcon<HostType extends HasTrayLifecycle>(host: HostType, options?: Readonly<TrayIconOptions>): Promise<TrayCreateResult<TrayIconForHost<HostType>>>;
|
|
3
|
+
export declare function destroyTrayIcon(tray: TrayIcon): Promise<TrayDestroyResult>;
|
|
4
|
+
export declare function displayTrayBalloon(tray: TrayWithBalloon, options: Readonly<TrayBalloonOptions>): Promise<TrayBalloonDisplayResult>;
|
|
5
|
+
export declare function getTrayIconBounds(tray: TrayWithBounds): Promise<TrayBoundsResult>;
|
|
6
|
+
export declare function getTrayIcons(host: HasTrayLifecycle): readonly TrayIcon[];
|
|
7
|
+
export declare function getTrayIconTitle(tray: TrayWithTitle): Promise<TrayTitleReadResult>;
|
|
8
|
+
export declare function getTrayIconTooltip(tray: TrayWithTooltip): Promise<TrayTooltipReadResult>;
|
|
9
|
+
export declare function isTrayDestroyed(tray: Readonly<TrayIcon>): boolean;
|
|
10
|
+
export declare function isTrayIconAnimating(tray: Readonly<TrayIcon>): boolean;
|
|
11
|
+
export declare function onTrayBalloonEvent(tray: TrayWithBalloonEvents, listener: (event: Readonly<TrayBalloonEvent>) => void): TrayEventAttachResult;
|
|
12
|
+
export declare function onTrayDrop(tray: TrayWithDropEvents, listener: (event: Readonly<TrayDropEvent>) => void): TrayEventAttachResult;
|
|
13
|
+
export declare function onTrayInteraction(tray: TrayWithInteractionEvents, listener: (event: Readonly<TrayInteractionEvent>) => void): TrayEventAttachResult;
|
|
14
|
+
export declare function onTrayMenuSelection(tray: TrayWithMenuSelectionEvents, listener: (event: Readonly<TrayMenuSelectionEvent>) => void): TrayEventAttachResult;
|
|
15
|
+
export declare function popupTrayContextMenu(tray: TrayWithPopupMenu, position?: Readonly<Vector2Like>): Promise<TrayPopupMenuResult>;
|
|
16
|
+
export declare function removeTrayBalloon(tray: TrayWithBalloon): Promise<TrayBalloonRemoveResult>;
|
|
25
17
|
export declare function setTrayAnimationGuard(guard: ((tray: TrayIcon, frameCount: number, intervalMs: number) => void) | null): void;
|
|
26
|
-
export declare function
|
|
27
|
-
export declare function
|
|
28
|
-
export declare function
|
|
29
|
-
export declare function
|
|
30
|
-
export declare function
|
|
31
|
-
export declare function
|
|
32
|
-
export declare function
|
|
33
|
-
export declare function
|
|
34
|
-
export declare function
|
|
35
|
-
export declare function stopTrayIconAnimation(tray: TrayIcon): void;
|
|
18
|
+
export declare function setTrayIcon(tray: TrayWithImage, icon: TrayIconSource): Promise<TrayImageUpdateResult>;
|
|
19
|
+
export declare function setTrayIconContextMenu(tray: TrayWithMenu, items: readonly MenuItemTemplate[]): Promise<TrayMenuUpdateResult>;
|
|
20
|
+
export declare function setTrayIconTemplate(tray: TrayWithTemplateImage, isTemplate: boolean): Promise<TrayTemplateImageUpdateResult>;
|
|
21
|
+
export declare function setTrayIconTitle(tray: TrayWithTitle, title: string): Promise<TrayTitleUpdateResult>;
|
|
22
|
+
export declare function setTrayIconTooltip(tray: TrayWithTooltip, tooltip: string): Promise<TrayTooltipUpdateResult>;
|
|
23
|
+
export declare function setTrayIgnoreDoubleClickEvents(tray: TrayWithDoubleClickPolicy, ignore: boolean): Promise<TrayDoubleClickPolicyUpdateResult>;
|
|
24
|
+
export declare function setTrayPressedIcon(tray: TrayWithPressedImage, icon: TrayIconSource): Promise<TrayPressedImageUpdateResult>;
|
|
25
|
+
export declare function startTrayIconAnimation(tray: TrayWithImage, frames: readonly TrayIconSource[], intervalMs: number): Promise<TrayAnimationStartResult>;
|
|
26
|
+
export declare function stopTrayIconAnimation(tray: TrayIcon): TrayAnimationStopResult;
|
|
36
27
|
//# sourceMappingURL=tray.d.ts.map
|
package/dist/tray.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tray.d.ts","sourceRoot":"","sources":["../src/tray.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tray.d.ts","sourceRoot":"","sources":["../src/tray.ts"],"names":[],"mappings":"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"}
|
package/dist/tray.js
CHANGED
|
@@ -1,283 +1,231 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
pressedIcon: false,
|
|
10
|
-
title: false,
|
|
11
|
-
};
|
|
12
|
-
// Creates a tray icon, or null when the host has no system tray (e.g. web). The backend returns -1 to
|
|
13
|
-
// signal no tray; this translates that to a null sentinel for the caller.
|
|
14
|
-
export function createTrayIcon(options) {
|
|
15
|
-
const id = getTrayBackend().create(options ?? {});
|
|
16
|
-
return id < 0 ? null : { id };
|
|
17
|
-
}
|
|
18
|
-
// Destroys a tray icon and frees its host resource. No-op when the host has no tray.
|
|
19
|
-
// Any icon animation running on this tray is stopped first: the interval writes through setTrayIcon,
|
|
20
|
-
// and left running it would keep calling into a resource the host has freed — forever, since the
|
|
21
|
-
// timer holds its own closure alive. Destroying the thing being animated is the clearest possible
|
|
22
|
-
// signal that the animation is over, so the caller does not have to have kept the stop function.
|
|
23
|
-
export function destroyTrayIcon(tray) {
|
|
24
|
-
stopTrayIconAnimation(tray);
|
|
25
|
-
getTrayBackend().destroy(tray.id);
|
|
26
|
-
}
|
|
27
|
-
// Displays a Windows balloon notification from the tray icon. No-op on macOS/Linux and on web.
|
|
28
|
-
// Balloon lifecycle events (balloonShow/balloonClick/balloonClose) are emitted via onTrayEvent.
|
|
29
|
-
export function displayTrayBalloon(tray, options) {
|
|
30
|
-
getTrayBackend().displayBalloon(tray.id, options);
|
|
31
|
-
}
|
|
32
|
-
export function explainTrayBackend() {
|
|
33
|
-
if (_custom !== null) {
|
|
34
|
-
return { conflict: _hostConflict, layer: 'custom', operation: null, viability: 'unobserved' };
|
|
1
|
+
import { createEntity, createEntityRuntime } from '@flighthq/entity/contract';
|
|
2
|
+
import { connectSignal, disconnectSignal } from '@flighthq/signals/contract';
|
|
3
|
+
import { EntityRuntimeKey } from '@flighthq/types/contract';
|
|
4
|
+
export async function createTrayIcon(host, options = {}) {
|
|
5
|
+
const tray = createEntity();
|
|
6
|
+
let result;
|
|
7
|
+
try {
|
|
8
|
+
result = await host.tray.lifecycle.create(tray, options);
|
|
35
9
|
}
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
conflict: _hostConflict,
|
|
39
|
-
layer: 'host',
|
|
40
|
-
operation: _hostObservation !== null ? _hostObservation.operation : null,
|
|
41
|
-
viability: _hostObservation !== null ? _hostObservation.viability : 'unobserved',
|
|
42
|
-
};
|
|
10
|
+
catch (error) {
|
|
11
|
+
return { error, outcome: 'tray-create-failed' };
|
|
43
12
|
}
|
|
44
|
-
|
|
13
|
+
if (result.outcome !== 'created')
|
|
14
|
+
return result;
|
|
15
|
+
const runtime = createEntityRuntime();
|
|
16
|
+
runtime.animationGeneration = 0;
|
|
17
|
+
runtime.animationTimer = null;
|
|
18
|
+
runtime.animationWriteTail = Promise.resolve();
|
|
19
|
+
runtime.capabilities = { ...host.tray };
|
|
20
|
+
runtime.destroyPromise = null;
|
|
21
|
+
runtime.lifecycle = host.tray.lifecycle;
|
|
22
|
+
runtime.releases = new Set();
|
|
23
|
+
runtime.state = 'active';
|
|
24
|
+
tray[EntityRuntimeKey] = runtime;
|
|
25
|
+
return { outcome: 'created', tray };
|
|
45
26
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
27
|
+
export function destroyTrayIcon(tray) {
|
|
28
|
+
const runtime = getTrayRuntime(tray);
|
|
29
|
+
if (runtime === null || runtime.state === 'destroyed')
|
|
30
|
+
return Promise.resolve({ outcome: 'already-destroyed' });
|
|
31
|
+
if (runtime.destroyPromise !== null)
|
|
32
|
+
return runtime.destroyPromise;
|
|
33
|
+
runtime.destroyPromise = destroyTrayRuntime(tray, runtime).finally(() => {
|
|
34
|
+
runtime.destroyPromise = null;
|
|
35
|
+
});
|
|
36
|
+
return runtime.destroyPromise;
|
|
49
37
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
// capabilities.bounds before getTrayIconBounds. On web all flags are false.
|
|
53
|
-
export function getTrayCapabilities() {
|
|
54
|
-
return getTrayBackend().getCapabilities();
|
|
38
|
+
export function displayTrayBalloon(tray, options) {
|
|
39
|
+
return invokeUpdate(tray, 'balloon', 'balloon-display-failed', (backend) => backend.display(tray, options));
|
|
55
40
|
}
|
|
56
|
-
// Returns the screen bounds of the tray icon (x/y/width/height), or null when the platform does not
|
|
57
|
-
// expose icon geometry (Linux/AppIndicator, web). Use for anchoring popovers or windows to the icon.
|
|
58
41
|
export function getTrayIconBounds(tray) {
|
|
59
|
-
return
|
|
42
|
+
return invokeRead(tray, 'bounds', 'bounds-read-failed', (backend) => backend.get(tray));
|
|
43
|
+
}
|
|
44
|
+
async function destroyTrayRuntime(tray, runtime) {
|
|
45
|
+
runtime.state = 'destroying';
|
|
46
|
+
stopTrayAnimationRuntime(runtime);
|
|
47
|
+
for (const release of [...runtime.releases])
|
|
48
|
+
await release.release();
|
|
49
|
+
let result;
|
|
50
|
+
try {
|
|
51
|
+
result = await runtime.lifecycle.destroy(tray);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
result = { failures: [{ error, step: 'native-resource' }], outcome: 'tray-destroy-failed' };
|
|
55
|
+
}
|
|
56
|
+
if (result.outcome === 'destroyed') {
|
|
57
|
+
runtime.state = 'destroyed';
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
runtime.state = 'partially-destroyed';
|
|
61
|
+
return result;
|
|
60
62
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return getTrayBackend()
|
|
64
|
-
.listIds()
|
|
65
|
-
.map((id) => ({ id }));
|
|
63
|
+
export function getTrayIcons(host) {
|
|
64
|
+
return host.tray.lifecycle.list();
|
|
66
65
|
}
|
|
67
|
-
// Returns the current title text of a tray icon, or an empty string when unavailable (web, non-macOS).
|
|
68
66
|
export function getTrayIconTitle(tray) {
|
|
69
|
-
return
|
|
67
|
+
return invokeRead(tray, 'title', 'title-read-failed', (backend) => backend.get(tray));
|
|
70
68
|
}
|
|
71
|
-
// Returns the current hover tooltip text of a tray icon, or an empty string when unavailable (web).
|
|
72
69
|
export function getTrayIconTooltip(tray) {
|
|
73
|
-
return
|
|
74
|
-
}
|
|
75
|
-
export function installTrayHostBackend(backend) {
|
|
76
|
-
if (_host !== null) {
|
|
77
|
-
if (_host !== backend)
|
|
78
|
-
_hostConflict = true;
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
_host = backend;
|
|
70
|
+
return invokeRead(tray, 'tooltip', 'tooltip-read-failed', (backend) => backend.get(tray));
|
|
82
71
|
}
|
|
83
|
-
// Returns whether a tray icon has been destroyed. Returns true on web (no trays exist).
|
|
84
|
-
// Use this to guard calls after destroyTrayIcon when the tray lifecycle is unclear.
|
|
85
72
|
export function isTrayDestroyed(tray) {
|
|
86
|
-
|
|
73
|
+
const runtime = getTrayRuntime(tray);
|
|
74
|
+
return runtime === null || runtime.state !== 'active' || runtime.lifecycle.isDestroyed(tray);
|
|
87
75
|
}
|
|
88
|
-
// True while an icon animation started by startTrayIconAnimation is running on this tray. False once
|
|
89
|
-
// it is stopped, replaced by a later start, or the tray is destroyed.
|
|
90
76
|
export function isTrayIconAnimating(tray) {
|
|
91
|
-
return
|
|
77
|
+
return getTrayRuntime(tray)?.animationTimer !== null;
|
|
92
78
|
}
|
|
93
|
-
export function
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
79
|
+
export function onTrayBalloonEvent(tray, listener) {
|
|
80
|
+
return attachTrayEvent(tray, 'balloonEvents', listener);
|
|
81
|
+
}
|
|
82
|
+
export function onTrayDrop(tray, listener) {
|
|
83
|
+
return attachTrayEvent(tray, 'dropEvents', listener);
|
|
84
|
+
}
|
|
85
|
+
export function onTrayInteraction(tray, listener) {
|
|
86
|
+
return attachTrayEvent(tray, 'interactionEvents', listener);
|
|
98
87
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
// never fires (no tray); a native host is required.
|
|
102
|
-
export function onTrayEvent(listener) {
|
|
103
|
-
return getTrayBackend().subscribe(listener);
|
|
88
|
+
export function onTrayMenuSelection(tray, listener) {
|
|
89
|
+
return attachTrayEvent(tray, 'menuSelectionEvents', listener);
|
|
104
90
|
}
|
|
105
|
-
// Programmatically shows the attached context menu, optionally at a specific screen position.
|
|
106
|
-
// On web this is a no-op. Useful for showing the menu in response to a custom gesture or shortcut
|
|
107
|
-
// without waiting for the user to right-click the tray icon.
|
|
108
91
|
export function popupTrayContextMenu(tray, position) {
|
|
109
|
-
|
|
92
|
+
return invokeUpdate(tray, 'popupMenu', 'popup-failed', (backend) => backend.popup(tray, position));
|
|
110
93
|
}
|
|
111
|
-
// Dismisses the currently-displayed Windows balloon notification. No-op on macOS/Linux and web.
|
|
112
94
|
export function removeTrayBalloon(tray) {
|
|
113
|
-
|
|
95
|
+
return invokeUpdate(tray, 'balloon', 'balloon-remove-failed', (backend) => backend.remove(tray));
|
|
114
96
|
}
|
|
115
|
-
export function resetTrayBackendForTest() {
|
|
116
|
-
_custom = null;
|
|
117
|
-
_host = null;
|
|
118
|
-
_hostConflict = false;
|
|
119
|
-
_hostObservation = null;
|
|
120
|
-
}
|
|
121
|
-
/** Installs the tray guard, or clears it with `null`. The seam exists so the messages and the
|
|
122
|
-
* `@flighthq/log` dependency live in the separately-importable guard module rather than here; not
|
|
123
|
-
* importing that module costs production nothing. Called by `enableTrayGuards`, not directly. */
|
|
124
97
|
export function setTrayAnimationGuard(guard) {
|
|
125
98
|
_animationGuard = guard;
|
|
126
99
|
}
|
|
127
|
-
// Installs a custom tray backend; pass null to clear the custom override.
|
|
128
|
-
export function setTrayBackend(backend) {
|
|
129
|
-
_custom = backend;
|
|
130
|
-
}
|
|
131
|
-
// Sets the image for the tray icon. Accepts the same icon path/data-URI accepted by createTrayIcon.
|
|
132
|
-
// Use this for runtime status updates (indicators, spinners, theme changes).
|
|
133
100
|
export function setTrayIcon(tray, icon) {
|
|
134
|
-
|
|
101
|
+
return invokeUpdate(tray, 'image', 'image-update-failed', (backend) => backend.set(tray, icon));
|
|
135
102
|
}
|
|
136
|
-
// Sets the context menu attached to a tray icon. Shown on right-click (or popupTrayContextMenu).
|
|
137
|
-
// No-op when the host has no tray.
|
|
138
103
|
export function setTrayIconContextMenu(tray, items) {
|
|
139
|
-
|
|
104
|
+
return invokeUpdate(tray, 'menu', 'menu-install-failed', (backend) => backend.set(tray, items));
|
|
140
105
|
}
|
|
141
|
-
// Marks the tray icon as a macOS template image. Template images auto-invert for light/dark menu
|
|
142
|
-
// bars. No-op on Windows/Linux and on web. Set iconTemplate:true on TrayIconOptions at creation to
|
|
143
|
-
// combine with the initial icon, or call this after creation to update the flag dynamically.
|
|
144
106
|
export function setTrayIconTemplate(tray, isTemplate) {
|
|
145
|
-
|
|
107
|
+
return invokeUpdate(tray, 'templateImage', 'template-image-update-failed', (backend) => backend.set(tray, isTemplate));
|
|
146
108
|
}
|
|
147
|
-
// Sets the title text displayed next to the tray icon (macOS menu bar only). No-op on other platforms.
|
|
148
109
|
export function setTrayIconTitle(tray, title) {
|
|
149
|
-
|
|
110
|
+
return invokeUpdate(tray, 'title', 'title-update-failed', (backend) => backend.set(tray, title));
|
|
150
111
|
}
|
|
151
|
-
// Sets the hover tooltip for the tray icon. No-op when the host has no tray.
|
|
152
112
|
export function setTrayIconTooltip(tray, tooltip) {
|
|
153
|
-
|
|
113
|
+
return invokeUpdate(tray, 'tooltip', 'tooltip-update-failed', (backend) => backend.set(tray, tooltip));
|
|
114
|
+
}
|
|
115
|
+
function attachTrayEvent(tray, slot, listener) {
|
|
116
|
+
const runtime = getActiveTrayRuntime(tray);
|
|
117
|
+
if (runtime === null)
|
|
118
|
+
return { outcome: 'tray-destroyed' };
|
|
119
|
+
try {
|
|
120
|
+
const backend = runtime.capabilities[slot];
|
|
121
|
+
const signal = backend?.getSignal(tray) ?? null;
|
|
122
|
+
if (signal === null)
|
|
123
|
+
return { outcome: 'tray-destroyed' };
|
|
124
|
+
connectSignal(signal, listener);
|
|
125
|
+
const release = {
|
|
126
|
+
released: false,
|
|
127
|
+
async release() {
|
|
128
|
+
if (release.released)
|
|
129
|
+
return { outcome: 'already-released' };
|
|
130
|
+
try {
|
|
131
|
+
disconnectSignal(signal, listener);
|
|
132
|
+
release.released = true;
|
|
133
|
+
runtime.releases.delete(release);
|
|
134
|
+
return { outcome: 'released' };
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
return { error, outcome: 'release-failed' };
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
runtime.releases.add(release);
|
|
142
|
+
return { outcome: 'attached', release };
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
return { error, outcome: 'subscription-failed' };
|
|
146
|
+
}
|
|
154
147
|
}
|
|
155
|
-
// Sets whether the host should collapse double-click events into individual click events (macOS).
|
|
156
|
-
// No-op on Windows/Linux and on web.
|
|
157
148
|
export function setTrayIgnoreDoubleClickEvents(tray, ignore) {
|
|
158
|
-
|
|
149
|
+
return invokeUpdate(tray, 'doubleClickPolicy', 'double-click-policy-update-failed', (backend) => backend.setIgnore(tray, ignore));
|
|
159
150
|
}
|
|
160
|
-
// Sets the image shown when the tray icon is pressed (macOS only). Electron calls this
|
|
161
|
-
// setPressedImage. No-op on Windows/Linux and on web.
|
|
162
151
|
export function setTrayPressedIcon(tray, icon) {
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// returning a stop function. An empty frame list is a no-op. The tray icon is not destroyed when the
|
|
167
|
-
// animation stops, and the last frame shown stays.
|
|
168
|
-
//
|
|
169
|
-
// A tray icon has one image, so it has one animation: starting a second on the same tray replaces the
|
|
170
|
-
// first rather than running both. Without that, a perfectly ordinary sequence — swap a "syncing"
|
|
171
|
-
// animation for an "error" one — left two intervals writing to the same icon on the same tick, and
|
|
172
|
-
// the only handle to the orphaned one was a stop function the caller had no reason to still be
|
|
173
|
-
// holding. The returned function stops only the animation it started, so calling a stale one cannot
|
|
174
|
-
// cancel a newer animation.
|
|
175
|
-
//
|
|
176
|
-
// Note: interval timing is best-effort; the actual frame rate depends on the host event loop.
|
|
177
|
-
export function startTrayIconAnimation(tray, frames, intervalMs) {
|
|
152
|
+
return invokeUpdate(tray, 'pressedImage', 'pressed-image-update-failed', (backend) => backend.set(tray, icon));
|
|
153
|
+
}
|
|
154
|
+
export async function startTrayIconAnimation(tray, frames, intervalMs) {
|
|
178
155
|
if (frames.length === 0)
|
|
179
|
-
return
|
|
156
|
+
return { outcome: 'empty' };
|
|
157
|
+
const runtime = getActiveTrayRuntime(tray);
|
|
158
|
+
if (runtime === null)
|
|
159
|
+
return { outcome: 'tray-destroyed' };
|
|
180
160
|
_animationGuard?.(tray, frames.length, intervalMs);
|
|
181
|
-
|
|
161
|
+
stopTrayAnimationRuntime(runtime);
|
|
162
|
+
const generation = runtime.animationGeneration;
|
|
163
|
+
const first = await queueAnimationWrite(tray, runtime, generation, frames[0]);
|
|
164
|
+
if (first.outcome !== 'updated')
|
|
165
|
+
return first;
|
|
166
|
+
if (runtime.state !== 'active' || runtime.animationGeneration !== generation)
|
|
167
|
+
return { outcome: 'tray-destroyed' };
|
|
182
168
|
let index = 0;
|
|
183
|
-
|
|
184
|
-
const handle = setInterval(() => {
|
|
169
|
+
runtime.animationTimer = setInterval(() => {
|
|
185
170
|
index = (index + 1) % frames.length;
|
|
186
|
-
|
|
171
|
+
void queueAnimationWrite(tray, runtime, generation, frames[index]);
|
|
187
172
|
}, intervalMs);
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
173
|
+
const release = {
|
|
174
|
+
released: false,
|
|
175
|
+
async release() {
|
|
176
|
+
if (release.released)
|
|
177
|
+
return { outcome: 'already-released' };
|
|
178
|
+
release.released = true;
|
|
179
|
+
if (runtime.animationGeneration === generation)
|
|
180
|
+
stopTrayAnimationRuntime(runtime);
|
|
181
|
+
return { outcome: 'released' };
|
|
182
|
+
},
|
|
195
183
|
};
|
|
184
|
+
return { outcome: 'started', release };
|
|
185
|
+
}
|
|
186
|
+
async function queueAnimationWrite(tray, runtime, generation, frame) {
|
|
187
|
+
let result = { outcome: 'tray-destroyed' };
|
|
188
|
+
runtime.animationWriteTail = runtime.animationWriteTail.then(async () => {
|
|
189
|
+
if (runtime.state !== 'active' || runtime.animationGeneration !== generation)
|
|
190
|
+
return;
|
|
191
|
+
result = await setTrayIcon(tray, frame);
|
|
192
|
+
});
|
|
193
|
+
await runtime.animationWriteTail;
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
function stopTrayAnimationRuntime(runtime) {
|
|
197
|
+
runtime.animationGeneration++;
|
|
198
|
+
if (runtime.animationTimer !== null)
|
|
199
|
+
clearInterval(runtime.animationTimer);
|
|
200
|
+
runtime.animationTimer = null;
|
|
196
201
|
}
|
|
197
|
-
// The interval per animating tray id. Module-scoped like the backend, and correct at that scope: an
|
|
198
|
-
// id is the host's, so one id is one icon is one animation.
|
|
199
|
-
const _animations = new Map();
|
|
200
|
-
function _noopStop() { }
|
|
201
|
-
// Stops the icon animation running on this tray, if any. Idempotent, and safe on a tray that never
|
|
202
|
-
// animated. The named counterpart to startTrayIconAnimation for callers that hold the TrayIcon but
|
|
203
|
-
// not the stop function it returned — which is most of them, since the handle is what gets passed
|
|
204
|
-
// around and the closure is what gets dropped.
|
|
205
202
|
export function stopTrayIconAnimation(tray) {
|
|
206
|
-
const
|
|
207
|
-
if (
|
|
208
|
-
return;
|
|
209
|
-
|
|
210
|
-
|
|
203
|
+
const runtime = getTrayRuntime(tray);
|
|
204
|
+
if (runtime === null || runtime.animationTimer === null)
|
|
205
|
+
return { outcome: 'already-stopped' };
|
|
206
|
+
stopTrayAnimationRuntime(runtime);
|
|
207
|
+
return { outcome: 'stopped' };
|
|
211
208
|
}
|
|
212
209
|
let _animationGuard = null;
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
},
|
|
235
|
-
getTitle() {
|
|
236
|
-
// No tray on web.
|
|
237
|
-
return '';
|
|
238
|
-
},
|
|
239
|
-
getTooltip() {
|
|
240
|
-
// No tray on web.
|
|
241
|
-
return '';
|
|
242
|
-
},
|
|
243
|
-
isDestroyed() {
|
|
244
|
-
// No tray icons exist on web; treat every id as destroyed.
|
|
245
|
-
return true;
|
|
246
|
-
},
|
|
247
|
-
listIds() {
|
|
248
|
-
// No tray icons exist on web.
|
|
249
|
-
return [];
|
|
250
|
-
},
|
|
251
|
-
popUpContextMenu() {
|
|
252
|
-
// No-op: web has no context menu to pop up.
|
|
253
|
-
},
|
|
254
|
-
removeBalloon() {
|
|
255
|
-
// No-op: balloon notifications require a native host (Windows only).
|
|
256
|
-
},
|
|
257
|
-
setContextMenu() {
|
|
258
|
-
// No-op: web has no tray icon — a native host (Electron/Tauri) is required.
|
|
259
|
-
},
|
|
260
|
-
setIcon() {
|
|
261
|
-
// No-op: web has no tray icon to update.
|
|
262
|
-
},
|
|
263
|
-
setIgnoreDoubleClickEvents() {
|
|
264
|
-
// No-op: web has no tray icon double-click behavior to configure.
|
|
265
|
-
},
|
|
266
|
-
setPressedIcon() {
|
|
267
|
-
// No-op: web has no tray icon; pressed icon is macOS-specific.
|
|
268
|
-
},
|
|
269
|
-
setTemplate() {
|
|
270
|
-
// No-op: template images are a macOS menu-bar concept; irrelevant on web.
|
|
271
|
-
},
|
|
272
|
-
setTitle() {
|
|
273
|
-
// No-op: web has no tray icon to update.
|
|
274
|
-
},
|
|
275
|
-
setTooltip() {
|
|
276
|
-
// No-op: web has no tray icon to update.
|
|
277
|
-
},
|
|
278
|
-
subscribe() {
|
|
279
|
-
// No tray on web — a native host (Electron/Tauri) is required to emit tray events.
|
|
280
|
-
return () => { };
|
|
281
|
-
},
|
|
282
|
-
};
|
|
210
|
+
function getTrayRuntime(tray) {
|
|
211
|
+
return tray[EntityRuntimeKey] ?? null;
|
|
212
|
+
}
|
|
213
|
+
function getActiveTrayRuntime(tray) {
|
|
214
|
+
const runtime = getTrayRuntime(tray);
|
|
215
|
+
return runtime?.state === 'active' ? runtime : null;
|
|
216
|
+
}
|
|
217
|
+
async function invokeUpdate(tray, slot, failure, operation) {
|
|
218
|
+
const runtime = getActiveTrayRuntime(tray);
|
|
219
|
+
if (runtime === null)
|
|
220
|
+
return { outcome: 'tray-destroyed' };
|
|
221
|
+
try {
|
|
222
|
+
return await operation(runtime.capabilities[slot]);
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
return { error, outcome: failure };
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function invokeRead(tray, slot, failure, operation) {
|
|
229
|
+
return invokeUpdate(tray, slot, failure, operation);
|
|
230
|
+
}
|
|
283
231
|
//# sourceMappingURL=tray.js.map
|