├── .gitignore
├── Extension
├── .prettierignore
├── README.md
├── _locales
│ └── en
│ │ └── messages.json
├── images
│ └── .gitkeep
├── manifest.json
├── package-lock.json
├── package.json
├── public
│ ├── popup.html
│ └── settings.html
├── scripts
│ ├── build.js
│ ├── firefox-build.js
│ ├── package-extension.js
│ └── prod-build.js
├── src
│ ├── background.ts
│ ├── content.ts
│ ├── models.ts
│ ├── popup
│ │ ├── popup.svelte
│ │ └── popup.ts
│ ├── settings-connector.ts
│ └── settings
│ │ ├── settings.svelte
│ │ └── settings.ts
└── tsconfig.json
├── LICENSE
├── README.md
└── Safari
├── REPLACEME.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcshareddata
│ └── xcschemes
│ │ ├── REPLACEME (iOS).xcscheme
│ │ └── REPLACEME (macOS).xcscheme
└── xcuserdata
│ ├── kylenazario.xcuserdatad
│ └── xcschemes
│ │ └── xcschememanagement.plist
│ └── tester.xcuserdatad
│ └── xcschemes
│ └── xcschememanagement.plist
├── Shared (App)
├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── Contents.json
│ └── LargeIcon.imageset
│ │ └── Contents.json
├── Models.swift
└── Views.swift
├── Shared (Extension)
└── SafariWebExtensionHandler.swift
├── iOS (App)
├── Info.plist
├── iOS AppView.swift
└── iOS Assets.xcassets
│ └── Contents.json
├── iOS (Extension)
└── Info.plist
├── macOS (App)
├── AppDelegate.swift
├── REPLACEME.entitlements
├── SafariConnector.swift
├── macOS AppView.swift
├── macOS Assets.xcassets
│ └── Contents.json
└── main.swift
└── macOS (Extension)
├── Info.plist
└── REPLACEME.entitlements
/.gitignore:
--------------------------------------------------------------------------------
1 | # macOS
2 | .DS_Store
3 |
4 | # web
5 | **/node_modules
6 | Extension/dist
7 |
8 | # safari
9 | **/xcdebugger
10 | Shared\ (Extension)/Resources/dist/
11 | **/xcuserdata
12 | **/*.xcuserstate
13 |
--------------------------------------------------------------------------------
/Extension/.prettierignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 |
--------------------------------------------------------------------------------
/Extension/README.md:
--------------------------------------------------------------------------------
1 | # REPLACEME
2 |
3 | ## Building and running locally
4 |
5 | Build scripts are tested on **Mac**. These will probably work on Linux and Windows Subsystem for Linux, but I have not tested them.
6 |
7 | - Make sure you have Node.js 19.7.0 or later installed
8 | - Open the folder containing this README
9 | - Run `npm install`
10 | - Run `npm run build`
11 |
12 | Continue the process with the steps listed below.
13 |
14 | ### Firefox
15 |
16 | - Open Firefox's [debugging page](about:debugging#/runtime/this-firefox) (`about:debugging#/runtime/this-firefox`)
17 | - Click "Load Temporary Add-on..."
18 | - Navigate to this project's root and select `manifest.json`
19 |
--------------------------------------------------------------------------------
/Extension/_locales/en/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "extension_name": {
3 | "message": "REPLACEME",
4 | "description": "The display name for the extension."
5 | },
6 | "extension_description": {
7 | "message": "REPLACEME",
8 | "description": "Description of what the extension does."
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Extension/images/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyle-n/WebExtensionTemplate/d8e86218aa593ac667edf5758c541212eee7d56b/Extension/images/.gitkeep
--------------------------------------------------------------------------------
/Extension/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 3,
3 | "default_locale": "en",
4 |
5 | "name": "__MSG_extension_name__",
6 | "description": "__MSG_extension_description__",
7 | "version": "1.0.0",
8 |
9 | "icons": {
10 | "48": "images/app_icon_48px.png",
11 | "96": "images/app_icon_96px.png",
12 | "128": "images/app_icon_128px.png",
13 | "256": "images/app_icon_256px.png",
14 | "512": "images/app_icon_512px.png"
15 | },
16 |
17 | "background": {
18 | "service_worker": "dist/background.js"
19 | },
20 |
21 | "host_permissions": [],
22 |
23 | "content_scripts": [
24 | {
25 | "js": ["dist/content.js"],
26 | "matches": ["*://example.com/*"]
27 | }
28 | ],
29 |
30 | "action": {
31 | "default_popup": "public/popup.html",
32 | "default_icon": {
33 | "16": "images/toolbar_16px.png",
34 | "19": "images/toolbar_19px.png",
35 | "32": "images/toolbar_32px.png",
36 | "38": "images/toolbar_38px.png",
37 | "48": "images/toolbar_48px.png",
38 | "72": "images/toolbar_72px.png"
39 | }
40 | },
41 |
42 | "permissions": ["storage"],
43 |
44 | "options_ui": {
45 | "page": "public/settings.html",
46 | "open_in_tab": true
47 | },
48 |
49 | "browser_specific_settings": {
50 | "gecko": {
51 | "id": "REPLACEME"
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Extension/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "REPLACEME",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "REPLACEME",
9 | "version": "1.0.0",
10 | "license": "MIT",
11 | "dependencies": {
12 | "detect-browser": "^5.3.0",
13 | "svelte": "^5.25.7",
14 | "webextension-polyfill": "^0.12.0"
15 | },
16 | "devDependencies": {
17 | "@tsconfig/svelte": "^5.0.4",
18 | "@types/webextension-polyfill": "^0.12.3",
19 | "esbuild": "^0.25.2",
20 | "esbuild-svelte": "^0.9.2",
21 | "fs-extra": "^11.3.0",
22 | "prettier": "^3.5.3",
23 | "prettier-plugin-svelte": "^3.3.3",
24 | "svelte-preprocess": "^6.0.3",
25 | "typescript": "^5.8.3",
26 | "zip-local": "^0.3.5"
27 | }
28 | },
29 | "node_modules/@ampproject/remapping": {
30 | "version": "2.3.0",
31 | "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
32 | "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
33 | "license": "Apache-2.0",
34 | "dependencies": {
35 | "@jridgewell/gen-mapping": "^0.3.5",
36 | "@jridgewell/trace-mapping": "^0.3.24"
37 | },
38 | "engines": {
39 | "node": ">=6.0.0"
40 | }
41 | },
42 | "node_modules/@esbuild/aix-ppc64": {
43 | "version": "0.25.2",
44 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz",
45 | "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==",
46 | "cpu": [
47 | "ppc64"
48 | ],
49 | "dev": true,
50 | "license": "MIT",
51 | "optional": true,
52 | "os": [
53 | "aix"
54 | ],
55 | "engines": {
56 | "node": ">=18"
57 | }
58 | },
59 | "node_modules/@esbuild/android-arm": {
60 | "version": "0.25.2",
61 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz",
62 | "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==",
63 | "cpu": [
64 | "arm"
65 | ],
66 | "dev": true,
67 | "license": "MIT",
68 | "optional": true,
69 | "os": [
70 | "android"
71 | ],
72 | "engines": {
73 | "node": ">=18"
74 | }
75 | },
76 | "node_modules/@esbuild/android-arm64": {
77 | "version": "0.25.2",
78 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz",
79 | "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==",
80 | "cpu": [
81 | "arm64"
82 | ],
83 | "dev": true,
84 | "license": "MIT",
85 | "optional": true,
86 | "os": [
87 | "android"
88 | ],
89 | "engines": {
90 | "node": ">=18"
91 | }
92 | },
93 | "node_modules/@esbuild/android-x64": {
94 | "version": "0.25.2",
95 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz",
96 | "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==",
97 | "cpu": [
98 | "x64"
99 | ],
100 | "dev": true,
101 | "license": "MIT",
102 | "optional": true,
103 | "os": [
104 | "android"
105 | ],
106 | "engines": {
107 | "node": ">=18"
108 | }
109 | },
110 | "node_modules/@esbuild/darwin-arm64": {
111 | "version": "0.25.2",
112 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz",
113 | "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==",
114 | "cpu": [
115 | "arm64"
116 | ],
117 | "dev": true,
118 | "license": "MIT",
119 | "optional": true,
120 | "os": [
121 | "darwin"
122 | ],
123 | "engines": {
124 | "node": ">=18"
125 | }
126 | },
127 | "node_modules/@esbuild/darwin-x64": {
128 | "version": "0.25.2",
129 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz",
130 | "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==",
131 | "cpu": [
132 | "x64"
133 | ],
134 | "dev": true,
135 | "license": "MIT",
136 | "optional": true,
137 | "os": [
138 | "darwin"
139 | ],
140 | "engines": {
141 | "node": ">=18"
142 | }
143 | },
144 | "node_modules/@esbuild/freebsd-arm64": {
145 | "version": "0.25.2",
146 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz",
147 | "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==",
148 | "cpu": [
149 | "arm64"
150 | ],
151 | "dev": true,
152 | "license": "MIT",
153 | "optional": true,
154 | "os": [
155 | "freebsd"
156 | ],
157 | "engines": {
158 | "node": ">=18"
159 | }
160 | },
161 | "node_modules/@esbuild/freebsd-x64": {
162 | "version": "0.25.2",
163 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz",
164 | "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==",
165 | "cpu": [
166 | "x64"
167 | ],
168 | "dev": true,
169 | "license": "MIT",
170 | "optional": true,
171 | "os": [
172 | "freebsd"
173 | ],
174 | "engines": {
175 | "node": ">=18"
176 | }
177 | },
178 | "node_modules/@esbuild/linux-arm": {
179 | "version": "0.25.2",
180 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz",
181 | "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==",
182 | "cpu": [
183 | "arm"
184 | ],
185 | "dev": true,
186 | "license": "MIT",
187 | "optional": true,
188 | "os": [
189 | "linux"
190 | ],
191 | "engines": {
192 | "node": ">=18"
193 | }
194 | },
195 | "node_modules/@esbuild/linux-arm64": {
196 | "version": "0.25.2",
197 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz",
198 | "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==",
199 | "cpu": [
200 | "arm64"
201 | ],
202 | "dev": true,
203 | "license": "MIT",
204 | "optional": true,
205 | "os": [
206 | "linux"
207 | ],
208 | "engines": {
209 | "node": ">=18"
210 | }
211 | },
212 | "node_modules/@esbuild/linux-ia32": {
213 | "version": "0.25.2",
214 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz",
215 | "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==",
216 | "cpu": [
217 | "ia32"
218 | ],
219 | "dev": true,
220 | "license": "MIT",
221 | "optional": true,
222 | "os": [
223 | "linux"
224 | ],
225 | "engines": {
226 | "node": ">=18"
227 | }
228 | },
229 | "node_modules/@esbuild/linux-loong64": {
230 | "version": "0.25.2",
231 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz",
232 | "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==",
233 | "cpu": [
234 | "loong64"
235 | ],
236 | "dev": true,
237 | "license": "MIT",
238 | "optional": true,
239 | "os": [
240 | "linux"
241 | ],
242 | "engines": {
243 | "node": ">=18"
244 | }
245 | },
246 | "node_modules/@esbuild/linux-mips64el": {
247 | "version": "0.25.2",
248 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz",
249 | "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==",
250 | "cpu": [
251 | "mips64el"
252 | ],
253 | "dev": true,
254 | "license": "MIT",
255 | "optional": true,
256 | "os": [
257 | "linux"
258 | ],
259 | "engines": {
260 | "node": ">=18"
261 | }
262 | },
263 | "node_modules/@esbuild/linux-ppc64": {
264 | "version": "0.25.2",
265 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz",
266 | "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==",
267 | "cpu": [
268 | "ppc64"
269 | ],
270 | "dev": true,
271 | "license": "MIT",
272 | "optional": true,
273 | "os": [
274 | "linux"
275 | ],
276 | "engines": {
277 | "node": ">=18"
278 | }
279 | },
280 | "node_modules/@esbuild/linux-riscv64": {
281 | "version": "0.25.2",
282 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz",
283 | "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==",
284 | "cpu": [
285 | "riscv64"
286 | ],
287 | "dev": true,
288 | "license": "MIT",
289 | "optional": true,
290 | "os": [
291 | "linux"
292 | ],
293 | "engines": {
294 | "node": ">=18"
295 | }
296 | },
297 | "node_modules/@esbuild/linux-s390x": {
298 | "version": "0.25.2",
299 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz",
300 | "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==",
301 | "cpu": [
302 | "s390x"
303 | ],
304 | "dev": true,
305 | "license": "MIT",
306 | "optional": true,
307 | "os": [
308 | "linux"
309 | ],
310 | "engines": {
311 | "node": ">=18"
312 | }
313 | },
314 | "node_modules/@esbuild/linux-x64": {
315 | "version": "0.25.2",
316 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz",
317 | "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==",
318 | "cpu": [
319 | "x64"
320 | ],
321 | "dev": true,
322 | "license": "MIT",
323 | "optional": true,
324 | "os": [
325 | "linux"
326 | ],
327 | "engines": {
328 | "node": ">=18"
329 | }
330 | },
331 | "node_modules/@esbuild/netbsd-arm64": {
332 | "version": "0.25.2",
333 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz",
334 | "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==",
335 | "cpu": [
336 | "arm64"
337 | ],
338 | "dev": true,
339 | "license": "MIT",
340 | "optional": true,
341 | "os": [
342 | "netbsd"
343 | ],
344 | "engines": {
345 | "node": ">=18"
346 | }
347 | },
348 | "node_modules/@esbuild/netbsd-x64": {
349 | "version": "0.25.2",
350 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz",
351 | "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==",
352 | "cpu": [
353 | "x64"
354 | ],
355 | "dev": true,
356 | "license": "MIT",
357 | "optional": true,
358 | "os": [
359 | "netbsd"
360 | ],
361 | "engines": {
362 | "node": ">=18"
363 | }
364 | },
365 | "node_modules/@esbuild/openbsd-arm64": {
366 | "version": "0.25.2",
367 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz",
368 | "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==",
369 | "cpu": [
370 | "arm64"
371 | ],
372 | "dev": true,
373 | "license": "MIT",
374 | "optional": true,
375 | "os": [
376 | "openbsd"
377 | ],
378 | "engines": {
379 | "node": ">=18"
380 | }
381 | },
382 | "node_modules/@esbuild/openbsd-x64": {
383 | "version": "0.25.2",
384 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz",
385 | "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==",
386 | "cpu": [
387 | "x64"
388 | ],
389 | "dev": true,
390 | "license": "MIT",
391 | "optional": true,
392 | "os": [
393 | "openbsd"
394 | ],
395 | "engines": {
396 | "node": ">=18"
397 | }
398 | },
399 | "node_modules/@esbuild/sunos-x64": {
400 | "version": "0.25.2",
401 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz",
402 | "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==",
403 | "cpu": [
404 | "x64"
405 | ],
406 | "dev": true,
407 | "license": "MIT",
408 | "optional": true,
409 | "os": [
410 | "sunos"
411 | ],
412 | "engines": {
413 | "node": ">=18"
414 | }
415 | },
416 | "node_modules/@esbuild/win32-arm64": {
417 | "version": "0.25.2",
418 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz",
419 | "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==",
420 | "cpu": [
421 | "arm64"
422 | ],
423 | "dev": true,
424 | "license": "MIT",
425 | "optional": true,
426 | "os": [
427 | "win32"
428 | ],
429 | "engines": {
430 | "node": ">=18"
431 | }
432 | },
433 | "node_modules/@esbuild/win32-ia32": {
434 | "version": "0.25.2",
435 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz",
436 | "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==",
437 | "cpu": [
438 | "ia32"
439 | ],
440 | "dev": true,
441 | "license": "MIT",
442 | "optional": true,
443 | "os": [
444 | "win32"
445 | ],
446 | "engines": {
447 | "node": ">=18"
448 | }
449 | },
450 | "node_modules/@esbuild/win32-x64": {
451 | "version": "0.25.2",
452 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz",
453 | "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==",
454 | "cpu": [
455 | "x64"
456 | ],
457 | "dev": true,
458 | "license": "MIT",
459 | "optional": true,
460 | "os": [
461 | "win32"
462 | ],
463 | "engines": {
464 | "node": ">=18"
465 | }
466 | },
467 | "node_modules/@jridgewell/gen-mapping": {
468 | "version": "0.3.8",
469 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
470 | "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
471 | "license": "MIT",
472 | "dependencies": {
473 | "@jridgewell/set-array": "^1.2.1",
474 | "@jridgewell/sourcemap-codec": "^1.4.10",
475 | "@jridgewell/trace-mapping": "^0.3.24"
476 | },
477 | "engines": {
478 | "node": ">=6.0.0"
479 | }
480 | },
481 | "node_modules/@jridgewell/resolve-uri": {
482 | "version": "3.1.2",
483 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
484 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
485 | "license": "MIT",
486 | "engines": {
487 | "node": ">=6.0.0"
488 | }
489 | },
490 | "node_modules/@jridgewell/set-array": {
491 | "version": "1.2.1",
492 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
493 | "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
494 | "license": "MIT",
495 | "engines": {
496 | "node": ">=6.0.0"
497 | }
498 | },
499 | "node_modules/@jridgewell/sourcemap-codec": {
500 | "version": "1.5.0",
501 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
502 | "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
503 | "license": "MIT"
504 | },
505 | "node_modules/@jridgewell/trace-mapping": {
506 | "version": "0.3.25",
507 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
508 | "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
509 | "license": "MIT",
510 | "dependencies": {
511 | "@jridgewell/resolve-uri": "^3.1.0",
512 | "@jridgewell/sourcemap-codec": "^1.4.14"
513 | }
514 | },
515 | "node_modules/@sveltejs/acorn-typescript": {
516 | "version": "1.0.5",
517 | "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.5.tgz",
518 | "integrity": "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==",
519 | "license": "MIT",
520 | "peerDependencies": {
521 | "acorn": "^8.9.0"
522 | }
523 | },
524 | "node_modules/@tsconfig/svelte": {
525 | "version": "5.0.4",
526 | "resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-5.0.4.tgz",
527 | "integrity": "sha512-BV9NplVgLmSi4mwKzD8BD/NQ8erOY/nUE/GpgWe2ckx+wIQF5RyRirn/QsSSCPeulVpc3RA/iJt6DpfTIZps0Q==",
528 | "dev": true,
529 | "license": "MIT"
530 | },
531 | "node_modules/@types/estree": {
532 | "version": "1.0.7",
533 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
534 | "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
535 | "license": "MIT"
536 | },
537 | "node_modules/@types/webextension-polyfill": {
538 | "version": "0.12.3",
539 | "resolved": "https://registry.npmjs.org/@types/webextension-polyfill/-/webextension-polyfill-0.12.3.tgz",
540 | "integrity": "sha512-F58aDVSeN/MjUGazXo/cPsmR76EvqQhQ1v4x23hFjUX0cfAJYE+JBWwiOGW36/VJGGxoH74sVlRIF3z7SJCKyg==",
541 | "dev": true,
542 | "license": "MIT"
543 | },
544 | "node_modules/acorn": {
545 | "version": "8.14.1",
546 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
547 | "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
548 | "license": "MIT",
549 | "bin": {
550 | "acorn": "bin/acorn"
551 | },
552 | "engines": {
553 | "node": ">=0.4.0"
554 | }
555 | },
556 | "node_modules/aria-query": {
557 | "version": "5.3.2",
558 | "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
559 | "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
560 | "license": "Apache-2.0",
561 | "engines": {
562 | "node": ">= 0.4"
563 | }
564 | },
565 | "node_modules/async": {
566 | "version": "1.5.2",
567 | "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
568 | "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==",
569 | "dev": true
570 | },
571 | "node_modules/axobject-query": {
572 | "version": "4.1.0",
573 | "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
574 | "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
575 | "license": "Apache-2.0",
576 | "engines": {
577 | "node": ">= 0.4"
578 | }
579 | },
580 | "node_modules/clsx": {
581 | "version": "2.1.1",
582 | "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
583 | "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
584 | "license": "MIT",
585 | "engines": {
586 | "node": ">=6"
587 | }
588 | },
589 | "node_modules/detect-browser": {
590 | "version": "5.3.0",
591 | "resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.3.0.tgz",
592 | "integrity": "sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==",
593 | "license": "MIT"
594 | },
595 | "node_modules/esbuild": {
596 | "version": "0.25.2",
597 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz",
598 | "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==",
599 | "dev": true,
600 | "hasInstallScript": true,
601 | "license": "MIT",
602 | "bin": {
603 | "esbuild": "bin/esbuild"
604 | },
605 | "engines": {
606 | "node": ">=18"
607 | },
608 | "optionalDependencies": {
609 | "@esbuild/aix-ppc64": "0.25.2",
610 | "@esbuild/android-arm": "0.25.2",
611 | "@esbuild/android-arm64": "0.25.2",
612 | "@esbuild/android-x64": "0.25.2",
613 | "@esbuild/darwin-arm64": "0.25.2",
614 | "@esbuild/darwin-x64": "0.25.2",
615 | "@esbuild/freebsd-arm64": "0.25.2",
616 | "@esbuild/freebsd-x64": "0.25.2",
617 | "@esbuild/linux-arm": "0.25.2",
618 | "@esbuild/linux-arm64": "0.25.2",
619 | "@esbuild/linux-ia32": "0.25.2",
620 | "@esbuild/linux-loong64": "0.25.2",
621 | "@esbuild/linux-mips64el": "0.25.2",
622 | "@esbuild/linux-ppc64": "0.25.2",
623 | "@esbuild/linux-riscv64": "0.25.2",
624 | "@esbuild/linux-s390x": "0.25.2",
625 | "@esbuild/linux-x64": "0.25.2",
626 | "@esbuild/netbsd-arm64": "0.25.2",
627 | "@esbuild/netbsd-x64": "0.25.2",
628 | "@esbuild/openbsd-arm64": "0.25.2",
629 | "@esbuild/openbsd-x64": "0.25.2",
630 | "@esbuild/sunos-x64": "0.25.2",
631 | "@esbuild/win32-arm64": "0.25.2",
632 | "@esbuild/win32-ia32": "0.25.2",
633 | "@esbuild/win32-x64": "0.25.2"
634 | }
635 | },
636 | "node_modules/esbuild-svelte": {
637 | "version": "0.9.2",
638 | "resolved": "https://registry.npmjs.org/esbuild-svelte/-/esbuild-svelte-0.9.2.tgz",
639 | "integrity": "sha512-8Jq6+rh+g1E2mkBOZKdYZ8JtlbtDq2Fydwvn+/cBvUX9S0cdKv6AISZcEbErKQ0TpLC/Cv04l1vKaqXOBO8+VQ==",
640 | "dev": true,
641 | "license": "MIT",
642 | "dependencies": {
643 | "@jridgewell/trace-mapping": "^0.3.19"
644 | },
645 | "engines": {
646 | "node": ">=18"
647 | },
648 | "peerDependencies": {
649 | "esbuild": ">=0.17.0",
650 | "svelte": ">=4.2.1 <6"
651 | }
652 | },
653 | "node_modules/esm-env": {
654 | "version": "1.2.2",
655 | "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
656 | "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==",
657 | "license": "MIT"
658 | },
659 | "node_modules/esrap": {
660 | "version": "1.4.6",
661 | "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.4.6.tgz",
662 | "integrity": "sha512-F/D2mADJ9SHY3IwksD4DAXjTt7qt7GWUf3/8RhCNWmC/67tyb55dpimHmy7EplakFaflV0R/PC+fdSPqrRHAQw==",
663 | "license": "MIT",
664 | "dependencies": {
665 | "@jridgewell/sourcemap-codec": "^1.4.15"
666 | }
667 | },
668 | "node_modules/fs-extra": {
669 | "version": "11.3.0",
670 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
671 | "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
672 | "dev": true,
673 | "license": "MIT",
674 | "dependencies": {
675 | "graceful-fs": "^4.2.0",
676 | "jsonfile": "^6.0.1",
677 | "universalify": "^2.0.0"
678 | },
679 | "engines": {
680 | "node": ">=14.14"
681 | }
682 | },
683 | "node_modules/graceful-fs": {
684 | "version": "4.2.11",
685 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
686 | "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
687 | "dev": true
688 | },
689 | "node_modules/is-reference": {
690 | "version": "3.0.3",
691 | "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
692 | "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
693 | "license": "MIT",
694 | "dependencies": {
695 | "@types/estree": "^1.0.6"
696 | }
697 | },
698 | "node_modules/jsonfile": {
699 | "version": "6.1.0",
700 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
701 | "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
702 | "dev": true,
703 | "dependencies": {
704 | "universalify": "^2.0.0"
705 | },
706 | "optionalDependencies": {
707 | "graceful-fs": "^4.1.6"
708 | }
709 | },
710 | "node_modules/locate-character": {
711 | "version": "3.0.0",
712 | "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
713 | "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="
714 | },
715 | "node_modules/pako": {
716 | "version": "1.0.11",
717 | "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
718 | "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
719 | "dev": true
720 | },
721 | "node_modules/prettier": {
722 | "version": "3.5.3",
723 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
724 | "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
725 | "dev": true,
726 | "license": "MIT",
727 | "bin": {
728 | "prettier": "bin/prettier.cjs"
729 | },
730 | "engines": {
731 | "node": ">=14"
732 | },
733 | "funding": {
734 | "url": "https://github.com/prettier/prettier?sponsor=1"
735 | }
736 | },
737 | "node_modules/prettier-plugin-svelte": {
738 | "version": "3.3.3",
739 | "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.3.3.tgz",
740 | "integrity": "sha512-yViK9zqQ+H2qZD1w/bH7W8i+bVfKrD8GIFjkFe4Thl6kCT9SlAsXVNmt3jCvQOCsnOhcvYgsoVlRV/Eu6x5nNw==",
741 | "dev": true,
742 | "license": "MIT",
743 | "peerDependencies": {
744 | "prettier": "^3.0.0",
745 | "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0"
746 | }
747 | },
748 | "node_modules/q": {
749 | "version": "1.5.1",
750 | "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
751 | "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==",
752 | "dev": true,
753 | "engines": {
754 | "node": ">=0.6.0",
755 | "teleport": ">=0.2.0"
756 | }
757 | },
758 | "node_modules/svelte": {
759 | "version": "5.25.7",
760 | "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.25.7.tgz",
761 | "integrity": "sha512-0fzXbXaKfSvFUs6Wxev2h4CoEhexZotbTF9EJ4+Cg7MHW64ZnZ9+xUedZyEpgj0Tt9HrYGv9aASHkqjn9b/cPw==",
762 | "license": "MIT",
763 | "dependencies": {
764 | "@ampproject/remapping": "^2.3.0",
765 | "@jridgewell/sourcemap-codec": "^1.5.0",
766 | "@sveltejs/acorn-typescript": "^1.0.5",
767 | "@types/estree": "^1.0.5",
768 | "acorn": "^8.12.1",
769 | "aria-query": "^5.3.1",
770 | "axobject-query": "^4.1.0",
771 | "clsx": "^2.1.1",
772 | "esm-env": "^1.2.1",
773 | "esrap": "^1.4.6",
774 | "is-reference": "^3.0.3",
775 | "locate-character": "^3.0.0",
776 | "magic-string": "^0.30.11",
777 | "zimmerframe": "^1.1.2"
778 | },
779 | "engines": {
780 | "node": ">=18"
781 | }
782 | },
783 | "node_modules/svelte-preprocess": {
784 | "version": "6.0.3",
785 | "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-6.0.3.tgz",
786 | "integrity": "sha512-PLG2k05qHdhmRG7zR/dyo5qKvakhm8IJ+hD2eFRQmMLHp7X3eJnjeupUtvuRpbNiF31RjVw45W+abDwHEmP5OA==",
787 | "dev": true,
788 | "hasInstallScript": true,
789 | "license": "MIT",
790 | "engines": {
791 | "node": ">= 18.0.0"
792 | },
793 | "peerDependencies": {
794 | "@babel/core": "^7.10.2",
795 | "coffeescript": "^2.5.1",
796 | "less": "^3.11.3 || ^4.0.0",
797 | "postcss": "^7 || ^8",
798 | "postcss-load-config": ">=3",
799 | "pug": "^3.0.0",
800 | "sass": "^1.26.8",
801 | "stylus": ">=0.55",
802 | "sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0",
803 | "svelte": "^4.0.0 || ^5.0.0-next.100 || ^5.0.0",
804 | "typescript": "^5.0.0"
805 | },
806 | "peerDependenciesMeta": {
807 | "@babel/core": {
808 | "optional": true
809 | },
810 | "coffeescript": {
811 | "optional": true
812 | },
813 | "less": {
814 | "optional": true
815 | },
816 | "postcss": {
817 | "optional": true
818 | },
819 | "postcss-load-config": {
820 | "optional": true
821 | },
822 | "pug": {
823 | "optional": true
824 | },
825 | "sass": {
826 | "optional": true
827 | },
828 | "stylus": {
829 | "optional": true
830 | },
831 | "sugarss": {
832 | "optional": true
833 | },
834 | "typescript": {
835 | "optional": true
836 | }
837 | }
838 | },
839 | "node_modules/svelte/node_modules/magic-string": {
840 | "version": "0.30.17",
841 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
842 | "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
843 | "license": "MIT",
844 | "dependencies": {
845 | "@jridgewell/sourcemap-codec": "^1.5.0"
846 | }
847 | },
848 | "node_modules/typescript": {
849 | "version": "5.8.3",
850 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
851 | "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
852 | "dev": true,
853 | "license": "Apache-2.0",
854 | "bin": {
855 | "tsc": "bin/tsc",
856 | "tsserver": "bin/tsserver"
857 | },
858 | "engines": {
859 | "node": ">=14.17"
860 | }
861 | },
862 | "node_modules/universalify": {
863 | "version": "2.0.0",
864 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
865 | "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
866 | "dev": true,
867 | "engines": {
868 | "node": ">= 10.0.0"
869 | }
870 | },
871 | "node_modules/webextension-polyfill": {
872 | "version": "0.12.0",
873 | "resolved": "https://registry.npmjs.org/webextension-polyfill/-/webextension-polyfill-0.12.0.tgz",
874 | "integrity": "sha512-97TBmpoWJEE+3nFBQ4VocyCdLKfw54rFaJ6EVQYLBCXqCIpLSZkwGgASpv4oPt9gdKCJ80RJlcmNzNn008Ag6Q==",
875 | "license": "MPL-2.0"
876 | },
877 | "node_modules/zimmerframe": {
878 | "version": "1.1.2",
879 | "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz",
880 | "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==",
881 | "license": "MIT"
882 | },
883 | "node_modules/zip-local": {
884 | "version": "0.3.5",
885 | "resolved": "https://registry.npmjs.org/zip-local/-/zip-local-0.3.5.tgz",
886 | "integrity": "sha512-GRV3D5TJY+/PqyeRm5CYBs7xVrKTKzljBoEXvocZu0HJ7tPEcgpSOYa2zFIsCZWgKWMuc4U3yMFgFkERGFIB9w==",
887 | "dev": true,
888 | "license": "MIT",
889 | "dependencies": {
890 | "async": "^1.4.2",
891 | "graceful-fs": "^4.1.3",
892 | "jszip": "^2.6.1",
893 | "q": "^1.4.1"
894 | }
895 | },
896 | "node_modules/zip-local/node_modules/jszip": {
897 | "version": "2.7.0",
898 | "resolved": "https://registry.npmjs.org/jszip/-/jszip-2.7.0.tgz",
899 | "integrity": "sha512-JIsRKRVC3gTRo2vM4Wy9WBC3TRcfnIZU8k65Phi3izkvPH975FowRYtKGT6PxevA0XnJ/yO8b0QwV0ydVyQwfw==",
900 | "dev": true,
901 | "dependencies": {
902 | "pako": "~1.0.2"
903 | }
904 | }
905 | }
906 | }
907 |
--------------------------------------------------------------------------------
/Extension/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "REPLACEME",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "background.js",
6 | "scripts": {
7 | "format": "prettier --write .",
8 | "build": "node scripts/build.js",
9 | "build:prod": "node scripts/prod-build.js",
10 | "build:chrome": "node scripts/package-extension.js",
11 | "build:firefox": "node scripts/firefox-build.js"
12 | },
13 | "keywords": [],
14 | "author": "REPLACEME",
15 | "license": "MIT",
16 | "devDependencies": {
17 | "@tsconfig/svelte": "^5.0.4",
18 | "@types/webextension-polyfill": "^0.12.3",
19 | "esbuild": "^0.25.2",
20 | "esbuild-svelte": "^0.9.2",
21 | "fs-extra": "^11.3.0",
22 | "prettier": "^3.5.3",
23 | "prettier-plugin-svelte": "^3.3.3",
24 | "svelte-preprocess": "^6.0.3",
25 | "typescript": "^5.8.3",
26 | "zip-local": "^0.3.5"
27 | },
28 | "prettier": {
29 | "tabWidth": 2,
30 | "semi": true,
31 | "singleQuote": true,
32 | "trailingComma": "none",
33 | "arrowParens": "avoid"
34 | },
35 | "dependencies": {
36 | "detect-browser": "^5.3.0",
37 | "svelte": "^5.25.7",
38 | "webextension-polyfill": "^0.12.0"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Extension/public/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
15 |
16 |
22 |
23 |
--------------------------------------------------------------------------------
/Extension/public/settings.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | REPLACEME
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Extension/scripts/build.js:
--------------------------------------------------------------------------------
1 | const { build } = require('esbuild');
2 | const sveltePlugin = require('esbuild-svelte');
3 | const sveltePreprocess = require('svelte-preprocess');
4 |
5 | const isProdBuild = process.argv.includes('--prod');
6 |
7 | main();
8 |
9 | async function main() {
10 | const commonConfig = {
11 | outbase: './src',
12 | platform: 'browser',
13 | external: [],
14 | bundle: true,
15 | sourcemap: !isProdBuild,
16 | minify: isProdBuild,
17 | tsconfig: './tsconfig.json',
18 | drop: isProdBuild ? ['console'] : undefined
19 | };
20 | const contentJob = build({
21 | ...commonConfig,
22 | entryPoints: ['./src/content.ts'],
23 | outfile: './dist/content.js'
24 | });
25 |
26 | const backgroundJob = build({
27 | ...commonConfig,
28 | entryPoints: ['./src/background.ts'],
29 | outfile: './dist/background.js'
30 | });
31 |
32 | const popupJob = build({
33 | ...commonConfig,
34 | entryPoints: ['./src/popup/popup.ts'],
35 | outbase: './src/popup',
36 | outdir: './dist',
37 | mainFields: ['svelte', 'module', 'main', 'browser'],
38 | plugins: [
39 | sveltePlugin({
40 | preprocess: sveltePreprocess()
41 | })
42 | ]
43 | });
44 |
45 | const settingsJob = build({
46 | ...commonConfig,
47 | entryPoints: ['./src/settings/settings.ts'],
48 | outbase: './src/settings',
49 | outdir: './dist',
50 | mainFields: ['svelte', 'module', 'main', 'browser'],
51 | plugins: [
52 | sveltePlugin({
53 | preprocess: sveltePreprocess()
54 | })
55 | ]
56 | });
57 |
58 | return Promise.all([contentJob, backgroundJob, popupJob, settingsJob]).then(
59 | () => console.log('⚡ Compiled')
60 | );
61 | }
62 |
--------------------------------------------------------------------------------
/Extension/scripts/firefox-build.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs-extra');
2 | const util = require('util');
3 | const exec = util.promisify(require('child_process').exec);
4 | const zipper = require('zip-local');
5 |
6 | main();
7 |
8 | async function main() {
9 | await exec('npm run build:chrome');
10 |
11 | // Removes non-source files
12 | const tmpDir = '../tmp';
13 | if (fs.existsSync(tmpDir)) {
14 | await fs.rm(tmpDir, { recursive: true });
15 | }
16 | await fs.mkdir(tmpDir);
17 | await fs.move('node_modules', `${tmpDir}/node_modules`);
18 | await fs.move('packaged-extension.zip', `${tmpDir}/packaged-extension.zip`);
19 |
20 | zipper.sync.zip('.').compress().save('extension-source.zip');
21 |
22 | // Move project files back
23 | await fs.move(`${tmpDir}/node_modules`, 'node_modules');
24 | await fs.move(`${tmpDir}/packaged-extension.zip`, 'packaged-extension.zip');
25 |
26 | console.log('Built for Firefox');
27 | }
28 |
--------------------------------------------------------------------------------
/Extension/scripts/package-extension.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs-extra');
2 | const util = require('util');
3 | const exec = util.promisify(require('child_process').exec);
4 | const zipper = require('zip-local');
5 |
6 | main();
7 |
8 | async function main() {
9 | await exec('npm run build:prod');
10 | const packageDir = 'packaged-extension';
11 | if (fs.existsSync(packageDir)) {
12 | await fs.rm(packageDir, { recursive: true });
13 | }
14 | await fs.mkdir(packageDir);
15 | const zipContents = ['_locales', 'dist', 'images', 'public', 'manifest.json'];
16 | for await (const filename of zipContents) {
17 | await fs.copy(filename, `${packageDir}/${filename}`);
18 | }
19 |
20 | zipper.sync
21 | .zip(packageDir)
22 | .compress()
23 | .save(packageDir + '.zip');
24 |
25 | await fs.rm(packageDir, { recursive: true });
26 | console.log('Extension packaged');
27 | }
28 |
--------------------------------------------------------------------------------
/Extension/scripts/prod-build.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs/promises');
2 | const util = require('util');
3 | const exec = util.promisify(require('child_process').exec);
4 |
5 | main();
6 |
7 | async function main() {
8 | await fs.rm('./dist', { recursive: true });
9 | await fs.mkdir('./dist');
10 | await exec('npm run build -- --prod');
11 | console.log('Done');
12 | }
13 |
--------------------------------------------------------------------------------
/Extension/src/background.ts:
--------------------------------------------------------------------------------
1 | import browser from 'webextension-polyfill';
2 | import { detect } from 'detect-browser';
3 | import {
4 | type BrowserMessage,
5 | type BrowserMessageType,
6 | type ColorScheme
7 | } from './models';
8 | import settingsConnector from './settings-connector';
9 |
10 | console.log('background script running...');
11 |
12 | browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
13 | console.log('got message', message);
14 | switch (message.type as BrowserMessageType) {
15 | case 'gotColorScheme': {
16 | updateIcon(message.value as ColorScheme).then(sendResponse);
17 | return true;
18 | }
19 | }
20 | });
21 |
22 | async function updateIcon(colorScheme: ColorScheme) {
23 | console.log('updating icon', colorScheme);
24 | // do work here
25 | }
26 |
--------------------------------------------------------------------------------
/Extension/src/content.ts:
--------------------------------------------------------------------------------
1 | import browser from 'webextension-polyfill';
2 | import { type BrowserMessageType, type ColorScheme } from './models';
3 |
4 | browser.runtime.onMessage.addListener(message => {
5 | console.log('got message', message);
6 | switch (message.type as BrowserMessageType) {
7 | case 'getColorScheme': {
8 | return Promise.resolve(getColorScheme());
9 | }
10 | }
11 | });
12 |
13 | function getColorScheme() {
14 | let scheme: ColorScheme = 'light';
15 | const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
16 | if (darkModeMediaQuery.matches) {
17 | scheme = 'dark';
18 | }
19 | return scheme;
20 | }
21 |
--------------------------------------------------------------------------------
/Extension/src/models.ts:
--------------------------------------------------------------------------------
1 | export type BrowserMessageType = 'getColorScheme' | 'gotColorScheme';
2 |
3 | export type BrowserMessage = {
4 | type: BrowserMessageType;
5 | value?: any;
6 | };
7 |
8 | export type AppSettings = {
9 | displayHelpMessage: boolean;
10 | };
11 |
12 | export const DEFAULT_SETTINGS: AppSettings = {
13 | displayHelpMessage: true
14 | };
15 |
16 | export type ColorScheme = 'light' | 'dark';
17 |
--------------------------------------------------------------------------------
/Extension/src/popup/popup.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
My New Web Extension
7 |
8 |
9 |
15 |
--------------------------------------------------------------------------------
/Extension/src/popup/popup.ts:
--------------------------------------------------------------------------------
1 | import App from './popup.svelte';
2 | new App({ target: document.getElementById('root') as HTMLDivElement });
3 |
--------------------------------------------------------------------------------
/Extension/src/settings-connector.ts:
--------------------------------------------------------------------------------
1 | import browser from 'webextension-polyfill';
2 | import { type AppSettings, DEFAULT_SETTINGS } from './models';
3 |
4 | class SettingsConnector {
5 | private static readonly settingsKey = 'settings';
6 | async getAppSettings() {
7 | let settings = (
8 | await browser.storage.sync.get(SettingsConnector.settingsKey)
9 | )[SettingsConnector.settingsKey] as Partial | undefined;
10 | if (
11 | !settings ||
12 | Object.keys(settings).length !== Object.keys(DEFAULT_SETTINGS).length
13 | ) {
14 | console.log('no settings found, using default settings');
15 | await browser.storage.sync.set({
16 | [SettingsConnector.settingsKey]: DEFAULT_SETTINGS
17 | });
18 | settings = DEFAULT_SETTINGS;
19 | }
20 | return settings as AppSettings;
21 | }
22 |
23 | async updateSettings(newSettings: Partial) {
24 | const settings = await this.getAppSettings();
25 | const updatedSettings = { ...settings, ...newSettings };
26 | await browser.storage.sync.set({
27 | [SettingsConnector.settingsKey]: updatedSettings
28 | });
29 | return updatedSettings;
30 | }
31 | }
32 |
33 | const singleton = new SettingsConnector();
34 | export default singleton;
35 |
--------------------------------------------------------------------------------
/Extension/src/settings/settings.svelte:
--------------------------------------------------------------------------------
1 |
24 |
25 | {#if appSettings}
26 | Settings
27 |
41 | {/if}
42 |
43 |
48 |
--------------------------------------------------------------------------------
/Extension/src/settings/settings.ts:
--------------------------------------------------------------------------------
1 | import App from './settings.svelte';
2 | new App({ target: document.getElementById('root') as HTMLDivElement });
3 |
--------------------------------------------------------------------------------
/Extension/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@tsconfig/svelte/tsconfig.json",
3 | "compilerOptions": {
4 | "target": "ES2016",
5 | "module": "ES2022",
6 | "lib": ["ES2022", "dom"],
7 | "sourceMap": true,
8 | "outDir": "./dist",
9 | "rootDir": "./src",
10 | "strict": true,
11 | "esModuleInterop": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "alwaysStrict": true,
14 | "resolveJsonModule": true,
15 | "importsNotUsedAsValues": "remove",
16 | "jsx": "react",
17 | "types": ["svelte"]
18 | },
19 | "include": ["src/**/*"],
20 | "exclude": ["node_modules/*", "dist/*"]
21 | }
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Kyle Nazario
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.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WebExtensionTemplate
2 |
3 | Create a browser extension for Chrome, Firefox and Safari in no time.
4 |
5 | WebExtensionTemplate lets you skip the boilerplate and write a [Web Extension](https://developer.mozilla.org/en-US/docs/Glossary/WebExtensions) with [TypeScript](https://www.typescriptlang.org) and [Svelte](https://www.typescriptlang.org) or [React](https://react.dev).
6 |
7 | ## Features
8 |
9 | - Popup window written in Svelte ([or React](https://github.com/kyle-n/WebExtensionTemplate/tree/react)) when you click the extension toolbar icon
10 | - Extension settings page written in Svelte ([or React](https://github.com/kyle-n/WebExtensionTemplate/tree/react))
11 | - Lightning-fast build process to compile source code with [esbuild](https://esbuild.github.io)
12 | - Build scripts to package the extension for Chrome, Firefox and Safari
13 | - Sourcemaps for local builds
14 | - Minifies production builds
15 | - iOS and macOS container apps written in SwiftUI for the Safari extension (no Swift / SwiftUI knowledge required to use)
16 | - [Prettier](https://prettier.io) formatting
17 | - [webextension-polyfill](https://www.npmjs.com/package/webextension-polyfill) to add a couple missing Promise-based APIs to Chrome
18 |
19 | ### Future roadmap
20 |
21 | - Separate branch with the popup and settings page written in React
22 | - Tests
23 |
24 | ## Setup
25 |
26 | First, fork this repository. Then, follow the steps below. By the end of this, you should be able to search across the project for `REPLACEME` and get no results.
27 |
28 | ### All browsers
29 |
30 | - Update `Extension/_locales/en/messages.json` with an extension name and description
31 | - Update `Extension/README.md` with your app's name
32 | - Open `Extension/package.json` and update...
33 | - `name` with your app's name
34 | - `author` with your name
35 | - `license` with the app's license
36 | - Open `Extension/public/settings.html` and update the `` with your app's name
37 | - Delete the `` in `Extension/settings.html` if you don't like the provided CSS
38 | - Create a toolbar icon for your app
39 | - Should be a small, simple glyph that makes sense when monochromatic in Safari
40 | - Make versions in 16, 19, 32, 38, 48 and 72-pixel sizes saved to `Extension/images`
41 | - Should be named `toolbar_Qpx.png` where `Q` is 16, 19, etc
42 | - Make an app icon that is 1024 x 1024
43 | - Copy versions of it to `Extension/images` in 48, 96, 128, 256 and 512-pixel sizes
44 | - Should be named `app_icon_Qpx.png` , where `Q` is 48, 96, etc
45 |
46 | ### Firefox
47 |
48 | - Update `manifest.json` with a Firefox extension id (under `gecko` > `id`) in the format `appname@domain.com`
49 |
50 | ### Safari
51 |
52 | Do the following steps in Xcode.
53 |
54 | Note: “Open the project config” means double-click the app name at the top of the file view in Xcode.
55 |
56 | - [Change the Safari app name to your app’s name](https://stackoverflow.com/a/20418989)
57 | - Open `Shared (App)/Models.swift` and update `APP_NAME` with your app's name
58 | - Create a new bundle identifier in the format `com.domain.App-Name`
59 | - Open the project config and go to `AppName (iOS)` > Signing & Capabilities and update the bundle id
60 | - Repeat for the macOS app
61 | - Create a new bundle identifier. It should be your app bundle identifier with `.Extension` added onto the end. So if your app bundle ID is `com.domain.App-Name`, this should be `com.domain.App-Name.Extension`
62 | - Open the project config and go to `AppName Extension (iOS)` > Signing & Capabilities and update the bundle id with the extension bundle id
63 | - Repeat for the macOS extension
64 | - Update `MAC_EXTENSION_BUNDLE_ID` in `Shared (App)/Models.swift` with the extension bundle ID as well
65 | - Update `macOS (App)/AppDelegate.swift` with a help documentation link
66 | - Under project config > Signing & Capabilities, set the team for both apps and both extensions
67 | - Under project config > General, update the display name for iOS and macOS
68 | - Rename both files named `REPLACEME.entitlements` to be `Your App Name.entitlements`
69 | - Open the project config and to go to App Name (macOS) > Build Settings and find the setting for “Code Signing Entitlements.” Replace `REPLACEME.entitlements` with the name of your new entitlements file
70 | - Repeat for App Name Extension (macOS) > Build Settings > Code Signing Entitlements
71 | - Open the project config and go to App Name Extension (macOS) > Build Settings and find the setting for “Bundle Display Name.” Update its value with your app’s name
72 | - Repeat for App Name Extension (iOS)
73 | - Go to Product > Schemes > Manage Schemes… and update the iOS and macOS schemes with your app’s name
74 | - iOS app icon:
75 | - Add the app icon to `iOS (App)/iOS Assets` as `AppIcon` with all the required sizes
76 | - Add a copy of the app icon named `Icon.png` in `Shared (App)/Resources`
77 | - macOS app icon
78 | - Reduce the size of the app icon by 20% while keeping the canvas the same size
79 | - Add the app icon to `macOS (App)/macOS Assets` as `AppIcon` with all the required sizes
80 |
81 | ## Building your extension
82 |
83 | All `npm` commands should be run in `Extension`. Safari extensions should be built in Xcode.
84 |
85 | | Browser | Local | Production |
86 | | - | - | - |
87 | | Chrome | `npm run build` | `npm run build:chrome` |
88 | | Firefox | `npm run build` | `npm run build:firefox` |
89 | | Safari | Product > Build | Product > Archive |
90 |
91 | - `console.log` calls are stripped out of prod builds
92 | - The Chrome build script generates a zip that can be uploaded to the Chrome Web Store
93 | - The Firefox build script generates a zip for the Mozilla Add-On Store as well as a zip of the source code for the store review
94 |
95 | ## Other notes
96 |
97 | - Firefox [does not support service workers in the background](https://github.com/mozilla/web-ext/issues/2532#issuecomment-1285039773), so I would maintain a separate branch `firefox` that runs `dist/background.js` as a background script
--------------------------------------------------------------------------------
/Safari/REPLACEME.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 56;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 308C56572A27BF1C005C001E /* iOS AppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 308C56562A27BF1C005C001E /* iOS AppView.swift */; };
11 | 308C56682A27BF1C005C001E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 308C56672A27BF1C005C001E /* AppDelegate.swift */; };
12 | 308C56722A27BF1C005C001E /* REPLACEME Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 308C56712A27BF1C005C001E /* REPLACEME Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
13 | 308C567C2A27BF1C005C001E /* REPLACEME Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 308C567B2A27BF1C005C001E /* REPLACEME Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
14 | 308C568C2A27BF1C005C001E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 308C56432A27BF1C005C001E /* Assets.xcassets */; };
15 | 308C568D2A27BF1C005C001E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 308C56432A27BF1C005C001E /* Assets.xcassets */; };
16 | 308C568E2A27BF1C005C001E /* SafariWebExtensionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 308C56452A27BF1C005C001E /* SafariWebExtensionHandler.swift */; };
17 | 308C568F2A27BF1C005C001E /* SafariWebExtensionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 308C56452A27BF1C005C001E /* SafariWebExtensionHandler.swift */; };
18 | 30A8D0632A29467500FD77A6 /* iOS Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 30A8D0622A29467500FD77A6 /* iOS Assets.xcassets */; };
19 | 30A8D0642A29467500FD77A6 /* iOS Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 30A8D0622A29467500FD77A6 /* iOS Assets.xcassets */; };
20 | 30A8D0662A29469A00FD77A6 /* macOS Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 30A8D0652A29469A00FD77A6 /* macOS Assets.xcassets */; };
21 | 30A8D0672A29469A00FD77A6 /* macOS Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 30A8D0652A29469A00FD77A6 /* macOS Assets.xcassets */; };
22 | 30DD362A2A55BF1D00361103 /* Views.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30DD36292A55BF1D00361103 /* Views.swift */; };
23 | 30DD362B2A55BF1D00361103 /* Views.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30DD36292A55BF1D00361103 /* Views.swift */; };
24 | 30DD36392A55C01F00361103 /* public in Resources */ = {isa = PBXBuildFile; fileRef = 30DD362D2A55C01E00361103 /* public */; };
25 | 30DD363A2A55C01F00361103 /* public in Resources */ = {isa = PBXBuildFile; fileRef = 30DD362D2A55C01E00361103 /* public */; };
26 | 30DD363D2A55C01F00361103 /* images in Resources */ = {isa = PBXBuildFile; fileRef = 30DD362F2A55C01F00361103 /* images */; };
27 | 30DD363E2A55C01F00361103 /* images in Resources */ = {isa = PBXBuildFile; fileRef = 30DD362F2A55C01F00361103 /* images */; };
28 | 30DD363F2A55C01F00361103 /* dist in Resources */ = {isa = PBXBuildFile; fileRef = 30DD36302A55C01F00361103 /* dist */; };
29 | 30DD36402A55C01F00361103 /* dist in Resources */ = {isa = PBXBuildFile; fileRef = 30DD36302A55C01F00361103 /* dist */; };
30 | 30DD36432A55C01F00361103 /* _locales in Resources */ = {isa = PBXBuildFile; fileRef = 30DD36322A55C01F00361103 /* _locales */; };
31 | 30DD36442A55C01F00361103 /* _locales in Resources */ = {isa = PBXBuildFile; fileRef = 30DD36322A55C01F00361103 /* _locales */; };
32 | 30DD36472A55C01F00361103 /* manifest.json in Resources */ = {isa = PBXBuildFile; fileRef = 30DD36342A55C01F00361103 /* manifest.json */; };
33 | 30DD36482A55C01F00361103 /* manifest.json in Resources */ = {isa = PBXBuildFile; fileRef = 30DD36342A55C01F00361103 /* manifest.json */; };
34 | 99B503372A4CC7A2006397D1 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99B503362A4CC7A2006397D1 /* main.swift */; };
35 | 99B503392A5224ED006397D1 /* macOS AppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99B503382A5224ED006397D1 /* macOS AppView.swift */; };
36 | 99B5033B2A52250E006397D1 /* Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99B5033A2A52250E006397D1 /* Models.swift */; };
37 | 99B5033C2A52250E006397D1 /* Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99B5033A2A52250E006397D1 /* Models.swift */; };
38 | 99B5033D2A52250E006397D1 /* Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99B5033A2A52250E006397D1 /* Models.swift */; };
39 | 99B5033E2A52250E006397D1 /* Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99B5033A2A52250E006397D1 /* Models.swift */; };
40 | 99E155EE2A522E1D005325D0 /* SafariConnector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99E155ED2A522E1D005325D0 /* SafariConnector.swift */; };
41 | /* End PBXBuildFile section */
42 |
43 | /* Begin PBXContainerItemProxy section */
44 | 308C56732A27BF1C005C001E /* PBXContainerItemProxy */ = {
45 | isa = PBXContainerItemProxy;
46 | containerPortal = 308C56372A27BF1A005C001E /* Project object */;
47 | proxyType = 1;
48 | remoteGlobalIDString = 308C56702A27BF1C005C001E;
49 | remoteInfo = "Mute Chat for BoardGameArena Extension (iOS)";
50 | };
51 | 308C567D2A27BF1C005C001E /* PBXContainerItemProxy */ = {
52 | isa = PBXContainerItemProxy;
53 | containerPortal = 308C56372A27BF1A005C001E /* Project object */;
54 | proxyType = 1;
55 | remoteGlobalIDString = 308C567A2A27BF1C005C001E;
56 | remoteInfo = "Mute Chat for BoardGameArena Extension (macOS)";
57 | };
58 | /* End PBXContainerItemProxy section */
59 |
60 | /* Begin PBXCopyFilesBuildPhase section */
61 | 308C56A52A27BF1C005C001E /* Embed Foundation Extensions */ = {
62 | isa = PBXCopyFilesBuildPhase;
63 | buildActionMask = 2147483647;
64 | dstPath = "";
65 | dstSubfolderSpec = 13;
66 | files = (
67 | 308C56722A27BF1C005C001E /* REPLACEME Extension.appex in Embed Foundation Extensions */,
68 | );
69 | name = "Embed Foundation Extensions";
70 | runOnlyForDeploymentPostprocessing = 0;
71 | };
72 | 308C56AC2A27BF1C005C001E /* Embed Foundation Extensions */ = {
73 | isa = PBXCopyFilesBuildPhase;
74 | buildActionMask = 2147483647;
75 | dstPath = "";
76 | dstSubfolderSpec = 13;
77 | files = (
78 | 308C567C2A27BF1C005C001E /* REPLACEME Extension.appex in Embed Foundation Extensions */,
79 | );
80 | name = "Embed Foundation Extensions";
81 | runOnlyForDeploymentPostprocessing = 0;
82 | };
83 | /* End PBXCopyFilesBuildPhase section */
84 |
85 | /* Begin PBXFileReference section */
86 | 308C56432A27BF1C005C001E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
87 | 308C56452A27BF1C005C001E /* SafariWebExtensionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariWebExtensionHandler.swift; sourceTree = ""; };
88 | 308C56532A27BF1C005C001E /* REPLACEME.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = REPLACEME.app; sourceTree = BUILT_PRODUCTS_DIR; };
89 | 308C56562A27BF1C005C001E /* iOS AppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "iOS AppView.swift"; sourceTree = ""; };
90 | 308C56602A27BF1C005C001E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
91 | 308C56652A27BF1C005C001E /* REPLACEME.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = REPLACEME.app; sourceTree = BUILT_PRODUCTS_DIR; };
92 | 308C56672A27BF1C005C001E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
93 | 308C56712A27BF1C005C001E /* REPLACEME Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "REPLACEME Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
94 | 308C56762A27BF1C005C001E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
95 | 308C567B2A27BF1C005C001E /* REPLACEME Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "REPLACEME Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
96 | 308C56802A27BF1C005C001E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
97 | 30A8D0622A29467500FD77A6 /* iOS Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "iOS Assets.xcassets"; sourceTree = ""; };
98 | 30A8D0652A29469A00FD77A6 /* macOS Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "macOS Assets.xcassets"; sourceTree = ""; };
99 | 30DD36292A55BF1D00361103 /* Views.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Views.swift; sourceTree = ""; };
100 | 30DD362D2A55C01E00361103 /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; name = public; path = ../../../Extension/public; sourceTree = ""; };
101 | 30DD362F2A55C01F00361103 /* images */ = {isa = PBXFileReference; lastKnownFileType = folder; name = images; path = ../../../Extension/images; sourceTree = ""; };
102 | 30DD36302A55C01F00361103 /* dist */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dist; path = ../../../Extension/dist; sourceTree = ""; };
103 | 30DD36322A55C01F00361103 /* _locales */ = {isa = PBXFileReference; lastKnownFileType = folder; name = _locales; path = ../../../Extension/_locales; sourceTree = ""; };
104 | 30DD36342A55C01F00361103 /* manifest.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = manifest.json; path = ../../../Extension/manifest.json; sourceTree = ""; };
105 | 99B503362A4CC7A2006397D1 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; };
106 | 99B503382A5224ED006397D1 /* macOS AppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "macOS AppView.swift"; sourceTree = ""; };
107 | 99B5033A2A52250E006397D1 /* Models.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Models.swift; sourceTree = ""; };
108 | 99E155ED2A522E1D005325D0 /* SafariConnector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariConnector.swift; sourceTree = ""; };
109 | 99E155EF2A5312DD005325D0 /* REPLACEME.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = REPLACEME.entitlements; sourceTree = ""; };
110 | 99E155F02A5313B1005325D0 /* REPLACEME.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = REPLACEME.entitlements; sourceTree = ""; };
111 | /* End PBXFileReference section */
112 |
113 | /* Begin PBXFrameworksBuildPhase section */
114 | 308C56502A27BF1C005C001E /* Frameworks */ = {
115 | isa = PBXFrameworksBuildPhase;
116 | buildActionMask = 2147483647;
117 | files = (
118 | );
119 | runOnlyForDeploymentPostprocessing = 0;
120 | };
121 | 308C56622A27BF1C005C001E /* Frameworks */ = {
122 | isa = PBXFrameworksBuildPhase;
123 | buildActionMask = 2147483647;
124 | files = (
125 | );
126 | runOnlyForDeploymentPostprocessing = 0;
127 | };
128 | 308C566E2A27BF1C005C001E /* Frameworks */ = {
129 | isa = PBXFrameworksBuildPhase;
130 | buildActionMask = 2147483647;
131 | files = (
132 | );
133 | runOnlyForDeploymentPostprocessing = 0;
134 | };
135 | 308C56782A27BF1C005C001E /* Frameworks */ = {
136 | isa = PBXFrameworksBuildPhase;
137 | buildActionMask = 2147483647;
138 | files = (
139 | );
140 | runOnlyForDeploymentPostprocessing = 0;
141 | };
142 | /* End PBXFrameworksBuildPhase section */
143 |
144 | /* Begin PBXGroup section */
145 | 308C56362A27BF1A005C001E = {
146 | isa = PBXGroup;
147 | children = (
148 | 308C563B2A27BF1A005C001E /* Shared (App) */,
149 | 308C56442A27BF1C005C001E /* Shared (Extension) */,
150 | 308C56552A27BF1C005C001E /* iOS (App) */,
151 | 308C56662A27BF1C005C001E /* macOS (App) */,
152 | 308C56752A27BF1C005C001E /* iOS (Extension) */,
153 | 308C567F2A27BF1C005C001E /* macOS (Extension) */,
154 | 308C56542A27BF1C005C001E /* Products */,
155 | );
156 | sourceTree = "";
157 | };
158 | 308C563B2A27BF1A005C001E /* Shared (App) */ = {
159 | isa = PBXGroup;
160 | children = (
161 | 308C56432A27BF1C005C001E /* Assets.xcassets */,
162 | 99B5033A2A52250E006397D1 /* Models.swift */,
163 | 30DD36292A55BF1D00361103 /* Views.swift */,
164 | );
165 | path = "Shared (App)";
166 | sourceTree = "";
167 | };
168 | 308C56442A27BF1C005C001E /* Shared (Extension) */ = {
169 | isa = PBXGroup;
170 | children = (
171 | 30DD362C2A55BFDB00361103 /* Resources */,
172 | 308C56452A27BF1C005C001E /* SafariWebExtensionHandler.swift */,
173 | );
174 | path = "Shared (Extension)";
175 | sourceTree = "";
176 | };
177 | 308C56542A27BF1C005C001E /* Products */ = {
178 | isa = PBXGroup;
179 | children = (
180 | 308C56532A27BF1C005C001E /* REPLACEME.app */,
181 | 308C56652A27BF1C005C001E /* REPLACEME.app */,
182 | 308C56712A27BF1C005C001E /* REPLACEME Extension.appex */,
183 | 308C567B2A27BF1C005C001E /* REPLACEME Extension.appex */,
184 | );
185 | name = Products;
186 | sourceTree = "";
187 | };
188 | 308C56552A27BF1C005C001E /* iOS (App) */ = {
189 | isa = PBXGroup;
190 | children = (
191 | 308C56562A27BF1C005C001E /* iOS AppView.swift */,
192 | 308C56602A27BF1C005C001E /* Info.plist */,
193 | 30A8D0622A29467500FD77A6 /* iOS Assets.xcassets */,
194 | );
195 | path = "iOS (App)";
196 | sourceTree = "";
197 | };
198 | 308C56662A27BF1C005C001E /* macOS (App) */ = {
199 | isa = PBXGroup;
200 | children = (
201 | 308C56672A27BF1C005C001E /* AppDelegate.swift */,
202 | 99E155EF2A5312DD005325D0 /* REPLACEME.entitlements */,
203 | 30A8D0652A29469A00FD77A6 /* macOS Assets.xcassets */,
204 | 99B503362A4CC7A2006397D1 /* main.swift */,
205 | 99B503382A5224ED006397D1 /* macOS AppView.swift */,
206 | 99E155ED2A522E1D005325D0 /* SafariConnector.swift */,
207 | );
208 | path = "macOS (App)";
209 | sourceTree = "";
210 | };
211 | 308C56752A27BF1C005C001E /* iOS (Extension) */ = {
212 | isa = PBXGroup;
213 | children = (
214 | 308C56762A27BF1C005C001E /* Info.plist */,
215 | );
216 | path = "iOS (Extension)";
217 | sourceTree = "";
218 | };
219 | 308C567F2A27BF1C005C001E /* macOS (Extension) */ = {
220 | isa = PBXGroup;
221 | children = (
222 | 99E155F02A5313B1005325D0 /* REPLACEME.entitlements */,
223 | 308C56802A27BF1C005C001E /* Info.plist */,
224 | );
225 | path = "macOS (Extension)";
226 | sourceTree = "";
227 | };
228 | 30DD362C2A55BFDB00361103 /* Resources */ = {
229 | isa = PBXGroup;
230 | children = (
231 | 30DD36322A55C01F00361103 /* _locales */,
232 | 30DD36302A55C01F00361103 /* dist */,
233 | 30DD362F2A55C01F00361103 /* images */,
234 | 30DD36342A55C01F00361103 /* manifest.json */,
235 | 30DD362D2A55C01E00361103 /* public */,
236 | );
237 | path = Resources;
238 | sourceTree = "";
239 | };
240 | /* End PBXGroup section */
241 |
242 | /* Begin PBXNativeTarget section */
243 | 308C56522A27BF1C005C001E /* REPLACEME (iOS) */ = {
244 | isa = PBXNativeTarget;
245 | buildConfigurationList = 308C56A62A27BF1C005C001E /* Build configuration list for PBXNativeTarget "REPLACEME (iOS)" */;
246 | buildPhases = (
247 | 308C564F2A27BF1C005C001E /* Sources */,
248 | 308C56502A27BF1C005C001E /* Frameworks */,
249 | 308C56512A27BF1C005C001E /* Resources */,
250 | 308C56A52A27BF1C005C001E /* Embed Foundation Extensions */,
251 | );
252 | buildRules = (
253 | );
254 | dependencies = (
255 | 308C56742A27BF1C005C001E /* PBXTargetDependency */,
256 | );
257 | name = "REPLACEME (iOS)";
258 | productName = "Mute Chat for BoardGameArena (iOS)";
259 | productReference = 308C56532A27BF1C005C001E /* REPLACEME.app */;
260 | productType = "com.apple.product-type.application";
261 | };
262 | 308C56642A27BF1C005C001E /* REPLACEME (macOS) */ = {
263 | isa = PBXNativeTarget;
264 | buildConfigurationList = 308C56AD2A27BF1C005C001E /* Build configuration list for PBXNativeTarget "REPLACEME (macOS)" */;
265 | buildPhases = (
266 | 308C56612A27BF1C005C001E /* Sources */,
267 | 308C56622A27BF1C005C001E /* Frameworks */,
268 | 308C56632A27BF1C005C001E /* Resources */,
269 | 308C56AC2A27BF1C005C001E /* Embed Foundation Extensions */,
270 | );
271 | buildRules = (
272 | );
273 | dependencies = (
274 | 308C567E2A27BF1C005C001E /* PBXTargetDependency */,
275 | );
276 | name = "REPLACEME (macOS)";
277 | productName = "Mute Chat for BoardGameArena (macOS)";
278 | productReference = 308C56652A27BF1C005C001E /* REPLACEME.app */;
279 | productType = "com.apple.product-type.application";
280 | };
281 | 308C56702A27BF1C005C001E /* REPLACEME Extension (iOS) */ = {
282 | isa = PBXNativeTarget;
283 | buildConfigurationList = 308C56A22A27BF1C005C001E /* Build configuration list for PBXNativeTarget "REPLACEME Extension (iOS)" */;
284 | buildPhases = (
285 | 308C566D2A27BF1C005C001E /* Sources */,
286 | 308C56B32A27C579005C001E /* Build Extension */,
287 | 308C56B42A27C5EC005C001E /* Build Extension (Production) */,
288 | 308C566E2A27BF1C005C001E /* Frameworks */,
289 | 308C566F2A27BF1C005C001E /* Resources */,
290 | );
291 | buildRules = (
292 | );
293 | dependencies = (
294 | );
295 | name = "REPLACEME Extension (iOS)";
296 | productName = "Mute Chat for BoardGameArena Extension (iOS)";
297 | productReference = 308C56712A27BF1C005C001E /* REPLACEME Extension.appex */;
298 | productType = "com.apple.product-type.app-extension";
299 | };
300 | 308C567A2A27BF1C005C001E /* REPLACEME Extension (macOS) */ = {
301 | isa = PBXNativeTarget;
302 | buildConfigurationList = 308C56A92A27BF1C005C001E /* Build configuration list for PBXNativeTarget "REPLACEME Extension (macOS)" */;
303 | buildPhases = (
304 | 308C56772A27BF1C005C001E /* Sources */,
305 | 308C56B52A27C651005C001E /* Build Extension */,
306 | 308C56B62A27C662005C001E /* Build Extension (Production) */,
307 | 308C56782A27BF1C005C001E /* Frameworks */,
308 | 308C56792A27BF1C005C001E /* Resources */,
309 | );
310 | buildRules = (
311 | );
312 | dependencies = (
313 | );
314 | name = "REPLACEME Extension (macOS)";
315 | productName = "Mute Chat for BoardGameArena Extension (macOS)";
316 | productReference = 308C567B2A27BF1C005C001E /* REPLACEME Extension.appex */;
317 | productType = "com.apple.product-type.app-extension";
318 | };
319 | /* End PBXNativeTarget section */
320 |
321 | /* Begin PBXProject section */
322 | 308C56372A27BF1A005C001E /* Project object */ = {
323 | isa = PBXProject;
324 | attributes = {
325 | BuildIndependentTargetsInParallel = 1;
326 | LastSwiftUpdateCheck = 1430;
327 | LastUpgradeCheck = 1430;
328 | TargetAttributes = {
329 | 308C56522A27BF1C005C001E = {
330 | CreatedOnToolsVersion = 14.3;
331 | };
332 | 308C56642A27BF1C005C001E = {
333 | CreatedOnToolsVersion = 14.3;
334 | };
335 | 308C56702A27BF1C005C001E = {
336 | CreatedOnToolsVersion = 14.3;
337 | };
338 | 308C567A2A27BF1C005C001E = {
339 | CreatedOnToolsVersion = 14.3;
340 | };
341 | };
342 | };
343 | buildConfigurationList = 308C563A2A27BF1A005C001E /* Build configuration list for PBXProject "REPLACEME" */;
344 | compatibilityVersion = "Xcode 14.0";
345 | developmentRegion = en;
346 | hasScannedForEncodings = 0;
347 | knownRegions = (
348 | en,
349 | Base,
350 | );
351 | mainGroup = 308C56362A27BF1A005C001E;
352 | productRefGroup = 308C56542A27BF1C005C001E /* Products */;
353 | projectDirPath = "";
354 | projectRoot = "";
355 | targets = (
356 | 308C56522A27BF1C005C001E /* REPLACEME (iOS) */,
357 | 308C56642A27BF1C005C001E /* REPLACEME (macOS) */,
358 | 308C56702A27BF1C005C001E /* REPLACEME Extension (iOS) */,
359 | 308C567A2A27BF1C005C001E /* REPLACEME Extension (macOS) */,
360 | );
361 | };
362 | /* End PBXProject section */
363 |
364 | /* Begin PBXResourcesBuildPhase section */
365 | 308C56512A27BF1C005C001E /* Resources */ = {
366 | isa = PBXResourcesBuildPhase;
367 | buildActionMask = 2147483647;
368 | files = (
369 | 308C568C2A27BF1C005C001E /* Assets.xcassets in Resources */,
370 | 30A8D0632A29467500FD77A6 /* iOS Assets.xcassets in Resources */,
371 | );
372 | runOnlyForDeploymentPostprocessing = 0;
373 | };
374 | 308C56632A27BF1C005C001E /* Resources */ = {
375 | isa = PBXResourcesBuildPhase;
376 | buildActionMask = 2147483647;
377 | files = (
378 | 308C568D2A27BF1C005C001E /* Assets.xcassets in Resources */,
379 | 30A8D0662A29469A00FD77A6 /* macOS Assets.xcassets in Resources */,
380 | );
381 | runOnlyForDeploymentPostprocessing = 0;
382 | };
383 | 308C566F2A27BF1C005C001E /* Resources */ = {
384 | isa = PBXResourcesBuildPhase;
385 | buildActionMask = 2147483647;
386 | files = (
387 | 30DD363D2A55C01F00361103 /* images in Resources */,
388 | 30DD363F2A55C01F00361103 /* dist in Resources */,
389 | 30DD36472A55C01F00361103 /* manifest.json in Resources */,
390 | 30A8D0642A29467500FD77A6 /* iOS Assets.xcassets in Resources */,
391 | 30DD36432A55C01F00361103 /* _locales in Resources */,
392 | 30DD36392A55C01F00361103 /* public in Resources */,
393 | );
394 | runOnlyForDeploymentPostprocessing = 0;
395 | };
396 | 308C56792A27BF1C005C001E /* Resources */ = {
397 | isa = PBXResourcesBuildPhase;
398 | buildActionMask = 2147483647;
399 | files = (
400 | 30DD363E2A55C01F00361103 /* images in Resources */,
401 | 30DD36402A55C01F00361103 /* dist in Resources */,
402 | 30DD36482A55C01F00361103 /* manifest.json in Resources */,
403 | 30A8D0672A29469A00FD77A6 /* macOS Assets.xcassets in Resources */,
404 | 30DD36442A55C01F00361103 /* _locales in Resources */,
405 | 30DD363A2A55C01F00361103 /* public in Resources */,
406 | );
407 | runOnlyForDeploymentPostprocessing = 0;
408 | };
409 | /* End PBXResourcesBuildPhase section */
410 |
411 | /* Begin PBXShellScriptBuildPhase section */
412 | 308C56B32A27C579005C001E /* Build Extension */ = {
413 | isa = PBXShellScriptBuildPhase;
414 | buildActionMask = 2147483647;
415 | files = (
416 | );
417 | inputFileListPaths = (
418 | );
419 | inputPaths = (
420 | );
421 | name = "Build Extension";
422 | outputFileListPaths = (
423 | );
424 | outputPaths = (
425 | );
426 | runOnlyForDeploymentPostprocessing = 0;
427 | shellPath = /bin/sh;
428 | shellScript = "# Type a script or drag a script file from your workspace to insert its path.\ncd ../Extension\nnpm run build\n";
429 | };
430 | 308C56B42A27C5EC005C001E /* Build Extension (Production) */ = {
431 | isa = PBXShellScriptBuildPhase;
432 | buildActionMask = 8;
433 | files = (
434 | );
435 | inputFileListPaths = (
436 | );
437 | inputPaths = (
438 | );
439 | name = "Build Extension (Production)";
440 | outputFileListPaths = (
441 | );
442 | outputPaths = (
443 | );
444 | runOnlyForDeploymentPostprocessing = 1;
445 | shellPath = /bin/sh;
446 | shellScript = "# Type a script or drag a script file from your workspace to insert its path.\ncd ../Extension\nnpm run build:prod\n";
447 | };
448 | 308C56B52A27C651005C001E /* Build Extension */ = {
449 | isa = PBXShellScriptBuildPhase;
450 | buildActionMask = 2147483647;
451 | files = (
452 | );
453 | inputFileListPaths = (
454 | );
455 | inputPaths = (
456 | );
457 | name = "Build Extension";
458 | outputFileListPaths = (
459 | );
460 | outputPaths = (
461 | );
462 | runOnlyForDeploymentPostprocessing = 0;
463 | shellPath = /bin/sh;
464 | shellScript = "# Type a script or drag a script file from your workspace to insert its path.\ncd ../Extension\nnpm run build\n";
465 | };
466 | 308C56B62A27C662005C001E /* Build Extension (Production) */ = {
467 | isa = PBXShellScriptBuildPhase;
468 | buildActionMask = 8;
469 | files = (
470 | );
471 | inputFileListPaths = (
472 | );
473 | inputPaths = (
474 | );
475 | name = "Build Extension (Production)";
476 | outputFileListPaths = (
477 | );
478 | outputPaths = (
479 | );
480 | runOnlyForDeploymentPostprocessing = 1;
481 | shellPath = /bin/sh;
482 | shellScript = "# Type a script or drag a script file from your workspace to insert its path.\ncd ../Extension\nnpm run build:prod\n";
483 | };
484 | /* End PBXShellScriptBuildPhase section */
485 |
486 | /* Begin PBXSourcesBuildPhase section */
487 | 308C564F2A27BF1C005C001E /* Sources */ = {
488 | isa = PBXSourcesBuildPhase;
489 | buildActionMask = 2147483647;
490 | files = (
491 | 99B5033B2A52250E006397D1 /* Models.swift in Sources */,
492 | 308C56572A27BF1C005C001E /* iOS AppView.swift in Sources */,
493 | 30DD362A2A55BF1D00361103 /* Views.swift in Sources */,
494 | );
495 | runOnlyForDeploymentPostprocessing = 0;
496 | };
497 | 308C56612A27BF1C005C001E /* Sources */ = {
498 | isa = PBXSourcesBuildPhase;
499 | buildActionMask = 2147483647;
500 | files = (
501 | 99B503372A4CC7A2006397D1 /* main.swift in Sources */,
502 | 308C56682A27BF1C005C001E /* AppDelegate.swift in Sources */,
503 | 30DD362B2A55BF1D00361103 /* Views.swift in Sources */,
504 | 99E155EE2A522E1D005325D0 /* SafariConnector.swift in Sources */,
505 | 99B503392A5224ED006397D1 /* macOS AppView.swift in Sources */,
506 | 99B5033C2A52250E006397D1 /* Models.swift in Sources */,
507 | );
508 | runOnlyForDeploymentPostprocessing = 0;
509 | };
510 | 308C566D2A27BF1C005C001E /* Sources */ = {
511 | isa = PBXSourcesBuildPhase;
512 | buildActionMask = 2147483647;
513 | files = (
514 | 308C568E2A27BF1C005C001E /* SafariWebExtensionHandler.swift in Sources */,
515 | 99B5033D2A52250E006397D1 /* Models.swift in Sources */,
516 | );
517 | runOnlyForDeploymentPostprocessing = 0;
518 | };
519 | 308C56772A27BF1C005C001E /* Sources */ = {
520 | isa = PBXSourcesBuildPhase;
521 | buildActionMask = 2147483647;
522 | files = (
523 | 308C568F2A27BF1C005C001E /* SafariWebExtensionHandler.swift in Sources */,
524 | 99B5033E2A52250E006397D1 /* Models.swift in Sources */,
525 | );
526 | runOnlyForDeploymentPostprocessing = 0;
527 | };
528 | /* End PBXSourcesBuildPhase section */
529 |
530 | /* Begin PBXTargetDependency section */
531 | 308C56742A27BF1C005C001E /* PBXTargetDependency */ = {
532 | isa = PBXTargetDependency;
533 | target = 308C56702A27BF1C005C001E /* REPLACEME Extension (iOS) */;
534 | targetProxy = 308C56732A27BF1C005C001E /* PBXContainerItemProxy */;
535 | };
536 | 308C567E2A27BF1C005C001E /* PBXTargetDependency */ = {
537 | isa = PBXTargetDependency;
538 | target = 308C567A2A27BF1C005C001E /* REPLACEME Extension (macOS) */;
539 | targetProxy = 308C567D2A27BF1C005C001E /* PBXContainerItemProxy */;
540 | };
541 | /* End PBXTargetDependency section */
542 |
543 | /* Begin XCBuildConfiguration section */
544 | 308C56A02A27BF1C005C001E /* Debug */ = {
545 | isa = XCBuildConfiguration;
546 | buildSettings = {
547 | ALWAYS_SEARCH_USER_PATHS = NO;
548 | CLANG_ANALYZER_NONNULL = YES;
549 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
550 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
551 | CLANG_ENABLE_MODULES = YES;
552 | CLANG_ENABLE_OBJC_ARC = YES;
553 | CLANG_ENABLE_OBJC_WEAK = YES;
554 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
555 | CLANG_WARN_BOOL_CONVERSION = YES;
556 | CLANG_WARN_COMMA = YES;
557 | CLANG_WARN_CONSTANT_CONVERSION = YES;
558 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
559 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
560 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
561 | CLANG_WARN_EMPTY_BODY = YES;
562 | CLANG_WARN_ENUM_CONVERSION = YES;
563 | CLANG_WARN_INFINITE_RECURSION = YES;
564 | CLANG_WARN_INT_CONVERSION = YES;
565 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
566 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
567 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
568 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
569 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
570 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
571 | CLANG_WARN_STRICT_PROTOTYPES = YES;
572 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
573 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
574 | CLANG_WARN_UNREACHABLE_CODE = YES;
575 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
576 | COPY_PHASE_STRIP = NO;
577 | DEBUG_INFORMATION_FORMAT = dwarf;
578 | ENABLE_STRICT_OBJC_MSGSEND = YES;
579 | ENABLE_TESTABILITY = YES;
580 | GCC_C_LANGUAGE_STANDARD = gnu11;
581 | GCC_DYNAMIC_NO_PIC = NO;
582 | GCC_NO_COMMON_BLOCKS = YES;
583 | GCC_OPTIMIZATION_LEVEL = 0;
584 | GCC_PREPROCESSOR_DEFINITIONS = (
585 | "DEBUG=1",
586 | "$(inherited)",
587 | );
588 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
589 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
590 | GCC_WARN_UNDECLARED_SELECTOR = YES;
591 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
592 | GCC_WARN_UNUSED_FUNCTION = YES;
593 | GCC_WARN_UNUSED_VARIABLE = YES;
594 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
595 | MTL_FAST_MATH = YES;
596 | ONLY_ACTIVE_ARCH = YES;
597 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
598 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
599 | };
600 | name = Debug;
601 | };
602 | 308C56A12A27BF1C005C001E /* Release */ = {
603 | isa = XCBuildConfiguration;
604 | buildSettings = {
605 | ALWAYS_SEARCH_USER_PATHS = NO;
606 | CLANG_ANALYZER_NONNULL = YES;
607 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
608 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
609 | CLANG_ENABLE_MODULES = YES;
610 | CLANG_ENABLE_OBJC_ARC = YES;
611 | CLANG_ENABLE_OBJC_WEAK = YES;
612 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
613 | CLANG_WARN_BOOL_CONVERSION = YES;
614 | CLANG_WARN_COMMA = YES;
615 | CLANG_WARN_CONSTANT_CONVERSION = YES;
616 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
617 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
618 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
619 | CLANG_WARN_EMPTY_BODY = YES;
620 | CLANG_WARN_ENUM_CONVERSION = YES;
621 | CLANG_WARN_INFINITE_RECURSION = YES;
622 | CLANG_WARN_INT_CONVERSION = YES;
623 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
624 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
625 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
626 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
627 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
628 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
629 | CLANG_WARN_STRICT_PROTOTYPES = YES;
630 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
631 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
632 | CLANG_WARN_UNREACHABLE_CODE = YES;
633 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
634 | COPY_PHASE_STRIP = NO;
635 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
636 | ENABLE_NS_ASSERTIONS = NO;
637 | ENABLE_STRICT_OBJC_MSGSEND = YES;
638 | GCC_C_LANGUAGE_STANDARD = gnu11;
639 | GCC_NO_COMMON_BLOCKS = YES;
640 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
641 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
642 | GCC_WARN_UNDECLARED_SELECTOR = YES;
643 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
644 | GCC_WARN_UNUSED_FUNCTION = YES;
645 | GCC_WARN_UNUSED_VARIABLE = YES;
646 | MTL_ENABLE_DEBUG_INFO = NO;
647 | MTL_FAST_MATH = YES;
648 | SWIFT_COMPILATION_MODE = wholemodule;
649 | SWIFT_OPTIMIZATION_LEVEL = "-O";
650 | };
651 | name = Release;
652 | };
653 | 308C56A32A27BF1C005C001E /* Debug */ = {
654 | isa = XCBuildConfiguration;
655 | buildSettings = {
656 | CODE_SIGN_STYLE = Automatic;
657 | CURRENT_PROJECT_VERSION = 1;
658 | DEVELOPMENT_TEAM = "";
659 | GENERATE_INFOPLIST_FILE = YES;
660 | INFOPLIST_FILE = "iOS (Extension)/Info.plist";
661 | INFOPLIST_KEY_CFBundleDisplayName = REPLACEME;
662 | INFOPLIST_KEY_NSHumanReadableCopyright = "";
663 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
664 | LD_RUNPATH_SEARCH_PATHS = (
665 | "$(inherited)",
666 | "@executable_path/Frameworks",
667 | "@executable_path/../../Frameworks",
668 | );
669 | MARKETING_VERSION = 1.0.0;
670 | OTHER_LDFLAGS = (
671 | "-framework",
672 | SafariServices,
673 | );
674 | PRODUCT_BUNDLE_IDENTIFIER = REPLACEME;
675 | PRODUCT_NAME = "REPLACEME Extension";
676 | SDKROOT = iphoneos;
677 | SKIP_INSTALL = YES;
678 | SWIFT_EMIT_LOC_STRINGS = YES;
679 | SWIFT_VERSION = 5.0;
680 | TARGETED_DEVICE_FAMILY = "1,2";
681 | };
682 | name = Debug;
683 | };
684 | 308C56A42A27BF1C005C001E /* Release */ = {
685 | isa = XCBuildConfiguration;
686 | buildSettings = {
687 | CODE_SIGN_STYLE = Automatic;
688 | CURRENT_PROJECT_VERSION = 1;
689 | DEVELOPMENT_TEAM = "";
690 | GENERATE_INFOPLIST_FILE = YES;
691 | INFOPLIST_FILE = "iOS (Extension)/Info.plist";
692 | INFOPLIST_KEY_CFBundleDisplayName = REPLACEME;
693 | INFOPLIST_KEY_NSHumanReadableCopyright = "";
694 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
695 | LD_RUNPATH_SEARCH_PATHS = (
696 | "$(inherited)",
697 | "@executable_path/Frameworks",
698 | "@executable_path/../../Frameworks",
699 | );
700 | MARKETING_VERSION = 1.0.0;
701 | OTHER_LDFLAGS = (
702 | "-framework",
703 | SafariServices,
704 | );
705 | PRODUCT_BUNDLE_IDENTIFIER = REPLACEME;
706 | PRODUCT_NAME = "REPLACEME Extension";
707 | SDKROOT = iphoneos;
708 | SKIP_INSTALL = YES;
709 | SWIFT_EMIT_LOC_STRINGS = YES;
710 | SWIFT_VERSION = 5.0;
711 | TARGETED_DEVICE_FAMILY = "1,2";
712 | VALIDATE_PRODUCT = YES;
713 | };
714 | name = Release;
715 | };
716 | 308C56A72A27BF1C005C001E /* Debug */ = {
717 | isa = XCBuildConfiguration;
718 | buildSettings = {
719 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
720 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
721 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
722 | CODE_SIGN_STYLE = Automatic;
723 | CURRENT_PROJECT_VERSION = 1;
724 | DEVELOPMENT_TEAM = "";
725 | GENERATE_INFOPLIST_FILE = YES;
726 | INFOPLIST_FILE = "iOS (App)/Info.plist";
727 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
728 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
729 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
730 | INFOPLIST_KEY_UIMainStoryboardFile = Main;
731 | INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
732 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
733 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
734 | LD_RUNPATH_SEARCH_PATHS = (
735 | "$(inherited)",
736 | "@executable_path/Frameworks",
737 | );
738 | MARKETING_VERSION = 1.0.0;
739 | OTHER_LDFLAGS = (
740 | "-framework",
741 | SafariServices,
742 | "-framework",
743 | WebKit,
744 | );
745 | PRODUCT_BUNDLE_IDENTIFIER = REPLACEME;
746 | PRODUCT_NAME = REPLACEME;
747 | SDKROOT = iphoneos;
748 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
749 | SUPPORTS_MACCATALYST = NO;
750 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
751 | SWIFT_EMIT_LOC_STRINGS = YES;
752 | SWIFT_VERSION = 5.0;
753 | TARGETED_DEVICE_FAMILY = "1,2";
754 | };
755 | name = Debug;
756 | };
757 | 308C56A82A27BF1C005C001E /* Release */ = {
758 | isa = XCBuildConfiguration;
759 | buildSettings = {
760 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
761 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
762 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
763 | CODE_SIGN_STYLE = Automatic;
764 | CURRENT_PROJECT_VERSION = 1;
765 | DEVELOPMENT_TEAM = "";
766 | GENERATE_INFOPLIST_FILE = YES;
767 | INFOPLIST_FILE = "iOS (App)/Info.plist";
768 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
769 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
770 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
771 | INFOPLIST_KEY_UIMainStoryboardFile = Main;
772 | INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
773 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
774 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
775 | LD_RUNPATH_SEARCH_PATHS = (
776 | "$(inherited)",
777 | "@executable_path/Frameworks",
778 | );
779 | MARKETING_VERSION = 1.0.0;
780 | OTHER_LDFLAGS = (
781 | "-framework",
782 | SafariServices,
783 | "-framework",
784 | WebKit,
785 | );
786 | PRODUCT_BUNDLE_IDENTIFIER = REPLACEME;
787 | PRODUCT_NAME = REPLACEME;
788 | SDKROOT = iphoneos;
789 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
790 | SUPPORTS_MACCATALYST = NO;
791 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
792 | SWIFT_EMIT_LOC_STRINGS = YES;
793 | SWIFT_VERSION = 5.0;
794 | TARGETED_DEVICE_FAMILY = "1,2";
795 | VALIDATE_PRODUCT = YES;
796 | };
797 | name = Release;
798 | };
799 | 308C56AA2A27BF1C005C001E /* Debug */ = {
800 | isa = XCBuildConfiguration;
801 | buildSettings = {
802 | CODE_SIGN_ENTITLEMENTS = "macOS (Extension)/REPLACEME.entitlements";
803 | CODE_SIGN_STYLE = Automatic;
804 | CURRENT_PROJECT_VERSION = 1;
805 | DEVELOPMENT_TEAM = "";
806 | ENABLE_HARDENED_RUNTIME = YES;
807 | GENERATE_INFOPLIST_FILE = YES;
808 | INFOPLIST_FILE = "macOS (Extension)/Info.plist";
809 | INFOPLIST_KEY_CFBundleDisplayName = REPLACEME;
810 | INFOPLIST_KEY_NSHumanReadableCopyright = "";
811 | LD_RUNPATH_SEARCH_PATHS = (
812 | "$(inherited)",
813 | "@executable_path/../Frameworks",
814 | "@executable_path/../../../../Frameworks",
815 | );
816 | MACOSX_DEPLOYMENT_TARGET = 10.14;
817 | MARKETING_VERSION = 1.0.0;
818 | OTHER_LDFLAGS = (
819 | "-framework",
820 | SafariServices,
821 | );
822 | PRODUCT_BUNDLE_IDENTIFIER = REPLACEME;
823 | PRODUCT_NAME = "REPLACEME Extension";
824 | SDKROOT = macosx;
825 | SKIP_INSTALL = YES;
826 | SWIFT_EMIT_LOC_STRINGS = YES;
827 | SWIFT_VERSION = 5.0;
828 | };
829 | name = Debug;
830 | };
831 | 308C56AB2A27BF1C005C001E /* Release */ = {
832 | isa = XCBuildConfiguration;
833 | buildSettings = {
834 | CODE_SIGN_ENTITLEMENTS = "macOS (Extension)/REPLACEME.entitlements";
835 | CODE_SIGN_STYLE = Automatic;
836 | CURRENT_PROJECT_VERSION = 1;
837 | DEVELOPMENT_TEAM = "";
838 | ENABLE_HARDENED_RUNTIME = YES;
839 | GENERATE_INFOPLIST_FILE = YES;
840 | INFOPLIST_FILE = "macOS (Extension)/Info.plist";
841 | INFOPLIST_KEY_CFBundleDisplayName = REPLACEME;
842 | INFOPLIST_KEY_NSHumanReadableCopyright = "";
843 | LD_RUNPATH_SEARCH_PATHS = (
844 | "$(inherited)",
845 | "@executable_path/../Frameworks",
846 | "@executable_path/../../../../Frameworks",
847 | );
848 | MACOSX_DEPLOYMENT_TARGET = 10.14;
849 | MARKETING_VERSION = 1.0.0;
850 | OTHER_LDFLAGS = (
851 | "-framework",
852 | SafariServices,
853 | );
854 | PRODUCT_BUNDLE_IDENTIFIER = REPLACEME;
855 | PRODUCT_NAME = "REPLACEME Extension";
856 | SDKROOT = macosx;
857 | SKIP_INSTALL = YES;
858 | SWIFT_EMIT_LOC_STRINGS = YES;
859 | SWIFT_VERSION = 5.0;
860 | };
861 | name = Release;
862 | };
863 | 308C56AE2A27BF1C005C001E /* Debug */ = {
864 | isa = XCBuildConfiguration;
865 | buildSettings = {
866 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
867 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
868 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
869 | CODE_SIGN_ENTITLEMENTS = "macOS (App)/REPLACEME.entitlements";
870 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
871 | CODE_SIGN_STYLE = Automatic;
872 | CURRENT_PROJECT_VERSION = 1;
873 | DEVELOPMENT_TEAM = "";
874 | ENABLE_HARDENED_RUNTIME = YES;
875 | GENERATE_INFOPLIST_FILE = YES;
876 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
877 | INFOPLIST_KEY_NSMainStoryboardFile = "";
878 | INFOPLIST_KEY_NSPrincipalClass = NSApplication;
879 | LD_RUNPATH_SEARCH_PATHS = (
880 | "$(inherited)",
881 | "@executable_path/../Frameworks",
882 | );
883 | MACOSX_DEPLOYMENT_TARGET = 10.15;
884 | MARKETING_VERSION = 1.0.0;
885 | OTHER_LDFLAGS = (
886 | "-framework",
887 | SafariServices,
888 | "-framework",
889 | WebKit,
890 | );
891 | PRODUCT_BUNDLE_IDENTIFIER = REPLACEME;
892 | PRODUCT_NAME = REPLACEME;
893 | SDKROOT = macosx;
894 | SWIFT_EMIT_LOC_STRINGS = YES;
895 | SWIFT_VERSION = 5.0;
896 | };
897 | name = Debug;
898 | };
899 | 308C56AF2A27BF1C005C001E /* Release */ = {
900 | isa = XCBuildConfiguration;
901 | buildSettings = {
902 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
903 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
904 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
905 | CODE_SIGN_ENTITLEMENTS = "macOS (App)/REPLACEME.entitlements";
906 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
907 | CODE_SIGN_STYLE = Automatic;
908 | CURRENT_PROJECT_VERSION = 1;
909 | DEVELOPMENT_TEAM = "";
910 | ENABLE_HARDENED_RUNTIME = YES;
911 | GENERATE_INFOPLIST_FILE = YES;
912 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
913 | INFOPLIST_KEY_NSMainStoryboardFile = "";
914 | INFOPLIST_KEY_NSPrincipalClass = NSApplication;
915 | LD_RUNPATH_SEARCH_PATHS = (
916 | "$(inherited)",
917 | "@executable_path/../Frameworks",
918 | );
919 | MACOSX_DEPLOYMENT_TARGET = 10.15;
920 | MARKETING_VERSION = 1.0.0;
921 | OTHER_LDFLAGS = (
922 | "-framework",
923 | SafariServices,
924 | "-framework",
925 | WebKit,
926 | );
927 | PRODUCT_BUNDLE_IDENTIFIER = REPLACEME;
928 | PRODUCT_NAME = REPLACEME;
929 | SDKROOT = macosx;
930 | SWIFT_EMIT_LOC_STRINGS = YES;
931 | SWIFT_VERSION = 5.0;
932 | };
933 | name = Release;
934 | };
935 | /* End XCBuildConfiguration section */
936 |
937 | /* Begin XCConfigurationList section */
938 | 308C563A2A27BF1A005C001E /* Build configuration list for PBXProject "REPLACEME" */ = {
939 | isa = XCConfigurationList;
940 | buildConfigurations = (
941 | 308C56A02A27BF1C005C001E /* Debug */,
942 | 308C56A12A27BF1C005C001E /* Release */,
943 | );
944 | defaultConfigurationIsVisible = 0;
945 | defaultConfigurationName = Release;
946 | };
947 | 308C56A22A27BF1C005C001E /* Build configuration list for PBXNativeTarget "REPLACEME Extension (iOS)" */ = {
948 | isa = XCConfigurationList;
949 | buildConfigurations = (
950 | 308C56A32A27BF1C005C001E /* Debug */,
951 | 308C56A42A27BF1C005C001E /* Release */,
952 | );
953 | defaultConfigurationIsVisible = 0;
954 | defaultConfigurationName = Release;
955 | };
956 | 308C56A62A27BF1C005C001E /* Build configuration list for PBXNativeTarget "REPLACEME (iOS)" */ = {
957 | isa = XCConfigurationList;
958 | buildConfigurations = (
959 | 308C56A72A27BF1C005C001E /* Debug */,
960 | 308C56A82A27BF1C005C001E /* Release */,
961 | );
962 | defaultConfigurationIsVisible = 0;
963 | defaultConfigurationName = Release;
964 | };
965 | 308C56A92A27BF1C005C001E /* Build configuration list for PBXNativeTarget "REPLACEME Extension (macOS)" */ = {
966 | isa = XCConfigurationList;
967 | buildConfigurations = (
968 | 308C56AA2A27BF1C005C001E /* Debug */,
969 | 308C56AB2A27BF1C005C001E /* Release */,
970 | );
971 | defaultConfigurationIsVisible = 0;
972 | defaultConfigurationName = Release;
973 | };
974 | 308C56AD2A27BF1C005C001E /* Build configuration list for PBXNativeTarget "REPLACEME (macOS)" */ = {
975 | isa = XCConfigurationList;
976 | buildConfigurations = (
977 | 308C56AE2A27BF1C005C001E /* Debug */,
978 | 308C56AF2A27BF1C005C001E /* Release */,
979 | );
980 | defaultConfigurationIsVisible = 0;
981 | defaultConfigurationName = Release;
982 | };
983 | /* End XCConfigurationList section */
984 | };
985 | rootObject = 308C56372A27BF1A005C001E /* Project object */;
986 | }
987 |
--------------------------------------------------------------------------------
/Safari/REPLACEME.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Safari/REPLACEME.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Safari/REPLACEME.xcodeproj/xcshareddata/xcschemes/REPLACEME (iOS).xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
42 |
44 |
50 |
51 |
52 |
53 |
59 |
61 |
67 |
68 |
69 |
70 |
72 |
73 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/Safari/REPLACEME.xcodeproj/xcshareddata/xcschemes/REPLACEME (macOS).xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
42 |
44 |
50 |
51 |
52 |
53 |
59 |
61 |
67 |
68 |
69 |
70 |
72 |
73 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/Safari/REPLACEME.xcodeproj/xcuserdata/kylenazario.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Mute Chat for BoardGameArena (iOS).xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 1
11 |
12 | Mute Chat for BoardGameArena (macOS).xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 0
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Safari/REPLACEME.xcodeproj/xcuserdata/tester.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Mute Chat for BoardGameArena (iOS).xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 1
11 |
12 | Mute Chat for BoardGameArena (macOS).xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 0
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Safari/Shared (App)/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Safari/Shared (App)/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Safari/Shared (App)/Assets.xcassets/LargeIcon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "scale" : "3x"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Safari/Shared (App)/Models.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Models.swift
3 | // REPLACEME
4 | //
5 | // Created by Kyle Nazario on 7/2/23.
6 | //
7 |
8 | import Foundation
9 |
10 | let MAC_WINDOW_SIZE: CGFloat = 400
11 | let MAC_EXTENSION_BUNDLE_ID = "REPLACEME"
12 | let APP_NAME = "REPLACEME"
13 |
--------------------------------------------------------------------------------
/Safari/Shared (App)/Views.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Views.swift
3 | // Beet Engine
4 | //
5 | // Created by tester on 7/5/23.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 | #if os(iOS)
11 | import UIKit
12 | typealias PlatformImage = UIImage
13 | #endif
14 | #if os(macOS)
15 | import AppKit
16 | typealias PlatformImage = NSImage
17 | #endif
18 |
19 | struct AppIconView: View {
20 | private let size: CGFloat = 100
21 | let appIcon = PlatformImage(named: "AppIcon")!
22 |
23 | var body: some View {
24 | image
25 | .resizable()
26 | .frame(width: size, height: size)
27 | }
28 |
29 | private var image: Image {
30 | #if os(iOS)
31 | return Image(uiImage: appIcon)
32 | #else
33 | return Image(nsImage: appIcon)
34 | #endif
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Safari/Shared (Extension)/SafariWebExtensionHandler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SafariWebExtensionHandler.swift
3 | // Shared (Extension)
4 | //
5 | // Created by tester on 5/31/23.
6 | //
7 |
8 | import SafariServices
9 | import os.log
10 |
11 | let SFExtensionMessageKey = "message"
12 |
13 | class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling {
14 |
15 | func beginRequest(with context: NSExtensionContext) {
16 | let item = context.inputItems[0] as! NSExtensionItem
17 | let message = item.userInfo?[SFExtensionMessageKey]
18 | os_log(.default, "Received message from browser.runtime.sendNativeMessage: %@", message as! CVarArg)
19 |
20 | let response = NSExtensionItem()
21 | response.userInfo = [ SFExtensionMessageKey: [ "Response to": message ] ]
22 |
23 | context.completeRequest(returningItems: [response], completionHandler: nil)
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/Safari/iOS (App)/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIApplicationSceneManifest
6 |
7 | UIApplicationSupportsMultipleScenes
8 |
9 | UISceneConfigurations
10 |
11 | UIWindowSceneSessionRoleApplication
12 |
13 |
14 | UISceneConfigurationName
15 | Default Configuration
16 | UISceneDelegateClassName
17 | $(PRODUCT_MODULE_NAME).SceneDelegate
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Safari/iOS (App)/iOS AppView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // iOS (App)
4 | //
5 | // Created by tester on 5/31/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct AppView: App {
12 | var body: some Scene {
13 | WindowGroup {
14 | InstructionsView()
15 | }
16 | }
17 | }
18 |
19 | struct InstructionsView: View {
20 | var body: some View {
21 | VStack {
22 | AppIconView()
23 | .cornerRadius(15)
24 | .padding(.bottom)
25 | .padding(.bottom)
26 | ForEach(steps, id: \.self) { step in
27 | Text(step)
28 | .font(.system(size: 20))
29 | .padding(.bottom)
30 | }
31 | }
32 | }
33 |
34 | private let steps = [
35 | "Open **Settings**",
36 | "Tap **Safari**",
37 | "Tap **Extensions**",
38 | "Tap **\(APP_NAME)**",
39 | "Enable **\(APP_NAME)**"
40 | ].map { try! AttributedString(markdown: $0) }
41 | }
42 |
--------------------------------------------------------------------------------
/Safari/iOS (App)/iOS Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Safari/iOS (Extension)/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSExtension
6 |
7 | NSExtensionPointIdentifier
8 | com.apple.Safari.web-extension
9 | NSExtensionPrincipalClass
10 | $(PRODUCT_MODULE_NAME).SafariWebExtensionHandler
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Safari/macOS (App)/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // macOS (App)
4 | //
5 | // Created by tester on 5/31/23.
6 | //
7 |
8 | import Cocoa
9 | import SwiftUI
10 |
11 | class AppDelegate: NSObject, NSApplicationDelegate {
12 |
13 | private var window: NSWindow!
14 |
15 | func applicationDidFinishLaunching(_ notification: Notification) {
16 | // Override point for customization after application launch.
17 | NSApplication.shared.helpMenu?.items.first?.action = #selector(openHelpPage)
18 |
19 | let contentRect = NSRect(x: 0, y: 0, width: 400, height: 400)
20 | let styleMask: NSWindow.StyleMask = [.miniaturizable, .closable, .resizable, .titled]
21 | let backing = NSWindow.BackingStoreType.buffered
22 | let deferVal = false
23 | window = NSWindow(contentRect: contentRect, styleMask: styleMask, backing: backing, defer: deferVal)
24 | window.center()
25 | window.makeKeyAndOrderFront(nil)
26 |
27 | window.contentView = NSHostingView(rootView: AppView().frame(width: MAC_WINDOW_SIZE, height: MAC_WINDOW_SIZE))
28 |
29 | initAppMenu()
30 | }
31 |
32 | private func initAppMenu() {
33 | let mainMenu = NSMenu()
34 | NSApp.mainMenu = mainMenu
35 |
36 | let appMenuItem = NSMenuItem()
37 | mainMenu.addItem(appMenuItem)
38 | let appMenu = NSMenu()
39 | appMenuItem.submenu = appMenu
40 | appMenu.addItem(withTitle:"Quit \(APP_NAME)", action:#selector(NSApplication.terminate), keyEquivalent: "q")
41 |
42 | let helpMenuItem = NSMenuItem()
43 | mainMenu.addItem(helpMenuItem)
44 | let helpMenu = NSMenu(title: "Help")
45 | helpMenuItem.submenu = helpMenu
46 | helpMenu.addItem(withTitle: "\(APP_NAME) Help", action: #selector(openHelpPage), keyEquivalent: "")
47 | }
48 |
49 | @objc
50 | private func openHelpPage() {
51 | let helpLink = URL(string: "REPLACEME")!
52 | NSWorkspace.shared.open(helpLink)
53 | }
54 |
55 | func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
56 | return true
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/Safari/macOS (App)/REPLACEME.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 | com.apple.security.network.client
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Safari/macOS (App)/SafariConnector.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SafariConnector.swift
3 | // REPLACEME (macOS)
4 | //
5 | // Created by Kyle Nazario on 7/2/23.
6 | //
7 |
8 | import Foundation
9 | import SafariServices
10 |
11 | enum SafariConnector {
12 | static func extensionIsEnabled() async -> Bool {
13 | do {
14 | return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in
15 | SFSafariExtensionManager.getStateOfSafariExtension(withIdentifier: MAC_EXTENSION_BUNDLE_ID) { state, error in
16 | if let error {
17 | continuation.resume(throwing: error)
18 | } else {
19 | continuation.resume(returning: state?.isEnabled ?? false)
20 | }
21 | }
22 | }
23 | } catch {
24 | return false
25 | }
26 | }
27 |
28 | static func openExtensionPrefs() async {
29 | do {
30 | return try await withCheckedThrowingContinuation({ continuation in
31 | SFSafariApplication.showPreferencesForExtension(withIdentifier: MAC_EXTENSION_BUNDLE_ID) { error in
32 | if let error {
33 | continuation.resume(throwing: error)
34 | } else {
35 | continuation.resume()
36 | }
37 | }
38 | })
39 | } catch {
40 | return
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Safari/macOS (App)/macOS AppView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppView.swift
3 | // REPLACEME (macOS)
4 | //
5 | // Created by Kyle Nazario on 7/2/23.
6 | //
7 |
8 | import SwiftUI
9 | import SafariServices
10 |
11 | struct AppView: View {
12 |
13 | private let appIcon = NSImage(named: "AppIcon")
14 | var body: some View {
15 | VStack {
16 | AppIconView()
17 | EnabledMessageView()
18 | .padding(.vertical)
19 | SafariPrefsButton()
20 | }
21 | }
22 | }
23 |
24 | struct EnabledMessageView: View {
25 | @State private var enabled = false
26 |
27 | var body: some View {
28 | Text(message)
29 | .onAppear(perform: loadEnabled)
30 | }
31 |
32 | private var message: String {
33 | enabled ? "\(APP_NAME) is enabled" : "\(APP_NAME) is disabled. Enable it in Safari Preferences."
34 | }
35 |
36 | private func loadEnabled() {
37 | Task {
38 | self.enabled = await SafariConnector.extensionIsEnabled()
39 | }
40 | }
41 | }
42 |
43 | struct SafariPrefsButton: View {
44 | var body: some View {
45 | Button("Quit and Open Safari Preferences", action: openPrefs)
46 | }
47 |
48 | private func openPrefs() {
49 | Task {
50 | await SafariConnector.openExtensionPrefs()
51 | await NSApplication.shared.terminate(nil)
52 | }
53 | }
54 | }
55 |
56 | struct AppView_Previews: PreviewProvider {
57 | static var previews: some View {
58 | AppView()
59 | .frame(width: MAC_WINDOW_SIZE, height: MAC_WINDOW_SIZE)
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Safari/macOS (App)/macOS Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Safari/macOS (App)/main.swift:
--------------------------------------------------------------------------------
1 | //
2 | // main.swift
3 | // REPLACEME (macOS)
4 | //
5 | // Created by Kyle Nazario on 6/28/23.
6 | //
7 |
8 | import Foundation
9 | import AppKit
10 |
11 | let app = NSApplication.shared
12 | let delegate = AppDelegate()
13 | app.delegate = delegate
14 |
15 | _ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
16 |
--------------------------------------------------------------------------------
/Safari/macOS (Extension)/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSExtension
6 |
7 | NSExtensionPointIdentifier
8 | com.apple.Safari.web-extension
9 | NSExtensionPrincipalClass
10 | $(PRODUCT_MODULE_NAME).SafariWebExtensionHandler
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Safari/macOS (Extension)/REPLACEME.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------