@zappdev/runtime 0.1.0 → 0.6.0-alpha.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/app.ts +28 -81
- package/bridge.ts +23 -0
- package/context-menu.ts +84 -0
- package/dialog.ts +29 -103
- package/dock.ts +76 -0
- package/events.ts +104 -226
- package/index.ts +17 -25
- package/menu.ts +59 -68
- package/notification.ts +199 -0
- package/package.json +7 -5
- package/services.ts +46 -81
- package/sync.ts +60 -44
- package/window.ts +125 -0
- package/worker-globals.ts +47 -0
- package/worker.ts +104 -248
- package/README.md +0 -51
- package/bindings-contract.ts +0 -31
- package/protocol.ts +0 -94
- package/windows.ts +0 -222
package/app.ts
CHANGED
|
@@ -1,91 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
function getBridge(): AppBridge | null {
|
|
23
|
-
return ((globalThis as unknown as Record<symbol, unknown>)[
|
|
24
|
-
Symbol.for("zapp.bridge")
|
|
25
|
-
] as AppBridge | undefined) ?? null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function isMainContext(): boolean {
|
|
29
|
-
return (globalThis as unknown as Record<symbol, unknown>)[
|
|
30
|
-
Symbol.for("zapp.context")
|
|
31
|
-
] !== "worker";
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const defaultConfig: AppConfig = {
|
|
35
|
-
name: "Zapp App",
|
|
36
|
-
applicationShouldTerminateAfterLastWindowClosed: false,
|
|
37
|
-
webContentInspectable: true,
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/** Top-level application API for lifecycle, visibility, and configuration. */
|
|
41
|
-
export interface AppAPI {
|
|
42
|
-
/** Returns the current application configuration. */
|
|
43
|
-
getConfig(): AppConfig;
|
|
44
|
-
/** Registers a callback to run when the app is ready. */
|
|
45
|
-
onReady(callback: ReadyCallback): void;
|
|
46
|
-
/** Quits the application. */
|
|
47
|
-
quit(): void;
|
|
48
|
-
/** Hides the application. */
|
|
49
|
-
hide(): void;
|
|
50
|
-
/** Shows the application. */
|
|
51
|
-
show(): void;
|
|
52
|
-
/** Opens a URL in the user's default browser. */
|
|
53
|
-
openExternal(url: string): void;
|
|
54
|
-
/** Sets the application menu from a menu definition. */
|
|
55
|
-
setMenu(menu: { items: unknown[] }): void;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** The singleton application API instance. */
|
|
59
|
-
export const App: AppAPI = {
|
|
60
|
-
getConfig(): AppConfig {
|
|
61
|
-
return getBridge()?.getConfig?.() ?? defaultConfig;
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
onReady(callback: ReadyCallback): void {
|
|
65
|
-
if (!isMainContext()) {
|
|
66
|
-
console.warn("App.onReady() is only available in the main/webview context.");
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
Events.on("ready", callback);
|
|
1
|
+
/**
|
|
2
|
+
* App — application lifecycle, events, and configuration.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { App, AppEvent } from "@zappdev/runtime";
|
|
7
|
+
*
|
|
8
|
+
* App.on(AppEvent.REOPEN, () => console.log("dock icon clicked"));
|
|
9
|
+
* App.on(AppEvent.OPEN_URL, (data) => console.log("deep link:", data.url));
|
|
10
|
+
* App.quit();
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { getBridge } from "./bridge";
|
|
15
|
+
import { Events, AppEvent, eventName } from "./events";
|
|
16
|
+
|
|
17
|
+
export const App = {
|
|
18
|
+
/** Listen for app lifecycle events. Returns unsubscribe function. */
|
|
19
|
+
on(event: AppEvent, handler: (data?: any) => void): () => void {
|
|
20
|
+
const name = eventName(event);
|
|
21
|
+
return Events.on(name, handler);
|
|
70
22
|
},
|
|
71
23
|
|
|
24
|
+
/** Quit the application. */
|
|
72
25
|
quit(): void {
|
|
73
|
-
getBridge()
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
hide(): void {
|
|
77
|
-
getBridge()?.appAction?.("hide");
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
show(): void {
|
|
81
|
-
getBridge()?.appAction?.("show");
|
|
26
|
+
getBridge().emit("app:quit");
|
|
82
27
|
},
|
|
83
28
|
|
|
29
|
+
/** Open a URL in the system browser. */
|
|
84
30
|
openExternal(url: string): void {
|
|
85
|
-
getBridge()
|
|
31
|
+
(getBridge() as any).post(JSON.stringify({ t: 4, m: "openExternal", a: { url } }));
|
|
86
32
|
},
|
|
87
33
|
|
|
88
|
-
|
|
89
|
-
|
|
34
|
+
/** Get the app config injected by the native bootstrap. */
|
|
35
|
+
getConfig(): Record<string, unknown> {
|
|
36
|
+
return (globalThis as any)[Symbol.for("zapp.bootstrapConfig")] ?? {};
|
|
90
37
|
},
|
|
91
38
|
};
|
package/bridge.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge accessor — internal module, not exported to users.
|
|
3
|
+
* All runtime modules talk to native through this.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const BRIDGE_KEY = Symbol.for("zapp.bridge");
|
|
7
|
+
|
|
8
|
+
export interface ZappBridge {
|
|
9
|
+
invoke(method: string, args?: Record<string, unknown>, opts?: { timeout?: number }): Promise<unknown> & { cancel(): void };
|
|
10
|
+
emit(name: string, payload?: Record<string, unknown>): void;
|
|
11
|
+
on(name: string, handler: (payload: unknown) => void): () => void;
|
|
12
|
+
_onInvokeResult(id: number, ok: boolean, payload: string): void;
|
|
13
|
+
_onEvent(name: string, payload: string): void;
|
|
14
|
+
dispatchWindowEvent(windowId: string, eventName: string, dataJson?: string): void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getBridge(): ZappBridge {
|
|
18
|
+
const bridge = (globalThis as any)[BRIDGE_KEY] as ZappBridge | undefined;
|
|
19
|
+
if (!bridge) {
|
|
20
|
+
throw new Error("[zapp] Bridge not available. Is the app running in a Zapp WebView?");
|
|
21
|
+
}
|
|
22
|
+
return bridge;
|
|
23
|
+
}
|
package/context-menu.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ContextMenu — native context menus.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { ContextMenu } from "@zappdev/runtime";
|
|
7
|
+
*
|
|
8
|
+
* ContextMenu.show([
|
|
9
|
+
* { label: "Copy", role: "copy" },
|
|
10
|
+
* { label: "Paste", role: "paste" },
|
|
11
|
+
* { type: "separator" },
|
|
12
|
+
* { label: "Custom Action", action: () => console.log("clicked!") },
|
|
13
|
+
* ]);
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { getBridge } from "./bridge";
|
|
18
|
+
import { Events } from "./events";
|
|
19
|
+
import type { MenuItemDef } from "./menu";
|
|
20
|
+
|
|
21
|
+
let ctxActionCounter = 0;
|
|
22
|
+
let lastContextX = 0;
|
|
23
|
+
let lastContextY = 0;
|
|
24
|
+
|
|
25
|
+
// Track right-click position
|
|
26
|
+
if (typeof document !== "undefined") {
|
|
27
|
+
document.addEventListener("contextmenu", (e) => {
|
|
28
|
+
lastContextX = e.clientX;
|
|
29
|
+
lastContextY = e.clientY;
|
|
30
|
+
}, true);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function collectAndStrip(items: MenuItemDef[]): { clean: any[]; actions: Map<string, () => void> } {
|
|
34
|
+
const actions = new Map<string, () => void>();
|
|
35
|
+
|
|
36
|
+
function walk(items: MenuItemDef[]): any[] {
|
|
37
|
+
return items.map(item => {
|
|
38
|
+
const clean: any = { ...item };
|
|
39
|
+
if (clean.action) {
|
|
40
|
+
if (!clean.id) clean.id = `__ctx_${++ctxActionCounter}`;
|
|
41
|
+
actions.set(clean.id, clean.action);
|
|
42
|
+
delete clean.action;
|
|
43
|
+
}
|
|
44
|
+
if (clean.submenu) clean.submenu = walk(clean.submenu);
|
|
45
|
+
return clean;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return { clean: walk(items), actions };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface ContextMenuOptions {
|
|
53
|
+
/** X position in CSS pixels. Defaults to last contextmenu event position. */
|
|
54
|
+
x?: number;
|
|
55
|
+
/** Y position in CSS pixels. Defaults to last contextmenu event position. */
|
|
56
|
+
y?: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const ContextMenu = {
|
|
60
|
+
show(items: MenuItemDef[], options?: ContextMenuOptions): void {
|
|
61
|
+
const { clean, actions } = collectAndStrip(items);
|
|
62
|
+
|
|
63
|
+
// One-shot event listener for action clicks
|
|
64
|
+
if (actions.size > 0) {
|
|
65
|
+
const off = Events.on("__menu:click", (payload: any) => {
|
|
66
|
+
const id = typeof payload === "string" ? JSON.parse(payload).id : payload?.id;
|
|
67
|
+
const handler = actions.get(id);
|
|
68
|
+
if (handler) handler();
|
|
69
|
+
off(); // One-shot: remove after first click
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Auto-cleanup after 30 seconds if no click
|
|
73
|
+
setTimeout(() => off(), 30000);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const x = options?.x ?? lastContextX;
|
|
77
|
+
const y = options?.y ?? lastContextY;
|
|
78
|
+
|
|
79
|
+
(getBridge() as any).post(JSON.stringify({
|
|
80
|
+
t: 4, m: "showContextMenu",
|
|
81
|
+
a: { items: clean, x, y }
|
|
82
|
+
}));
|
|
83
|
+
},
|
|
84
|
+
};
|
package/dialog.ts
CHANGED
|
@@ -1,148 +1,74 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Dialog — native file and message dialogs.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { Dialog } from "@zappdev/runtime";
|
|
7
|
+
*
|
|
8
|
+
* const result = await Dialog.openFile({ multiple: true, filters: [{ name: "Images", extensions: ["png", "jpg"] }] });
|
|
9
|
+
* if (!result.cancelled) console.log(result.paths);
|
|
10
|
+
*
|
|
11
|
+
* const save = await Dialog.saveFile({ defaultName: "doc.txt" });
|
|
12
|
+
* const msg = await Dialog.message({ message: "Are you sure?", buttons: ["Yes", "No"] });
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { getBridge } from "./bridge";
|
|
2
17
|
|
|
3
|
-
/** A file type filter for open/save dialogs. */
|
|
4
18
|
export interface FileFilter {
|
|
5
|
-
/** Display name for the filter (e.g. "Images"). */
|
|
6
19
|
name: string;
|
|
7
|
-
/** Allowed file extensions without dots (e.g. ["png", "jpg"]). */
|
|
8
20
|
extensions: string[];
|
|
9
21
|
}
|
|
10
22
|
|
|
11
|
-
/** Options for the open-file dialog. */
|
|
12
23
|
export interface OpenFileOptions {
|
|
13
|
-
/** Dialog window title. */
|
|
14
24
|
title?: string;
|
|
15
|
-
/** Initial directory or file path. */
|
|
16
25
|
defaultPath?: string;
|
|
17
|
-
/** File type filters shown in the dialog. */
|
|
18
26
|
filters?: FileFilter[];
|
|
19
|
-
/** Whether the user can select multiple files. */
|
|
20
27
|
multiple?: boolean;
|
|
21
|
-
/** Whether to select directories instead of files. */
|
|
22
28
|
directory?: boolean;
|
|
23
29
|
}
|
|
24
30
|
|
|
25
|
-
/** Options for the save-file dialog. */
|
|
26
31
|
export interface SaveFileOptions {
|
|
27
|
-
/** Dialog window title. */
|
|
28
32
|
title?: string;
|
|
29
|
-
/** Initial directory path. */
|
|
30
33
|
defaultPath?: string;
|
|
31
|
-
/** Default file name pre-filled in the dialog. */
|
|
32
34
|
defaultName?: string;
|
|
33
|
-
/** File type filters shown in the dialog. */
|
|
34
35
|
filters?: FileFilter[];
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
/** Options for a message dialog (alert/confirm). */
|
|
38
38
|
export interface MessageOptions {
|
|
39
|
-
/** Dialog window title. */
|
|
40
|
-
title?: string;
|
|
41
|
-
/** The message body text. */
|
|
42
39
|
message: string;
|
|
43
|
-
|
|
40
|
+
title?: string;
|
|
44
41
|
kind?: "info" | "warning" | "critical";
|
|
45
|
-
/** Button labels to display (e.g. ["OK", "Cancel"]). */
|
|
46
42
|
buttons?: string[];
|
|
47
43
|
}
|
|
48
44
|
|
|
49
|
-
/** Result from an open-file dialog. */
|
|
50
45
|
export interface OpenFileResult {
|
|
51
|
-
|
|
52
|
-
ok: boolean;
|
|
53
|
-
/** True if the user cancelled the dialog. */
|
|
54
|
-
cancelled?: boolean;
|
|
55
|
-
/** Selected file or directory paths. */
|
|
46
|
+
cancelled: boolean;
|
|
56
47
|
paths?: string[];
|
|
57
48
|
}
|
|
58
49
|
|
|
59
|
-
/** Result from a save-file dialog. */
|
|
60
50
|
export interface SaveFileResult {
|
|
61
|
-
|
|
62
|
-
ok: boolean;
|
|
63
|
-
/** True if the user cancelled the dialog. */
|
|
64
|
-
cancelled?: boolean;
|
|
65
|
-
/** The chosen save path. */
|
|
51
|
+
cancelled: boolean;
|
|
66
52
|
path?: string;
|
|
67
53
|
}
|
|
68
54
|
|
|
69
|
-
/** Result from a message dialog. */
|
|
70
55
|
export interface MessageResult {
|
|
71
|
-
/** Whether the dialog completed successfully. */
|
|
72
|
-
ok: boolean;
|
|
73
|
-
/** Zero-based index of the button the user clicked. */
|
|
74
56
|
button: number;
|
|
75
57
|
}
|
|
76
58
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
/** Show a native save-file dialog. */
|
|
82
|
-
saveFile(options?: SaveFileOptions): Promise<SaveFileResult>;
|
|
83
|
-
/** Show a native message dialog (alert/confirm). */
|
|
84
|
-
message(options: MessageOptions): Promise<MessageResult>;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
let dialogSeq = 0;
|
|
88
|
-
|
|
89
|
-
function assertNotWorker(): void {
|
|
90
|
-
const ctx = (globalThis as unknown as Record<symbol, unknown>)[Symbol.for("zapp.context")];
|
|
91
|
-
if (ctx === "worker") {
|
|
92
|
-
throw new Error("Dialog APIs are not available in a worker context.");
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function postDialog<T>(action: string, params: Record<string, unknown>): Promise<T> {
|
|
97
|
-
return new Promise((resolve, reject) => {
|
|
98
|
-
const requestId = `dialog-${Date.now()}-${++dialogSeq}`;
|
|
99
|
-
const timer = setTimeout(() => {
|
|
100
|
-
off();
|
|
101
|
-
reject(new Error("Dialog timed out."));
|
|
102
|
-
}, 300000);
|
|
103
|
-
|
|
104
|
-
// Listen for the result via the bridge event system
|
|
105
|
-
const off = Events.on(`__zapp:dialog:${requestId}`, (payload) => {
|
|
106
|
-
clearTimeout(timer);
|
|
107
|
-
const result = { ...(payload as Record<string, unknown>) };
|
|
108
|
-
delete result.requestId;
|
|
109
|
-
resolve(result as T);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
// Post the request to native
|
|
113
|
-
const handler = (globalThis as unknown as Record<string, Record<string, Record<string, { postMessage?: (m: string) => void }>>>)
|
|
114
|
-
.webkit?.messageHandlers?.zapp;
|
|
115
|
-
const chromeWebview = (globalThis as unknown as Record<string, Record<string, { postMessage?: (m: string) => void }>>)
|
|
116
|
-
.chrome?.webview;
|
|
117
|
-
|
|
118
|
-
const msg = `dialog\n${action}\n${JSON.stringify({ requestId, ...params })}`;
|
|
119
|
-
|
|
120
|
-
if (handler?.postMessage) {
|
|
121
|
-
handler.postMessage(msg);
|
|
122
|
-
} else if (chromeWebview?.postMessage) {
|
|
123
|
-
chromeWebview.postMessage(msg);
|
|
124
|
-
} else {
|
|
125
|
-
clearTimeout(timer);
|
|
126
|
-
off();
|
|
127
|
-
reject(new Error("Dialog bridge unavailable."));
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** The singleton dialog API instance. Not available in worker contexts. */
|
|
133
|
-
export const Dialog: DialogAPI = {
|
|
134
|
-
openFile(options: OpenFileOptions = {}): Promise<OpenFileResult> {
|
|
135
|
-
assertNotWorker();
|
|
136
|
-
return postDialog<OpenFileResult>("openFile", options as unknown as Record<string, unknown>);
|
|
59
|
+
export const Dialog = {
|
|
60
|
+
async openFile(options?: OpenFileOptions): Promise<OpenFileResult> {
|
|
61
|
+
const result = await getBridge().invoke("__dialog:open", options as any);
|
|
62
|
+
return result as OpenFileResult;
|
|
137
63
|
},
|
|
138
64
|
|
|
139
|
-
saveFile(options
|
|
140
|
-
|
|
141
|
-
return
|
|
65
|
+
async saveFile(options?: SaveFileOptions): Promise<SaveFileResult> {
|
|
66
|
+
const result = await getBridge().invoke("__dialog:save", options as any);
|
|
67
|
+
return result as SaveFileResult;
|
|
142
68
|
},
|
|
143
69
|
|
|
144
|
-
message(options: MessageOptions): Promise<MessageResult> {
|
|
145
|
-
|
|
146
|
-
return
|
|
70
|
+
async message(options: MessageOptions): Promise<MessageResult> {
|
|
71
|
+
const result = await getBridge().invoke("__dialog:message", options as any);
|
|
72
|
+
return result as MessageResult;
|
|
147
73
|
},
|
|
148
74
|
};
|
package/dock.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dock — dock icon and badge control.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { Dock } from "@zappdev/runtime";
|
|
7
|
+
*
|
|
8
|
+
* Dock.setBadge("5");
|
|
9
|
+
* Dock.bounce();
|
|
10
|
+
* Dock.hideIcon();
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { getBridge } from "./bridge";
|
|
15
|
+
|
|
16
|
+
// In worker contexts, __zappBridge.dock is a sync host dispatcher that
|
|
17
|
+
// calls darwin_dock_* directly. Webview fallback uses IPC-style post.
|
|
18
|
+
function dockHost(): ((action: string, args?: unknown) => void) | null {
|
|
19
|
+
const host = (globalThis as any).__zappBridge;
|
|
20
|
+
return host?.dock ?? null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const Dock = {
|
|
24
|
+
/** Show the app icon in the dock/taskbar. */
|
|
25
|
+
showIcon(): void {
|
|
26
|
+
const host = dockHost();
|
|
27
|
+
if (host) { host("showIcon"); return; }
|
|
28
|
+
(getBridge() as any).post(JSON.stringify({ t: 4, m: "dock:showIcon", a: {} }));
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
/** Hide the app icon from the dock/taskbar. */
|
|
32
|
+
hideIcon(): void {
|
|
33
|
+
const host = dockHost();
|
|
34
|
+
if (host) { host("hideIcon"); return; }
|
|
35
|
+
(getBridge() as any).post(JSON.stringify({ t: 4, m: "dock:hideIcon", a: {} }));
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
/** Set a text badge on the dock icon (e.g. "5", "new"). */
|
|
39
|
+
setBadge(label: string): void {
|
|
40
|
+
const host = dockHost();
|
|
41
|
+
if (host) { host("setBadge", { label }); return; }
|
|
42
|
+
(getBridge() as any).post(JSON.stringify({ t: 4, m: "dock:setBadge", a: { label } }));
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
/** Remove the badge from the dock icon. */
|
|
46
|
+
removeBadge(): void {
|
|
47
|
+
const host = dockHost();
|
|
48
|
+
if (host) { host("removeBadge"); return; }
|
|
49
|
+
(getBridge() as any).post(JSON.stringify({ t: 4, m: "dock:removeBadge", a: {} }));
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
/** Bounce the dock icon to get user attention.
|
|
53
|
+
* @param type "informational" (bounces once) or "critical" (bounces until activated). Default: "informational"
|
|
54
|
+
*/
|
|
55
|
+
bounce(type: "informational" | "critical" = "informational"): void {
|
|
56
|
+
const host = dockHost();
|
|
57
|
+
if (host) { host("bounce", { type: type === "critical" ? 1 : 0 }); return; }
|
|
58
|
+
(getBridge() as any).post(JSON.stringify({
|
|
59
|
+
t: 4, m: "dock:bounce", a: { type: type === "critical" ? 1 : 0 },
|
|
60
|
+
}));
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
/** Set a custom dock icon from a file path. */
|
|
64
|
+
setIcon(path: string): void {
|
|
65
|
+
const host = dockHost();
|
|
66
|
+
if (host) { host("setIcon", { path }); return; }
|
|
67
|
+
(getBridge() as any).post(JSON.stringify({ t: 4, m: "dock:setIcon", a: { path } }));
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
/** Reset the dock icon to the app bundle default. */
|
|
71
|
+
resetIcon(): void {
|
|
72
|
+
const host = dockHost();
|
|
73
|
+
if (host) { host("resetIcon"); return; }
|
|
74
|
+
(getBridge() as any).post(JSON.stringify({ t: 4, m: "dock:resetIcon", a: {} }));
|
|
75
|
+
},
|
|
76
|
+
};
|