@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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 MyWallpaper Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
# @mywallpaper/addon-sdk
|
|
2
|
+
|
|
3
|
+
Official SDK for building MyWallpaper addons.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @mywallpaper/addon-sdk
|
|
9
|
+
# or
|
|
10
|
+
yarn add @mywallpaper/addon-sdk
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @mywallpaper/addon-sdk
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
### 1. Create your addon files
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
my-addon/
|
|
21
|
+
├── manifest.json # Addon metadata and settings
|
|
22
|
+
├── index.html # Main HTML file
|
|
23
|
+
├── styles.css # (optional) Styles
|
|
24
|
+
└── script.js # (optional) JavaScript
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 2. Define your manifest.json
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"name": "My Awesome Addon",
|
|
32
|
+
"version": "1.0.0",
|
|
33
|
+
"description": "A cool widget for your desktop",
|
|
34
|
+
"author": "Your Name",
|
|
35
|
+
"capabilities": {
|
|
36
|
+
"hotReload": true,
|
|
37
|
+
"systemEvents": ["viewport:resize", "theme:change"]
|
|
38
|
+
},
|
|
39
|
+
"settings": {
|
|
40
|
+
"backgroundColor": {
|
|
41
|
+
"type": "color",
|
|
42
|
+
"label": "Background Color",
|
|
43
|
+
"default": "#1a1a2e"
|
|
44
|
+
},
|
|
45
|
+
"animationSpeed": {
|
|
46
|
+
"type": "range",
|
|
47
|
+
"label": "Animation Speed",
|
|
48
|
+
"min": 0.1,
|
|
49
|
+
"max": 5,
|
|
50
|
+
"step": 0.1,
|
|
51
|
+
"default": 1
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 3. Use the SDK in your addon
|
|
58
|
+
|
|
59
|
+
```html
|
|
60
|
+
<!DOCTYPE html>
|
|
61
|
+
<html>
|
|
62
|
+
<head>
|
|
63
|
+
<style>
|
|
64
|
+
body {
|
|
65
|
+
margin: 0;
|
|
66
|
+
overflow: hidden;
|
|
67
|
+
background: var(--bg-color, #1a1a2e);
|
|
68
|
+
}
|
|
69
|
+
</style>
|
|
70
|
+
</head>
|
|
71
|
+
<body>
|
|
72
|
+
<canvas id="canvas"></canvas>
|
|
73
|
+
<script>
|
|
74
|
+
// The MyWallpaper API is automatically injected
|
|
75
|
+
const api = window.MyWallpaper
|
|
76
|
+
|
|
77
|
+
// Get initial config
|
|
78
|
+
const { backgroundColor, animationSpeed } = api.config
|
|
79
|
+
|
|
80
|
+
// Apply initial config
|
|
81
|
+
document.body.style.setProperty('--bg-color', backgroundColor)
|
|
82
|
+
|
|
83
|
+
// React to settings changes (hot-reload)
|
|
84
|
+
api.onSettingsChange((settings, changedKeys) => {
|
|
85
|
+
if (changedKeys.includes('backgroundColor')) {
|
|
86
|
+
document.body.style.setProperty('--bg-color', settings.backgroundColor)
|
|
87
|
+
}
|
|
88
|
+
if (changedKeys.includes('animationSpeed')) {
|
|
89
|
+
updateAnimationSpeed(settings.animationSpeed)
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
// Handle viewport resize
|
|
94
|
+
api.onEvent('viewport:resize', ({ width, height }) => {
|
|
95
|
+
const canvas = document.getElementById('canvas')
|
|
96
|
+
canvas.width = width
|
|
97
|
+
canvas.height = height
|
|
98
|
+
redraw()
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
// Signal addon is ready
|
|
102
|
+
api.ready({
|
|
103
|
+
capabilities: ['hot-reload'],
|
|
104
|
+
subscribedEvents: ['viewport:resize']
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
// After visual setup is complete
|
|
108
|
+
initCanvas()
|
|
109
|
+
api.renderComplete()
|
|
110
|
+
</script>
|
|
111
|
+
</body>
|
|
112
|
+
</html>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## TypeScript Support
|
|
116
|
+
|
|
117
|
+
This package includes full TypeScript definitions. Use them for type-safe development:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
import type {
|
|
121
|
+
MyWallpaperAPI,
|
|
122
|
+
SystemEventType,
|
|
123
|
+
AddonManifest
|
|
124
|
+
} from '@mywallpaper/addon-sdk'
|
|
125
|
+
|
|
126
|
+
// Type-safe event handling
|
|
127
|
+
const api = window.MyWallpaper
|
|
128
|
+
|
|
129
|
+
api.onEvent('viewport:resize', ({ width, height }) => {
|
|
130
|
+
// TypeScript knows width and height are numbers
|
|
131
|
+
console.log(`${width}x${height}`)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
api.onEvent('theme:change', ({ theme }) => {
|
|
135
|
+
// TypeScript knows theme is 'light' | 'dark' | 'system'
|
|
136
|
+
document.body.className = theme
|
|
137
|
+
})
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Manifest Validation
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { validateManifest, type AddonManifest } from '@mywallpaper/addon-sdk/manifest'
|
|
144
|
+
|
|
145
|
+
const manifest: AddonManifest = {
|
|
146
|
+
name: 'My Addon',
|
|
147
|
+
version: '1.0.0',
|
|
148
|
+
settings: {
|
|
149
|
+
primaryColor: { type: 'color', default: '#ff0000' }
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const result = validateManifest(manifest)
|
|
154
|
+
if (!result.success) {
|
|
155
|
+
console.error('Invalid manifest:', result.errors)
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## API Reference
|
|
160
|
+
|
|
161
|
+
### `window.MyWallpaper`
|
|
162
|
+
|
|
163
|
+
The main API object, automatically injected into your addon.
|
|
164
|
+
|
|
165
|
+
#### Properties
|
|
166
|
+
|
|
167
|
+
| Property | Type | Description |
|
|
168
|
+
|----------|------|-------------|
|
|
169
|
+
| `config` | `Record<string, unknown>` | Current settings values |
|
|
170
|
+
| `layerId` | `string` | Unique layer instance ID |
|
|
171
|
+
| `version` | `'2.0'` | SDK version |
|
|
172
|
+
| `storage` | `StorageAPI` | Persistent storage API |
|
|
173
|
+
| `network` | `NetworkAPI` | Secure network requests API |
|
|
174
|
+
|
|
175
|
+
#### Methods
|
|
176
|
+
|
|
177
|
+
##### `ready(options?)`
|
|
178
|
+
|
|
179
|
+
Signal that your addon is initialized and ready.
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
api.ready({
|
|
183
|
+
capabilities: ['hot-reload', 'storage'],
|
|
184
|
+
subscribedEvents: ['viewport:resize', 'theme:change']
|
|
185
|
+
})
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
##### `renderComplete()`
|
|
189
|
+
|
|
190
|
+
Signal that visual rendering is complete (for screenshot service).
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
await loadAllImages()
|
|
194
|
+
drawCanvas()
|
|
195
|
+
api.renderComplete()
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
##### `onSettingsChange(callback)`
|
|
199
|
+
|
|
200
|
+
React to settings changes (hot-reload).
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
api.onSettingsChange((settings, changedKeys) => {
|
|
204
|
+
changedKeys.forEach(key => {
|
|
205
|
+
console.log(`${key} changed to ${settings[key]}`)
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
##### `onEvent(event, callback)` / `offEvent(event, callback)`
|
|
211
|
+
|
|
212
|
+
Subscribe/unsubscribe to system events.
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
const handler = ({ width, height }) => resize(width, height)
|
|
216
|
+
|
|
217
|
+
api.onEvent('viewport:resize', handler)
|
|
218
|
+
// Later...
|
|
219
|
+
api.offEvent('viewport:resize', handler)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
##### `requestPermission(permission, reason)`
|
|
223
|
+
|
|
224
|
+
Request a permission from the user.
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
const granted = await api.requestPermission(
|
|
228
|
+
'storage',
|
|
229
|
+
'Save your preferences for next time'
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
if (granted) {
|
|
233
|
+
await api.storage.set('initialized', true)
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
#### Lifecycle Methods
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
api.onMount(() => console.log('Addon mounted'))
|
|
241
|
+
api.onUnmount(() => cleanup())
|
|
242
|
+
api.onPause(() => pauseAnimations())
|
|
243
|
+
api.onResume(() => resumeAnimations())
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Storage API
|
|
247
|
+
|
|
248
|
+
Persistent key-value storage that syncs across devices.
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
const { storage } = window.MyWallpaper
|
|
252
|
+
|
|
253
|
+
// Store data
|
|
254
|
+
await storage.set('preferences', { theme: 'dark', fontSize: 14 })
|
|
255
|
+
|
|
256
|
+
// Retrieve data (with TypeScript generics)
|
|
257
|
+
const prefs = await storage.get<{ theme: string; fontSize: number }>('preferences')
|
|
258
|
+
|
|
259
|
+
// List all keys
|
|
260
|
+
const keys = await storage.keys()
|
|
261
|
+
|
|
262
|
+
// Check storage usage
|
|
263
|
+
const bytesUsed = await storage.size()
|
|
264
|
+
console.log(`Using ${bytesUsed} of 1MB quota`)
|
|
265
|
+
|
|
266
|
+
// Delete a key
|
|
267
|
+
await storage.delete('oldData')
|
|
268
|
+
|
|
269
|
+
// Clear all storage
|
|
270
|
+
await storage.clear()
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### OAuth API
|
|
274
|
+
|
|
275
|
+
Access OAuth provider APIs (GitHub, Google, Discord, Spotify, Twitch) securely without ever seeing the user's tokens.
|
|
276
|
+
|
|
277
|
+
**How it works:**
|
|
278
|
+
1. User connects their OAuth provider in MyWallpaper settings
|
|
279
|
+
2. Your addon requests permission (e.g., `oauth:github`)
|
|
280
|
+
3. API calls are proxied through the host - your addon never sees the token
|
|
281
|
+
4. The host handles token refresh automatically
|
|
282
|
+
|
|
283
|
+
**Step 1:** Declare required OAuth permission in `manifest.json`:
|
|
284
|
+
|
|
285
|
+
```json
|
|
286
|
+
{
|
|
287
|
+
"permissions": {
|
|
288
|
+
"required": ["oauth:github"],
|
|
289
|
+
"optional": ["oauth:spotify"]
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**Step 2:** Request permission and make API calls:
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
const api = window.MyWallpaper
|
|
298
|
+
|
|
299
|
+
// Request GitHub permission
|
|
300
|
+
const granted = await api.requestPermission(
|
|
301
|
+
'oauth:github',
|
|
302
|
+
'Display your GitHub profile information'
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
if (!granted) {
|
|
306
|
+
console.log('GitHub access denied')
|
|
307
|
+
return
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Make API calls through the proxy
|
|
311
|
+
const response = await api.oauth.request('github', '/user')
|
|
312
|
+
|
|
313
|
+
if (response.ok) {
|
|
314
|
+
console.log('GitHub profile:', response.data)
|
|
315
|
+
console.log('Username:', response.data.login)
|
|
316
|
+
console.log('Followers:', response.data.followers)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// POST request example
|
|
320
|
+
const result = await api.oauth.request('github', '/user/repos', {
|
|
321
|
+
method: 'POST',
|
|
322
|
+
body: { name: 'new-repo', private: true }
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
// Check if user has connected a provider
|
|
326
|
+
const isConnected = await api.oauth.isConnected('github')
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
#### Supported OAuth Providers
|
|
330
|
+
|
|
331
|
+
| Provider | Permission | API Base URL |
|
|
332
|
+
|----------|------------|--------------|
|
|
333
|
+
| GitHub | `oauth:github` | `https://api.github.com` |
|
|
334
|
+
| Google | `oauth:google` | `https://www.googleapis.com` |
|
|
335
|
+
| Discord | `oauth:discord` | `https://discord.com/api/v10` |
|
|
336
|
+
| Spotify | `oauth:spotify` | `https://api.spotify.com/v1` |
|
|
337
|
+
| Twitch | `oauth:twitch` | `https://api.twitch.tv/helix` |
|
|
338
|
+
|
|
339
|
+
#### OAuthResponse
|
|
340
|
+
|
|
341
|
+
| Property | Type | Description |
|
|
342
|
+
|----------|------|-------------|
|
|
343
|
+
| `ok` | `boolean` | `true` if status is 200-299 |
|
|
344
|
+
| `status` | `number` | HTTP status code |
|
|
345
|
+
| `headers` | `Record<string, string>` | Response headers |
|
|
346
|
+
| `data` | `unknown` | Parsed JSON response |
|
|
347
|
+
|
|
348
|
+
**Security:** Your addon NEVER sees OAuth tokens. All requests are proxied through the host, which adds the `Authorization` header server-side. Tokens are encrypted at rest with AES-256-GCM.
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
### Network API
|
|
353
|
+
|
|
354
|
+
**SECURITY:** Direct `fetch()` and `XMLHttpRequest` are blocked for security.
|
|
355
|
+
All network requests MUST go through `MyWallpaper.network.fetch()`.
|
|
356
|
+
|
|
357
|
+
**Step 1:** Declare allowed domains in your `manifest.json`:
|
|
358
|
+
|
|
359
|
+
```json
|
|
360
|
+
{
|
|
361
|
+
"permissions": {
|
|
362
|
+
"network": {
|
|
363
|
+
"domains": ["api.weather.com", "api.example.com"]
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
**Step 2:** Request permission and make requests:
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
const { network } = window.MyWallpaper
|
|
373
|
+
|
|
374
|
+
// Request network permission first
|
|
375
|
+
const granted = await MyWallpaper.requestPermission(
|
|
376
|
+
'network',
|
|
377
|
+
'Fetch weather data to display current conditions'
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
if (!granted) {
|
|
381
|
+
console.log('Network permission denied')
|
|
382
|
+
return
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// GET request
|
|
386
|
+
const response = await network.fetch('https://api.weather.com/current?city=Paris')
|
|
387
|
+
if (response.ok) {
|
|
388
|
+
const weather = response.data
|
|
389
|
+
console.log(`Temperature: ${weather.temp}°C`)
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// POST request with JSON
|
|
393
|
+
const result = await network.fetch('https://api.example.com/data', {
|
|
394
|
+
method: 'POST',
|
|
395
|
+
headers: { 'Content-Type': 'application/json' },
|
|
396
|
+
body: JSON.stringify({ key: 'value' })
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
// Handle errors
|
|
400
|
+
try {
|
|
401
|
+
const data = await network.fetch('https://blocked-domain.com/api')
|
|
402
|
+
} catch (error) {
|
|
403
|
+
// Error: Domain not allowed. Allowed domains: api.weather.com, api.example.com
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
#### NetworkResponse
|
|
408
|
+
|
|
409
|
+
| Property | Type | Description |
|
|
410
|
+
|----------|------|-------------|
|
|
411
|
+
| `ok` | `boolean` | `true` if status is 200-299 |
|
|
412
|
+
| `status` | `number` | HTTP status code |
|
|
413
|
+
| `statusText` | `string` | HTTP status text |
|
|
414
|
+
| `headers` | `Record<string, string>` | Response headers |
|
|
415
|
+
| `data` | `unknown` | Auto-parsed response (JSON, text, or base64) |
|
|
416
|
+
|
|
417
|
+
## System Events
|
|
418
|
+
|
|
419
|
+
| Event | Data | Description |
|
|
420
|
+
|-------|------|-------------|
|
|
421
|
+
| `viewport:resize` | `{ width, height, aspectRatio }` | Viewport dimensions changed |
|
|
422
|
+
| `theme:change` | `{ theme: 'light' \| 'dark' \| 'system' }` | User changed theme |
|
|
423
|
+
| `visibility:change` | `{ visible: boolean }` | Page visibility changed |
|
|
424
|
+
| `layer:focus` | `{ layerId: string }` | Layer gained focus |
|
|
425
|
+
| `layer:blur` | `{ layerId: string }` | Layer lost focus |
|
|
426
|
+
| `app:idle` | `{ idleSeconds: number }` | User is idle |
|
|
427
|
+
| `app:active` | `{}` | User became active |
|
|
428
|
+
|
|
429
|
+
## Setting Types
|
|
430
|
+
|
|
431
|
+
| Type | UI Control | Value Type |
|
|
432
|
+
|------|------------|------------|
|
|
433
|
+
| `string` | Text input | `string` |
|
|
434
|
+
| `number` | Number input | `number` |
|
|
435
|
+
| `boolean` | Toggle | `boolean` |
|
|
436
|
+
| `color` | Color picker | `string` (hex) |
|
|
437
|
+
| `select` | Dropdown | `string` |
|
|
438
|
+
| `range` | Slider | `number` |
|
|
439
|
+
| `image` | Image upload | `string` (URL) |
|
|
440
|
+
| `textarea` | Multi-line text | `string` |
|
|
441
|
+
| `radio` | Radio buttons | `string` |
|
|
442
|
+
| `multi-select` | Checkboxes | `string[]` |
|
|
443
|
+
| `gradient` | Gradient editor | `{ stops: [...] }` |
|
|
444
|
+
| `vector2` | X/Y inputs | `{ x, y }` |
|
|
445
|
+
| `section` | Visual divider | (no value) |
|
|
446
|
+
| `button` | Action button | (no value) |
|
|
447
|
+
|
|
448
|
+
### Setting Definition Example
|
|
449
|
+
|
|
450
|
+
```json
|
|
451
|
+
{
|
|
452
|
+
"settings": {
|
|
453
|
+
"header": {
|
|
454
|
+
"type": "section",
|
|
455
|
+
"label": "Appearance"
|
|
456
|
+
},
|
|
457
|
+
"primaryColor": {
|
|
458
|
+
"type": "color",
|
|
459
|
+
"label": "Primary Color",
|
|
460
|
+
"default": "#3b82f6",
|
|
461
|
+
"description": "Main accent color"
|
|
462
|
+
},
|
|
463
|
+
"opacity": {
|
|
464
|
+
"type": "range",
|
|
465
|
+
"label": "Opacity",
|
|
466
|
+
"min": 0,
|
|
467
|
+
"max": 100,
|
|
468
|
+
"step": 1,
|
|
469
|
+
"default": 100
|
|
470
|
+
},
|
|
471
|
+
"style": {
|
|
472
|
+
"type": "select",
|
|
473
|
+
"label": "Style",
|
|
474
|
+
"default": "modern",
|
|
475
|
+
"options": [
|
|
476
|
+
{ "value": "modern", "label": "Modern" },
|
|
477
|
+
{ "value": "classic", "label": "Classic" },
|
|
478
|
+
{ "value": "minimal", "label": "Minimal" }
|
|
479
|
+
]
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
## Permissions
|
|
486
|
+
|
|
487
|
+
| Permission | Description | Default Quota |
|
|
488
|
+
|------------|-------------|---------------|
|
|
489
|
+
| `storage` | Persistent storage | 1MB |
|
|
490
|
+
| `network` | External requests | Domain whitelist |
|
|
491
|
+
| `audio` | Audio playback | - |
|
|
492
|
+
| `cpu-high` | Higher CPU quota | - |
|
|
493
|
+
| `notifications` | Desktop notifications | - |
|
|
494
|
+
| `oauth:github` | GitHub API access | - |
|
|
495
|
+
| `oauth:google` | Google API access | - |
|
|
496
|
+
| `oauth:discord` | Discord API access | - |
|
|
497
|
+
| `oauth:spotify` | Spotify API access | - |
|
|
498
|
+
| `oauth:twitch` | Twitch API access | - |
|
|
499
|
+
|
|
500
|
+
Declare permissions in manifest for better UX:
|
|
501
|
+
|
|
502
|
+
```json
|
|
503
|
+
{
|
|
504
|
+
"permissions": {
|
|
505
|
+
"storage": { "quota": 512 },
|
|
506
|
+
"network": { "domains": ["api.example.com"] }
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
## Utilities
|
|
512
|
+
|
|
513
|
+
The SDK includes helpful utilities:
|
|
514
|
+
|
|
515
|
+
```typescript
|
|
516
|
+
import {
|
|
517
|
+
createDefaultConfig,
|
|
518
|
+
mergeConfigs,
|
|
519
|
+
compareVersions,
|
|
520
|
+
formatBytes,
|
|
521
|
+
debounce,
|
|
522
|
+
throttle
|
|
523
|
+
} from '@mywallpaper/addon-sdk'
|
|
524
|
+
|
|
525
|
+
// Create defaults from manifest
|
|
526
|
+
const defaults = createDefaultConfig(manifest)
|
|
527
|
+
|
|
528
|
+
// Merge with user config
|
|
529
|
+
const config = mergeConfigs(defaults, userConfig)
|
|
530
|
+
|
|
531
|
+
// Version comparison
|
|
532
|
+
if (compareVersions('2.0.0', '1.9.9') > 0) {
|
|
533
|
+
console.log('v2 is newer')
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Format storage size
|
|
537
|
+
console.log(formatBytes(1048576)) // "1 MB"
|
|
538
|
+
|
|
539
|
+
// Debounce expensive operations
|
|
540
|
+
const saveSettings = debounce((settings) => {
|
|
541
|
+
localStorage.setItem('settings', JSON.stringify(settings))
|
|
542
|
+
}, 1000)
|
|
543
|
+
|
|
544
|
+
// Throttle frequent events
|
|
545
|
+
const updatePosition = throttle((x, y) => {
|
|
546
|
+
element.style.transform = `translate(${x}px, ${y}px)`
|
|
547
|
+
}, 16) // ~60fps
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
## Examples
|
|
551
|
+
|
|
552
|
+
Check out the `/examples` directory for complete addon examples:
|
|
553
|
+
|
|
554
|
+
- `basic/` - Minimal addon structure with settings
|
|
555
|
+
- `weather-widget/` - Weather widget with network API
|
|
556
|
+
- `github-profile/` - GitHub profile using OAuth API proxy
|
|
557
|
+
|
|
558
|
+
## Best Practices
|
|
559
|
+
|
|
560
|
+
1. **Always call `ready()`** - The host waits for this signal
|
|
561
|
+
2. **Call `renderComplete()`** - Helps screenshot service capture your addon correctly
|
|
562
|
+
3. **Declare capabilities** - Helps the host optimize resource allocation
|
|
563
|
+
4. **Use hot-reload** - Better UX when users change settings
|
|
564
|
+
5. **Request permissions early** - Ask for storage permission on first interaction
|
|
565
|
+
6. **Handle lifecycle** - Pause animations when hidden/paused to save resources
|
|
566
|
+
7. **Validate your manifest** - Use `validateManifest()` during development
|
|
567
|
+
|
|
568
|
+
## Migration from v1.0
|
|
569
|
+
|
|
570
|
+
v2.0 removes backward compatibility with v1.0 addons. Key changes:
|
|
571
|
+
|
|
572
|
+
- `apiVersion` field removed (always v2.0)
|
|
573
|
+
- Trust system types removed (unused)
|
|
574
|
+
- `convertToLegacyFormat()` removed
|
|
575
|
+
- `isLegacyAddonMessage()` removed
|
|
576
|
+
- Permissions now use 30s timeout (was 100ms bug)
|
|
577
|
+
- Storage now properly waits for response
|
|
578
|
+
|
|
579
|
+
## License
|
|
580
|
+
|
|
581
|
+
MIT
|