@opentray/ext-badge 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -4
- package/dist/index.d.mts +141 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +179 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +24 -6
package/README.md
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
# @opentray/ext-badge
|
|
2
2
|
|
|
3
|
-
Official
|
|
3
|
+
Official status extension for OpenTray.
|
|
4
4
|
|
|
5
5
|
## Role
|
|
6
6
|
|
|
7
|
-
- Expose badge counts, progress bars, overlay icons, and attention
|
|
8
|
-
-
|
|
9
|
-
-
|
|
7
|
+
- Expose badge counts, progress bars, overlay icons, and attention state.
|
|
8
|
+
- Keep badge truth capability-gated and honest.
|
|
9
|
+
- Route badge commands through the normal tray extension ABI.
|
|
10
|
+
|
|
11
|
+
Windows and Linux support is intentionally reduced until a real native projection exists. Unsupported families reject explicitly.
|
|
12
|
+
On the current macOS Dock proof surface, progress and progress state are explicitly unsupported and should not be treated as a working Dock projection.
|
|
13
|
+
|
|
14
|
+
## Debug Panel
|
|
15
|
+
|
|
16
|
+
The repository ships a macOS-facing proof surface at `pnpm --filter opentray example:badge`.
|
|
17
|
+
That panel uses `@opentray/ext-webview` IPC to drive the real badge contract and show capability state.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { TrayHandle } from "opentray";
|
|
2
|
+
|
|
3
|
+
//#region ../spec/src/index.d.ts
|
|
4
|
+
type SpaceId = string;
|
|
5
|
+
type TrayId = string;
|
|
6
|
+
interface ExtensionScope {
|
|
7
|
+
spaceId: SpaceId;
|
|
8
|
+
trayId?: TrayId;
|
|
9
|
+
ext: string;
|
|
10
|
+
}
|
|
11
|
+
interface ExtensionEnvelope<TData = unknown> {
|
|
12
|
+
scope: ExtensionScope;
|
|
13
|
+
data: TData;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/shared.d.ts
|
|
17
|
+
type BadgeCapabilityLevel = "native" | "reduced" | "unsupported";
|
|
18
|
+
type BadgePlatform = "darwin" | "win32" | "linux";
|
|
19
|
+
interface BadgeState {
|
|
20
|
+
badgeText: string;
|
|
21
|
+
badgeCount: number | null;
|
|
22
|
+
progressValue: number;
|
|
23
|
+
progressMax: number;
|
|
24
|
+
progressState: "none" | "indeterminate" | "normal" | "paused" | "error";
|
|
25
|
+
overlayIcon: "none" | "dot" | "alert";
|
|
26
|
+
attention: boolean;
|
|
27
|
+
}
|
|
28
|
+
interface BadgeCapabilityFamily {
|
|
29
|
+
badgeText: BadgeCapabilityLevel;
|
|
30
|
+
progress: BadgeCapabilityLevel;
|
|
31
|
+
overlayIcon: BadgeCapabilityLevel;
|
|
32
|
+
attention: BadgeCapabilityLevel;
|
|
33
|
+
}
|
|
34
|
+
interface BadgeCapabilities {
|
|
35
|
+
platform: BadgePlatform;
|
|
36
|
+
mode: "native" | "reduced" | "unsupported";
|
|
37
|
+
capabilities: BadgeCapabilityFamily;
|
|
38
|
+
state: BadgeState;
|
|
39
|
+
reason?: string;
|
|
40
|
+
}
|
|
41
|
+
type BadgeCommand = {
|
|
42
|
+
type: "getCapabilities";
|
|
43
|
+
} | {
|
|
44
|
+
type: "setBadge";
|
|
45
|
+
value: string;
|
|
46
|
+
} | {
|
|
47
|
+
type: "clearBadge";
|
|
48
|
+
} | {
|
|
49
|
+
type: "setProgress";
|
|
50
|
+
value: number;
|
|
51
|
+
max?: number;
|
|
52
|
+
} | {
|
|
53
|
+
type: "setProgressState";
|
|
54
|
+
value: BadgeState["progressState"];
|
|
55
|
+
} | {
|
|
56
|
+
type: "setOverlayIcon";
|
|
57
|
+
value: BadgeState["overlayIcon"];
|
|
58
|
+
} | {
|
|
59
|
+
type: "setAttention";
|
|
60
|
+
value: boolean;
|
|
61
|
+
} | {
|
|
62
|
+
type: "showPanel";
|
|
63
|
+
} | {
|
|
64
|
+
type: "hidePanel";
|
|
65
|
+
} | {
|
|
66
|
+
type: "reset";
|
|
67
|
+
};
|
|
68
|
+
type BadgeEvent = {
|
|
69
|
+
type: "capabilities";
|
|
70
|
+
snapshot: BadgeCapabilities;
|
|
71
|
+
} | {
|
|
72
|
+
type: "state";
|
|
73
|
+
snapshot: BadgeCapabilities;
|
|
74
|
+
} | {
|
|
75
|
+
type: "result";
|
|
76
|
+
op: string;
|
|
77
|
+
ok: true;
|
|
78
|
+
snapshot: BadgeCapabilities;
|
|
79
|
+
} | {
|
|
80
|
+
type: "result";
|
|
81
|
+
op: string;
|
|
82
|
+
ok: false;
|
|
83
|
+
error: string;
|
|
84
|
+
snapshot: BadgeCapabilities;
|
|
85
|
+
};
|
|
86
|
+
interface BadgeHandle {
|
|
87
|
+
getCapabilities(): Promise<BadgeCapabilities>;
|
|
88
|
+
setBadge(value: string): Promise<BadgeCapabilities>;
|
|
89
|
+
clearBadge(): Promise<BadgeCapabilities>;
|
|
90
|
+
setProgress(value: number, max?: number): Promise<BadgeCapabilities>;
|
|
91
|
+
setProgressState(value: BadgeState["progressState"]): Promise<BadgeCapabilities>;
|
|
92
|
+
setOverlayIcon(value: BadgeState["overlayIcon"]): Promise<BadgeCapabilities>;
|
|
93
|
+
setAttention(value: boolean): Promise<BadgeCapabilities>;
|
|
94
|
+
showPanel(): Promise<BadgeCapabilities>;
|
|
95
|
+
hidePanel(): Promise<BadgeCapabilities>;
|
|
96
|
+
reset(): Promise<BadgeCapabilities>;
|
|
97
|
+
}
|
|
98
|
+
interface BadgePanelEnvelope {
|
|
99
|
+
platform: BadgePlatform;
|
|
100
|
+
mode: "native" | "reduced" | "unsupported";
|
|
101
|
+
capabilities: BadgeCapabilityFamily;
|
|
102
|
+
state: BadgeState;
|
|
103
|
+
log: string[];
|
|
104
|
+
reason?: string;
|
|
105
|
+
}
|
|
106
|
+
declare const defaultBadgeState: () => BadgeState;
|
|
107
|
+
declare const defaultBadgeCapabilities: (platform: BadgePlatform) => BadgeCapabilities;
|
|
108
|
+
declare const normalizeProgress: (value: number, max?: number) => {
|
|
109
|
+
value: number;
|
|
110
|
+
max: number;
|
|
111
|
+
};
|
|
112
|
+
declare const normalizeBadgeText: (value: string) => {
|
|
113
|
+
badgeText: string;
|
|
114
|
+
badgeCount: number | null;
|
|
115
|
+
};
|
|
116
|
+
declare const badgePanelEnvelopeFromCapabilities: (snapshot: BadgeCapabilities) => BadgePanelEnvelope;
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/index.d.ts
|
|
119
|
+
interface BadgeExtensionOptions {
|
|
120
|
+
mountId?: string;
|
|
121
|
+
path?: string;
|
|
122
|
+
platform?: BadgePlatform;
|
|
123
|
+
}
|
|
124
|
+
interface BadgeExtensionCapability extends BadgeHandle {
|
|
125
|
+
readonly platform: BadgePlatform;
|
|
126
|
+
readonly getPanelEnvelope: () => Promise<BadgePanelEnvelope>;
|
|
127
|
+
}
|
|
128
|
+
declare const attachBadge: (tray: TrayHandle, options?: BadgeExtensionOptions) => BadgeExtensionCapability;
|
|
129
|
+
declare const BadgeExt: {
|
|
130
|
+
name: string;
|
|
131
|
+
path: string;
|
|
132
|
+
resolveMount(options: BadgeExtensionOptions | undefined): {
|
|
133
|
+
path?: string;
|
|
134
|
+
mountId?: string;
|
|
135
|
+
};
|
|
136
|
+
extend(tray: TrayHandle, context: import("opentray").TrayExtensionContext, options: BadgeExtensionOptions | undefined): BadgeHandle;
|
|
137
|
+
};
|
|
138
|
+
declare const isBadgeEvent: (event: ExtensionEnvelope) => event is ExtensionEnvelope<BadgeEvent>;
|
|
139
|
+
//#endregion
|
|
140
|
+
export { type BadgeCapabilities, type BadgeCapabilityFamily, type BadgeCapabilityLevel, type BadgeCommand, type BadgeEvent, BadgeExt, BadgeExtensionCapability, BadgeExtensionOptions, type BadgeHandle, type BadgePanelEnvelope, type BadgePlatform, type BadgeState, attachBadge, badgePanelEnvelopeFromCapabilities, type defaultBadgeCapabilities, type defaultBadgeState, isBadgeEvent, type normalizeBadgeText, type normalizeProgress };
|
|
141
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../spec/src/index.ts","../src/shared.ts","../src/index.ts"],"mappings":"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"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
//#region src/shared.ts
|
|
2
|
+
const defaultBadgeState = () => ({
|
|
3
|
+
badgeText: "12",
|
|
4
|
+
badgeCount: 12,
|
|
5
|
+
progressValue: 0,
|
|
6
|
+
progressMax: 100,
|
|
7
|
+
progressState: "none",
|
|
8
|
+
overlayIcon: "dot",
|
|
9
|
+
attention: false
|
|
10
|
+
});
|
|
11
|
+
const defaultBadgeCapabilities = (platform) => {
|
|
12
|
+
return {
|
|
13
|
+
platform,
|
|
14
|
+
mode: "reduced",
|
|
15
|
+
capabilities: {
|
|
16
|
+
badgeText: "reduced",
|
|
17
|
+
progress: "unsupported",
|
|
18
|
+
overlayIcon: "reduced",
|
|
19
|
+
attention: "reduced"
|
|
20
|
+
},
|
|
21
|
+
state: defaultBadgeState(),
|
|
22
|
+
reason: "badge projection is currently reduced to a local proof surface"
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
const normalizeProgress = (value, max) => {
|
|
26
|
+
const normalizedMax = Number.isFinite(max ?? 100) ? Math.max(1, Math.round(max ?? 100)) : 100;
|
|
27
|
+
return {
|
|
28
|
+
value: Number.isFinite(value) ? Math.max(0, Math.min(normalizedMax, Math.round(value))) : 0,
|
|
29
|
+
max: normalizedMax
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
const normalizeBadgeText = (value) => {
|
|
33
|
+
const badgeText = value.trim();
|
|
34
|
+
const parsed = Number.parseInt(badgeText, 10);
|
|
35
|
+
return {
|
|
36
|
+
badgeText,
|
|
37
|
+
badgeCount: Number.isFinite(parsed) ? parsed : null
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
const badgePanelEnvelopeFromCapabilities = (snapshot) => ({
|
|
41
|
+
platform: snapshot.platform,
|
|
42
|
+
mode: snapshot.mode,
|
|
43
|
+
capabilities: snapshot.capabilities,
|
|
44
|
+
state: snapshot.state,
|
|
45
|
+
log: [],
|
|
46
|
+
...snapshot.reason === void 0 ? {} : { reason: snapshot.reason }
|
|
47
|
+
});
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/index.ts
|
|
50
|
+
const BADGE_EXTENSION_NAME = "badge";
|
|
51
|
+
const BADGE_EXTENSION_PACKAGE = "@opentray/ext-badge";
|
|
52
|
+
const attachBadge = (tray, options = {}) => {
|
|
53
|
+
const platform = options.platform ?? process.platform;
|
|
54
|
+
const extension = tray.extend(BadgeExt, options);
|
|
55
|
+
return {
|
|
56
|
+
...extension,
|
|
57
|
+
platform,
|
|
58
|
+
getPanelEnvelope: async () => badgePanelEnvelopeFromCapabilities(await extension.getCapabilities())
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
const BadgeExt = {
|
|
62
|
+
name: BADGE_EXTENSION_NAME,
|
|
63
|
+
path: BADGE_EXTENSION_PACKAGE,
|
|
64
|
+
resolveMount(options) {
|
|
65
|
+
return {
|
|
66
|
+
...options?.mountId === void 0 ? {} : { mountId: options.mountId },
|
|
67
|
+
...options?.path === void 0 ? {} : { path: options.path }
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
extend(tray, context, options) {
|
|
71
|
+
const snapshot = defaultBadgeCapabilities(options?.platform ?? process.platform);
|
|
72
|
+
return createBadgeHandle(() => context.ensureLoaded(), tray, context.mountId, snapshot);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const isBadgeEvent = (event) => event.scope.ext === BADGE_EXTENSION_NAME && typeof event.data === "object" && event.data !== null && "type" in event.data;
|
|
76
|
+
function createBadgeHandle(ensureLoaded, tray, mountId, seed) {
|
|
77
|
+
const state = structuredClone(seed.state);
|
|
78
|
+
const capabilities = { ...seed.capabilities };
|
|
79
|
+
const platform = seed.platform;
|
|
80
|
+
const mode = seed.mode;
|
|
81
|
+
const reason = seed.reason;
|
|
82
|
+
const snapshot = () => ({
|
|
83
|
+
platform,
|
|
84
|
+
mode,
|
|
85
|
+
capabilities: { ...capabilities },
|
|
86
|
+
state: structuredClone(state),
|
|
87
|
+
...reason === void 0 ? {} : { reason }
|
|
88
|
+
});
|
|
89
|
+
const rejectUnsupported = (family, op) => {
|
|
90
|
+
const support = capabilities[family];
|
|
91
|
+
throw new Error(`${op} is ${support} on ${platform}`);
|
|
92
|
+
};
|
|
93
|
+
return {
|
|
94
|
+
getCapabilities: async () => {
|
|
95
|
+
await ensureLoaded();
|
|
96
|
+
await tray.requestExtension(mountId, { type: "getCapabilities" });
|
|
97
|
+
return snapshot();
|
|
98
|
+
},
|
|
99
|
+
setBadge: async (value) => {
|
|
100
|
+
await ensureLoaded();
|
|
101
|
+
const normalized = normalizeBadgeText(value);
|
|
102
|
+
state.badgeText = normalized.badgeText;
|
|
103
|
+
state.badgeCount = normalized.badgeCount;
|
|
104
|
+
await tray.commandExtension(mountId, {
|
|
105
|
+
type: "setBadge",
|
|
106
|
+
value: normalized.badgeText
|
|
107
|
+
});
|
|
108
|
+
return snapshot();
|
|
109
|
+
},
|
|
110
|
+
clearBadge: async () => {
|
|
111
|
+
await ensureLoaded();
|
|
112
|
+
state.badgeText = "";
|
|
113
|
+
state.badgeCount = null;
|
|
114
|
+
await tray.commandExtension(mountId, { type: "clearBadge" });
|
|
115
|
+
return snapshot();
|
|
116
|
+
},
|
|
117
|
+
setProgress: async (value, max) => {
|
|
118
|
+
await ensureLoaded();
|
|
119
|
+
if (capabilities.progress === "unsupported") rejectUnsupported("progress", "setProgress");
|
|
120
|
+
const normalized = normalizeProgress(value, max);
|
|
121
|
+
state.progressValue = normalized.value;
|
|
122
|
+
state.progressMax = normalized.max;
|
|
123
|
+
await tray.commandExtension(mountId, {
|
|
124
|
+
type: "setProgress",
|
|
125
|
+
value: normalized.value,
|
|
126
|
+
max: normalized.max
|
|
127
|
+
});
|
|
128
|
+
return snapshot();
|
|
129
|
+
},
|
|
130
|
+
setProgressState: async (value) => {
|
|
131
|
+
await ensureLoaded();
|
|
132
|
+
if (capabilities.progress === "unsupported") rejectUnsupported("progress", "setProgressState");
|
|
133
|
+
state.progressState = value;
|
|
134
|
+
await tray.commandExtension(mountId, {
|
|
135
|
+
type: "setProgressState",
|
|
136
|
+
value
|
|
137
|
+
});
|
|
138
|
+
return snapshot();
|
|
139
|
+
},
|
|
140
|
+
setOverlayIcon: async (value) => {
|
|
141
|
+
await ensureLoaded();
|
|
142
|
+
state.overlayIcon = value;
|
|
143
|
+
await tray.commandExtension(mountId, {
|
|
144
|
+
type: "setOverlayIcon",
|
|
145
|
+
value
|
|
146
|
+
});
|
|
147
|
+
return snapshot();
|
|
148
|
+
},
|
|
149
|
+
setAttention: async (value) => {
|
|
150
|
+
await ensureLoaded();
|
|
151
|
+
state.attention = Boolean(value);
|
|
152
|
+
await tray.commandExtension(mountId, {
|
|
153
|
+
type: "setAttention",
|
|
154
|
+
value: Boolean(value)
|
|
155
|
+
});
|
|
156
|
+
return snapshot();
|
|
157
|
+
},
|
|
158
|
+
showPanel: async () => {
|
|
159
|
+
await ensureLoaded();
|
|
160
|
+
await tray.commandExtension(mountId, { type: "showPanel" });
|
|
161
|
+
return snapshot();
|
|
162
|
+
},
|
|
163
|
+
hidePanel: async () => {
|
|
164
|
+
await ensureLoaded();
|
|
165
|
+
await tray.commandExtension(mountId, { type: "hidePanel" });
|
|
166
|
+
return snapshot();
|
|
167
|
+
},
|
|
168
|
+
reset: async () => {
|
|
169
|
+
await ensureLoaded();
|
|
170
|
+
Object.assign(state, seed.state);
|
|
171
|
+
await tray.commandExtension(mountId, { type: "reset" });
|
|
172
|
+
return snapshot();
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
//#endregion
|
|
177
|
+
export { BadgeExt, attachBadge, badgePanelEnvelopeFromCapabilities, isBadgeEvent };
|
|
178
|
+
|
|
179
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/shared.ts","../src/index.ts"],"sourcesContent":["export type BadgeCapabilityLevel = \"native\" | \"reduced\" | \"unsupported\";\n\nexport type BadgePlatform = \"darwin\" | \"win32\" | \"linux\";\n\nexport interface BadgeState {\n badgeText: string;\n badgeCount: number | null;\n progressValue: number;\n progressMax: number;\n progressState: \"none\" | \"indeterminate\" | \"normal\" | \"paused\" | \"error\";\n overlayIcon: \"none\" | \"dot\" | \"alert\";\n attention: boolean;\n}\n\nexport interface BadgeCapabilityFamily {\n badgeText: BadgeCapabilityLevel;\n progress: BadgeCapabilityLevel;\n overlayIcon: BadgeCapabilityLevel;\n attention: BadgeCapabilityLevel;\n}\n\nexport interface BadgeCapabilities {\n platform: BadgePlatform;\n mode: \"native\" | \"reduced\" | \"unsupported\";\n capabilities: BadgeCapabilityFamily;\n state: BadgeState;\n reason?: string;\n}\n\nexport type BadgeCommand =\n | { type: \"getCapabilities\" }\n | { type: \"setBadge\"; value: string }\n | { type: \"clearBadge\" }\n | { type: \"setProgress\"; value: number; max?: number }\n | { type: \"setProgressState\"; value: BadgeState[\"progressState\"] }\n | { type: \"setOverlayIcon\"; value: BadgeState[\"overlayIcon\"] }\n | { type: \"setAttention\"; value: boolean }\n | { type: \"showPanel\" }\n | { type: \"hidePanel\" }\n | { type: \"reset\" }\n ;\n\nexport type BadgeEvent =\n | { type: \"capabilities\"; snapshot: BadgeCapabilities }\n | { type: \"state\"; snapshot: BadgeCapabilities }\n | { type: \"result\"; op: string; ok: true; snapshot: BadgeCapabilities }\n | { type: \"result\"; op: string; ok: false; error: string; snapshot: BadgeCapabilities };\n\nexport interface BadgeHandle {\n getCapabilities(): Promise<BadgeCapabilities>;\n setBadge(value: string): Promise<BadgeCapabilities>;\n clearBadge(): Promise<BadgeCapabilities>;\n setProgress(value: number, max?: number): Promise<BadgeCapabilities>;\n setProgressState(value: BadgeState[\"progressState\"]): Promise<BadgeCapabilities>;\n setOverlayIcon(value: BadgeState[\"overlayIcon\"]): Promise<BadgeCapabilities>;\n setAttention(value: boolean): Promise<BadgeCapabilities>;\n showPanel(): Promise<BadgeCapabilities>;\n hidePanel(): Promise<BadgeCapabilities>;\n reset(): Promise<BadgeCapabilities>;\n}\n\nexport interface BadgePanelEnvelope {\n platform: BadgePlatform;\n mode: \"native\" | \"reduced\" | \"unsupported\";\n capabilities: BadgeCapabilityFamily;\n state: BadgeState;\n log: string[];\n reason?: string;\n}\n\nexport const defaultBadgeState = (): BadgeState => ({\n badgeText: \"12\",\n badgeCount: 12,\n progressValue: 0,\n progressMax: 100,\n progressState: \"none\",\n overlayIcon: \"dot\",\n attention: false,\n});\n\nexport const defaultBadgeCapabilities = (platform: BadgePlatform): BadgeCapabilities => {\n const common = {\n badgeText: \"reduced\",\n progress: \"unsupported\",\n overlayIcon: \"reduced\",\n attention: \"reduced\",\n } satisfies BadgeCapabilityFamily;\n return {\n platform,\n mode: \"reduced\",\n capabilities: common,\n state: defaultBadgeState(),\n reason: \"badge projection is currently reduced to a local proof surface\",\n };\n};\n\nexport const normalizeProgress = (value: number, max?: number): { value: number; max: number } => {\n const normalizedMax = Number.isFinite(max ?? 100) ? Math.max(1, Math.round(max ?? 100)) : 100;\n const normalizedValue = Number.isFinite(value) ? Math.max(0, Math.min(normalizedMax, Math.round(value))) : 0;\n return { value: normalizedValue, max: normalizedMax };\n};\n\nexport const normalizeBadgeText = (value: string): { badgeText: string; badgeCount: number | null } => {\n const badgeText = value.trim();\n const parsed = Number.parseInt(badgeText, 10);\n return {\n badgeText,\n badgeCount: Number.isFinite(parsed) ? parsed : null,\n };\n};\n\nexport const badgePanelEnvelopeFromCapabilities = (\n snapshot: BadgeCapabilities,\n): BadgePanelEnvelope => ({\n platform: snapshot.platform,\n mode: snapshot.mode,\n capabilities: snapshot.capabilities,\n state: snapshot.state,\n log: [],\n ...(snapshot.reason === undefined ? {} : { reason: snapshot.reason }),\n});\n","import type { ExtensionEnvelope } from \"@opentray/spec\";\nimport type { TrayHandle } from \"opentray\";\n\nimport {\n badgePanelEnvelopeFromCapabilities,\n defaultBadgeCapabilities,\n normalizeBadgeText,\n normalizeProgress,\n type BadgeCapabilities,\n type BadgeCapabilityFamily,\n type BadgeEvent,\n type BadgeHandle,\n type BadgePanelEnvelope,\n type BadgePlatform,\n type BadgeState,\n} from \"./shared\";\n\nexport type * from \"./shared\";\nexport { badgePanelEnvelopeFromCapabilities } from \"./shared\";\n\nconst BADGE_EXTENSION_NAME = \"badge\";\nconst BADGE_EXTENSION_PACKAGE = \"@opentray/ext-badge\";\n\nexport interface BadgeExtensionOptions {\n mountId?: string;\n path?: string;\n platform?: BadgePlatform;\n}\n\nexport interface BadgeExtensionCapability extends BadgeHandle {\n readonly platform: BadgePlatform;\n readonly getPanelEnvelope: () => Promise<BadgePanelEnvelope>;\n}\n\nexport const attachBadge = (tray: TrayHandle, options: BadgeExtensionOptions = {}): BadgeExtensionCapability => {\n const platform = options.platform ?? process.platform as BadgePlatform;\n const extension = tray.extend(BadgeExt, options);\n return {\n ...extension,\n platform,\n getPanelEnvelope: async () => badgePanelEnvelopeFromCapabilities(await extension.getCapabilities()),\n };\n};\n\nexport const BadgeExt = {\n name: BADGE_EXTENSION_NAME,\n path: BADGE_EXTENSION_PACKAGE,\n resolveMount(options: BadgeExtensionOptions | undefined) {\n return {\n ...(options?.mountId === undefined ? {} : { mountId: options.mountId }),\n ...(options?.path === undefined ? {} : { path: options.path }),\n };\n },\n extend(tray: TrayHandle, context, options: BadgeExtensionOptions | undefined): BadgeHandle {\n const requestedPlatform = options?.platform ?? (process.platform as BadgePlatform);\n const snapshot = defaultBadgeCapabilities(requestedPlatform);\n return createBadgeHandle(() => context.ensureLoaded(), tray, context.mountId, snapshot);\n },\n} satisfies import(\"opentray\").TrayExtension<BadgeHandle, BadgeExtensionOptions>;\n\nexport const isBadgeEvent = (event: ExtensionEnvelope): event is ExtensionEnvelope<BadgeEvent> =>\n event.scope.ext === BADGE_EXTENSION_NAME &&\n typeof event.data === \"object\" &&\n event.data !== null &&\n \"type\" in event.data;\n\nfunction createBadgeHandle(\n ensureLoaded: () => Promise<void>,\n tray: TrayHandle,\n mountId: string,\n seed: BadgeCapabilities,\n): BadgeHandle {\n const state: BadgeState = structuredClone(seed.state);\n const capabilities: BadgeCapabilityFamily = { ...seed.capabilities };\n const platform = seed.platform;\n const mode = seed.mode;\n const reason = seed.reason;\n\n const snapshot = (): BadgeCapabilities => ({\n platform,\n mode,\n capabilities: { ...capabilities },\n state: structuredClone(state),\n ...(reason === undefined ? {} : { reason }),\n });\n\n const rejectUnsupported = (family: keyof BadgeCapabilityFamily, op: string): never => {\n const support = capabilities[family];\n throw new Error(`${op} is ${support} on ${platform}`);\n };\n\n return {\n getCapabilities: async () => {\n await ensureLoaded();\n await tray.requestExtension(mountId, { type: \"getCapabilities\" });\n return snapshot();\n },\n setBadge: async (value: string) => {\n await ensureLoaded();\n const normalized = normalizeBadgeText(value);\n state.badgeText = normalized.badgeText;\n state.badgeCount = normalized.badgeCount;\n await tray.commandExtension(mountId, { type: \"setBadge\", value: normalized.badgeText });\n return snapshot();\n },\n clearBadge: async () => {\n await ensureLoaded();\n state.badgeText = \"\";\n state.badgeCount = null;\n await tray.commandExtension(mountId, { type: \"clearBadge\" });\n return snapshot();\n },\n setProgress: async (value: number, max?: number) => {\n await ensureLoaded();\n if (capabilities.progress === \"unsupported\") {\n rejectUnsupported(\"progress\", \"setProgress\");\n }\n const normalized = normalizeProgress(value, max);\n state.progressValue = normalized.value;\n state.progressMax = normalized.max;\n await tray.commandExtension(mountId, { type: \"setProgress\", value: normalized.value, max: normalized.max });\n return snapshot();\n },\n setProgressState: async (value: BadgeState[\"progressState\"]) => {\n await ensureLoaded();\n if (capabilities.progress === \"unsupported\") {\n rejectUnsupported(\"progress\", \"setProgressState\");\n }\n state.progressState = value;\n await tray.commandExtension(mountId, { type: \"setProgressState\", value });\n return snapshot();\n },\n setOverlayIcon: async (value: BadgeState[\"overlayIcon\"]) => {\n await ensureLoaded();\n state.overlayIcon = value;\n await tray.commandExtension(mountId, { type: \"setOverlayIcon\", value });\n return snapshot();\n },\n setAttention: async (value: boolean) => {\n await ensureLoaded();\n state.attention = Boolean(value);\n await tray.commandExtension(mountId, { type: \"setAttention\", value: Boolean(value) });\n return snapshot();\n },\n showPanel: async () => {\n await ensureLoaded();\n await tray.commandExtension(mountId, { type: \"showPanel\" });\n return snapshot();\n },\n hidePanel: async () => {\n await ensureLoaded();\n await tray.commandExtension(mountId, { type: \"hidePanel\" });\n return snapshot();\n },\n reset: async () => {\n await ensureLoaded();\n Object.assign(state, seed.state);\n await tray.commandExtension(mountId, { type: \"reset\" });\n return snapshot();\n },\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,26 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opentray/ext-badge",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Official OpenTray
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official OpenTray status extension backed by the OpenTray extension ABI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/jixoai/opentray"
|
|
10
|
+
},
|
|
7
11
|
"exports": {
|
|
8
12
|
".": {
|
|
9
|
-
"types": "./dist/index.d.
|
|
10
|
-
"import": "./dist/index.
|
|
13
|
+
"types": "./dist/index.d.mts",
|
|
14
|
+
"import": "./dist/index.mjs"
|
|
11
15
|
},
|
|
12
16
|
"./package.json": "./package.json"
|
|
13
17
|
},
|
|
14
18
|
"files": [
|
|
15
19
|
"dist",
|
|
16
|
-
"platforms",
|
|
17
20
|
"README.md"
|
|
18
21
|
],
|
|
19
22
|
"sideEffects": false,
|
|
20
23
|
"peerDependencies": {
|
|
21
24
|
"opentray": ">=0.0.0"
|
|
22
25
|
},
|
|
26
|
+
"optionalDependencies": {
|
|
27
|
+
"@opentray/ext-badge-darwin-arm64": "0.1.0",
|
|
28
|
+
"@opentray/ext-badge-darwin-x64": "0.1.0",
|
|
29
|
+
"@opentray/ext-badge-windows-arm64": "0.1.0",
|
|
30
|
+
"@opentray/ext-badge-windows-x64": "0.1.0"
|
|
31
|
+
},
|
|
23
32
|
"devDependencies": {
|
|
24
|
-
"
|
|
33
|
+
"tsdown": "^0.22.1",
|
|
34
|
+
"typescript": "^6.0.3",
|
|
35
|
+
"vitest": "^4.1.7",
|
|
36
|
+
"@opentray/spec": "0.5.0",
|
|
37
|
+
"opentray": "0.7.0"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsdown src/index.ts --format esm --dts",
|
|
41
|
+
"test": "vitest run",
|
|
42
|
+
"typecheck": "tsc --noEmit"
|
|
25
43
|
}
|
|
26
44
|
}
|