@mywallpaper/addon-sdk 2.8.1 → 2.10.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/dist/index.d.mts +147 -6
- package/dist/index.d.ts +147 -6
- package/dist/manifest.d.mts +147 -6
- package/dist/manifest.d.ts +147 -6
- package/package.json +1 -1
- package/src/runtime/addon-client.js +145 -1
package/dist/index.d.mts
CHANGED
|
@@ -151,6 +151,15 @@ interface NetworkResponse {
|
|
|
151
151
|
/** Response data (auto-parsed JSON, text, or base64 for binary) */
|
|
152
152
|
data: unknown;
|
|
153
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Result of a network domain access request.
|
|
156
|
+
*/
|
|
157
|
+
interface NetworkAccessResult {
|
|
158
|
+
/** Whether access was granted */
|
|
159
|
+
granted: boolean;
|
|
160
|
+
/** Error message if denied */
|
|
161
|
+
error?: string;
|
|
162
|
+
}
|
|
154
163
|
/**
|
|
155
164
|
* Network API for making HTTP requests through the secure host proxy.
|
|
156
165
|
* Access via `window.MyWallpaper.network`
|
|
@@ -158,7 +167,9 @@ interface NetworkResponse {
|
|
|
158
167
|
* **IMPORTANT:** Direct `fetch()` and `XMLHttpRequest` are blocked for security.
|
|
159
168
|
* All network requests MUST go through this API.
|
|
160
169
|
*
|
|
161
|
-
* **
|
|
170
|
+
* **Two ways to access external domains:**
|
|
171
|
+
*
|
|
172
|
+
* 1. **Manifest declaration** (pre-approved):
|
|
162
173
|
* ```json
|
|
163
174
|
* {
|
|
164
175
|
* "permissions": {
|
|
@@ -167,14 +178,25 @@ interface NetworkResponse {
|
|
|
167
178
|
* }
|
|
168
179
|
* ```
|
|
169
180
|
*
|
|
181
|
+
* 2. **On-demand permission** (user approval at runtime):
|
|
182
|
+
* ```typescript
|
|
183
|
+
* const result = await network.requestAccess('fonts.example.com', 'Load custom fonts')
|
|
184
|
+
* if (result.granted) {
|
|
185
|
+
* const response = await network.fetch('https://fonts.example.com/myfont.woff2')
|
|
186
|
+
* }
|
|
187
|
+
* ```
|
|
188
|
+
*
|
|
170
189
|
* @example
|
|
171
190
|
* ```typescript
|
|
172
191
|
* const { network } = window.MyWallpaper
|
|
173
192
|
*
|
|
174
|
-
* //
|
|
175
|
-
* const
|
|
176
|
-
* if (
|
|
177
|
-
*
|
|
193
|
+
* // On-demand access to a domain (shows permission modal)
|
|
194
|
+
* const access = await network.requestAccess('api.weather.com', 'Fetch weather data')
|
|
195
|
+
* if (access.granted) {
|
|
196
|
+
* const response = await network.fetch('https://api.weather.com/current')
|
|
197
|
+
* if (response.ok) {
|
|
198
|
+
* console.log(response.data)
|
|
199
|
+
* }
|
|
178
200
|
* }
|
|
179
201
|
*
|
|
180
202
|
* // POST request with JSON body
|
|
@@ -186,9 +208,33 @@ interface NetworkResponse {
|
|
|
186
208
|
* ```
|
|
187
209
|
*/
|
|
188
210
|
interface NetworkAPI {
|
|
211
|
+
/**
|
|
212
|
+
* Request on-demand access to a domain.
|
|
213
|
+
* Shows a permission modal to the user: "Widget X requests access to: domain.com"
|
|
214
|
+
* This permission lasts for the current session only (not persisted).
|
|
215
|
+
*
|
|
216
|
+
* @param domain - The domain to request access to (e.g., 'fonts.cdnfonts.com')
|
|
217
|
+
* @param reason - User-friendly explanation of why access is needed
|
|
218
|
+
* @returns Promise resolving to NetworkAccessResult
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```typescript
|
|
222
|
+
* const result = await api.network.requestAccess(
|
|
223
|
+
* 'fonts.cdnfonts.com',
|
|
224
|
+
* 'Load custom font for text display'
|
|
225
|
+
* )
|
|
226
|
+
* if (result.granted) {
|
|
227
|
+
* // Now we can fetch from this domain
|
|
228
|
+
* const css = await api.network.fetch('https://fonts.cdnfonts.com/css/anurati')
|
|
229
|
+
* }
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
requestAccess(domain: string, reason: string): Promise<NetworkAccessResult>;
|
|
189
233
|
/**
|
|
190
234
|
* Make an HTTP request through the secure host proxy.
|
|
191
|
-
* Domain must be
|
|
235
|
+
* Domain must be either:
|
|
236
|
+
* - Whitelisted in manifest.json permissions, OR
|
|
237
|
+
* - Approved via requestAccess() in the current session
|
|
192
238
|
*
|
|
193
239
|
* @param url - Full URL to fetch (must be in allowed domains)
|
|
194
240
|
* @param options - Optional request options
|
|
@@ -557,6 +603,21 @@ interface MyWallpaperAPI {
|
|
|
557
603
|
* ```
|
|
558
604
|
*/
|
|
559
605
|
audio: AudioAPI;
|
|
606
|
+
/**
|
|
607
|
+
* Settings API for dynamically modifying setting options at runtime.
|
|
608
|
+
* Allows widgets to populate dropdowns based on external data.
|
|
609
|
+
*
|
|
610
|
+
* @example
|
|
611
|
+
* ```typescript
|
|
612
|
+
* // After loading font CSS, update the font family dropdown
|
|
613
|
+
* const fonts = parseFontFaces(cssContent)
|
|
614
|
+
* api.settings.updateOptions('fontFamily', fonts.map(f => ({
|
|
615
|
+
* label: f.family,
|
|
616
|
+
* value: f.family
|
|
617
|
+
* })))
|
|
618
|
+
* ```
|
|
619
|
+
*/
|
|
620
|
+
settings: SettingsAPI;
|
|
560
621
|
/**
|
|
561
622
|
* File Access API for requesting access to user-uploaded files.
|
|
562
623
|
* Files are not sent automatically - the addon must request access.
|
|
@@ -824,6 +885,86 @@ interface AudioAPI {
|
|
|
824
885
|
*/
|
|
825
886
|
onStateChange(callback: (state: AudioState) => void): () => void;
|
|
826
887
|
}
|
|
888
|
+
/**
|
|
889
|
+
* A setting option for select/dropdown fields.
|
|
890
|
+
*/
|
|
891
|
+
interface SettingOption {
|
|
892
|
+
/** Display label shown to user */
|
|
893
|
+
label: string;
|
|
894
|
+
/** Value stored when selected */
|
|
895
|
+
value: string | number | boolean;
|
|
896
|
+
/** Optional description */
|
|
897
|
+
description?: string;
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Settings API for dynamically modifying setting options at runtime.
|
|
901
|
+
* Access via `window.MyWallpaper.settings`
|
|
902
|
+
*
|
|
903
|
+
* **Why this exists:**
|
|
904
|
+
* Some widgets need to populate setting options dynamically based on
|
|
905
|
+
* external data (e.g., font families from a CSS file, available themes
|
|
906
|
+
* from an API, etc.). This API allows widgets to modify their own
|
|
907
|
+
* setting options after loading.
|
|
908
|
+
*
|
|
909
|
+
* **Important:**
|
|
910
|
+
* - Only works for settings declared in manifest.json
|
|
911
|
+
* - Only 'select' type settings can have their options updated
|
|
912
|
+
* - Changes are session-only (manifest defines the defaults)
|
|
913
|
+
*
|
|
914
|
+
* @example
|
|
915
|
+
* ```typescript
|
|
916
|
+
* const api = window.MyWallpaper
|
|
917
|
+
*
|
|
918
|
+
* // After fetching a font CSS, parse available fonts and update dropdown
|
|
919
|
+
* const fonts = parseFontFaces(cssContent)
|
|
920
|
+
* api.settings.updateOptions('fontFamily', fonts.map(f => ({
|
|
921
|
+
* label: f.family,
|
|
922
|
+
* value: f.family
|
|
923
|
+
* })))
|
|
924
|
+
*
|
|
925
|
+
* // Update multiple settings at once
|
|
926
|
+
* api.settings.updateOptions('fontWeight', [
|
|
927
|
+
* { label: 'Light', value: '300' },
|
|
928
|
+
* { label: 'Regular', value: '400' },
|
|
929
|
+
* { label: 'Bold', value: '700' }
|
|
930
|
+
* ])
|
|
931
|
+
* ```
|
|
932
|
+
*/
|
|
933
|
+
interface SettingsAPI {
|
|
934
|
+
/**
|
|
935
|
+
* Update the options for a select-type setting.
|
|
936
|
+
* The new options replace existing ones from the manifest.
|
|
937
|
+
*
|
|
938
|
+
* @param settingKey - The setting key to update (must be a 'select' type)
|
|
939
|
+
* @param options - Array of new options
|
|
940
|
+
* @param defaultValue - Optional new default value
|
|
941
|
+
*
|
|
942
|
+
* @example
|
|
943
|
+
* ```typescript
|
|
944
|
+
* // Update font family options based on loaded CSS
|
|
945
|
+
* api.settings.updateOptions('customFontFamily', [
|
|
946
|
+
* { label: 'Roboto', value: 'Roboto' },
|
|
947
|
+
* { label: 'Open Sans', value: 'Open Sans' },
|
|
948
|
+
* { label: 'Lato', value: 'Lato' }
|
|
949
|
+
* ], 'Roboto')
|
|
950
|
+
* ```
|
|
951
|
+
*/
|
|
952
|
+
updateOptions(settingKey: string, options: SettingOption[], defaultValue?: string | number | boolean): void;
|
|
953
|
+
/**
|
|
954
|
+
* Get the current options for a setting.
|
|
955
|
+
* Returns the dynamic options if set, otherwise the manifest defaults.
|
|
956
|
+
*
|
|
957
|
+
* @param settingKey - The setting key to get options for
|
|
958
|
+
* @returns Array of options or undefined if not a select setting
|
|
959
|
+
*/
|
|
960
|
+
getOptions(settingKey: string): SettingOption[] | undefined;
|
|
961
|
+
/**
|
|
962
|
+
* Reset a setting's options back to the manifest defaults.
|
|
963
|
+
*
|
|
964
|
+
* @param settingKey - The setting key to reset
|
|
965
|
+
*/
|
|
966
|
+
resetOptions(settingKey: string): void;
|
|
967
|
+
}
|
|
827
968
|
/**
|
|
828
969
|
* Generic addon values (settings configuration).
|
|
829
970
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -151,6 +151,15 @@ interface NetworkResponse {
|
|
|
151
151
|
/** Response data (auto-parsed JSON, text, or base64 for binary) */
|
|
152
152
|
data: unknown;
|
|
153
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Result of a network domain access request.
|
|
156
|
+
*/
|
|
157
|
+
interface NetworkAccessResult {
|
|
158
|
+
/** Whether access was granted */
|
|
159
|
+
granted: boolean;
|
|
160
|
+
/** Error message if denied */
|
|
161
|
+
error?: string;
|
|
162
|
+
}
|
|
154
163
|
/**
|
|
155
164
|
* Network API for making HTTP requests through the secure host proxy.
|
|
156
165
|
* Access via `window.MyWallpaper.network`
|
|
@@ -158,7 +167,9 @@ interface NetworkResponse {
|
|
|
158
167
|
* **IMPORTANT:** Direct `fetch()` and `XMLHttpRequest` are blocked for security.
|
|
159
168
|
* All network requests MUST go through this API.
|
|
160
169
|
*
|
|
161
|
-
* **
|
|
170
|
+
* **Two ways to access external domains:**
|
|
171
|
+
*
|
|
172
|
+
* 1. **Manifest declaration** (pre-approved):
|
|
162
173
|
* ```json
|
|
163
174
|
* {
|
|
164
175
|
* "permissions": {
|
|
@@ -167,14 +178,25 @@ interface NetworkResponse {
|
|
|
167
178
|
* }
|
|
168
179
|
* ```
|
|
169
180
|
*
|
|
181
|
+
* 2. **On-demand permission** (user approval at runtime):
|
|
182
|
+
* ```typescript
|
|
183
|
+
* const result = await network.requestAccess('fonts.example.com', 'Load custom fonts')
|
|
184
|
+
* if (result.granted) {
|
|
185
|
+
* const response = await network.fetch('https://fonts.example.com/myfont.woff2')
|
|
186
|
+
* }
|
|
187
|
+
* ```
|
|
188
|
+
*
|
|
170
189
|
* @example
|
|
171
190
|
* ```typescript
|
|
172
191
|
* const { network } = window.MyWallpaper
|
|
173
192
|
*
|
|
174
|
-
* //
|
|
175
|
-
* const
|
|
176
|
-
* if (
|
|
177
|
-
*
|
|
193
|
+
* // On-demand access to a domain (shows permission modal)
|
|
194
|
+
* const access = await network.requestAccess('api.weather.com', 'Fetch weather data')
|
|
195
|
+
* if (access.granted) {
|
|
196
|
+
* const response = await network.fetch('https://api.weather.com/current')
|
|
197
|
+
* if (response.ok) {
|
|
198
|
+
* console.log(response.data)
|
|
199
|
+
* }
|
|
178
200
|
* }
|
|
179
201
|
*
|
|
180
202
|
* // POST request with JSON body
|
|
@@ -186,9 +208,33 @@ interface NetworkResponse {
|
|
|
186
208
|
* ```
|
|
187
209
|
*/
|
|
188
210
|
interface NetworkAPI {
|
|
211
|
+
/**
|
|
212
|
+
* Request on-demand access to a domain.
|
|
213
|
+
* Shows a permission modal to the user: "Widget X requests access to: domain.com"
|
|
214
|
+
* This permission lasts for the current session only (not persisted).
|
|
215
|
+
*
|
|
216
|
+
* @param domain - The domain to request access to (e.g., 'fonts.cdnfonts.com')
|
|
217
|
+
* @param reason - User-friendly explanation of why access is needed
|
|
218
|
+
* @returns Promise resolving to NetworkAccessResult
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```typescript
|
|
222
|
+
* const result = await api.network.requestAccess(
|
|
223
|
+
* 'fonts.cdnfonts.com',
|
|
224
|
+
* 'Load custom font for text display'
|
|
225
|
+
* )
|
|
226
|
+
* if (result.granted) {
|
|
227
|
+
* // Now we can fetch from this domain
|
|
228
|
+
* const css = await api.network.fetch('https://fonts.cdnfonts.com/css/anurati')
|
|
229
|
+
* }
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
requestAccess(domain: string, reason: string): Promise<NetworkAccessResult>;
|
|
189
233
|
/**
|
|
190
234
|
* Make an HTTP request through the secure host proxy.
|
|
191
|
-
* Domain must be
|
|
235
|
+
* Domain must be either:
|
|
236
|
+
* - Whitelisted in manifest.json permissions, OR
|
|
237
|
+
* - Approved via requestAccess() in the current session
|
|
192
238
|
*
|
|
193
239
|
* @param url - Full URL to fetch (must be in allowed domains)
|
|
194
240
|
* @param options - Optional request options
|
|
@@ -557,6 +603,21 @@ interface MyWallpaperAPI {
|
|
|
557
603
|
* ```
|
|
558
604
|
*/
|
|
559
605
|
audio: AudioAPI;
|
|
606
|
+
/**
|
|
607
|
+
* Settings API for dynamically modifying setting options at runtime.
|
|
608
|
+
* Allows widgets to populate dropdowns based on external data.
|
|
609
|
+
*
|
|
610
|
+
* @example
|
|
611
|
+
* ```typescript
|
|
612
|
+
* // After loading font CSS, update the font family dropdown
|
|
613
|
+
* const fonts = parseFontFaces(cssContent)
|
|
614
|
+
* api.settings.updateOptions('fontFamily', fonts.map(f => ({
|
|
615
|
+
* label: f.family,
|
|
616
|
+
* value: f.family
|
|
617
|
+
* })))
|
|
618
|
+
* ```
|
|
619
|
+
*/
|
|
620
|
+
settings: SettingsAPI;
|
|
560
621
|
/**
|
|
561
622
|
* File Access API for requesting access to user-uploaded files.
|
|
562
623
|
* Files are not sent automatically - the addon must request access.
|
|
@@ -824,6 +885,86 @@ interface AudioAPI {
|
|
|
824
885
|
*/
|
|
825
886
|
onStateChange(callback: (state: AudioState) => void): () => void;
|
|
826
887
|
}
|
|
888
|
+
/**
|
|
889
|
+
* A setting option for select/dropdown fields.
|
|
890
|
+
*/
|
|
891
|
+
interface SettingOption {
|
|
892
|
+
/** Display label shown to user */
|
|
893
|
+
label: string;
|
|
894
|
+
/** Value stored when selected */
|
|
895
|
+
value: string | number | boolean;
|
|
896
|
+
/** Optional description */
|
|
897
|
+
description?: string;
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Settings API for dynamically modifying setting options at runtime.
|
|
901
|
+
* Access via `window.MyWallpaper.settings`
|
|
902
|
+
*
|
|
903
|
+
* **Why this exists:**
|
|
904
|
+
* Some widgets need to populate setting options dynamically based on
|
|
905
|
+
* external data (e.g., font families from a CSS file, available themes
|
|
906
|
+
* from an API, etc.). This API allows widgets to modify their own
|
|
907
|
+
* setting options after loading.
|
|
908
|
+
*
|
|
909
|
+
* **Important:**
|
|
910
|
+
* - Only works for settings declared in manifest.json
|
|
911
|
+
* - Only 'select' type settings can have their options updated
|
|
912
|
+
* - Changes are session-only (manifest defines the defaults)
|
|
913
|
+
*
|
|
914
|
+
* @example
|
|
915
|
+
* ```typescript
|
|
916
|
+
* const api = window.MyWallpaper
|
|
917
|
+
*
|
|
918
|
+
* // After fetching a font CSS, parse available fonts and update dropdown
|
|
919
|
+
* const fonts = parseFontFaces(cssContent)
|
|
920
|
+
* api.settings.updateOptions('fontFamily', fonts.map(f => ({
|
|
921
|
+
* label: f.family,
|
|
922
|
+
* value: f.family
|
|
923
|
+
* })))
|
|
924
|
+
*
|
|
925
|
+
* // Update multiple settings at once
|
|
926
|
+
* api.settings.updateOptions('fontWeight', [
|
|
927
|
+
* { label: 'Light', value: '300' },
|
|
928
|
+
* { label: 'Regular', value: '400' },
|
|
929
|
+
* { label: 'Bold', value: '700' }
|
|
930
|
+
* ])
|
|
931
|
+
* ```
|
|
932
|
+
*/
|
|
933
|
+
interface SettingsAPI {
|
|
934
|
+
/**
|
|
935
|
+
* Update the options for a select-type setting.
|
|
936
|
+
* The new options replace existing ones from the manifest.
|
|
937
|
+
*
|
|
938
|
+
* @param settingKey - The setting key to update (must be a 'select' type)
|
|
939
|
+
* @param options - Array of new options
|
|
940
|
+
* @param defaultValue - Optional new default value
|
|
941
|
+
*
|
|
942
|
+
* @example
|
|
943
|
+
* ```typescript
|
|
944
|
+
* // Update font family options based on loaded CSS
|
|
945
|
+
* api.settings.updateOptions('customFontFamily', [
|
|
946
|
+
* { label: 'Roboto', value: 'Roboto' },
|
|
947
|
+
* { label: 'Open Sans', value: 'Open Sans' },
|
|
948
|
+
* { label: 'Lato', value: 'Lato' }
|
|
949
|
+
* ], 'Roboto')
|
|
950
|
+
* ```
|
|
951
|
+
*/
|
|
952
|
+
updateOptions(settingKey: string, options: SettingOption[], defaultValue?: string | number | boolean): void;
|
|
953
|
+
/**
|
|
954
|
+
* Get the current options for a setting.
|
|
955
|
+
* Returns the dynamic options if set, otherwise the manifest defaults.
|
|
956
|
+
*
|
|
957
|
+
* @param settingKey - The setting key to get options for
|
|
958
|
+
* @returns Array of options or undefined if not a select setting
|
|
959
|
+
*/
|
|
960
|
+
getOptions(settingKey: string): SettingOption[] | undefined;
|
|
961
|
+
/**
|
|
962
|
+
* Reset a setting's options back to the manifest defaults.
|
|
963
|
+
*
|
|
964
|
+
* @param settingKey - The setting key to reset
|
|
965
|
+
*/
|
|
966
|
+
resetOptions(settingKey: string): void;
|
|
967
|
+
}
|
|
827
968
|
/**
|
|
828
969
|
* Generic addon values (settings configuration).
|
|
829
970
|
*/
|
package/dist/manifest.d.mts
CHANGED
|
@@ -136,6 +136,15 @@ interface NetworkResponse {
|
|
|
136
136
|
/** Response data (auto-parsed JSON, text, or base64 for binary) */
|
|
137
137
|
data: unknown;
|
|
138
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Result of a network domain access request.
|
|
141
|
+
*/
|
|
142
|
+
interface NetworkAccessResult {
|
|
143
|
+
/** Whether access was granted */
|
|
144
|
+
granted: boolean;
|
|
145
|
+
/** Error message if denied */
|
|
146
|
+
error?: string;
|
|
147
|
+
}
|
|
139
148
|
/**
|
|
140
149
|
* Network API for making HTTP requests through the secure host proxy.
|
|
141
150
|
* Access via `window.MyWallpaper.network`
|
|
@@ -143,7 +152,9 @@ interface NetworkResponse {
|
|
|
143
152
|
* **IMPORTANT:** Direct `fetch()` and `XMLHttpRequest` are blocked for security.
|
|
144
153
|
* All network requests MUST go through this API.
|
|
145
154
|
*
|
|
146
|
-
* **
|
|
155
|
+
* **Two ways to access external domains:**
|
|
156
|
+
*
|
|
157
|
+
* 1. **Manifest declaration** (pre-approved):
|
|
147
158
|
* ```json
|
|
148
159
|
* {
|
|
149
160
|
* "permissions": {
|
|
@@ -152,14 +163,25 @@ interface NetworkResponse {
|
|
|
152
163
|
* }
|
|
153
164
|
* ```
|
|
154
165
|
*
|
|
166
|
+
* 2. **On-demand permission** (user approval at runtime):
|
|
167
|
+
* ```typescript
|
|
168
|
+
* const result = await network.requestAccess('fonts.example.com', 'Load custom fonts')
|
|
169
|
+
* if (result.granted) {
|
|
170
|
+
* const response = await network.fetch('https://fonts.example.com/myfont.woff2')
|
|
171
|
+
* }
|
|
172
|
+
* ```
|
|
173
|
+
*
|
|
155
174
|
* @example
|
|
156
175
|
* ```typescript
|
|
157
176
|
* const { network } = window.MyWallpaper
|
|
158
177
|
*
|
|
159
|
-
* //
|
|
160
|
-
* const
|
|
161
|
-
* if (
|
|
162
|
-
*
|
|
178
|
+
* // On-demand access to a domain (shows permission modal)
|
|
179
|
+
* const access = await network.requestAccess('api.weather.com', 'Fetch weather data')
|
|
180
|
+
* if (access.granted) {
|
|
181
|
+
* const response = await network.fetch('https://api.weather.com/current')
|
|
182
|
+
* if (response.ok) {
|
|
183
|
+
* console.log(response.data)
|
|
184
|
+
* }
|
|
163
185
|
* }
|
|
164
186
|
*
|
|
165
187
|
* // POST request with JSON body
|
|
@@ -171,9 +193,33 @@ interface NetworkResponse {
|
|
|
171
193
|
* ```
|
|
172
194
|
*/
|
|
173
195
|
interface NetworkAPI {
|
|
196
|
+
/**
|
|
197
|
+
* Request on-demand access to a domain.
|
|
198
|
+
* Shows a permission modal to the user: "Widget X requests access to: domain.com"
|
|
199
|
+
* This permission lasts for the current session only (not persisted).
|
|
200
|
+
*
|
|
201
|
+
* @param domain - The domain to request access to (e.g., 'fonts.cdnfonts.com')
|
|
202
|
+
* @param reason - User-friendly explanation of why access is needed
|
|
203
|
+
* @returns Promise resolving to NetworkAccessResult
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* ```typescript
|
|
207
|
+
* const result = await api.network.requestAccess(
|
|
208
|
+
* 'fonts.cdnfonts.com',
|
|
209
|
+
* 'Load custom font for text display'
|
|
210
|
+
* )
|
|
211
|
+
* if (result.granted) {
|
|
212
|
+
* // Now we can fetch from this domain
|
|
213
|
+
* const css = await api.network.fetch('https://fonts.cdnfonts.com/css/anurati')
|
|
214
|
+
* }
|
|
215
|
+
* ```
|
|
216
|
+
*/
|
|
217
|
+
requestAccess(domain: string, reason: string): Promise<NetworkAccessResult>;
|
|
174
218
|
/**
|
|
175
219
|
* Make an HTTP request through the secure host proxy.
|
|
176
|
-
* Domain must be
|
|
220
|
+
* Domain must be either:
|
|
221
|
+
* - Whitelisted in manifest.json permissions, OR
|
|
222
|
+
* - Approved via requestAccess() in the current session
|
|
177
223
|
*
|
|
178
224
|
* @param url - Full URL to fetch (must be in allowed domains)
|
|
179
225
|
* @param options - Optional request options
|
|
@@ -542,6 +588,21 @@ interface MyWallpaperAPI {
|
|
|
542
588
|
* ```
|
|
543
589
|
*/
|
|
544
590
|
audio: AudioAPI;
|
|
591
|
+
/**
|
|
592
|
+
* Settings API for dynamically modifying setting options at runtime.
|
|
593
|
+
* Allows widgets to populate dropdowns based on external data.
|
|
594
|
+
*
|
|
595
|
+
* @example
|
|
596
|
+
* ```typescript
|
|
597
|
+
* // After loading font CSS, update the font family dropdown
|
|
598
|
+
* const fonts = parseFontFaces(cssContent)
|
|
599
|
+
* api.settings.updateOptions('fontFamily', fonts.map(f => ({
|
|
600
|
+
* label: f.family,
|
|
601
|
+
* value: f.family
|
|
602
|
+
* })))
|
|
603
|
+
* ```
|
|
604
|
+
*/
|
|
605
|
+
settings: SettingsAPI;
|
|
545
606
|
/**
|
|
546
607
|
* File Access API for requesting access to user-uploaded files.
|
|
547
608
|
* Files are not sent automatically - the addon must request access.
|
|
@@ -805,6 +866,86 @@ interface AudioAPI {
|
|
|
805
866
|
*/
|
|
806
867
|
onStateChange(callback: (state: AudioState) => void): () => void;
|
|
807
868
|
}
|
|
869
|
+
/**
|
|
870
|
+
* A setting option for select/dropdown fields.
|
|
871
|
+
*/
|
|
872
|
+
interface SettingOption {
|
|
873
|
+
/** Display label shown to user */
|
|
874
|
+
label: string;
|
|
875
|
+
/** Value stored when selected */
|
|
876
|
+
value: string | number | boolean;
|
|
877
|
+
/** Optional description */
|
|
878
|
+
description?: string;
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Settings API for dynamically modifying setting options at runtime.
|
|
882
|
+
* Access via `window.MyWallpaper.settings`
|
|
883
|
+
*
|
|
884
|
+
* **Why this exists:**
|
|
885
|
+
* Some widgets need to populate setting options dynamically based on
|
|
886
|
+
* external data (e.g., font families from a CSS file, available themes
|
|
887
|
+
* from an API, etc.). This API allows widgets to modify their own
|
|
888
|
+
* setting options after loading.
|
|
889
|
+
*
|
|
890
|
+
* **Important:**
|
|
891
|
+
* - Only works for settings declared in manifest.json
|
|
892
|
+
* - Only 'select' type settings can have their options updated
|
|
893
|
+
* - Changes are session-only (manifest defines the defaults)
|
|
894
|
+
*
|
|
895
|
+
* @example
|
|
896
|
+
* ```typescript
|
|
897
|
+
* const api = window.MyWallpaper
|
|
898
|
+
*
|
|
899
|
+
* // After fetching a font CSS, parse available fonts and update dropdown
|
|
900
|
+
* const fonts = parseFontFaces(cssContent)
|
|
901
|
+
* api.settings.updateOptions('fontFamily', fonts.map(f => ({
|
|
902
|
+
* label: f.family,
|
|
903
|
+
* value: f.family
|
|
904
|
+
* })))
|
|
905
|
+
*
|
|
906
|
+
* // Update multiple settings at once
|
|
907
|
+
* api.settings.updateOptions('fontWeight', [
|
|
908
|
+
* { label: 'Light', value: '300' },
|
|
909
|
+
* { label: 'Regular', value: '400' },
|
|
910
|
+
* { label: 'Bold', value: '700' }
|
|
911
|
+
* ])
|
|
912
|
+
* ```
|
|
913
|
+
*/
|
|
914
|
+
interface SettingsAPI {
|
|
915
|
+
/**
|
|
916
|
+
* Update the options for a select-type setting.
|
|
917
|
+
* The new options replace existing ones from the manifest.
|
|
918
|
+
*
|
|
919
|
+
* @param settingKey - The setting key to update (must be a 'select' type)
|
|
920
|
+
* @param options - Array of new options
|
|
921
|
+
* @param defaultValue - Optional new default value
|
|
922
|
+
*
|
|
923
|
+
* @example
|
|
924
|
+
* ```typescript
|
|
925
|
+
* // Update font family options based on loaded CSS
|
|
926
|
+
* api.settings.updateOptions('customFontFamily', [
|
|
927
|
+
* { label: 'Roboto', value: 'Roboto' },
|
|
928
|
+
* { label: 'Open Sans', value: 'Open Sans' },
|
|
929
|
+
* { label: 'Lato', value: 'Lato' }
|
|
930
|
+
* ], 'Roboto')
|
|
931
|
+
* ```
|
|
932
|
+
*/
|
|
933
|
+
updateOptions(settingKey: string, options: SettingOption[], defaultValue?: string | number | boolean): void;
|
|
934
|
+
/**
|
|
935
|
+
* Get the current options for a setting.
|
|
936
|
+
* Returns the dynamic options if set, otherwise the manifest defaults.
|
|
937
|
+
*
|
|
938
|
+
* @param settingKey - The setting key to get options for
|
|
939
|
+
* @returns Array of options or undefined if not a select setting
|
|
940
|
+
*/
|
|
941
|
+
getOptions(settingKey: string): SettingOption[] | undefined;
|
|
942
|
+
/**
|
|
943
|
+
* Reset a setting's options back to the manifest defaults.
|
|
944
|
+
*
|
|
945
|
+
* @param settingKey - The setting key to reset
|
|
946
|
+
*/
|
|
947
|
+
resetOptions(settingKey: string): void;
|
|
948
|
+
}
|
|
808
949
|
|
|
809
950
|
/**
|
|
810
951
|
* @mywallpaper/addon-sdk - Manifest Schema & Validation
|
package/dist/manifest.d.ts
CHANGED
|
@@ -136,6 +136,15 @@ interface NetworkResponse {
|
|
|
136
136
|
/** Response data (auto-parsed JSON, text, or base64 for binary) */
|
|
137
137
|
data: unknown;
|
|
138
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Result of a network domain access request.
|
|
141
|
+
*/
|
|
142
|
+
interface NetworkAccessResult {
|
|
143
|
+
/** Whether access was granted */
|
|
144
|
+
granted: boolean;
|
|
145
|
+
/** Error message if denied */
|
|
146
|
+
error?: string;
|
|
147
|
+
}
|
|
139
148
|
/**
|
|
140
149
|
* Network API for making HTTP requests through the secure host proxy.
|
|
141
150
|
* Access via `window.MyWallpaper.network`
|
|
@@ -143,7 +152,9 @@ interface NetworkResponse {
|
|
|
143
152
|
* **IMPORTANT:** Direct `fetch()` and `XMLHttpRequest` are blocked for security.
|
|
144
153
|
* All network requests MUST go through this API.
|
|
145
154
|
*
|
|
146
|
-
* **
|
|
155
|
+
* **Two ways to access external domains:**
|
|
156
|
+
*
|
|
157
|
+
* 1. **Manifest declaration** (pre-approved):
|
|
147
158
|
* ```json
|
|
148
159
|
* {
|
|
149
160
|
* "permissions": {
|
|
@@ -152,14 +163,25 @@ interface NetworkResponse {
|
|
|
152
163
|
* }
|
|
153
164
|
* ```
|
|
154
165
|
*
|
|
166
|
+
* 2. **On-demand permission** (user approval at runtime):
|
|
167
|
+
* ```typescript
|
|
168
|
+
* const result = await network.requestAccess('fonts.example.com', 'Load custom fonts')
|
|
169
|
+
* if (result.granted) {
|
|
170
|
+
* const response = await network.fetch('https://fonts.example.com/myfont.woff2')
|
|
171
|
+
* }
|
|
172
|
+
* ```
|
|
173
|
+
*
|
|
155
174
|
* @example
|
|
156
175
|
* ```typescript
|
|
157
176
|
* const { network } = window.MyWallpaper
|
|
158
177
|
*
|
|
159
|
-
* //
|
|
160
|
-
* const
|
|
161
|
-
* if (
|
|
162
|
-
*
|
|
178
|
+
* // On-demand access to a domain (shows permission modal)
|
|
179
|
+
* const access = await network.requestAccess('api.weather.com', 'Fetch weather data')
|
|
180
|
+
* if (access.granted) {
|
|
181
|
+
* const response = await network.fetch('https://api.weather.com/current')
|
|
182
|
+
* if (response.ok) {
|
|
183
|
+
* console.log(response.data)
|
|
184
|
+
* }
|
|
163
185
|
* }
|
|
164
186
|
*
|
|
165
187
|
* // POST request with JSON body
|
|
@@ -171,9 +193,33 @@ interface NetworkResponse {
|
|
|
171
193
|
* ```
|
|
172
194
|
*/
|
|
173
195
|
interface NetworkAPI {
|
|
196
|
+
/**
|
|
197
|
+
* Request on-demand access to a domain.
|
|
198
|
+
* Shows a permission modal to the user: "Widget X requests access to: domain.com"
|
|
199
|
+
* This permission lasts for the current session only (not persisted).
|
|
200
|
+
*
|
|
201
|
+
* @param domain - The domain to request access to (e.g., 'fonts.cdnfonts.com')
|
|
202
|
+
* @param reason - User-friendly explanation of why access is needed
|
|
203
|
+
* @returns Promise resolving to NetworkAccessResult
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* ```typescript
|
|
207
|
+
* const result = await api.network.requestAccess(
|
|
208
|
+
* 'fonts.cdnfonts.com',
|
|
209
|
+
* 'Load custom font for text display'
|
|
210
|
+
* )
|
|
211
|
+
* if (result.granted) {
|
|
212
|
+
* // Now we can fetch from this domain
|
|
213
|
+
* const css = await api.network.fetch('https://fonts.cdnfonts.com/css/anurati')
|
|
214
|
+
* }
|
|
215
|
+
* ```
|
|
216
|
+
*/
|
|
217
|
+
requestAccess(domain: string, reason: string): Promise<NetworkAccessResult>;
|
|
174
218
|
/**
|
|
175
219
|
* Make an HTTP request through the secure host proxy.
|
|
176
|
-
* Domain must be
|
|
220
|
+
* Domain must be either:
|
|
221
|
+
* - Whitelisted in manifest.json permissions, OR
|
|
222
|
+
* - Approved via requestAccess() in the current session
|
|
177
223
|
*
|
|
178
224
|
* @param url - Full URL to fetch (must be in allowed domains)
|
|
179
225
|
* @param options - Optional request options
|
|
@@ -542,6 +588,21 @@ interface MyWallpaperAPI {
|
|
|
542
588
|
* ```
|
|
543
589
|
*/
|
|
544
590
|
audio: AudioAPI;
|
|
591
|
+
/**
|
|
592
|
+
* Settings API for dynamically modifying setting options at runtime.
|
|
593
|
+
* Allows widgets to populate dropdowns based on external data.
|
|
594
|
+
*
|
|
595
|
+
* @example
|
|
596
|
+
* ```typescript
|
|
597
|
+
* // After loading font CSS, update the font family dropdown
|
|
598
|
+
* const fonts = parseFontFaces(cssContent)
|
|
599
|
+
* api.settings.updateOptions('fontFamily', fonts.map(f => ({
|
|
600
|
+
* label: f.family,
|
|
601
|
+
* value: f.family
|
|
602
|
+
* })))
|
|
603
|
+
* ```
|
|
604
|
+
*/
|
|
605
|
+
settings: SettingsAPI;
|
|
545
606
|
/**
|
|
546
607
|
* File Access API for requesting access to user-uploaded files.
|
|
547
608
|
* Files are not sent automatically - the addon must request access.
|
|
@@ -805,6 +866,86 @@ interface AudioAPI {
|
|
|
805
866
|
*/
|
|
806
867
|
onStateChange(callback: (state: AudioState) => void): () => void;
|
|
807
868
|
}
|
|
869
|
+
/**
|
|
870
|
+
* A setting option for select/dropdown fields.
|
|
871
|
+
*/
|
|
872
|
+
interface SettingOption {
|
|
873
|
+
/** Display label shown to user */
|
|
874
|
+
label: string;
|
|
875
|
+
/** Value stored when selected */
|
|
876
|
+
value: string | number | boolean;
|
|
877
|
+
/** Optional description */
|
|
878
|
+
description?: string;
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Settings API for dynamically modifying setting options at runtime.
|
|
882
|
+
* Access via `window.MyWallpaper.settings`
|
|
883
|
+
*
|
|
884
|
+
* **Why this exists:**
|
|
885
|
+
* Some widgets need to populate setting options dynamically based on
|
|
886
|
+
* external data (e.g., font families from a CSS file, available themes
|
|
887
|
+
* from an API, etc.). This API allows widgets to modify their own
|
|
888
|
+
* setting options after loading.
|
|
889
|
+
*
|
|
890
|
+
* **Important:**
|
|
891
|
+
* - Only works for settings declared in manifest.json
|
|
892
|
+
* - Only 'select' type settings can have their options updated
|
|
893
|
+
* - Changes are session-only (manifest defines the defaults)
|
|
894
|
+
*
|
|
895
|
+
* @example
|
|
896
|
+
* ```typescript
|
|
897
|
+
* const api = window.MyWallpaper
|
|
898
|
+
*
|
|
899
|
+
* // After fetching a font CSS, parse available fonts and update dropdown
|
|
900
|
+
* const fonts = parseFontFaces(cssContent)
|
|
901
|
+
* api.settings.updateOptions('fontFamily', fonts.map(f => ({
|
|
902
|
+
* label: f.family,
|
|
903
|
+
* value: f.family
|
|
904
|
+
* })))
|
|
905
|
+
*
|
|
906
|
+
* // Update multiple settings at once
|
|
907
|
+
* api.settings.updateOptions('fontWeight', [
|
|
908
|
+
* { label: 'Light', value: '300' },
|
|
909
|
+
* { label: 'Regular', value: '400' },
|
|
910
|
+
* { label: 'Bold', value: '700' }
|
|
911
|
+
* ])
|
|
912
|
+
* ```
|
|
913
|
+
*/
|
|
914
|
+
interface SettingsAPI {
|
|
915
|
+
/**
|
|
916
|
+
* Update the options for a select-type setting.
|
|
917
|
+
* The new options replace existing ones from the manifest.
|
|
918
|
+
*
|
|
919
|
+
* @param settingKey - The setting key to update (must be a 'select' type)
|
|
920
|
+
* @param options - Array of new options
|
|
921
|
+
* @param defaultValue - Optional new default value
|
|
922
|
+
*
|
|
923
|
+
* @example
|
|
924
|
+
* ```typescript
|
|
925
|
+
* // Update font family options based on loaded CSS
|
|
926
|
+
* api.settings.updateOptions('customFontFamily', [
|
|
927
|
+
* { label: 'Roboto', value: 'Roboto' },
|
|
928
|
+
* { label: 'Open Sans', value: 'Open Sans' },
|
|
929
|
+
* { label: 'Lato', value: 'Lato' }
|
|
930
|
+
* ], 'Roboto')
|
|
931
|
+
* ```
|
|
932
|
+
*/
|
|
933
|
+
updateOptions(settingKey: string, options: SettingOption[], defaultValue?: string | number | boolean): void;
|
|
934
|
+
/**
|
|
935
|
+
* Get the current options for a setting.
|
|
936
|
+
* Returns the dynamic options if set, otherwise the manifest defaults.
|
|
937
|
+
*
|
|
938
|
+
* @param settingKey - The setting key to get options for
|
|
939
|
+
* @returns Array of options or undefined if not a select setting
|
|
940
|
+
*/
|
|
941
|
+
getOptions(settingKey: string): SettingOption[] | undefined;
|
|
942
|
+
/**
|
|
943
|
+
* Reset a setting's options back to the manifest defaults.
|
|
944
|
+
*
|
|
945
|
+
* @param settingKey - The setting key to reset
|
|
946
|
+
*/
|
|
947
|
+
resetOptions(settingKey: string): void;
|
|
948
|
+
}
|
|
808
949
|
|
|
809
950
|
/**
|
|
810
951
|
* @mywallpaper/addon-sdk - Manifest Schema & Validation
|
package/package.json
CHANGED
|
@@ -29,7 +29,9 @@
|
|
|
29
29
|
var pendingOAuth = new Map()
|
|
30
30
|
var pendingOAuthScopes = new Map()
|
|
31
31
|
var pendingFileAccess = new Map()
|
|
32
|
+
var pendingNetworkAccess = new Map() // For on-demand domain permission requests
|
|
32
33
|
var grantedBlobUrls = new Map() // settingKey -> blobUrl
|
|
34
|
+
var dynamicSettingsOptions = new Map() // settingKey -> options[] (dynamic options from widget)
|
|
33
35
|
|
|
34
36
|
// Audio state (synced from host)
|
|
35
37
|
var audioState = {
|
|
@@ -52,6 +54,39 @@
|
|
|
52
54
|
if (!d || d.source !== 'MyWallpaperHost') return
|
|
53
55
|
|
|
54
56
|
switch (d.type) {
|
|
57
|
+
case 'SETTINGS_PREVIEW':
|
|
58
|
+
// FAST PATH: Lightweight preview for drag operations (color picker, sliders)
|
|
59
|
+
// Updates CSS variables AND triggers callbacks for immediate visual feedback
|
|
60
|
+
var previewPayload = d.payload || d
|
|
61
|
+
var previewSettings = previewPayload.settings || {}
|
|
62
|
+
var previewKeys = previewPayload.changedKeys || Object.keys(previewSettings)
|
|
63
|
+
|
|
64
|
+
// Update CSS variables
|
|
65
|
+
try {
|
|
66
|
+
var previewRoot = document.documentElement
|
|
67
|
+
for (var pi = 0; pi < previewKeys.length; pi++) {
|
|
68
|
+
var pKey = previewKeys[pi]
|
|
69
|
+
var pValue = previewSettings[pKey]
|
|
70
|
+
if (pValue !== undefined && pValue !== null) {
|
|
71
|
+
var pType = typeof pValue
|
|
72
|
+
if (pType === 'string' || pType === 'number' || pType === 'boolean') {
|
|
73
|
+
previewRoot.style.setProperty('--mw-config-' + pKey, String(pValue))
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
} catch (previewErr) {
|
|
78
|
+
// Silent fail
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Also update config and trigger callbacks for addons that use JS
|
|
82
|
+
for (var pk = 0; pk < previewKeys.length; pk++) {
|
|
83
|
+
config[previewKeys[pk]] = previewSettings[previewKeys[pk]]
|
|
84
|
+
}
|
|
85
|
+
settingsCallbacks.forEach(function (cb) {
|
|
86
|
+
try { cb(config, previewKeys) } catch (err) { /* silent */ }
|
|
87
|
+
})
|
|
88
|
+
break
|
|
89
|
+
|
|
55
90
|
case 'SETTINGS_UPDATE':
|
|
56
91
|
var p = d.payload || d
|
|
57
92
|
config = p.settings || p
|
|
@@ -197,6 +232,15 @@
|
|
|
197
232
|
}
|
|
198
233
|
break
|
|
199
234
|
|
|
235
|
+
case 'NETWORK_ACCESS_RESPONSE':
|
|
236
|
+
// Handle network domain access response from host
|
|
237
|
+
var netAccessOp = pendingNetworkAccess.get(d.requestId)
|
|
238
|
+
if (netAccessOp) {
|
|
239
|
+
pendingNetworkAccess.delete(d.requestId)
|
|
240
|
+
netAccessOp.resolve({ granted: d.granted === true, error: d.error })
|
|
241
|
+
}
|
|
242
|
+
break
|
|
243
|
+
|
|
200
244
|
case 'AUDIO_STATE':
|
|
201
245
|
// Sync audio state from host
|
|
202
246
|
audioState = d.payload || d
|
|
@@ -225,10 +269,33 @@
|
|
|
225
269
|
})
|
|
226
270
|
}
|
|
227
271
|
|
|
272
|
+
/**
|
|
273
|
+
* Request on-demand access to a network domain
|
|
274
|
+
* Shows a permission modal to the user
|
|
275
|
+
* @param {string} domain - The domain to request access to
|
|
276
|
+
* @param {string} reason - User-friendly explanation
|
|
277
|
+
* @returns {Promise<{granted: boolean, error?: string}>}
|
|
278
|
+
*/
|
|
279
|
+
function networkRequestAccess(domain, reason) {
|
|
280
|
+
return new Promise(function (resolve) {
|
|
281
|
+
var id = Date.now() + '-' + Math.random().toString(36).slice(2)
|
|
282
|
+
pendingNetworkAccess.set(id, { resolve: resolve })
|
|
283
|
+
// 60s timeout - user needs time to respond to modal
|
|
284
|
+
setTimeout(function () {
|
|
285
|
+
if (pendingNetworkAccess.has(id)) {
|
|
286
|
+
pendingNetworkAccess.delete(id)
|
|
287
|
+
resolve({ granted: false, error: 'Request timeout' })
|
|
288
|
+
}
|
|
289
|
+
}, 60000)
|
|
290
|
+
send('NETWORK_DOMAIN_REQUEST', { domain: domain, reason: reason, requestId: id })
|
|
291
|
+
})
|
|
292
|
+
}
|
|
293
|
+
|
|
228
294
|
/**
|
|
229
295
|
* Network fetch via secure host proxy
|
|
230
296
|
* This is the ONLY way addons can make network requests
|
|
231
297
|
* Domain must be declared in manifest.json permissions.network.domains
|
|
298
|
+
* OR approved via networkRequestAccess()
|
|
232
299
|
*/
|
|
233
300
|
function networkFetch(url, options) {
|
|
234
301
|
return new Promise(function (resolve, reject) {
|
|
@@ -414,11 +481,18 @@
|
|
|
414
481
|
* All requests go through the host which validates domain whitelist
|
|
415
482
|
*
|
|
416
483
|
* @example
|
|
484
|
+
* // Option 1: Pre-declare domains in manifest.json
|
|
417
485
|
* // manifest.json: "permissions": { "network": { "domains": ["api.weather.com"] } }
|
|
418
486
|
* const response = await MyWallpaper.network.fetch('https://api.weather.com/current')
|
|
419
|
-
*
|
|
487
|
+
*
|
|
488
|
+
* // Option 2: On-demand permission (shows modal to user)
|
|
489
|
+
* const access = await MyWallpaper.network.requestAccess('fonts.example.com', 'Load fonts')
|
|
490
|
+
* if (access.granted) {
|
|
491
|
+
* const response = await MyWallpaper.network.fetch('https://fonts.example.com/font.woff2')
|
|
492
|
+
* }
|
|
420
493
|
*/
|
|
421
494
|
network: {
|
|
495
|
+
requestAccess: networkRequestAccess,
|
|
422
496
|
fetch: networkFetch
|
|
423
497
|
},
|
|
424
498
|
|
|
@@ -641,6 +715,76 @@
|
|
|
641
715
|
callback(audioState)
|
|
642
716
|
return function () { audioCallbacks.delete(callback) }
|
|
643
717
|
}
|
|
718
|
+
},
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Settings API - Dynamically modify setting options at runtime
|
|
722
|
+
* Allows widgets to populate dropdowns based on external data
|
|
723
|
+
*
|
|
724
|
+
* @example
|
|
725
|
+
* // After fetching a font CSS, parse available fonts and update dropdown
|
|
726
|
+
* const fonts = parseFontFaces(cssContent)
|
|
727
|
+
* MyWallpaper.settings.updateOptions('fontFamily', fonts.map(f => ({
|
|
728
|
+
* label: f.family,
|
|
729
|
+
* value: f.family
|
|
730
|
+
* })))
|
|
731
|
+
*/
|
|
732
|
+
settings: {
|
|
733
|
+
/**
|
|
734
|
+
* Update the options for a select-type setting
|
|
735
|
+
* @param {string} settingKey - The setting key to update
|
|
736
|
+
* @param {Array<{label: string, value: string|number|boolean, description?: string}>} options - New options
|
|
737
|
+
* @param {string|number|boolean} defaultValue - Optional new default value
|
|
738
|
+
*/
|
|
739
|
+
updateOptions: function (settingKey, options, defaultValue) {
|
|
740
|
+
if (!settingKey || typeof settingKey !== 'string') {
|
|
741
|
+
console.error('[MyWallpaper] settings.updateOptions: settingKey is required')
|
|
742
|
+
return
|
|
743
|
+
}
|
|
744
|
+
if (!Array.isArray(options)) {
|
|
745
|
+
console.error('[MyWallpaper] settings.updateOptions: options must be an array')
|
|
746
|
+
return
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// Validate options format
|
|
750
|
+
var validOptions = options.filter(function (opt) {
|
|
751
|
+
return opt && typeof opt === 'object' && typeof opt.label === 'string' && opt.value !== undefined
|
|
752
|
+
})
|
|
753
|
+
|
|
754
|
+
if (validOptions.length === 0) {
|
|
755
|
+
console.warn('[MyWallpaper] settings.updateOptions: no valid options provided')
|
|
756
|
+
return
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// Store locally for getOptions()
|
|
760
|
+
dynamicSettingsOptions.set(settingKey, validOptions)
|
|
761
|
+
|
|
762
|
+
// Send to host to update the settings panel
|
|
763
|
+
send('SETTINGS_OPTIONS_UPDATE', {
|
|
764
|
+
settingKey: settingKey,
|
|
765
|
+
options: validOptions,
|
|
766
|
+
defaultValue: defaultValue
|
|
767
|
+
})
|
|
768
|
+
},
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Get the current options for a setting
|
|
772
|
+
* Returns dynamic options if set, otherwise undefined
|
|
773
|
+
* @param {string} settingKey - The setting key
|
|
774
|
+
* @returns {Array|undefined} The options array or undefined
|
|
775
|
+
*/
|
|
776
|
+
getOptions: function (settingKey) {
|
|
777
|
+
return dynamicSettingsOptions.get(settingKey)
|
|
778
|
+
},
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Reset a setting's options back to the manifest defaults
|
|
782
|
+
* @param {string} settingKey - The setting key to reset
|
|
783
|
+
*/
|
|
784
|
+
resetOptions: function (settingKey) {
|
|
785
|
+
dynamicSettingsOptions.delete(settingKey)
|
|
786
|
+
send('SETTINGS_OPTIONS_RESET', { settingKey: settingKey })
|
|
787
|
+
}
|
|
644
788
|
}
|
|
645
789
|
}
|
|
646
790
|
|