@mywallpaper/addon-sdk 2.0.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/LICENSE +21 -0
- package/README.md +581 -0
- package/dist/index.d.mts +1087 -0
- package/dist/index.d.ts +1087 -0
- package/dist/index.js +293 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +274 -0
- package/dist/index.mjs.map +1 -0
- package/dist/manifest.d.mts +851 -0
- package/dist/manifest.d.ts +851 -0
- package/dist/manifest.js +170 -0
- package/dist/manifest.js.map +1 -0
- package/dist/manifest.mjs +164 -0
- package/dist/manifest.mjs.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,851 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mywallpaper/addon-sdk - TypeScript Types v2.0
|
|
3
|
+
*
|
|
4
|
+
* Complete type definitions for building MyWallpaper addons.
|
|
5
|
+
* These types describe the runtime API available via window.MyWallpaper
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import type { MyWallpaperAPI, SystemEventType } from '@mywallpaper/addon-sdk'
|
|
10
|
+
*
|
|
11
|
+
* // Type-safe event handling
|
|
12
|
+
* const api = window.MyWallpaper
|
|
13
|
+
* api.onEvent('viewport:resize', ({ width, height }) => {
|
|
14
|
+
* console.log(`Resized to ${width}x${height}`)
|
|
15
|
+
* })
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* System events that addons can subscribe to.
|
|
20
|
+
* Subscribe via `window.MyWallpaper.onEvent(eventType, callback)`
|
|
21
|
+
*/
|
|
22
|
+
type SystemEventType = 'viewport:resize' | 'theme:change' | 'visibility:change' | 'layer:focus' | 'layer:blur' | 'app:idle' | 'app:active';
|
|
23
|
+
/**
|
|
24
|
+
* Event data payloads for each system event type.
|
|
25
|
+
* TypeScript will infer the correct payload type based on event name.
|
|
26
|
+
*/
|
|
27
|
+
interface SystemEventData {
|
|
28
|
+
'viewport:resize': {
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
aspectRatio: string;
|
|
32
|
+
};
|
|
33
|
+
'theme:change': {
|
|
34
|
+
theme: 'light' | 'dark' | 'system';
|
|
35
|
+
};
|
|
36
|
+
'visibility:change': {
|
|
37
|
+
visible: boolean;
|
|
38
|
+
};
|
|
39
|
+
'layer:focus': {
|
|
40
|
+
layerId: string;
|
|
41
|
+
};
|
|
42
|
+
'layer:blur': {
|
|
43
|
+
layerId: string;
|
|
44
|
+
};
|
|
45
|
+
'app:idle': {
|
|
46
|
+
idleSeconds: number;
|
|
47
|
+
};
|
|
48
|
+
'app:active': Record<string, never>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Capabilities that an addon can declare.
|
|
52
|
+
* Declared in `manifest.json` and sent via `ready()` call.
|
|
53
|
+
*/
|
|
54
|
+
type AddonCapability = 'hot-reload' | 'system-events' | 'storage' | 'audio' | 'network';
|
|
55
|
+
/**
|
|
56
|
+
* OAuth providers supported for addon API access.
|
|
57
|
+
* Use with `oauth:{provider}` permission format.
|
|
58
|
+
*/
|
|
59
|
+
type OAuthProvider = 'github' | 'google' | 'discord' | 'spotify' | 'twitch';
|
|
60
|
+
/**
|
|
61
|
+
* OAuth permission types for accessing provider APIs.
|
|
62
|
+
* Format: `oauth:{provider}`
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const permission: OAuthPermissionType = 'oauth:github'
|
|
67
|
+
* await api.requestPermission(permission, 'Display your GitHub profile')
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
type OAuthPermissionType = 'oauth:github' | 'oauth:google' | 'oauth:discord' | 'oauth:spotify' | 'oauth:twitch';
|
|
71
|
+
/**
|
|
72
|
+
* Permission types that addons can request.
|
|
73
|
+
* Requested via `window.MyWallpaper.requestPermission(type, reason)`
|
|
74
|
+
*/
|
|
75
|
+
type PermissionType = 'storage' | 'cpu-high' | 'network' | 'audio' | 'notifications' | OAuthPermissionType;
|
|
76
|
+
/**
|
|
77
|
+
* Storage API for persistent key-value data.
|
|
78
|
+
* Access via `window.MyWallpaper.storage`
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* const { storage } = window.MyWallpaper
|
|
83
|
+
*
|
|
84
|
+
* // Save user preferences
|
|
85
|
+
* await storage.set('preferences', { darkMode: true, fontSize: 14 })
|
|
86
|
+
*
|
|
87
|
+
* // Retrieve data
|
|
88
|
+
* const prefs = await storage.get<{ darkMode: boolean; fontSize: number }>('preferences')
|
|
89
|
+
*
|
|
90
|
+
* // Check storage usage
|
|
91
|
+
* const usedBytes = await storage.size()
|
|
92
|
+
* console.log(`Using ${usedBytes} bytes of storage`)
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
interface StorageAPI {
|
|
96
|
+
/**
|
|
97
|
+
* Get a value by key.
|
|
98
|
+
* Returns null if key doesn't exist.
|
|
99
|
+
*/
|
|
100
|
+
get<T = unknown>(key: string): Promise<T | null>;
|
|
101
|
+
/**
|
|
102
|
+
* Set a value for a key.
|
|
103
|
+
* Throws if quota exceeded (default 1MB per addon).
|
|
104
|
+
*/
|
|
105
|
+
set<T = unknown>(key: string, value: T): Promise<void>;
|
|
106
|
+
/**
|
|
107
|
+
* Delete a key and its value.
|
|
108
|
+
*/
|
|
109
|
+
delete(key: string): Promise<void>;
|
|
110
|
+
/**
|
|
111
|
+
* Clear all stored data for this addon.
|
|
112
|
+
*/
|
|
113
|
+
clear(): Promise<void>;
|
|
114
|
+
/**
|
|
115
|
+
* Get all storage keys.
|
|
116
|
+
*/
|
|
117
|
+
keys(): Promise<string[]>;
|
|
118
|
+
/**
|
|
119
|
+
* Get total storage usage in bytes.
|
|
120
|
+
*/
|
|
121
|
+
size(): Promise<number>;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Response from network.fetch() requests.
|
|
125
|
+
* Similar to native Response but with pre-parsed data.
|
|
126
|
+
*/
|
|
127
|
+
interface NetworkResponse {
|
|
128
|
+
/** Whether the request was successful (status 200-299) */
|
|
129
|
+
ok: boolean;
|
|
130
|
+
/** HTTP status code */
|
|
131
|
+
status: number;
|
|
132
|
+
/** HTTP status text */
|
|
133
|
+
statusText: string;
|
|
134
|
+
/** Response headers */
|
|
135
|
+
headers: Record<string, string>;
|
|
136
|
+
/** Response data (auto-parsed JSON, text, or base64 for binary) */
|
|
137
|
+
data: unknown;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Network API for making HTTP requests through the secure host proxy.
|
|
141
|
+
* Access via `window.MyWallpaper.network`
|
|
142
|
+
*
|
|
143
|
+
* **IMPORTANT:** Direct `fetch()` and `XMLHttpRequest` are blocked for security.
|
|
144
|
+
* All network requests MUST go through this API.
|
|
145
|
+
*
|
|
146
|
+
* **IMPORTANT:** Domains must be declared in your manifest.json:
|
|
147
|
+
* ```json
|
|
148
|
+
* {
|
|
149
|
+
* "permissions": {
|
|
150
|
+
* "network": { "domains": ["api.example.com"] }
|
|
151
|
+
* }
|
|
152
|
+
* }
|
|
153
|
+
* ```
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```typescript
|
|
157
|
+
* const { network } = window.MyWallpaper
|
|
158
|
+
*
|
|
159
|
+
* // Simple GET request
|
|
160
|
+
* const response = await network.fetch('https://api.weather.com/current')
|
|
161
|
+
* if (response.ok) {
|
|
162
|
+
* console.log(response.data)
|
|
163
|
+
* }
|
|
164
|
+
*
|
|
165
|
+
* // POST request with JSON body
|
|
166
|
+
* const result = await network.fetch('https://api.example.com/data', {
|
|
167
|
+
* method: 'POST',
|
|
168
|
+
* headers: { 'Content-Type': 'application/json' },
|
|
169
|
+
* body: JSON.stringify({ key: 'value' })
|
|
170
|
+
* })
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
interface NetworkAPI {
|
|
174
|
+
/**
|
|
175
|
+
* Make an HTTP request through the secure host proxy.
|
|
176
|
+
* Domain must be whitelisted in manifest.json permissions.
|
|
177
|
+
*
|
|
178
|
+
* @param url - Full URL to fetch (must be in allowed domains)
|
|
179
|
+
* @param options - Optional request options
|
|
180
|
+
* @returns Promise resolving to NetworkResponse
|
|
181
|
+
* @throws Error if domain not allowed or network permission not granted
|
|
182
|
+
*/
|
|
183
|
+
fetch(url: string, options?: {
|
|
184
|
+
/** HTTP method (default: GET) */
|
|
185
|
+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
186
|
+
/** Request headers */
|
|
187
|
+
headers?: Record<string, string>;
|
|
188
|
+
/** Request body (for POST/PUT/PATCH) */
|
|
189
|
+
body?: string;
|
|
190
|
+
}): Promise<NetworkResponse>;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Response from OAuth API proxy requests.
|
|
194
|
+
*/
|
|
195
|
+
interface OAuthResponse {
|
|
196
|
+
/** Whether the request was successful (status 200-299) */
|
|
197
|
+
ok: boolean;
|
|
198
|
+
/** HTTP status code from the provider API */
|
|
199
|
+
status: number;
|
|
200
|
+
/** Response headers from the provider API */
|
|
201
|
+
headers: Record<string, string>;
|
|
202
|
+
/** Response data (auto-parsed JSON) */
|
|
203
|
+
data: unknown;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Error when OAuth scopes are insufficient.
|
|
207
|
+
* Returned when the addon needs scopes the current token doesn't have.
|
|
208
|
+
*/
|
|
209
|
+
interface OAuthScopesError {
|
|
210
|
+
/** Error code: 'insufficient_scopes' */
|
|
211
|
+
error: 'insufficient_scopes';
|
|
212
|
+
/** Human-readable error message */
|
|
213
|
+
message: string;
|
|
214
|
+
/** The OAuth provider that needs re-authorization */
|
|
215
|
+
provider: OAuthProvider;
|
|
216
|
+
/** Scopes that are missing from the current token */
|
|
217
|
+
missingScopes: string[];
|
|
218
|
+
/** All scopes needed for this request */
|
|
219
|
+
requiredScopes: string[];
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* OAuth API for making authenticated requests to provider APIs.
|
|
223
|
+
* Access via `window.MyWallpaper.oauth`
|
|
224
|
+
*
|
|
225
|
+
* **How it works:**
|
|
226
|
+
* 1. User connects their OAuth provider in MyWallpaper settings
|
|
227
|
+
* 2. Your addon requests permission (e.g., `oauth:github`)
|
|
228
|
+
* 3. API calls are proxied through the host - your addon never sees the token
|
|
229
|
+
* 4. The host handles token refresh automatically
|
|
230
|
+
*
|
|
231
|
+
* **SECURITY:** Your addon NEVER sees OAuth tokens. All requests are proxied
|
|
232
|
+
* through the backend, which adds authentication headers server-side.
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* ```typescript
|
|
236
|
+
* const api = window.MyWallpaper
|
|
237
|
+
*
|
|
238
|
+
* // Request GitHub permission
|
|
239
|
+
* const granted = await api.requestPermission(
|
|
240
|
+
* 'oauth:github',
|
|
241
|
+
* 'Display your GitHub profile and repositories'
|
|
242
|
+
* )
|
|
243
|
+
*
|
|
244
|
+
* if (granted) {
|
|
245
|
+
* // Make API calls through the proxy
|
|
246
|
+
* const response = await api.oauth.request('github', '/user')
|
|
247
|
+
* if (response.ok) {
|
|
248
|
+
* console.log('Username:', response.data.login)
|
|
249
|
+
* }
|
|
250
|
+
* }
|
|
251
|
+
* ```
|
|
252
|
+
*/
|
|
253
|
+
interface OAuthAPI {
|
|
254
|
+
/**
|
|
255
|
+
* Make an authenticated request to an OAuth provider's API.
|
|
256
|
+
*
|
|
257
|
+
* @param provider - The OAuth provider (github, google, etc.)
|
|
258
|
+
* @param endpoint - The API endpoint path (e.g., '/user', '/user/repos')
|
|
259
|
+
* @param options - Optional request options
|
|
260
|
+
* @returns Promise resolving to OAuthResponse or OAuthScopesError
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* ```typescript
|
|
264
|
+
* // GET request
|
|
265
|
+
* const profile = await api.oauth.request('github', '/user')
|
|
266
|
+
*
|
|
267
|
+
* // POST request
|
|
268
|
+
* const repo = await api.oauth.request('github', '/user/repos', {
|
|
269
|
+
* method: 'POST',
|
|
270
|
+
* body: { name: 'new-repo', private: true }
|
|
271
|
+
* })
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
request(provider: OAuthProvider, endpoint: string, options?: {
|
|
275
|
+
/** HTTP method (default: GET) */
|
|
276
|
+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
277
|
+
/** Request body (for POST/PUT/PATCH) - will be JSON serialized */
|
|
278
|
+
body?: unknown;
|
|
279
|
+
/** Additional headers (Authorization is added automatically) */
|
|
280
|
+
headers?: Record<string, string>;
|
|
281
|
+
/** Required scopes for this request - triggers re-auth if missing */
|
|
282
|
+
requiredScopes?: string[];
|
|
283
|
+
}): Promise<OAuthResponse | OAuthScopesError>;
|
|
284
|
+
/**
|
|
285
|
+
* Check if the user has connected a specific OAuth provider.
|
|
286
|
+
*
|
|
287
|
+
* @param provider - The OAuth provider to check
|
|
288
|
+
* @returns Promise resolving to true if connected
|
|
289
|
+
*
|
|
290
|
+
* @example
|
|
291
|
+
* ```typescript
|
|
292
|
+
* if (await api.oauth.isConnected('github')) {
|
|
293
|
+
* console.log('GitHub is connected')
|
|
294
|
+
* } else {
|
|
295
|
+
* console.log('Please connect your GitHub account')
|
|
296
|
+
* }
|
|
297
|
+
* ```
|
|
298
|
+
*/
|
|
299
|
+
isConnected(provider: OAuthProvider): Promise<boolean>;
|
|
300
|
+
/**
|
|
301
|
+
* Get the scopes granted for a connected OAuth provider.
|
|
302
|
+
*
|
|
303
|
+
* @param provider - The OAuth provider
|
|
304
|
+
* @returns Promise resolving to array of granted scopes
|
|
305
|
+
*
|
|
306
|
+
* @example
|
|
307
|
+
* ```typescript
|
|
308
|
+
* const scopes = await api.oauth.getScopes('github')
|
|
309
|
+
* if (scopes.includes('repo')) {
|
|
310
|
+
* // Can access private repos
|
|
311
|
+
* }
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
314
|
+
getScopes(provider: OAuthProvider): Promise<string[]>;
|
|
315
|
+
/**
|
|
316
|
+
* Request re-authorization with additional scopes.
|
|
317
|
+
* Opens the OAuth flow with the new scopes, preserving existing ones.
|
|
318
|
+
*
|
|
319
|
+
* @param provider - The OAuth provider
|
|
320
|
+
* @param scopes - Additional scopes to request
|
|
321
|
+
* @param reason - User-friendly explanation of why scopes are needed
|
|
322
|
+
* @returns Promise resolving to true if re-auth was successful
|
|
323
|
+
*
|
|
324
|
+
* @example
|
|
325
|
+
* ```typescript
|
|
326
|
+
* // Request repo scope for private repository access
|
|
327
|
+
* const granted = await api.oauth.requestScopes(
|
|
328
|
+
* 'github',
|
|
329
|
+
* ['repo'],
|
|
330
|
+
* 'Access your private repositories to display commit stats'
|
|
331
|
+
* )
|
|
332
|
+
*
|
|
333
|
+
* if (granted) {
|
|
334
|
+
* // Now we can access private repos
|
|
335
|
+
* const repos = await api.oauth.request('github', '/user/repos')
|
|
336
|
+
* }
|
|
337
|
+
* ```
|
|
338
|
+
*/
|
|
339
|
+
requestScopes(provider: OAuthProvider, scopes: string[], reason: string): Promise<boolean>;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Callback for settings changes (hot-reload).
|
|
343
|
+
* @param settings - Complete settings object with new values
|
|
344
|
+
* @param changedKeys - Array of keys that changed (for optimization)
|
|
345
|
+
*/
|
|
346
|
+
interface SettingsCallback {
|
|
347
|
+
(settings: Record<string, unknown>, changedKeys: string[]): void;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Generic callback for system events.
|
|
351
|
+
* @param data - Event-specific payload
|
|
352
|
+
*/
|
|
353
|
+
interface EventCallback<T = unknown> {
|
|
354
|
+
(data: T): void;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Options passed to `ready()` call.
|
|
358
|
+
*/
|
|
359
|
+
interface ReadyOptions {
|
|
360
|
+
/** Capabilities this addon supports */
|
|
361
|
+
capabilities?: AddonCapability[];
|
|
362
|
+
/** System events this addon wants to receive */
|
|
363
|
+
subscribedEvents?: SystemEventType[];
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* The main MyWallpaper API available on `window.MyWallpaper`.
|
|
367
|
+
*
|
|
368
|
+
* This API is automatically injected when your addon loads.
|
|
369
|
+
* No imports needed - just use `window.MyWallpaper` in your code.
|
|
370
|
+
*
|
|
371
|
+
* @example
|
|
372
|
+
* ```typescript
|
|
373
|
+
* // Type-safe access
|
|
374
|
+
* declare const window: Window & { MyWallpaper: MyWallpaperAPI }
|
|
375
|
+
*
|
|
376
|
+
* const { config, storage } = window.MyWallpaper
|
|
377
|
+
*
|
|
378
|
+
* // React to settings changes
|
|
379
|
+
* window.MyWallpaper.onSettingsChange((settings, changedKeys) => {
|
|
380
|
+
* if (changedKeys.includes('primaryColor')) {
|
|
381
|
+
* updateTheme(settings.primaryColor as string)
|
|
382
|
+
* }
|
|
383
|
+
* })
|
|
384
|
+
*
|
|
385
|
+
* // Signal addon is ready
|
|
386
|
+
* window.MyWallpaper.ready({
|
|
387
|
+
* capabilities: ['hot-reload', 'storage'],
|
|
388
|
+
* subscribedEvents: ['viewport:resize', 'theme:change']
|
|
389
|
+
* })
|
|
390
|
+
* ```
|
|
391
|
+
*/
|
|
392
|
+
interface MyWallpaperAPI {
|
|
393
|
+
/**
|
|
394
|
+
* Current configuration values from user settings.
|
|
395
|
+
* Updated automatically when settings change.
|
|
396
|
+
*/
|
|
397
|
+
readonly config: Record<string, unknown>;
|
|
398
|
+
/**
|
|
399
|
+
* Unique identifier for this addon layer instance.
|
|
400
|
+
* Useful for multi-instance scenarios.
|
|
401
|
+
*/
|
|
402
|
+
readonly layerId: string;
|
|
403
|
+
/**
|
|
404
|
+
* SDK version. Always '2.0' for current API.
|
|
405
|
+
*/
|
|
406
|
+
readonly version: '2.0';
|
|
407
|
+
/**
|
|
408
|
+
* Register callback for settings changes (hot-reload).
|
|
409
|
+
* Called when user modifies addon settings in the UI.
|
|
410
|
+
*
|
|
411
|
+
* @param callback - Function called with new settings and changed keys
|
|
412
|
+
*/
|
|
413
|
+
onSettingsChange(callback: SettingsCallback): void;
|
|
414
|
+
/**
|
|
415
|
+
* Subscribe to a system event.
|
|
416
|
+
* @param event - Event type to subscribe to
|
|
417
|
+
* @param callback - Function called when event fires
|
|
418
|
+
*/
|
|
419
|
+
onEvent<E extends SystemEventType>(event: E, callback: EventCallback<SystemEventData[E]>): void;
|
|
420
|
+
/**
|
|
421
|
+
* Unsubscribe from a system event.
|
|
422
|
+
* @param event - Event type to unsubscribe from
|
|
423
|
+
* @param callback - Same callback function passed to onEvent
|
|
424
|
+
*/
|
|
425
|
+
offEvent<E extends SystemEventType>(event: E, callback: EventCallback<SystemEventData[E]>): void;
|
|
426
|
+
/**
|
|
427
|
+
* Called when addon is mounted (first load).
|
|
428
|
+
*/
|
|
429
|
+
onMount(callback: () => void): void;
|
|
430
|
+
/**
|
|
431
|
+
* Called when addon is unmounted (removed from page).
|
|
432
|
+
* Use for cleanup (event listeners, timers, etc.).
|
|
433
|
+
*/
|
|
434
|
+
onUnmount(callback: () => void): void;
|
|
435
|
+
/**
|
|
436
|
+
* Called when addon is paused (hidden, tab inactive).
|
|
437
|
+
* Use to pause animations and reduce resource usage.
|
|
438
|
+
*/
|
|
439
|
+
onPause(callback: () => void): void;
|
|
440
|
+
/**
|
|
441
|
+
* Called when addon is resumed (visible again).
|
|
442
|
+
* Use to restart animations and refresh data.
|
|
443
|
+
*/
|
|
444
|
+
onResume(callback: () => void): void;
|
|
445
|
+
/**
|
|
446
|
+
* Signal that the addon is ready and initialized.
|
|
447
|
+
* **Must be called** after your addon loads and initializes.
|
|
448
|
+
*
|
|
449
|
+
* @param options - Capabilities and event subscriptions
|
|
450
|
+
*
|
|
451
|
+
* @example
|
|
452
|
+
* ```typescript
|
|
453
|
+
* // After DOM is ready and addon is initialized
|
|
454
|
+
* window.MyWallpaper.ready({
|
|
455
|
+
* capabilities: ['hot-reload'],
|
|
456
|
+
* subscribedEvents: ['viewport:resize']
|
|
457
|
+
* })
|
|
458
|
+
* ```
|
|
459
|
+
*/
|
|
460
|
+
ready(options?: ReadyOptions): void;
|
|
461
|
+
/**
|
|
462
|
+
* Signal that visual rendering is complete.
|
|
463
|
+
* Call after images are loaded, canvas drawn, animations ready.
|
|
464
|
+
* Used by screenshot service to capture addon at right moment.
|
|
465
|
+
*
|
|
466
|
+
* @example
|
|
467
|
+
* ```typescript
|
|
468
|
+
* async function init() {
|
|
469
|
+
* await loadAllImages()
|
|
470
|
+
* drawCanvas()
|
|
471
|
+
* window.MyWallpaper.renderComplete()
|
|
472
|
+
* }
|
|
473
|
+
* ```
|
|
474
|
+
*/
|
|
475
|
+
renderComplete(): void;
|
|
476
|
+
/**
|
|
477
|
+
* Request a permission from the user.
|
|
478
|
+
* Shows a permission modal explaining why the permission is needed.
|
|
479
|
+
*
|
|
480
|
+
* @param permission - Permission type to request
|
|
481
|
+
* @param reason - User-friendly explanation of why permission is needed
|
|
482
|
+
* @returns Promise resolving to true if granted, false if denied
|
|
483
|
+
*
|
|
484
|
+
* @example
|
|
485
|
+
* ```typescript
|
|
486
|
+
* const granted = await window.MyWallpaper.requestPermission(
|
|
487
|
+
* 'storage',
|
|
488
|
+
* 'Save your preferences across sessions'
|
|
489
|
+
* )
|
|
490
|
+
* if (granted) {
|
|
491
|
+
* await window.MyWallpaper.storage.set('initialized', true)
|
|
492
|
+
* }
|
|
493
|
+
* ```
|
|
494
|
+
*/
|
|
495
|
+
requestPermission(permission: PermissionType, reason: string): Promise<boolean>;
|
|
496
|
+
/**
|
|
497
|
+
* Persistent key-value storage API.
|
|
498
|
+
* Requires 'storage' permission to be granted first.
|
|
499
|
+
* Data syncs across devices for logged-in users.
|
|
500
|
+
*/
|
|
501
|
+
storage: StorageAPI;
|
|
502
|
+
/**
|
|
503
|
+
* Network API for making HTTP requests through the secure host proxy.
|
|
504
|
+
* Requires 'network' permission and domains declared in manifest.
|
|
505
|
+
*
|
|
506
|
+
* **SECURITY:** Direct `fetch()` and `XMLHttpRequest` are blocked.
|
|
507
|
+
* This is the ONLY way to make network requests from addons.
|
|
508
|
+
*
|
|
509
|
+
* @example
|
|
510
|
+
* ```typescript
|
|
511
|
+
* // First, request permission
|
|
512
|
+
* const granted = await MyWallpaper.requestPermission('network', 'Fetch weather data')
|
|
513
|
+
* if (granted) {
|
|
514
|
+
* const response = await MyWallpaper.network.fetch('https://api.weather.com/current')
|
|
515
|
+
* if (response.ok) {
|
|
516
|
+
* console.log(response.data)
|
|
517
|
+
* }
|
|
518
|
+
* }
|
|
519
|
+
* ```
|
|
520
|
+
*/
|
|
521
|
+
network: NetworkAPI;
|
|
522
|
+
/**
|
|
523
|
+
* OAuth API for making authenticated requests to provider APIs.
|
|
524
|
+
* Requires corresponding oauth permission (e.g., 'oauth:github').
|
|
525
|
+
*
|
|
526
|
+
* **SECURITY:** Your addon NEVER sees OAuth tokens. All requests are
|
|
527
|
+
* proxied through the backend which adds authentication server-side.
|
|
528
|
+
*
|
|
529
|
+
* **How it works:**
|
|
530
|
+
* 1. User connects their OAuth provider in MyWallpaper settings
|
|
531
|
+
* 2. Your addon requests permission (e.g., `oauth:github`)
|
|
532
|
+
* 3. API calls are proxied through the host
|
|
533
|
+
* 4. The host handles token refresh automatically
|
|
534
|
+
*
|
|
535
|
+
* @example
|
|
536
|
+
* ```typescript
|
|
537
|
+
* // First, request GitHub permission
|
|
538
|
+
* const granted = await MyWallpaper.requestPermission(
|
|
539
|
+
* 'oauth:github',
|
|
540
|
+
* 'Display your GitHub profile'
|
|
541
|
+
* )
|
|
542
|
+
*
|
|
543
|
+
* if (granted) {
|
|
544
|
+
* // Make authenticated API calls
|
|
545
|
+
* const response = await MyWallpaper.oauth.request('github', '/user')
|
|
546
|
+
* if (response.ok) {
|
|
547
|
+
* console.log('GitHub username:', response.data.login)
|
|
548
|
+
* }
|
|
549
|
+
* }
|
|
550
|
+
* ```
|
|
551
|
+
*/
|
|
552
|
+
oauth: OAuthAPI;
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Augments the global Window interface with MyWallpaper API.
|
|
556
|
+
* Allows `window.MyWallpaper` to be used without type assertions.
|
|
557
|
+
*/
|
|
558
|
+
declare global {
|
|
559
|
+
interface Window {
|
|
560
|
+
/** Layer ID injected before addon loads */
|
|
561
|
+
__MYWALLPAPER_LAYER_ID__?: string;
|
|
562
|
+
/** Initial config injected before addon loads */
|
|
563
|
+
__MYWALLPAPER_INITIAL_CONFIG__?: Record<string, unknown>;
|
|
564
|
+
/** The main MyWallpaper API */
|
|
565
|
+
MyWallpaper: MyWallpaperAPI;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* @mywallpaper/addon-sdk - Manifest Schema & Validation
|
|
571
|
+
*
|
|
572
|
+
* Defines the addon manifest format (manifest.json) and provides
|
|
573
|
+
* Zod-based validation for type safety at runtime.
|
|
574
|
+
*
|
|
575
|
+
* @example
|
|
576
|
+
* ```typescript
|
|
577
|
+
* import { validateManifest, type AddonManifest } from '@mywallpaper/addon-sdk/manifest'
|
|
578
|
+
*
|
|
579
|
+
* const manifest: AddonManifest = {
|
|
580
|
+
* name: 'My Addon',
|
|
581
|
+
* version: '1.0.0',
|
|
582
|
+
* description: 'A cool addon',
|
|
583
|
+
* settings: {
|
|
584
|
+
* primaryColor: { type: 'color', default: '#ffffff', label: 'Primary Color' }
|
|
585
|
+
* }
|
|
586
|
+
* }
|
|
587
|
+
*
|
|
588
|
+
* // Validate at runtime
|
|
589
|
+
* const result = validateManifest(manifest)
|
|
590
|
+
* if (!result.success) {
|
|
591
|
+
* console.error('Invalid manifest:', result.errors)
|
|
592
|
+
* }
|
|
593
|
+
* ```
|
|
594
|
+
*/
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Available setting types for addon configuration.
|
|
598
|
+
*/
|
|
599
|
+
type SettingType = 'string' | 'number' | 'boolean' | 'color' | 'select' | 'range' | 'image' | 'section' | 'textarea' | 'radio' | 'gradient' | 'vector2' | 'button' | 'multi-select';
|
|
600
|
+
/**
|
|
601
|
+
* Definition for a single addon setting.
|
|
602
|
+
*
|
|
603
|
+
* @example
|
|
604
|
+
* ```typescript
|
|
605
|
+
* const colorSetting: SettingDefinition = {
|
|
606
|
+
* type: 'color',
|
|
607
|
+
* label: 'Background Color',
|
|
608
|
+
* description: 'Choose a background color',
|
|
609
|
+
* default: '#000000'
|
|
610
|
+
* }
|
|
611
|
+
*
|
|
612
|
+
* const speedSetting: SettingDefinition = {
|
|
613
|
+
* type: 'range',
|
|
614
|
+
* label: 'Animation Speed',
|
|
615
|
+
* min: 0.1,
|
|
616
|
+
* max: 10,
|
|
617
|
+
* step: 0.1,
|
|
618
|
+
* default: 1
|
|
619
|
+
* }
|
|
620
|
+
* ```
|
|
621
|
+
*/
|
|
622
|
+
interface SettingDefinition {
|
|
623
|
+
/** Setting type determines the UI control */
|
|
624
|
+
type: SettingType;
|
|
625
|
+
/** Human-readable label */
|
|
626
|
+
label?: string;
|
|
627
|
+
/** Help text shown below the control */
|
|
628
|
+
description?: string;
|
|
629
|
+
/** Default value (type depends on setting type) */
|
|
630
|
+
default?: unknown;
|
|
631
|
+
/** Options for select, radio, multi-select types */
|
|
632
|
+
options?: Array<{
|
|
633
|
+
value: string;
|
|
634
|
+
label: string;
|
|
635
|
+
}>;
|
|
636
|
+
/** Minimum value for range type */
|
|
637
|
+
min?: number;
|
|
638
|
+
/** Maximum value for range type */
|
|
639
|
+
max?: number;
|
|
640
|
+
/** Step increment for range type */
|
|
641
|
+
step?: number;
|
|
642
|
+
/** Label text on button */
|
|
643
|
+
buttonLabel?: string;
|
|
644
|
+
/** Axis labels for vector types, e.g., ['X', 'Y'] */
|
|
645
|
+
axisLabels?: [string, string] | [string, string, string];
|
|
646
|
+
/** Maximum number of selections */
|
|
647
|
+
maxItems?: number;
|
|
648
|
+
/** Only show if another setting has a specific value */
|
|
649
|
+
showIf?: {
|
|
650
|
+
setting: string;
|
|
651
|
+
equals: unknown;
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Default layout configuration for addon positioning.
|
|
656
|
+
*/
|
|
657
|
+
interface AddonDefaultLayout {
|
|
658
|
+
/** X position as percentage of viewport width (0-100) */
|
|
659
|
+
xPercent?: number;
|
|
660
|
+
/** Y position as percentage of viewport height (0-100) */
|
|
661
|
+
yPercent?: number;
|
|
662
|
+
/** Width as percentage of viewport width (0-100) */
|
|
663
|
+
widthPercent?: number;
|
|
664
|
+
/** Height as percentage of viewport height (0-100) */
|
|
665
|
+
heightPercent?: number;
|
|
666
|
+
/** Rotation in degrees */
|
|
667
|
+
rotation?: number;
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* The complete addon manifest schema.
|
|
671
|
+
* This defines your addon's identity, settings, and capabilities.
|
|
672
|
+
*
|
|
673
|
+
* @example
|
|
674
|
+
* ```json
|
|
675
|
+
* {
|
|
676
|
+
* "name": "Weather Widget",
|
|
677
|
+
* "version": "1.2.0",
|
|
678
|
+
* "description": "Displays current weather conditions",
|
|
679
|
+
* "author": "Jane Developer",
|
|
680
|
+
* "categories": ["utilities", "weather"],
|
|
681
|
+
* "capabilities": {
|
|
682
|
+
* "hotReload": true,
|
|
683
|
+
* "systemEvents": ["viewport:resize", "theme:change"]
|
|
684
|
+
* },
|
|
685
|
+
* "permissions": {
|
|
686
|
+
* "storage": { "quota": 512 },
|
|
687
|
+
* "network": { "domains": ["api.openweathermap.org"] }
|
|
688
|
+
* },
|
|
689
|
+
* "settings": {
|
|
690
|
+
* "apiKey": {
|
|
691
|
+
* "type": "string",
|
|
692
|
+
* "label": "API Key",
|
|
693
|
+
* "description": "Get your key at openweathermap.org"
|
|
694
|
+
* },
|
|
695
|
+
* "unit": {
|
|
696
|
+
* "type": "select",
|
|
697
|
+
* "label": "Temperature Unit",
|
|
698
|
+
* "default": "celsius",
|
|
699
|
+
* "options": [
|
|
700
|
+
* { "value": "celsius", "label": "Celsius" },
|
|
701
|
+
* { "value": "fahrenheit", "label": "Fahrenheit" }
|
|
702
|
+
* ]
|
|
703
|
+
* }
|
|
704
|
+
* }
|
|
705
|
+
* }
|
|
706
|
+
* ```
|
|
707
|
+
*/
|
|
708
|
+
interface AddonManifest {
|
|
709
|
+
/** Addon name (displayed in UI) */
|
|
710
|
+
name: string;
|
|
711
|
+
/** Semantic version (e.g., "1.0.0") */
|
|
712
|
+
version: string;
|
|
713
|
+
/** Short description of the addon */
|
|
714
|
+
description?: string;
|
|
715
|
+
/** Author name or organization */
|
|
716
|
+
author?: string;
|
|
717
|
+
/** Addon type/category for filtering */
|
|
718
|
+
type?: string;
|
|
719
|
+
/** Categories for discoverability */
|
|
720
|
+
categories?: string[];
|
|
721
|
+
/** License (e.g., "MIT", "Apache-2.0") */
|
|
722
|
+
license?: string;
|
|
723
|
+
/** Homepage or documentation URL */
|
|
724
|
+
homepage?: string;
|
|
725
|
+
/** Repository URL */
|
|
726
|
+
repository?: string;
|
|
727
|
+
/**
|
|
728
|
+
* User-configurable settings.
|
|
729
|
+
* Key is the setting ID, value is the definition.
|
|
730
|
+
*/
|
|
731
|
+
settings?: Record<string, SettingDefinition>;
|
|
732
|
+
/**
|
|
733
|
+
* Addon capabilities and event subscriptions.
|
|
734
|
+
*/
|
|
735
|
+
capabilities?: {
|
|
736
|
+
/** Supports settings changes without full reload */
|
|
737
|
+
hotReload?: boolean;
|
|
738
|
+
/** System events the addon wants to receive */
|
|
739
|
+
systemEvents?: SystemEventType[];
|
|
740
|
+
};
|
|
741
|
+
/**
|
|
742
|
+
* Permissions the addon will request.
|
|
743
|
+
* Declaring them here improves UX by showing upfront.
|
|
744
|
+
*/
|
|
745
|
+
permissions?: {
|
|
746
|
+
/** Storage permission with quota in KB */
|
|
747
|
+
storage?: {
|
|
748
|
+
quota: number;
|
|
749
|
+
};
|
|
750
|
+
/** CPU usage limit */
|
|
751
|
+
cpu?: {
|
|
752
|
+
limit: 'low' | 'medium' | 'high';
|
|
753
|
+
};
|
|
754
|
+
/** Network access with allowed domains */
|
|
755
|
+
network?: {
|
|
756
|
+
domains: string[];
|
|
757
|
+
};
|
|
758
|
+
/** Audio playback permission */
|
|
759
|
+
audio?: boolean;
|
|
760
|
+
/** Notification permission */
|
|
761
|
+
notifications?: boolean;
|
|
762
|
+
};
|
|
763
|
+
/**
|
|
764
|
+
* Default layout for automatic positioning.
|
|
765
|
+
* Used when addon is first added to a wallpaper.
|
|
766
|
+
*/
|
|
767
|
+
defaultLayout?: AddonDefaultLayout;
|
|
768
|
+
/**
|
|
769
|
+
* OAuth providers and scopes for authenticated addons.
|
|
770
|
+
*
|
|
771
|
+
* Declare the OAuth providers your addon needs and the scopes it requires.
|
|
772
|
+
* Scopes are requested incrementally - users can connect with basic scopes
|
|
773
|
+
* first, and your addon can request additional scopes when needed.
|
|
774
|
+
*
|
|
775
|
+
* @example
|
|
776
|
+
* ```json
|
|
777
|
+
* {
|
|
778
|
+
* "permissions": {
|
|
779
|
+
* "oauth": {
|
|
780
|
+
* "required": [
|
|
781
|
+
* { "provider": "github", "scopes": ["read:user"] }
|
|
782
|
+
* ],
|
|
783
|
+
* "optional": [
|
|
784
|
+
* { "provider": "github", "scopes": ["repo"] }
|
|
785
|
+
* ]
|
|
786
|
+
* }
|
|
787
|
+
* }
|
|
788
|
+
* }
|
|
789
|
+
* ```
|
|
790
|
+
*/
|
|
791
|
+
oauth?: {
|
|
792
|
+
/** OAuth providers required for the addon to function */
|
|
793
|
+
required?: Array<{
|
|
794
|
+
provider: OAuthProvider;
|
|
795
|
+
scopes: string[];
|
|
796
|
+
/** Reason shown to user when requesting this permission */
|
|
797
|
+
reason?: string;
|
|
798
|
+
}>;
|
|
799
|
+
/** OAuth providers that enhance functionality but aren't required */
|
|
800
|
+
optional?: Array<{
|
|
801
|
+
provider: OAuthProvider;
|
|
802
|
+
scopes: string[];
|
|
803
|
+
/** Reason shown to user when requesting this permission */
|
|
804
|
+
reason?: string;
|
|
805
|
+
}>;
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* Validation result type.
|
|
810
|
+
*/
|
|
811
|
+
interface ValidationResult {
|
|
812
|
+
success: boolean;
|
|
813
|
+
errors?: string[];
|
|
814
|
+
manifest?: AddonManifest;
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Validates an addon manifest object.
|
|
818
|
+
*
|
|
819
|
+
* @param input - The manifest object to validate
|
|
820
|
+
* @returns ValidationResult with success status and any errors
|
|
821
|
+
*
|
|
822
|
+
* @example
|
|
823
|
+
* ```typescript
|
|
824
|
+
* const result = validateManifest(myManifest)
|
|
825
|
+
* if (!result.success) {
|
|
826
|
+
* result.errors?.forEach(err => console.error(err))
|
|
827
|
+
* }
|
|
828
|
+
* ```
|
|
829
|
+
*/
|
|
830
|
+
declare function validateManifest(input: unknown): ValidationResult;
|
|
831
|
+
/**
|
|
832
|
+
* Type guard to check if an object is a valid AddonManifest.
|
|
833
|
+
*
|
|
834
|
+
* @param input - Object to check
|
|
835
|
+
* @returns True if input is a valid AddonManifest
|
|
836
|
+
*/
|
|
837
|
+
declare function isValidManifest(input: unknown): input is AddonManifest;
|
|
838
|
+
/**
|
|
839
|
+
* Check if manifest declares hot-reload capability.
|
|
840
|
+
*/
|
|
841
|
+
declare function supportsHotReload(manifest: AddonManifest): boolean;
|
|
842
|
+
/**
|
|
843
|
+
* Get system events the addon subscribes to.
|
|
844
|
+
*/
|
|
845
|
+
declare function getSubscribedEvents(manifest: AddonManifest): SystemEventType[];
|
|
846
|
+
/**
|
|
847
|
+
* Get required permissions from manifest.
|
|
848
|
+
*/
|
|
849
|
+
declare function getRequiredPermissions(manifest: AddonManifest): PermissionType[];
|
|
850
|
+
|
|
851
|
+
export { type AddonDefaultLayout, type AddonManifest, type SettingDefinition, type SettingType, type ValidationResult, getRequiredPermissions, getSubscribedEvents, isValidManifest, supportsHotReload, validateManifest };
|