+24
.gitignore
+24
.gitignore
···
1
+
# Logs
2
+
logs
3
+
*.log
4
+
npm-debug.log*
5
+
yarn-debug.log*
6
+
yarn-error.log*
7
+
pnpm-debug.log*
8
+
lerna-debug.log*
9
+
10
+
node_modules
11
+
dist
12
+
dist-ssr
13
+
*.local
14
+
15
+
# Editor directories and files
16
+
.vscode/*
17
+
!.vscode/extensions.json
18
+
.idea
19
+
.DS_Store
20
+
*.suo
21
+
*.ntvs*
22
+
*.njsproj
23
+
*.sln
24
+
*.sw?
+1
.npmrc
+1
.npmrc
···
1
+
@jsr:registry=https://npm.jsr.io
+1
.prettierignore
+1
.prettierignore
···
1
+
pnpm-lock.yaml
+19
.prettierrc
+19
.prettierrc
···
1
+
{
2
+
"trailingComma": "all",
3
+
"useTabs": true,
4
+
"tabWidth": 2,
5
+
"printWidth": 110,
6
+
"semi": true,
7
+
"singleQuote": true,
8
+
"bracketSpacing": true,
9
+
"plugins": ["prettier-plugin-tailwindcss"],
10
+
"tailwindFunctions": ["tw"],
11
+
"overrides": [
12
+
{
13
+
"files": ["tsconfig.json", "jsconfig.json"],
14
+
"options": {
15
+
"parser": "jsonc"
16
+
}
17
+
}
18
+
]
19
+
}
+3
.vscode/extensions.json
+3
.vscode/extensions.json
+10
index.html
+10
index.html
+47
package.json
+47
package.json
···
1
+
{
2
+
"packageManager": "pnpm@9.1.0",
3
+
"type": "module",
4
+
"private": true,
5
+
"scripts": {
6
+
"dev": "vite",
7
+
"build": "vite build",
8
+
"preview": "vite preview",
9
+
"fmt": "prettier --cache --write ."
10
+
},
11
+
"dependencies": {
12
+
"@floating-ui/dom": "^1.6.5",
13
+
"@floating-ui/utils": "^0.2.2",
14
+
"@mary/bluesky-client": "npm:@jsr/mary__bluesky-client@^0.5.23",
15
+
"@mary/events": "npm:@jsr/mary__events@^0.1.0",
16
+
"@mary/solid-freeze": "npm:@externdefs/solid-freeze@^0.1.1",
17
+
"@mary/solid-query": "npm:@externdefs/solid-query@^0.1.4",
18
+
"cborg": "4.0.7",
19
+
"nanoid": "^5.0.7",
20
+
"solid-floating-ui": "~0.2.1",
21
+
"solid-js": "^1.8.17",
22
+
"solid-textarea-autosize": "^0.0.5"
23
+
},
24
+
"devDependencies": {
25
+
"@types/dom-close-watcher": "^1.0.0",
26
+
"autoprefixer": "^10.4.19",
27
+
"prettier": "^3.3.2",
28
+
"prettier-plugin-tailwindcss": "^0.6.3",
29
+
"tailwindcss": "^3.4.4",
30
+
"terser": "^5.31.1",
31
+
"typescript": "^5.4.5",
32
+
"vite": "^5.2.11",
33
+
"vite-plugin-pwa": "0.17.4",
34
+
"vite-plugin-solid": "^2.10.2"
35
+
},
36
+
"pnpm": {
37
+
"patchedDependencies": {
38
+
"@tanstack/query-core@5.17.19": "patches/@tanstack__query-core@5.17.19.patch",
39
+
"cborg@4.0.7": "patches/cborg@4.0.7.patch",
40
+
"solid-textarea-autosize@0.0.5": "patches/solid-textarea-autosize@0.0.5.patch",
41
+
"vite-plugin-pwa@0.17.4": "patches/vite-plugin-pwa@0.17.4.patch",
42
+
"solid-js@1.8.17": "patches/solid-js@1.8.17.patch",
43
+
"workbox-precaching@7.1.0": "patches/workbox-precaching@7.1.0.patch",
44
+
"vite@5.2.11": "patches/vite@5.2.11.patch"
45
+
}
46
+
}
47
+
}
+60
patches/@tanstack__query-core@5.17.19.patch
+60
patches/@tanstack__query-core@5.17.19.patch
···
1
+
diff --git a/build/modern/utils.cjs b/build/modern/utils.cjs
2
+
index 6ef9599fd446b7fa98a292f85160b46be88c0295..c9e3957446a81f4fee1da9ac9da779d5f0e90a5d 100644
3
+
--- a/build/modern/utils.cjs
4
+
+++ b/build/modern/utils.cjs
5
+
@@ -182,21 +182,12 @@ function isPlainArray(value) {
6
+
return Array.isArray(value) && value.length === Object.keys(value).length;
7
+
}
8
+
function isPlainObject(o) {
9
+
- if (!hasObjectPrototype(o)) {
10
+
+ if (typeof o !== "object" || o === null) {
11
+
return false;
12
+
}
13
+
- const ctor = o.constructor;
14
+
- if (typeof ctor === "undefined") {
15
+
- return true;
16
+
- }
17
+
- const prot = ctor.prototype;
18
+
- if (!hasObjectPrototype(prot)) {
19
+
- return false;
20
+
- }
21
+
- if (!prot.hasOwnProperty("isPrototypeOf")) {
22
+
- return false;
23
+
- }
24
+
- return true;
25
+
+
26
+
+ const proto = Object.getPrototypeOf(o);
27
+
+ return (proto === Object.prototype || proto === null) && Object.isExtensible(o);
28
+
}
29
+
function hasObjectPrototype(o) {
30
+
return Object.prototype.toString.call(o) === "[object Object]";
31
+
diff --git a/build/modern/utils.js b/build/modern/utils.js
32
+
index 0e93a35b45cfc2d53fc17120c741a91ec8a8c533..82b51475e4e12860552f4a0f5f3af23f416740fa 100644
33
+
--- a/build/modern/utils.js
34
+
+++ b/build/modern/utils.js
35
+
@@ -140,21 +140,12 @@ function isPlainArray(value) {
36
+
return Array.isArray(value) && value.length === Object.keys(value).length;
37
+
}
38
+
function isPlainObject(o) {
39
+
- if (!hasObjectPrototype(o)) {
40
+
+ if (typeof o !== "object" || o === null) {
41
+
return false;
42
+
}
43
+
- const ctor = o.constructor;
44
+
- if (typeof ctor === "undefined") {
45
+
- return true;
46
+
- }
47
+
- const prot = ctor.prototype;
48
+
- if (!hasObjectPrototype(prot)) {
49
+
- return false;
50
+
- }
51
+
- if (!prot.hasOwnProperty("isPrototypeOf")) {
52
+
- return false;
53
+
- }
54
+
- return true;
55
+
+
56
+
+ const proto = Object.getPrototypeOf(o);
57
+
+ return (proto === null || proto === Object) && Object.isExtensible(o);
58
+
}
59
+
function hasObjectPrototype(o) {
60
+
return Object.prototype.toString.call(o) === "[object Object]";
+98
patches/cborg@4.0.7.patch
+98
patches/cborg@4.0.7.patch
···
1
+
diff --git a/cborg.js b/cborg.js
2
+
index 90bde204e5f14e14d6a98a95413bb64fb8c807c2..b1bfe5bd9054c362a8c9b7216403b10d6f7d901b 100644
3
+
--- a/cborg.js
4
+
+++ b/cborg.js
5
+
@@ -4,11 +4,11 @@ import { Token, Type } from './lib/token.js'
6
+
7
+
/**
8
+
* Export the types that were present in the original manual cborg.d.ts
9
+
- * @typedef {import('./interface').TagDecoder} TagDecoder
10
+
+ * @typedef {import('./interface.js').TagDecoder} TagDecoder
11
+
* There was originally just `TypeEncoder` so don't break types by renaming or not exporting
12
+
- * @typedef {import('./interface').OptionalTypeEncoder} TypeEncoder
13
+
- * @typedef {import('./interface').DecodeOptions} DecodeOptions
14
+
- * @typedef {import('./interface').EncodeOptions} EncodeOptions
15
+
+ * @typedef {import('./interface.js').OptionalTypeEncoder} TypeEncoder
16
+
+ * @typedef {import('./interface.js').DecodeOptions} DecodeOptions
17
+
+ * @typedef {import('./interface.js').EncodeOptions} EncodeOptions
18
+
*/
19
+
20
+
export {
21
+
diff --git a/interface.ts b/interface.d.ts
22
+
similarity index 95%
23
+
rename from interface.ts
24
+
rename to interface.d.ts
25
+
index 020264da98b4ab5d184d5ef471e8c957c32b4da1..b26c48daab83440ab51773c922fe92d2af111acd 100644
26
+
--- a/interface.ts
27
+
+++ b/interface.d.ts
28
+
@@ -1,5 +1,5 @@
29
+
-import { Token } from './lib/token'
30
+
-import { Bl } from './lib/bl'
31
+
+import { Token } from './lib/token.js'
32
+
+import { Bl } from './lib/bl.js'
33
+
34
+
export type TokenOrNestedTokens = Token | Token[] | TokenOrNestedTokens[]
35
+
36
+
diff --git a/lib/encode.js b/lib/encode.js
37
+
index acd7bac1f580d27e8c0052c493e891136d0e8c47..3ae4185291dea9885d5a769f6e4818890ea1f87f 100644
38
+
--- a/lib/encode.js
39
+
+++ b/lib/encode.js
40
+
@@ -15,12 +15,12 @@ import { encodeTag } from './6tag.js'
41
+
import { encodeFloat } from './7float.js'
42
+
43
+
/**
44
+
- * @typedef {import('../interface').EncodeOptions} EncodeOptions
45
+
- * @typedef {import('../interface').OptionalTypeEncoder} OptionalTypeEncoder
46
+
- * @typedef {import('../interface').Reference} Reference
47
+
- * @typedef {import('../interface').StrictTypeEncoder} StrictTypeEncoder
48
+
- * @typedef {import('../interface').TokenTypeEncoder} TokenTypeEncoder
49
+
- * @typedef {import('../interface').TokenOrNestedTokens} TokenOrNestedTokens
50
+
+ * @typedef {import('../interface.js').EncodeOptions} EncodeOptions
51
+
+ * @typedef {import('../interface.js').OptionalTypeEncoder} OptionalTypeEncoder
52
+
+ * @typedef {import('../interface.js').Reference} Reference
53
+
+ * @typedef {import('../interface.js').StrictTypeEncoder} StrictTypeEncoder
54
+
+ * @typedef {import('../interface.js').TokenTypeEncoder} TokenTypeEncoder
55
+
+ * @typedef {import('../interface.js').TokenOrNestedTokens} TokenOrNestedTokens
56
+
*/
57
+
58
+
/** @type {EncodeOptions} */
59
+
diff --git a/types/cborg.d.ts b/types/cborg.d.ts
60
+
index 1f8711ab5dcc7cf16f3682c538544379094841dd..0b53251a245b295cff0daf02d025df8a9abf411a 100644
61
+
--- a/types/cborg.d.ts
62
+
+++ b/types/cborg.d.ts
63
+
@@ -1,19 +1,19 @@
64
+
/**
65
+
* There was originally just `TypeEncoder` so don't break types by renaming or not exporting
66
+
*/
67
+
-export type TagDecoder = import('./interface').TagDecoder;
68
+
+export type TagDecoder = import('./interface.js').TagDecoder;
69
+
/**
70
+
* Export the types that were present in the original manual cborg.d.ts
71
+
*/
72
+
-export type TypeEncoder = import('./interface').OptionalTypeEncoder;
73
+
+export type TypeEncoder = import('./interface.js').OptionalTypeEncoder;
74
+
/**
75
+
* Export the types that were present in the original manual cborg.d.ts
76
+
*/
77
+
-export type DecodeOptions = import('./interface').DecodeOptions;
78
+
+export type DecodeOptions = import('./interface.js').DecodeOptions;
79
+
/**
80
+
* Export the types that were present in the original manual cborg.d.ts
81
+
*/
82
+
-export type EncodeOptions = import('./interface').EncodeOptions;
83
+
+export type EncodeOptions = import('./interface.js').EncodeOptions;
84
+
import { decode } from './lib/decode.js';
85
+
import { decodeFirst } from './lib/decode.js';
86
+
import { encode } from './lib/encode.js';
87
+
diff --git a/types/interface.d.ts b/types/interface.d.ts
88
+
index 40b734f397df16956bd5e99c396f31027fbb2231..6692903b3fb3390c92b534258e421fc977697136 100644
89
+
--- a/types/interface.d.ts
90
+
+++ b/types/interface.d.ts
91
+
@@ -1,5 +1,5 @@
92
+
-import { Token } from './lib/token';
93
+
-import { Bl } from './lib/bl';
94
+
+import { Token } from './lib/token.js';
95
+
+import { Bl } from './lib/bl.js';
96
+
export type TokenOrNestedTokens = Token | Token[] | TokenOrNestedTokens[];
97
+
export interface Reference {
98
+
parent: Reference | undefined;
+20
patches/solid-js@1.8.17.patch
+20
patches/solid-js@1.8.17.patch
···
1
+
diff --git a/dist/solid.js b/dist/solid.js
2
+
index 7350fc223921ca774966b22cb77e8542e28b0e25..bfa0e6c2a0056e256fe31b4c8aebade1cdfcce8b 100644
3
+
--- a/dist/solid.js
4
+
+++ b/dist/solid.js
5
+
@@ -1573,7 +1573,6 @@ function Show(props) {
6
+
keyed
7
+
? c
8
+
: () => {
9
+
- if (!untrack(condition)) throw narrowedError("Show");
10
+
return props.when;
11
+
}
12
+
)
13
+
@@ -1620,7 +1619,6 @@ function Switch(props) {
14
+
keyed
15
+
? when
16
+
: () => {
17
+
- if (untrack(evalConditions)[0] !== index) throw narrowedError("Match");
18
+
return cond.when;
19
+
}
20
+
)
+14
patches/solid-textarea-autosize@0.0.5.patch
+14
patches/solid-textarea-autosize@0.0.5.patch
···
1
+
diff --git a/package.json b/package.json
2
+
index 663b11c362a6ffe995ca92663b9840ee57e405ba..da43af39a66d40495987f18448a70a0b96e39e7c 100644
3
+
--- a/package.json
4
+
+++ b/package.json
5
+
@@ -34,7 +34,8 @@
6
+
"require": "./dist/cjs/index.js"
7
+
},
8
+
"require": "./dist/cjs/index.js",
9
+
- "node": "./dist/cjs/index.js"
10
+
+ "node": "./dist/cjs/index.js",
11
+
+ "types": "./dist/types/index.d.ts"
12
+
}
13
+
},
14
+
"scripts": {
+37
patches/vite-plugin-pwa@0.17.4.patch
+37
patches/vite-plugin-pwa@0.17.4.patch
···
1
+
diff --git a/dist/client/build/register.js b/dist/client/build/register.js
2
+
index 0dc588160b1a58778ebc02757d9757e45cb212e3..aef9ecfc0eee73fd8406bfa3025e38ba2468064c 100644
3
+
--- a/dist/client/build/register.js
4
+
+++ b/dist/client/build/register.js
5
+
@@ -7,6 +7,7 @@ function registerSW(options = {}) {
6
+
const {
7
+
immediate = false,
8
+
onNeedRefresh,
9
+
+ onBeginUpdate,
10
+
onOfflineReady,
11
+
onRegistered,
12
+
onRegisteredSW,
13
+
@@ -71,6 +72,12 @@ function registerSW(options = {}) {
14
+
}
15
+
}
16
+
wb.register({ immediate }).then((r) => {
17
+
+ if (onBeginUpdate) {
18
+
+ r?.addEventListener('updatefound', () => {
19
+
+ onBeginUpdate();
20
+
+ });
21
+
+ }
22
+
+
23
+
if (onRegisteredSW)
24
+
onRegisteredSW("__SW__", r);
25
+
else
26
+
diff --git a/types/index.d.ts b/types/index.d.ts
27
+
index c2553517a12c98f4f7d1b0ef10a2dd203842d45e..694f29ec1ca485c3d620d3cd47517abdef7e17c1 100644
28
+
--- a/types/index.d.ts
29
+
+++ b/types/index.d.ts
30
+
@@ -1,6 +1,7 @@
31
+
export interface RegisterSWOptions {
32
+
immediate?: boolean
33
+
onNeedRefresh?: () => void
34
+
+ onBeginUpdate?: () => void
35
+
onOfflineReady?: () => void
36
+
/**
37
+
* Called only if `onRegisteredSW` is not provided.
+13
patches/vite@5.2.11.patch
+13
patches/vite@5.2.11.patch
···
1
+
diff --git a/dist/node/chunks/dep-cNe07EU9.js b/dist/node/chunks/dep-cNe07EU9.js
2
+
index 477583eaa3b1ececd28abc98d5171fd85254f10d..b3109c1c648f2c108c1a13b044a5e4a239ac97b6 100644
3
+
--- a/dist/node/chunks/dep-cNe07EU9.js
4
+
+++ b/dist/node/chunks/dep-cNe07EU9.js
5
+
@@ -67286,7 +67286,7 @@ async function resolveBuildPlugins(config) {
6
+
...(config.isWorker ? [webWorkerPostPlugin()] : []),
7
+
],
8
+
post: [
9
+
- buildImportAnalysisPlugin(config),
10
+
+ ...(config.build.modulePreload !== false ? [buildImportAnalysisPlugin(config)] : []),
11
+
...(config.esbuild !== false ? [buildEsbuildPlugin(config)] : []),
12
+
...(options.minify ? [terserPlugin(config)] : []),
13
+
...(!config.isWorker
+89
patches/workbox-precaching@7.1.0.patch
+89
patches/workbox-precaching@7.1.0.patch
···
1
+
diff --git a/PrecacheController.js b/PrecacheController.js
2
+
index e00975e3762dc6382c39bebee04a89a651aae3d0..5d830222ffbd8e5ed841bbf3fe2560d354ca87ca 100644
3
+
--- a/PrecacheController.js
4
+
+++ b/PrecacheController.js
5
+
@@ -150,9 +150,7 @@ class PrecacheController {
6
+
return waitUntil(event, async () => {
7
+
const installReportPlugin = new PrecacheInstallReportPlugin();
8
+
this.strategy.plugins.push(installReportPlugin);
9
+
- // Cache entries one at a time.
10
+
- // See https://github.com/GoogleChrome/workbox/issues/2528
11
+
- for (const [url, cacheKey] of this._urlsToCacheKeys) {
12
+
+ await eachLimit(Array.from(this._urlsToCacheKeys), 6, async ([url, cacheKey]) => {
13
+
const integrity = this._cacheKeysToIntegrities.get(cacheKey);
14
+
const cacheMode = this._urlsToCacheModes.get(url);
15
+
const request = new Request(url, {
16
+
@@ -165,7 +163,7 @@ class PrecacheController {
17
+
request,
18
+
event,
19
+
}));
20
+
- }
21
+
+ });
22
+
const { updatedURLs, notUpdatedURLs } = installReportPlugin;
23
+
if (process.env.NODE_ENV !== 'production') {
24
+
printInstallDetails(updatedURLs, notUpdatedURLs);
25
+
@@ -290,3 +288,64 @@ class PrecacheController {
26
+
}
27
+
}
28
+
export { PrecacheController };
29
+
+
30
+
+const eachLimit = (values, limit, iterator) => {
31
+
+ return new Promise((res, rej) => {
32
+
+ let active = 0;
33
+
+ let current = 0;
34
+
+
35
+
+ let fulfilled = false;
36
+
+
37
+
+ const resolve = () => {
38
+
+ if (fulfilled || active > 0) {
39
+
+ return;
40
+
+ }
41
+
+
42
+
+ fulfilled = true;
43
+
+ res();
44
+
+ };
45
+
+
46
+
+ const reject = (err) => {
47
+
+ if (fulfilled) {
48
+
+ return;
49
+
+ }
50
+
+
51
+
+ rej(err);
52
+
+ };
53
+
+
54
+
+ const run = () => {
55
+
+ const c = current++;
56
+
+
57
+
+ if (fulfilled) {
58
+
+ return;
59
+
+ }
60
+
+ if (c >= values.length) {
61
+
+ return resolve();
62
+
+ }
63
+
+
64
+
+ const value = values[c];
65
+
+
66
+
+ active++;
67
+
+
68
+
+ try {
69
+
+ const ret = iterator(value, c);
70
+
+
71
+
+ if (ret && 'then' in ret) {
72
+
+ ret.then(() => {
73
+
+ active--;
74
+
+ run();
75
+
+ }, rej);
76
+
+ } else {
77
+
+ active--;
78
+
+ run();
79
+
+ }
80
+
+ } catch (err) {
81
+
+ reject(err);
82
+
+ }
83
+
+ };
84
+
+
85
+
+ for (let i = 0; i < limit; i++) {
86
+
+ run();
87
+
+ }
88
+
+ });
89
+
+};
+4780
pnpm-lock.yaml
+4780
pnpm-lock.yaml
···
1
+
lockfileVersion: '9.0'
2
+
3
+
settings:
4
+
autoInstallPeers: false
5
+
excludeLinksFromLockfile: false
6
+
7
+
patchedDependencies:
8
+
'@tanstack/query-core@5.17.19':
9
+
hash: v3r5daycwz67li5mxwjnajy7bm
10
+
path: patches/@tanstack__query-core@5.17.19.patch
11
+
cborg@4.0.7:
12
+
hash: dz6b6r6dc4jadjfng7vtgi53hy
13
+
path: patches/cborg@4.0.7.patch
14
+
solid-js@1.8.17:
15
+
hash: wunpcbjxb5h4ujg4psj63uuluq
16
+
path: patches/solid-js@1.8.17.patch
17
+
solid-textarea-autosize@0.0.5:
18
+
hash: xoixqosplh7bmfbnvr4hschede
19
+
path: patches/solid-textarea-autosize@0.0.5.patch
20
+
vite-plugin-pwa@0.17.4:
21
+
hash: ve5hypcrajivuvoyst6zln6qyq
22
+
path: patches/vite-plugin-pwa@0.17.4.patch
23
+
vite@5.2.11:
24
+
hash: rtipi3fkkgeet3kqyzne4ksswy
25
+
path: patches/vite@5.2.11.patch
26
+
workbox-precaching@7.1.0:
27
+
hash: uwqzx25dqx6gokakqgp7nxcupi
28
+
path: patches/workbox-precaching@7.1.0.patch
29
+
30
+
importers:
31
+
32
+
.:
33
+
dependencies:
34
+
'@floating-ui/dom':
35
+
specifier: ^1.6.5
36
+
version: 1.6.5
37
+
'@floating-ui/utils':
38
+
specifier: ^0.2.2
39
+
version: 0.2.2
40
+
'@mary/bluesky-client':
41
+
specifier: npm:@jsr/mary__bluesky-client@^0.5.23
42
+
version: '@jsr/mary__bluesky-client@0.5.23'
43
+
'@mary/events':
44
+
specifier: npm:@jsr/mary__events@^0.1.0
45
+
version: '@jsr/mary__events@0.1.0'
46
+
'@mary/solid-freeze':
47
+
specifier: npm:@externdefs/solid-freeze@^0.1.1
48
+
version: '@externdefs/solid-freeze@0.1.1(solid-js@1.8.17(patch_hash=wunpcbjxb5h4ujg4psj63uuluq))'
49
+
'@mary/solid-query':
50
+
specifier: npm:@externdefs/solid-query@^0.1.4
51
+
version: '@externdefs/solid-query@0.1.4(solid-js@1.8.17(patch_hash=wunpcbjxb5h4ujg4psj63uuluq))'
52
+
cborg:
53
+
specifier: 4.0.7
54
+
version: 4.0.7(patch_hash=dz6b6r6dc4jadjfng7vtgi53hy)
55
+
nanoid:
56
+
specifier: ^5.0.7
57
+
version: 5.0.7
58
+
solid-floating-ui:
59
+
specifier: ~0.2.1
60
+
version: 0.2.1(@floating-ui/dom@1.6.5)(solid-js@1.8.17(patch_hash=wunpcbjxb5h4ujg4psj63uuluq))
61
+
solid-js:
62
+
specifier: ^1.8.17
63
+
version: 1.8.17(patch_hash=wunpcbjxb5h4ujg4psj63uuluq)
64
+
solid-textarea-autosize:
65
+
specifier: ^0.0.5
66
+
version: 0.0.5(patch_hash=xoixqosplh7bmfbnvr4hschede)(solid-js@1.8.17(patch_hash=wunpcbjxb5h4ujg4psj63uuluq))
67
+
devDependencies:
68
+
'@types/dom-close-watcher':
69
+
specifier: ^1.0.0
70
+
version: 1.0.0
71
+
autoprefixer:
72
+
specifier: ^10.4.19
73
+
version: 10.4.19(postcss@8.4.38)
74
+
prettier:
75
+
specifier: ^3.3.2
76
+
version: 3.3.2
77
+
prettier-plugin-tailwindcss:
78
+
specifier: ^0.6.3
79
+
version: 0.6.3(prettier@3.3.2)
80
+
tailwindcss:
81
+
specifier: ^3.4.4
82
+
version: 3.4.4
83
+
terser:
84
+
specifier: ^5.31.1
85
+
version: 5.31.1
86
+
typescript:
87
+
specifier: ^5.4.5
88
+
version: 5.4.5
89
+
vite:
90
+
specifier: ^5.2.11
91
+
version: 5.2.11(patch_hash=rtipi3fkkgeet3kqyzne4ksswy)(terser@5.31.1)
92
+
vite-plugin-pwa:
93
+
specifier: 0.17.4
94
+
version: 0.17.4(patch_hash=ve5hypcrajivuvoyst6zln6qyq)(@types/babel__core@7.20.5)(vite@5.2.11(patch_hash=rtipi3fkkgeet3kqyzne4ksswy)(terser@5.31.1))
95
+
vite-plugin-solid:
96
+
specifier: ^2.10.2
97
+
version: 2.10.2(solid-js@1.8.17(patch_hash=wunpcbjxb5h4ujg4psj63uuluq))(vite@5.2.11(patch_hash=rtipi3fkkgeet3kqyzne4ksswy)(terser@5.31.1))
98
+
99
+
packages:
100
+
101
+
'@alloc/quick-lru@5.2.0':
102
+
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
103
+
engines: {node: '>=10'}
104
+
105
+
'@ampproject/remapping@2.3.0':
106
+
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
107
+
engines: {node: '>=6.0.0'}
108
+
109
+
'@apideck/better-ajv-errors@0.3.6':
110
+
resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==}
111
+
engines: {node: '>=10'}
112
+
peerDependencies:
113
+
ajv: '>=8'
114
+
115
+
'@babel/code-frame@7.24.6':
116
+
resolution: {integrity: sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==}
117
+
engines: {node: '>=6.9.0'}
118
+
119
+
'@babel/compat-data@7.24.6':
120
+
resolution: {integrity: sha512-aC2DGhBq5eEdyXWqrDInSqQjO0k8xtPRf5YylULqx8MCd6jBtzqfta/3ETMRpuKIc5hyswfO80ObyA1MvkCcUQ==}
121
+
engines: {node: '>=6.9.0'}
122
+
123
+
'@babel/core@7.24.6':
124
+
resolution: {integrity: sha512-qAHSfAdVyFmIvl0VHELib8xar7ONuSHrE2hLnsaWkYNTI68dmi1x8GYDhJjMI/e7XWal9QBlZkwbOnkcw7Z8gQ==}
125
+
engines: {node: '>=6.9.0'}
126
+
127
+
'@babel/generator@7.24.6':
128
+
resolution: {integrity: sha512-S7m4eNa6YAPJRHmKsLHIDJhNAGNKoWNiWefz1MBbpnt8g9lvMDl1hir4P9bo/57bQEmuwEhnRU/AMWsD0G/Fbg==}
129
+
engines: {node: '>=6.9.0'}
130
+
131
+
'@babel/helper-annotate-as-pure@7.24.6':
132
+
resolution: {integrity: sha512-DitEzDfOMnd13kZnDqns1ccmftwJTS9DMkyn9pYTxulS7bZxUxpMly3Nf23QQ6NwA4UB8lAqjbqWtyvElEMAkg==}
133
+
engines: {node: '>=6.9.0'}
134
+
135
+
'@babel/helper-builder-binary-assignment-operator-visitor@7.24.6':
136
+
resolution: {integrity: sha512-+wnfqc5uHiMYtvRX7qu80Toef8BXeh4HHR1SPeonGb1SKPniNEd4a/nlaJJMv/OIEYvIVavvo0yR7u10Gqz0Iw==}
137
+
engines: {node: '>=6.9.0'}
138
+
139
+
'@babel/helper-compilation-targets@7.24.6':
140
+
resolution: {integrity: sha512-VZQ57UsDGlX/5fFA7GkVPplZhHsVc+vuErWgdOiysI9Ksnw0Pbbd6pnPiR/mmJyKHgyIW0c7KT32gmhiF+cirg==}
141
+
engines: {node: '>=6.9.0'}
142
+
143
+
'@babel/helper-create-class-features-plugin@7.24.6':
144
+
resolution: {integrity: sha512-djsosdPJVZE6Vsw3kk7IPRWethP94WHGOhQTc67SNXE0ZzMhHgALw8iGmYS0TD1bbMM0VDROy43od7/hN6WYcA==}
145
+
engines: {node: '>=6.9.0'}
146
+
peerDependencies:
147
+
'@babel/core': ^7.0.0
148
+
149
+
'@babel/helper-create-regexp-features-plugin@7.24.6':
150
+
resolution: {integrity: sha512-C875lFBIWWwyv6MHZUG9HmRrlTDgOsLWZfYR0nW69gaKJNe0/Mpxx5r0EID2ZdHQkdUmQo2t0uNckTL08/1BgA==}
151
+
engines: {node: '>=6.9.0'}
152
+
peerDependencies:
153
+
'@babel/core': ^7.0.0
154
+
155
+
'@babel/helper-define-polyfill-provider@0.6.2':
156
+
resolution: {integrity: sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==}
157
+
peerDependencies:
158
+
'@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
159
+
160
+
'@babel/helper-environment-visitor@7.24.6':
161
+
resolution: {integrity: sha512-Y50Cg3k0LKLMjxdPjIl40SdJgMB85iXn27Vk/qbHZCFx/o5XO3PSnpi675h1KEmmDb6OFArfd5SCQEQ5Q4H88g==}
162
+
engines: {node: '>=6.9.0'}
163
+
164
+
'@babel/helper-function-name@7.24.6':
165
+
resolution: {integrity: sha512-xpeLqeeRkbxhnYimfr2PC+iA0Q7ljX/d1eZ9/inYbmfG2jpl8Lu3DyXvpOAnrS5kxkfOWJjioIMQsaMBXFI05w==}
166
+
engines: {node: '>=6.9.0'}
167
+
168
+
'@babel/helper-hoist-variables@7.24.6':
169
+
resolution: {integrity: sha512-SF/EMrC3OD7dSta1bLJIlrsVxwtd0UpjRJqLno6125epQMJ/kyFmpTT4pbvPbdQHzCHg+biQ7Syo8lnDtbR+uA==}
170
+
engines: {node: '>=6.9.0'}
171
+
172
+
'@babel/helper-member-expression-to-functions@7.24.6':
173
+
resolution: {integrity: sha512-OTsCufZTxDUsv2/eDXanw/mUZHWOxSbEmC3pP8cgjcy5rgeVPWWMStnv274DV60JtHxTk0adT0QrCzC4M9NWGg==}
174
+
engines: {node: '>=6.9.0'}
175
+
176
+
'@babel/helper-module-imports@7.18.6':
177
+
resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==}
178
+
engines: {node: '>=6.9.0'}
179
+
180
+
'@babel/helper-module-imports@7.24.6':
181
+
resolution: {integrity: sha512-a26dmxFJBF62rRO9mmpgrfTLsAuyHk4e1hKTUkD/fcMfynt8gvEKwQPQDVxWhca8dHoDck+55DFt42zV0QMw5g==}
182
+
engines: {node: '>=6.9.0'}
183
+
184
+
'@babel/helper-module-transforms@7.24.6':
185
+
resolution: {integrity: sha512-Y/YMPm83mV2HJTbX1Qh2sjgjqcacvOlhbzdCCsSlblOKjSYmQqEbO6rUniWQyRo9ncyfjT8hnUjlG06RXDEmcA==}
186
+
engines: {node: '>=6.9.0'}
187
+
peerDependencies:
188
+
'@babel/core': ^7.0.0
189
+
190
+
'@babel/helper-optimise-call-expression@7.24.6':
191
+
resolution: {integrity: sha512-3SFDJRbx7KuPRl8XDUr8O7GAEB8iGyWPjLKJh/ywP/Iy9WOmEfMrsWbaZpvBu2HSYn4KQygIsz0O7m8y10ncMA==}
192
+
engines: {node: '>=6.9.0'}
193
+
194
+
'@babel/helper-plugin-utils@7.24.6':
195
+
resolution: {integrity: sha512-MZG/JcWfxybKwsA9N9PmtF2lOSFSEMVCpIRrbxccZFLJPrJciJdG/UhSh5W96GEteJI2ARqm5UAHxISwRDLSNg==}
196
+
engines: {node: '>=6.9.0'}
197
+
198
+
'@babel/helper-remap-async-to-generator@7.24.6':
199
+
resolution: {integrity: sha512-1Qursq9ArRZPAMOZf/nuzVW8HgJLkTB9y9LfP4lW2MVp4e9WkLJDovfKBxoDcCk6VuzIxyqWHyBoaCtSRP10yg==}
200
+
engines: {node: '>=6.9.0'}
201
+
peerDependencies:
202
+
'@babel/core': ^7.0.0
203
+
204
+
'@babel/helper-replace-supers@7.24.6':
205
+
resolution: {integrity: sha512-mRhfPwDqDpba8o1F8ESxsEkJMQkUF8ZIWrAc0FtWhxnjfextxMWxr22RtFizxxSYLjVHDeMgVsRq8BBZR2ikJQ==}
206
+
engines: {node: '>=6.9.0'}
207
+
peerDependencies:
208
+
'@babel/core': ^7.0.0
209
+
210
+
'@babel/helper-simple-access@7.24.6':
211
+
resolution: {integrity: sha512-nZzcMMD4ZhmB35MOOzQuiGO5RzL6tJbsT37Zx8M5L/i9KSrukGXWTjLe1knIbb/RmxoJE9GON9soq0c0VEMM5g==}
212
+
engines: {node: '>=6.9.0'}
213
+
214
+
'@babel/helper-skip-transparent-expression-wrappers@7.24.6':
215
+
resolution: {integrity: sha512-jhbbkK3IUKc4T43WadP96a27oYti9gEf1LdyGSP2rHGH77kwLwfhO7TgwnWvxxQVmke0ImmCSS47vcuxEMGD3Q==}
216
+
engines: {node: '>=6.9.0'}
217
+
218
+
'@babel/helper-split-export-declaration@7.24.6':
219
+
resolution: {integrity: sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw==}
220
+
engines: {node: '>=6.9.0'}
221
+
222
+
'@babel/helper-string-parser@7.24.6':
223
+
resolution: {integrity: sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q==}
224
+
engines: {node: '>=6.9.0'}
225
+
226
+
'@babel/helper-validator-identifier@7.24.6':
227
+
resolution: {integrity: sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==}
228
+
engines: {node: '>=6.9.0'}
229
+
230
+
'@babel/helper-validator-option@7.24.6':
231
+
resolution: {integrity: sha512-Jktc8KkF3zIkePb48QO+IapbXlSapOW9S+ogZZkcO6bABgYAxtZcjZ/O005111YLf+j4M84uEgwYoidDkXbCkQ==}
232
+
engines: {node: '>=6.9.0'}
233
+
234
+
'@babel/helper-wrap-function@7.24.6':
235
+
resolution: {integrity: sha512-f1JLrlw/jbiNfxvdrfBgio/gRBk3yTAEJWirpAkiJG2Hb22E7cEYKHWo0dFPTv/niPovzIdPdEDetrv6tC6gPQ==}
236
+
engines: {node: '>=6.9.0'}
237
+
238
+
'@babel/helpers@7.24.6':
239
+
resolution: {integrity: sha512-V2PI+NqnyFu1i0GyTd/O/cTpxzQCYioSkUIRmgo7gFEHKKCg5w46+r/A6WeUR1+P3TeQ49dspGPNd/E3n9AnnA==}
240
+
engines: {node: '>=6.9.0'}
241
+
242
+
'@babel/highlight@7.24.6':
243
+
resolution: {integrity: sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==}
244
+
engines: {node: '>=6.9.0'}
245
+
246
+
'@babel/parser@7.24.6':
247
+
resolution: {integrity: sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==}
248
+
engines: {node: '>=6.0.0'}
249
+
hasBin: true
250
+
251
+
'@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.6':
252
+
resolution: {integrity: sha512-bYndrJ6Ph6Ar+GaB5VAc0JPoP80bQCm4qon6JEzXfRl5QZyQ8Ur1K6k7htxWmPA5z+k7JQvaMUrtXlqclWYzKw==}
253
+
engines: {node: '>=6.9.0'}
254
+
peerDependencies:
255
+
'@babel/core': ^7.0.0
256
+
257
+
'@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.6':
258
+
resolution: {integrity: sha512-iVuhb6poq5ikqRq2XWU6OQ+R5o9wF+r/or9CeUyovgptz0UlnK4/seOQ1Istu/XybYjAhQv1FRSSfHHufIku5Q==}
259
+
engines: {node: '>=6.9.0'}
260
+
peerDependencies:
261
+
'@babel/core': ^7.0.0
262
+
263
+
'@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.6':
264
+
resolution: {integrity: sha512-c8TER5xMDYzzFcGqOEp9l4hvB7dcbhcGjcLVwxWfe4P5DOafdwjsBJZKsmv+o3aXh7NhopvayQIovHrh2zSRUQ==}
265
+
engines: {node: '>=6.9.0'}
266
+
peerDependencies:
267
+
'@babel/core': ^7.13.0
268
+
269
+
'@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.6':
270
+
resolution: {integrity: sha512-z8zEjYmwBUHN/pCF3NuWBhHQjJCrd33qAi8MgANfMrAvn72k2cImT8VjK9LJFu4ysOLJqhfkYYb3MvwANRUNZQ==}
271
+
engines: {node: '>=6.9.0'}
272
+
peerDependencies:
273
+
'@babel/core': ^7.0.0
274
+
275
+
'@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2':
276
+
resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==}
277
+
engines: {node: '>=6.9.0'}
278
+
peerDependencies:
279
+
'@babel/core': ^7.0.0-0
280
+
281
+
'@babel/plugin-syntax-async-generators@7.8.4':
282
+
resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
283
+
peerDependencies:
284
+
'@babel/core': ^7.0.0-0
285
+
286
+
'@babel/plugin-syntax-class-properties@7.12.13':
287
+
resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
288
+
peerDependencies:
289
+
'@babel/core': ^7.0.0-0
290
+
291
+
'@babel/plugin-syntax-class-static-block@7.14.5':
292
+
resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==}
293
+
engines: {node: '>=6.9.0'}
294
+
peerDependencies:
295
+
'@babel/core': ^7.0.0-0
296
+
297
+
'@babel/plugin-syntax-dynamic-import@7.8.3':
298
+
resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==}
299
+
peerDependencies:
300
+
'@babel/core': ^7.0.0-0
301
+
302
+
'@babel/plugin-syntax-export-namespace-from@7.8.3':
303
+
resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==}
304
+
peerDependencies:
305
+
'@babel/core': ^7.0.0-0
306
+
307
+
'@babel/plugin-syntax-import-assertions@7.24.6':
308
+
resolution: {integrity: sha512-BE6o2BogJKJImTmGpkmOic4V0hlRRxVtzqxiSPa8TIFxyhi4EFjHm08nq1M4STK4RytuLMgnSz0/wfflvGFNOg==}
309
+
engines: {node: '>=6.9.0'}
310
+
peerDependencies:
311
+
'@babel/core': ^7.0.0-0
312
+
313
+
'@babel/plugin-syntax-import-attributes@7.24.6':
314
+
resolution: {integrity: sha512-D+CfsVZousPXIdudSII7RGy52+dYRtbyKAZcvtQKq/NpsivyMVduepzcLqG5pMBugtMdedxdC8Ramdpcne9ZWQ==}
315
+
engines: {node: '>=6.9.0'}
316
+
peerDependencies:
317
+
'@babel/core': ^7.0.0-0
318
+
319
+
'@babel/plugin-syntax-import-meta@7.10.4':
320
+
resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
321
+
peerDependencies:
322
+
'@babel/core': ^7.0.0-0
323
+
324
+
'@babel/plugin-syntax-json-strings@7.8.3':
325
+
resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
326
+
peerDependencies:
327
+
'@babel/core': ^7.0.0-0
328
+
329
+
'@babel/plugin-syntax-jsx@7.24.6':
330
+
resolution: {integrity: sha512-lWfvAIFNWMlCsU0DRUun2GpFwZdGTukLaHJqRh1JRb80NdAP5Sb1HDHB5X9P9OtgZHQl089UzQkpYlBq2VTPRw==}
331
+
engines: {node: '>=6.9.0'}
332
+
peerDependencies:
333
+
'@babel/core': ^7.0.0-0
334
+
335
+
'@babel/plugin-syntax-logical-assignment-operators@7.10.4':
336
+
resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
337
+
peerDependencies:
338
+
'@babel/core': ^7.0.0-0
339
+
340
+
'@babel/plugin-syntax-nullish-coalescing-operator@7.8.3':
341
+
resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
342
+
peerDependencies:
343
+
'@babel/core': ^7.0.0-0
344
+
345
+
'@babel/plugin-syntax-numeric-separator@7.10.4':
346
+
resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
347
+
peerDependencies:
348
+
'@babel/core': ^7.0.0-0
349
+
350
+
'@babel/plugin-syntax-object-rest-spread@7.8.3':
351
+
resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
352
+
peerDependencies:
353
+
'@babel/core': ^7.0.0-0
354
+
355
+
'@babel/plugin-syntax-optional-catch-binding@7.8.3':
356
+
resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
357
+
peerDependencies:
358
+
'@babel/core': ^7.0.0-0
359
+
360
+
'@babel/plugin-syntax-optional-chaining@7.8.3':
361
+
resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
362
+
peerDependencies:
363
+
'@babel/core': ^7.0.0-0
364
+
365
+
'@babel/plugin-syntax-private-property-in-object@7.14.5':
366
+
resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==}
367
+
engines: {node: '>=6.9.0'}
368
+
peerDependencies:
369
+
'@babel/core': ^7.0.0-0
370
+
371
+
'@babel/plugin-syntax-top-level-await@7.14.5':
372
+
resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
373
+
engines: {node: '>=6.9.0'}
374
+
peerDependencies:
375
+
'@babel/core': ^7.0.0-0
376
+
377
+
'@babel/plugin-syntax-unicode-sets-regex@7.18.6':
378
+
resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==}
379
+
engines: {node: '>=6.9.0'}
380
+
peerDependencies:
381
+
'@babel/core': ^7.0.0
382
+
383
+
'@babel/plugin-transform-arrow-functions@7.24.6':
384
+
resolution: {integrity: sha512-jSSSDt4ZidNMggcLx8SaKsbGNEfIl0PHx/4mFEulorE7bpYLbN0d3pDW3eJ7Y5Z3yPhy3L3NaPCYyTUY7TuugQ==}
385
+
engines: {node: '>=6.9.0'}
386
+
peerDependencies:
387
+
'@babel/core': ^7.0.0-0
388
+
389
+
'@babel/plugin-transform-async-generator-functions@7.24.6':
390
+
resolution: {integrity: sha512-VEP2o4iR2DqQU6KPgizTW2mnMx6BG5b5O9iQdrW9HesLkv8GIA8x2daXBQxw1MrsIkFQGA/iJ204CKoQ8UcnAA==}
391
+
engines: {node: '>=6.9.0'}
392
+
peerDependencies:
393
+
'@babel/core': ^7.0.0-0
394
+
395
+
'@babel/plugin-transform-async-to-generator@7.24.6':
396
+
resolution: {integrity: sha512-NTBA2SioI3OsHeIn6sQmhvXleSl9T70YY/hostQLveWs0ic+qvbA3fa0kwAwQ0OA/XGaAerNZRQGJyRfhbJK4g==}
397
+
engines: {node: '>=6.9.0'}
398
+
peerDependencies:
399
+
'@babel/core': ^7.0.0-0
400
+
401
+
'@babel/plugin-transform-block-scoped-functions@7.24.6':
402
+
resolution: {integrity: sha512-XNW7jolYHW9CwORrZgA/97tL/k05qe/HL0z/qqJq1mdWhwwCM6D4BJBV7wAz9HgFziN5dTOG31znkVIzwxv+vw==}
403
+
engines: {node: '>=6.9.0'}
404
+
peerDependencies:
405
+
'@babel/core': ^7.0.0-0
406
+
407
+
'@babel/plugin-transform-block-scoping@7.24.6':
408
+
resolution: {integrity: sha512-S/t1Xh4ehW7sGA7c1j/hiOBLnEYCp/c2sEG4ZkL8kI1xX9tW2pqJTCHKtdhe/jHKt8nG0pFCrDHUXd4DvjHS9w==}
409
+
engines: {node: '>=6.9.0'}
410
+
peerDependencies:
411
+
'@babel/core': ^7.0.0-0
412
+
413
+
'@babel/plugin-transform-class-properties@7.24.6':
414
+
resolution: {integrity: sha512-j6dZ0Z2Z2slWLR3kt9aOmSIrBvnntWjMDN/TVcMPxhXMLmJVqX605CBRlcGI4b32GMbfifTEsdEjGjiE+j/c3A==}
415
+
engines: {node: '>=6.9.0'}
416
+
peerDependencies:
417
+
'@babel/core': ^7.0.0-0
418
+
419
+
'@babel/plugin-transform-class-static-block@7.24.6':
420
+
resolution: {integrity: sha512-1QSRfoPI9RoLRa8Mnakc6v3e0gJxiZQTYrMfLn+mD0sz5+ndSzwymp2hDcYJTyT0MOn0yuWzj8phlIvO72gTHA==}
421
+
engines: {node: '>=6.9.0'}
422
+
peerDependencies:
423
+
'@babel/core': ^7.12.0
424
+
425
+
'@babel/plugin-transform-classes@7.24.6':
426
+
resolution: {integrity: sha512-+fN+NO2gh8JtRmDSOB6gaCVo36ha8kfCW1nMq2Gc0DABln0VcHN4PrALDvF5/diLzIRKptC7z/d7Lp64zk92Fg==}
427
+
engines: {node: '>=6.9.0'}
428
+
peerDependencies:
429
+
'@babel/core': ^7.0.0-0
430
+
431
+
'@babel/plugin-transform-computed-properties@7.24.6':
432
+
resolution: {integrity: sha512-cRzPobcfRP0ZtuIEkA8QzghoUpSB3X3qSH5W2+FzG+VjWbJXExtx0nbRqwumdBN1x/ot2SlTNQLfBCnPdzp6kg==}
433
+
engines: {node: '>=6.9.0'}
434
+
peerDependencies:
435
+
'@babel/core': ^7.0.0-0
436
+
437
+
'@babel/plugin-transform-destructuring@7.24.6':
438
+
resolution: {integrity: sha512-YLW6AE5LQpk5npNXL7i/O+U9CE4XsBCuRPgyjl1EICZYKmcitV+ayuuUGMJm2lC1WWjXYszeTnIxF/dq/GhIZQ==}
439
+
engines: {node: '>=6.9.0'}
440
+
peerDependencies:
441
+
'@babel/core': ^7.0.0-0
442
+
443
+
'@babel/plugin-transform-dotall-regex@7.24.6':
444
+
resolution: {integrity: sha512-rCXPnSEKvkm/EjzOtLoGvKseK+dS4kZwx1HexO3BtRtgL0fQ34awHn34aeSHuXtZY2F8a1X8xqBBPRtOxDVmcA==}
445
+
engines: {node: '>=6.9.0'}
446
+
peerDependencies:
447
+
'@babel/core': ^7.0.0-0
448
+
449
+
'@babel/plugin-transform-duplicate-keys@7.24.6':
450
+
resolution: {integrity: sha512-/8Odwp/aVkZwPFJMllSbawhDAO3UJi65foB00HYnK/uXvvCPm0TAXSByjz1mpRmp0q6oX2SIxpkUOpPFHk7FLA==}
451
+
engines: {node: '>=6.9.0'}
452
+
peerDependencies:
453
+
'@babel/core': ^7.0.0-0
454
+
455
+
'@babel/plugin-transform-dynamic-import@7.24.6':
456
+
resolution: {integrity: sha512-vpq8SSLRTBLOHUZHSnBqVo0AKX3PBaoPs2vVzYVWslXDTDIpwAcCDtfhUcHSQQoYoUvcFPTdC8TZYXu9ZnLT/w==}
457
+
engines: {node: '>=6.9.0'}
458
+
peerDependencies:
459
+
'@babel/core': ^7.0.0-0
460
+
461
+
'@babel/plugin-transform-exponentiation-operator@7.24.6':
462
+
resolution: {integrity: sha512-EemYpHtmz0lHE7hxxxYEuTYOOBZ43WkDgZ4arQ4r+VX9QHuNZC+WH3wUWmRNvR8ECpTRne29aZV6XO22qpOtdA==}
463
+
engines: {node: '>=6.9.0'}
464
+
peerDependencies:
465
+
'@babel/core': ^7.0.0-0
466
+
467
+
'@babel/plugin-transform-export-namespace-from@7.24.6':
468
+
resolution: {integrity: sha512-inXaTM1SVrIxCkIJ5gqWiozHfFMStuGbGJAxZFBoHcRRdDP0ySLb3jH6JOwmfiinPwyMZqMBX+7NBDCO4z0NSA==}
469
+
engines: {node: '>=6.9.0'}
470
+
peerDependencies:
471
+
'@babel/core': ^7.0.0-0
472
+
473
+
'@babel/plugin-transform-for-of@7.24.6':
474
+
resolution: {integrity: sha512-n3Sf72TnqK4nw/jziSqEl1qaWPbCRw2CziHH+jdRYvw4J6yeCzsj4jdw8hIntOEeDGTmHVe2w4MVL44PN0GMzg==}
475
+
engines: {node: '>=6.9.0'}
476
+
peerDependencies:
477
+
'@babel/core': ^7.0.0-0
478
+
479
+
'@babel/plugin-transform-function-name@7.24.6':
480
+
resolution: {integrity: sha512-sOajCu6V0P1KPljWHKiDq6ymgqB+vfo3isUS4McqW1DZtvSVU2v/wuMhmRmkg3sFoq6GMaUUf8W4WtoSLkOV/Q==}
481
+
engines: {node: '>=6.9.0'}
482
+
peerDependencies:
483
+
'@babel/core': ^7.0.0-0
484
+
485
+
'@babel/plugin-transform-json-strings@7.24.6':
486
+
resolution: {integrity: sha512-Uvgd9p2gUnzYJxVdBLcU0KurF8aVhkmVyMKW4MIY1/BByvs3EBpv45q01o7pRTVmTvtQq5zDlytP3dcUgm7v9w==}
487
+
engines: {node: '>=6.9.0'}
488
+
peerDependencies:
489
+
'@babel/core': ^7.0.0-0
490
+
491
+
'@babel/plugin-transform-literals@7.24.6':
492
+
resolution: {integrity: sha512-f2wHfR2HF6yMj+y+/y07+SLqnOSwRp8KYLpQKOzS58XLVlULhXbiYcygfXQxJlMbhII9+yXDwOUFLf60/TL5tw==}
493
+
engines: {node: '>=6.9.0'}
494
+
peerDependencies:
495
+
'@babel/core': ^7.0.0-0
496
+
497
+
'@babel/plugin-transform-logical-assignment-operators@7.24.6':
498
+
resolution: {integrity: sha512-EKaWvnezBCMkRIHxMJSIIylzhqK09YpiJtDbr2wsXTwnO0TxyjMUkaw4RlFIZMIS0iDj0KyIg7H7XCguHu/YDA==}
499
+
engines: {node: '>=6.9.0'}
500
+
peerDependencies:
501
+
'@babel/core': ^7.0.0-0
502
+
503
+
'@babel/plugin-transform-member-expression-literals@7.24.6':
504
+
resolution: {integrity: sha512-9g8iV146szUo5GWgXpRbq/GALTnY+WnNuRTuRHWWFfWGbP9ukRL0aO/jpu9dmOPikclkxnNsjY8/gsWl6bmZJQ==}
505
+
engines: {node: '>=6.9.0'}
506
+
peerDependencies:
507
+
'@babel/core': ^7.0.0-0
508
+
509
+
'@babel/plugin-transform-modules-amd@7.24.6':
510
+
resolution: {integrity: sha512-eAGogjZgcwqAxhyFgqghvoHRr+EYRQPFjUXrTYKBRb5qPnAVxOOglaxc4/byHqjvq/bqO2F3/CGwTHsgKJYHhQ==}
511
+
engines: {node: '>=6.9.0'}
512
+
peerDependencies:
513
+
'@babel/core': ^7.0.0-0
514
+
515
+
'@babel/plugin-transform-modules-commonjs@7.24.6':
516
+
resolution: {integrity: sha512-JEV8l3MHdmmdb7S7Cmx6rbNEjRCgTQMZxllveHO0mx6uiclB0NflCawlQQ6+o5ZrwjUBYPzHm2XoK4wqGVUFuw==}
517
+
engines: {node: '>=6.9.0'}
518
+
peerDependencies:
519
+
'@babel/core': ^7.0.0-0
520
+
521
+
'@babel/plugin-transform-modules-systemjs@7.24.6':
522
+
resolution: {integrity: sha512-xg1Z0J5JVYxtpX954XqaaAT6NpAY6LtZXvYFCJmGFJWwtlz2EmJoR8LycFRGNE8dBKizGWkGQZGegtkV8y8s+w==}
523
+
engines: {node: '>=6.9.0'}
524
+
peerDependencies:
525
+
'@babel/core': ^7.0.0-0
526
+
527
+
'@babel/plugin-transform-modules-umd@7.24.6':
528
+
resolution: {integrity: sha512-esRCC/KsSEUvrSjv5rFYnjZI6qv4R1e/iHQrqwbZIoRJqk7xCvEUiN7L1XrmW5QSmQe3n1XD88wbgDTWLbVSyg==}
529
+
engines: {node: '>=6.9.0'}
530
+
peerDependencies:
531
+
'@babel/core': ^7.0.0-0
532
+
533
+
'@babel/plugin-transform-named-capturing-groups-regex@7.24.6':
534
+
resolution: {integrity: sha512-6DneiCiu91wm3YiNIGDWZsl6GfTTbspuj/toTEqLh9d4cx50UIzSdg+T96p8DuT7aJOBRhFyaE9ZvTHkXrXr6Q==}
535
+
engines: {node: '>=6.9.0'}
536
+
peerDependencies:
537
+
'@babel/core': ^7.0.0
538
+
539
+
'@babel/plugin-transform-new-target@7.24.6':
540
+
resolution: {integrity: sha512-f8liz9JG2Va8A4J5ZBuaSdwfPqN6axfWRK+y66fjKYbwf9VBLuq4WxtinhJhvp1w6lamKUwLG0slK2RxqFgvHA==}
541
+
engines: {node: '>=6.9.0'}
542
+
peerDependencies:
543
+
'@babel/core': ^7.0.0-0
544
+
545
+
'@babel/plugin-transform-nullish-coalescing-operator@7.24.6':
546
+
resolution: {integrity: sha512-+QlAiZBMsBK5NqrBWFXCYeXyiU1y7BQ/OYaiPAcQJMomn5Tyg+r5WuVtyEuvTbpV7L25ZSLfE+2E9ywj4FD48A==}
547
+
engines: {node: '>=6.9.0'}
548
+
peerDependencies:
549
+
'@babel/core': ^7.0.0-0
550
+
551
+
'@babel/plugin-transform-numeric-separator@7.24.6':
552
+
resolution: {integrity: sha512-6voawq8T25Jvvnc4/rXcWZQKKxUNZcKMS8ZNrjxQqoRFernJJKjE3s18Qo6VFaatG5aiX5JV1oPD7DbJhn0a4Q==}
553
+
engines: {node: '>=6.9.0'}
554
+
peerDependencies:
555
+
'@babel/core': ^7.0.0-0
556
+
557
+
'@babel/plugin-transform-object-rest-spread@7.24.6':
558
+
resolution: {integrity: sha512-OKmi5wiMoRW5Smttne7BwHM8s/fb5JFs+bVGNSeHWzwZkWXWValR1M30jyXo1s/RaqgwwhEC62u4rFH/FBcBPg==}
559
+
engines: {node: '>=6.9.0'}
560
+
peerDependencies:
561
+
'@babel/core': ^7.0.0-0
562
+
563
+
'@babel/plugin-transform-object-super@7.24.6':
564
+
resolution: {integrity: sha512-N/C76ihFKlZgKfdkEYKtaRUtXZAgK7sOY4h2qrbVbVTXPrKGIi8aww5WGe/+Wmg8onn8sr2ut6FXlsbu/j6JHg==}
565
+
engines: {node: '>=6.9.0'}
566
+
peerDependencies:
567
+
'@babel/core': ^7.0.0-0
568
+
569
+
'@babel/plugin-transform-optional-catch-binding@7.24.6':
570
+
resolution: {integrity: sha512-L5pZ+b3O1mSzJ71HmxSCmTVd03VOT2GXOigug6vDYJzE5awLI7P1g0wFcdmGuwSDSrQ0L2rDOe/hHws8J1rv3w==}
571
+
engines: {node: '>=6.9.0'}
572
+
peerDependencies:
573
+
'@babel/core': ^7.0.0-0
574
+
575
+
'@babel/plugin-transform-optional-chaining@7.24.6':
576
+
resolution: {integrity: sha512-cHbqF6l1QP11OkYTYQ+hhVx1E017O5ZcSPXk9oODpqhcAD1htsWG2NpHrrhthEO2qZomLK0FXS+u7NfrkF5aOQ==}
577
+
engines: {node: '>=6.9.0'}
578
+
peerDependencies:
579
+
'@babel/core': ^7.0.0-0
580
+
581
+
'@babel/plugin-transform-parameters@7.24.6':
582
+
resolution: {integrity: sha512-ST7guE8vLV+vI70wmAxuZpIKzVjvFX9Qs8bl5w6tN/6gOypPWUmMQL2p7LJz5E63vEGrDhAiYetniJFyBH1RkA==}
583
+
engines: {node: '>=6.9.0'}
584
+
peerDependencies:
585
+
'@babel/core': ^7.0.0-0
586
+
587
+
'@babel/plugin-transform-private-methods@7.24.6':
588
+
resolution: {integrity: sha512-T9LtDI0BgwXOzyXrvgLTT8DFjCC/XgWLjflczTLXyvxbnSR/gpv0hbmzlHE/kmh9nOvlygbamLKRo6Op4yB6aw==}
589
+
engines: {node: '>=6.9.0'}
590
+
peerDependencies:
591
+
'@babel/core': ^7.0.0-0
592
+
593
+
'@babel/plugin-transform-private-property-in-object@7.24.6':
594
+
resolution: {integrity: sha512-Qu/ypFxCY5NkAnEhCF86Mvg3NSabKsh/TPpBVswEdkGl7+FbsYHy1ziRqJpwGH4thBdQHh8zx+z7vMYmcJ7iaQ==}
595
+
engines: {node: '>=6.9.0'}
596
+
peerDependencies:
597
+
'@babel/core': ^7.0.0-0
598
+
599
+
'@babel/plugin-transform-property-literals@7.24.6':
600
+
resolution: {integrity: sha512-oARaglxhRsN18OYsnPTpb8TcKQWDYNsPNmTnx5++WOAsUJ0cSC/FZVlIJCKvPbU4yn/UXsS0551CFKJhN0CaMw==}
601
+
engines: {node: '>=6.9.0'}
602
+
peerDependencies:
603
+
'@babel/core': ^7.0.0-0
604
+
605
+
'@babel/plugin-transform-regenerator@7.24.6':
606
+
resolution: {integrity: sha512-SMDxO95I8WXRtXhTAc8t/NFQUT7VYbIWwJCJgEli9ml4MhqUMh4S6hxgH6SmAC3eAQNWCDJFxcFeEt9w2sDdXg==}
607
+
engines: {node: '>=6.9.0'}
608
+
peerDependencies:
609
+
'@babel/core': ^7.0.0-0
610
+
611
+
'@babel/plugin-transform-reserved-words@7.24.6':
612
+
resolution: {integrity: sha512-DcrgFXRRlK64dGE0ZFBPD5egM2uM8mgfrvTMOSB2yKzOtjpGegVYkzh3s1zZg1bBck3nkXiaOamJUqK3Syk+4A==}
613
+
engines: {node: '>=6.9.0'}
614
+
peerDependencies:
615
+
'@babel/core': ^7.0.0-0
616
+
617
+
'@babel/plugin-transform-shorthand-properties@7.24.6':
618
+
resolution: {integrity: sha512-xnEUvHSMr9eOWS5Al2YPfc32ten7CXdH7Zwyyk7IqITg4nX61oHj+GxpNvl+y5JHjfN3KXE2IV55wAWowBYMVw==}
619
+
engines: {node: '>=6.9.0'}
620
+
peerDependencies:
621
+
'@babel/core': ^7.0.0-0
622
+
623
+
'@babel/plugin-transform-spread@7.24.6':
624
+
resolution: {integrity: sha512-h/2j7oIUDjS+ULsIrNZ6/TKG97FgmEk1PXryk/HQq6op4XUUUwif2f69fJrzK0wza2zjCS1xhXmouACaWV5uPA==}
625
+
engines: {node: '>=6.9.0'}
626
+
peerDependencies:
627
+
'@babel/core': ^7.0.0-0
628
+
629
+
'@babel/plugin-transform-sticky-regex@7.24.6':
630
+
resolution: {integrity: sha512-fN8OcTLfGmYv7FnDrsjodYBo1DhPL3Pze/9mIIE2MGCT1KgADYIOD7rEglpLHZj8PZlC/JFX5WcD+85FLAQusw==}
631
+
engines: {node: '>=6.9.0'}
632
+
peerDependencies:
633
+
'@babel/core': ^7.0.0-0
634
+
635
+
'@babel/plugin-transform-template-literals@7.24.6':
636
+
resolution: {integrity: sha512-BJbEqJIcKwrqUP+KfUIkxz3q8VzXe2R8Wv8TaNgO1cx+nNavxn/2+H8kp9tgFSOL6wYPPEgFvU6IKS4qoGqhmg==}
637
+
engines: {node: '>=6.9.0'}
638
+
peerDependencies:
639
+
'@babel/core': ^7.0.0-0
640
+
641
+
'@babel/plugin-transform-typeof-symbol@7.24.6':
642
+
resolution: {integrity: sha512-IshCXQ+G9JIFJI7bUpxTE/oA2lgVLAIK8q1KdJNoPXOpvRaNjMySGuvLfBw/Xi2/1lLo953uE8hyYSDW3TSYig==}
643
+
engines: {node: '>=6.9.0'}
644
+
peerDependencies:
645
+
'@babel/core': ^7.0.0-0
646
+
647
+
'@babel/plugin-transform-unicode-escapes@7.24.6':
648
+
resolution: {integrity: sha512-bKl3xxcPbkQQo5eX9LjjDpU2xYHeEeNQbOhj0iPvetSzA+Tu9q/o5lujF4Sek60CM6MgYvOS/DJuwGbiEYAnLw==}
649
+
engines: {node: '>=6.9.0'}
650
+
peerDependencies:
651
+
'@babel/core': ^7.0.0-0
652
+
653
+
'@babel/plugin-transform-unicode-property-regex@7.24.6':
654
+
resolution: {integrity: sha512-8EIgImzVUxy15cZiPii9GvLZwsy7Vxc+8meSlR3cXFmBIl5W5Tn9LGBf7CDKkHj4uVfNXCJB8RsVfnmY61iedA==}
655
+
engines: {node: '>=6.9.0'}
656
+
peerDependencies:
657
+
'@babel/core': ^7.0.0-0
658
+
659
+
'@babel/plugin-transform-unicode-regex@7.24.6':
660
+
resolution: {integrity: sha512-pssN6ExsvxaKU638qcWb81RrvvgZom3jDgU/r5xFZ7TONkZGFf4MhI2ltMb8OcQWhHyxgIavEU+hgqtbKOmsPA==}
661
+
engines: {node: '>=6.9.0'}
662
+
peerDependencies:
663
+
'@babel/core': ^7.0.0-0
664
+
665
+
'@babel/plugin-transform-unicode-sets-regex@7.24.6':
666
+
resolution: {integrity: sha512-quiMsb28oXWIDK0gXLALOJRXLgICLiulqdZGOaPPd0vRT7fQp74NtdADAVu+D8s00C+0Xs0MxVP0VKF/sZEUgw==}
667
+
engines: {node: '>=6.9.0'}
668
+
peerDependencies:
669
+
'@babel/core': ^7.0.0
670
+
671
+
'@babel/preset-env@7.24.6':
672
+
resolution: {integrity: sha512-CrxEAvN7VxfjOG8JNF2Y/eMqMJbZPZ185amwGUBp8D9USK90xQmv7dLdFSa+VbD7fdIqcy/Mfv7WtzG8+/qxKg==}
673
+
engines: {node: '>=6.9.0'}
674
+
peerDependencies:
675
+
'@babel/core': ^7.0.0-0
676
+
677
+
'@babel/preset-modules@0.1.6-no-external-plugins':
678
+
resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==}
679
+
peerDependencies:
680
+
'@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0
681
+
682
+
'@babel/regjsgen@0.8.0':
683
+
resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==}
684
+
685
+
'@babel/runtime@7.24.6':
686
+
resolution: {integrity: sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==}
687
+
engines: {node: '>=6.9.0'}
688
+
689
+
'@babel/template@7.24.6':
690
+
resolution: {integrity: sha512-3vgazJlLwNXi9jhrR1ef8qiB65L1RK90+lEQwv4OxveHnqC3BfmnHdgySwRLzf6akhlOYenT+b7AfWq+a//AHw==}
691
+
engines: {node: '>=6.9.0'}
692
+
693
+
'@babel/traverse@7.24.6':
694
+
resolution: {integrity: sha512-OsNjaJwT9Zn8ozxcfoBc+RaHdj3gFmCmYoQLUII1o6ZrUwku0BMg80FoOTPx+Gi6XhcQxAYE4xyjPTo4SxEQqw==}
695
+
engines: {node: '>=6.9.0'}
696
+
697
+
'@babel/types@7.24.6':
698
+
resolution: {integrity: sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ==}
699
+
engines: {node: '>=6.9.0'}
700
+
701
+
'@esbuild/aix-ppc64@0.20.2':
702
+
resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==}
703
+
engines: {node: '>=12'}
704
+
cpu: [ppc64]
705
+
os: [aix]
706
+
707
+
'@esbuild/android-arm64@0.20.2':
708
+
resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==}
709
+
engines: {node: '>=12'}
710
+
cpu: [arm64]
711
+
os: [android]
712
+
713
+
'@esbuild/android-arm@0.20.2':
714
+
resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==}
715
+
engines: {node: '>=12'}
716
+
cpu: [arm]
717
+
os: [android]
718
+
719
+
'@esbuild/android-x64@0.20.2':
720
+
resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==}
721
+
engines: {node: '>=12'}
722
+
cpu: [x64]
723
+
os: [android]
724
+
725
+
'@esbuild/darwin-arm64@0.20.2':
726
+
resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==}
727
+
engines: {node: '>=12'}
728
+
cpu: [arm64]
729
+
os: [darwin]
730
+
731
+
'@esbuild/darwin-x64@0.20.2':
732
+
resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==}
733
+
engines: {node: '>=12'}
734
+
cpu: [x64]
735
+
os: [darwin]
736
+
737
+
'@esbuild/freebsd-arm64@0.20.2':
738
+
resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==}
739
+
engines: {node: '>=12'}
740
+
cpu: [arm64]
741
+
os: [freebsd]
742
+
743
+
'@esbuild/freebsd-x64@0.20.2':
744
+
resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==}
745
+
engines: {node: '>=12'}
746
+
cpu: [x64]
747
+
os: [freebsd]
748
+
749
+
'@esbuild/linux-arm64@0.20.2':
750
+
resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==}
751
+
engines: {node: '>=12'}
752
+
cpu: [arm64]
753
+
os: [linux]
754
+
755
+
'@esbuild/linux-arm@0.20.2':
756
+
resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==}
757
+
engines: {node: '>=12'}
758
+
cpu: [arm]
759
+
os: [linux]
760
+
761
+
'@esbuild/linux-ia32@0.20.2':
762
+
resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==}
763
+
engines: {node: '>=12'}
764
+
cpu: [ia32]
765
+
os: [linux]
766
+
767
+
'@esbuild/linux-loong64@0.20.2':
768
+
resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==}
769
+
engines: {node: '>=12'}
770
+
cpu: [loong64]
771
+
os: [linux]
772
+
773
+
'@esbuild/linux-mips64el@0.20.2':
774
+
resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==}
775
+
engines: {node: '>=12'}
776
+
cpu: [mips64el]
777
+
os: [linux]
778
+
779
+
'@esbuild/linux-ppc64@0.20.2':
780
+
resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==}
781
+
engines: {node: '>=12'}
782
+
cpu: [ppc64]
783
+
os: [linux]
784
+
785
+
'@esbuild/linux-riscv64@0.20.2':
786
+
resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==}
787
+
engines: {node: '>=12'}
788
+
cpu: [riscv64]
789
+
os: [linux]
790
+
791
+
'@esbuild/linux-s390x@0.20.2':
792
+
resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==}
793
+
engines: {node: '>=12'}
794
+
cpu: [s390x]
795
+
os: [linux]
796
+
797
+
'@esbuild/linux-x64@0.20.2':
798
+
resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==}
799
+
engines: {node: '>=12'}
800
+
cpu: [x64]
801
+
os: [linux]
802
+
803
+
'@esbuild/netbsd-x64@0.20.2':
804
+
resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==}
805
+
engines: {node: '>=12'}
806
+
cpu: [x64]
807
+
os: [netbsd]
808
+
809
+
'@esbuild/openbsd-x64@0.20.2':
810
+
resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==}
811
+
engines: {node: '>=12'}
812
+
cpu: [x64]
813
+
os: [openbsd]
814
+
815
+
'@esbuild/sunos-x64@0.20.2':
816
+
resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==}
817
+
engines: {node: '>=12'}
818
+
cpu: [x64]
819
+
os: [sunos]
820
+
821
+
'@esbuild/win32-arm64@0.20.2':
822
+
resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==}
823
+
engines: {node: '>=12'}
824
+
cpu: [arm64]
825
+
os: [win32]
826
+
827
+
'@esbuild/win32-ia32@0.20.2':
828
+
resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==}
829
+
engines: {node: '>=12'}
830
+
cpu: [ia32]
831
+
os: [win32]
832
+
833
+
'@esbuild/win32-x64@0.20.2':
834
+
resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==}
835
+
engines: {node: '>=12'}
836
+
cpu: [x64]
837
+
os: [win32]
838
+
839
+
'@externdefs/solid-freeze@0.1.1':
840
+
resolution: {integrity: sha512-duvZBfJB9oOLphx04ckKF534hP186xIBFaw4GHJ5fGeZY5syZs59UeumV5NC6aiEU9hVhAFMOnDDGkQrFqHrnQ==}
841
+
peerDependencies:
842
+
solid-js: ^1.8.5
843
+
844
+
'@externdefs/solid-query@0.1.4':
845
+
resolution: {integrity: sha512-dK5xls7YRkA+XQ8XgMFyASf8hIcz47KMQ/dlfRZhpzFLSKnWd29HQcLBGts9bQrajXai+x4x5u2uY2RKlz3+dw==}
846
+
peerDependencies:
847
+
solid-js: ^1.8.5
848
+
849
+
'@floating-ui/core@1.6.2':
850
+
resolution: {integrity: sha512-+2XpQV9LLZeanU4ZevzRnGFg2neDeKHgFLjP6YLW+tly0IvrhqT4u8enLGjLH3qeh85g19xY5rsAusfwTdn5lg==}
851
+
852
+
'@floating-ui/dom@1.6.5':
853
+
resolution: {integrity: sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==}
854
+
855
+
'@floating-ui/utils@0.2.2':
856
+
resolution: {integrity: sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==}
857
+
858
+
'@isaacs/cliui@8.0.2':
859
+
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
860
+
engines: {node: '>=12'}
861
+
862
+
'@jridgewell/gen-mapping@0.3.5':
863
+
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
864
+
engines: {node: '>=6.0.0'}
865
+
866
+
'@jridgewell/resolve-uri@3.1.2':
867
+
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
868
+
engines: {node: '>=6.0.0'}
869
+
870
+
'@jridgewell/set-array@1.2.1':
871
+
resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
872
+
engines: {node: '>=6.0.0'}
873
+
874
+
'@jridgewell/source-map@0.3.6':
875
+
resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==}
876
+
877
+
'@jridgewell/sourcemap-codec@1.4.15':
878
+
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
879
+
880
+
'@jridgewell/trace-mapping@0.3.25':
881
+
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
882
+
883
+
'@jsr/mary__bluesky-client@0.5.23':
884
+
resolution: {integrity: sha512-k1sZVbRvZ//kx3Fq/tEd6W5pAf3IrdE1cUSXl6lqem4cYGv67NrRHMdz51zVIud00BfwVLxHbb97cWeDHDm16Q==, tarball: https://npm.jsr.io/~/11/@jsr/mary__bluesky-client/0.5.23.tgz}
885
+
886
+
'@jsr/mary__events@0.1.0':
887
+
resolution: {integrity: sha512-oS6jVOaXTaNEa6avRncwrEtUYaBKrq/HEybPa9Z3aoeMs+RSly0vn0KcOj/fy2H6iTBkeh3wa8+/9nFjhKyKIg==, tarball: https://npm.jsr.io/~/11/@jsr/mary__events/0.1.0.tgz}
888
+
889
+
'@nodelib/fs.scandir@2.1.5':
890
+
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
891
+
engines: {node: '>= 8'}
892
+
893
+
'@nodelib/fs.stat@2.0.5':
894
+
resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
895
+
engines: {node: '>= 8'}
896
+
897
+
'@nodelib/fs.walk@1.2.8':
898
+
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
899
+
engines: {node: '>= 8'}
900
+
901
+
'@pkgjs/parseargs@0.11.0':
902
+
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
903
+
engines: {node: '>=14'}
904
+
905
+
'@rollup/plugin-babel@5.3.1':
906
+
resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==}
907
+
engines: {node: '>= 10.0.0'}
908
+
peerDependencies:
909
+
'@babel/core': ^7.0.0
910
+
'@types/babel__core': ^7.1.9
911
+
rollup: ^1.20.0||^2.0.0
912
+
peerDependenciesMeta:
913
+
'@types/babel__core':
914
+
optional: true
915
+
916
+
'@rollup/plugin-node-resolve@15.2.3':
917
+
resolution: {integrity: sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==}
918
+
engines: {node: '>=14.0.0'}
919
+
peerDependencies:
920
+
rollup: ^2.78.0||^3.0.0||^4.0.0
921
+
peerDependenciesMeta:
922
+
rollup:
923
+
optional: true
924
+
925
+
'@rollup/plugin-replace@2.4.2':
926
+
resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==}
927
+
peerDependencies:
928
+
rollup: ^1.20.0 || ^2.0.0
929
+
930
+
'@rollup/plugin-terser@0.4.4':
931
+
resolution: {integrity: sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==}
932
+
engines: {node: '>=14.0.0'}
933
+
peerDependencies:
934
+
rollup: ^2.0.0||^3.0.0||^4.0.0
935
+
peerDependenciesMeta:
936
+
rollup:
937
+
optional: true
938
+
939
+
'@rollup/pluginutils@3.1.0':
940
+
resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==}
941
+
engines: {node: '>= 8.0.0'}
942
+
peerDependencies:
943
+
rollup: ^1.20.0||^2.0.0
944
+
945
+
'@rollup/pluginutils@5.1.0':
946
+
resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
947
+
engines: {node: '>=14.0.0'}
948
+
peerDependencies:
949
+
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
950
+
peerDependenciesMeta:
951
+
rollup:
952
+
optional: true
953
+
954
+
'@rollup/rollup-android-arm-eabi@4.18.0':
955
+
resolution: {integrity: sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==}
956
+
cpu: [arm]
957
+
os: [android]
958
+
959
+
'@rollup/rollup-android-arm64@4.18.0':
960
+
resolution: {integrity: sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==}
961
+
cpu: [arm64]
962
+
os: [android]
963
+
964
+
'@rollup/rollup-darwin-arm64@4.18.0':
965
+
resolution: {integrity: sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==}
966
+
cpu: [arm64]
967
+
os: [darwin]
968
+
969
+
'@rollup/rollup-darwin-x64@4.18.0':
970
+
resolution: {integrity: sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==}
971
+
cpu: [x64]
972
+
os: [darwin]
973
+
974
+
'@rollup/rollup-linux-arm-gnueabihf@4.18.0':
975
+
resolution: {integrity: sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==}
976
+
cpu: [arm]
977
+
os: [linux]
978
+
979
+
'@rollup/rollup-linux-arm-musleabihf@4.18.0':
980
+
resolution: {integrity: sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==}
981
+
cpu: [arm]
982
+
os: [linux]
983
+
984
+
'@rollup/rollup-linux-arm64-gnu@4.18.0':
985
+
resolution: {integrity: sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==}
986
+
cpu: [arm64]
987
+
os: [linux]
988
+
989
+
'@rollup/rollup-linux-arm64-musl@4.18.0':
990
+
resolution: {integrity: sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==}
991
+
cpu: [arm64]
992
+
os: [linux]
993
+
994
+
'@rollup/rollup-linux-powerpc64le-gnu@4.18.0':
995
+
resolution: {integrity: sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==}
996
+
cpu: [ppc64]
997
+
os: [linux]
998
+
999
+
'@rollup/rollup-linux-riscv64-gnu@4.18.0':
1000
+
resolution: {integrity: sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==}
1001
+
cpu: [riscv64]
1002
+
os: [linux]
1003
+
1004
+
'@rollup/rollup-linux-s390x-gnu@4.18.0':
1005
+
resolution: {integrity: sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==}
1006
+
cpu: [s390x]
1007
+
os: [linux]
1008
+
1009
+
'@rollup/rollup-linux-x64-gnu@4.18.0':
1010
+
resolution: {integrity: sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==}
1011
+
cpu: [x64]
1012
+
os: [linux]
1013
+
1014
+
'@rollup/rollup-linux-x64-musl@4.18.0':
1015
+
resolution: {integrity: sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==}
1016
+
cpu: [x64]
1017
+
os: [linux]
1018
+
1019
+
'@rollup/rollup-win32-arm64-msvc@4.18.0':
1020
+
resolution: {integrity: sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==}
1021
+
cpu: [arm64]
1022
+
os: [win32]
1023
+
1024
+
'@rollup/rollup-win32-ia32-msvc@4.18.0':
1025
+
resolution: {integrity: sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==}
1026
+
cpu: [ia32]
1027
+
os: [win32]
1028
+
1029
+
'@rollup/rollup-win32-x64-msvc@4.18.0':
1030
+
resolution: {integrity: sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==}
1031
+
cpu: [x64]
1032
+
os: [win32]
1033
+
1034
+
'@surma/rollup-plugin-off-main-thread@2.2.3':
1035
+
resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==}
1036
+
1037
+
'@tanstack/query-core@5.17.19':
1038
+
resolution: {integrity: sha512-Lzw8FUtnLCc9Jwz0sw9xOjZB+/mCCmJev38v2wHMUl/ioXNIhnNWeMxu0NKUjIhAd62IRB3eAtvxAGDJ55UkyA==}
1039
+
1040
+
'@types/babel__core@7.20.5':
1041
+
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
1042
+
1043
+
'@types/babel__generator@7.6.8':
1044
+
resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==}
1045
+
1046
+
'@types/babel__template@7.4.4':
1047
+
resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
1048
+
1049
+
'@types/babel__traverse@7.20.6':
1050
+
resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==}
1051
+
1052
+
'@types/dom-close-watcher@1.0.0':
1053
+
resolution: {integrity: sha512-7pL0By56sVVGMSJ3HdSY+u08Id0ljStCaf1VnGFxwfpuNdA0HMz0sl2J24eSi9M6ptl9ySkVK35jF75Fn8trUg==}
1054
+
1055
+
'@types/estree@0.0.39':
1056
+
resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==}
1057
+
1058
+
'@types/estree@1.0.5':
1059
+
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
1060
+
1061
+
'@types/resolve@1.20.2':
1062
+
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
1063
+
1064
+
'@types/trusted-types@2.0.7':
1065
+
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
1066
+
1067
+
acorn@8.11.3:
1068
+
resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==}
1069
+
engines: {node: '>=0.4.0'}
1070
+
hasBin: true
1071
+
1072
+
ajv@8.14.0:
1073
+
resolution: {integrity: sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==}
1074
+
1075
+
ansi-regex@5.0.1:
1076
+
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
1077
+
engines: {node: '>=8'}
1078
+
1079
+
ansi-regex@6.0.1:
1080
+
resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
1081
+
engines: {node: '>=12'}
1082
+
1083
+
ansi-styles@3.2.1:
1084
+
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
1085
+
engines: {node: '>=4'}
1086
+
1087
+
ansi-styles@4.3.0:
1088
+
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
1089
+
engines: {node: '>=8'}
1090
+
1091
+
ansi-styles@6.2.1:
1092
+
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
1093
+
engines: {node: '>=12'}
1094
+
1095
+
any-promise@1.3.0:
1096
+
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
1097
+
1098
+
anymatch@3.1.3:
1099
+
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
1100
+
engines: {node: '>= 8'}
1101
+
1102
+
arg@5.0.2:
1103
+
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
1104
+
1105
+
array-buffer-byte-length@1.0.1:
1106
+
resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==}
1107
+
engines: {node: '>= 0.4'}
1108
+
1109
+
arraybuffer.prototype.slice@1.0.3:
1110
+
resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==}
1111
+
engines: {node: '>= 0.4'}
1112
+
1113
+
async@3.2.5:
1114
+
resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==}
1115
+
1116
+
at-least-node@1.0.0:
1117
+
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
1118
+
engines: {node: '>= 4.0.0'}
1119
+
1120
+
autoprefixer@10.4.19:
1121
+
resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==}
1122
+
engines: {node: ^10 || ^12 || >=14}
1123
+
hasBin: true
1124
+
peerDependencies:
1125
+
postcss: ^8.1.0
1126
+
1127
+
available-typed-arrays@1.0.7:
1128
+
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
1129
+
engines: {node: '>= 0.4'}
1130
+
1131
+
babel-plugin-jsx-dom-expressions@0.37.21:
1132
+
resolution: {integrity: sha512-WbQo1NQ241oki8bYasVzkMXOTSIri5GO/K47rYJb2ZBh8GaPUEWiWbMV3KwXz+96eU2i54N6ThzjQG/f5n8Azw==}
1133
+
peerDependencies:
1134
+
'@babel/core': ^7.20.12
1135
+
1136
+
babel-plugin-polyfill-corejs2@0.4.11:
1137
+
resolution: {integrity: sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==}
1138
+
peerDependencies:
1139
+
'@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
1140
+
1141
+
babel-plugin-polyfill-corejs3@0.10.4:
1142
+
resolution: {integrity: sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==}
1143
+
peerDependencies:
1144
+
'@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
1145
+
1146
+
babel-plugin-polyfill-regenerator@0.6.2:
1147
+
resolution: {integrity: sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==}
1148
+
peerDependencies:
1149
+
'@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
1150
+
1151
+
babel-preset-solid@1.8.17:
1152
+
resolution: {integrity: sha512-s/FfTZOeds0hYxYqce90Jb+0ycN2lrzC7VP1k1JIn3wBqcaexDKdYi6xjB+hMNkL+Q6HobKbwsriqPloasR9LA==}
1153
+
peerDependencies:
1154
+
'@babel/core': ^7.0.0
1155
+
1156
+
balanced-match@1.0.2:
1157
+
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
1158
+
1159
+
binary-extensions@2.3.0:
1160
+
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
1161
+
engines: {node: '>=8'}
1162
+
1163
+
brace-expansion@1.1.11:
1164
+
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
1165
+
1166
+
brace-expansion@2.0.1:
1167
+
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
1168
+
1169
+
braces@3.0.3:
1170
+
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
1171
+
engines: {node: '>=8'}
1172
+
1173
+
browserslist@4.23.0:
1174
+
resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==}
1175
+
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
1176
+
hasBin: true
1177
+
1178
+
buffer-from@1.1.2:
1179
+
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
1180
+
1181
+
builtin-modules@3.3.0:
1182
+
resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==}
1183
+
engines: {node: '>=6'}
1184
+
1185
+
call-bind@1.0.7:
1186
+
resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
1187
+
engines: {node: '>= 0.4'}
1188
+
1189
+
camelcase-css@2.0.1:
1190
+
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
1191
+
engines: {node: '>= 6'}
1192
+
1193
+
caniuse-lite@1.0.30001623:
1194
+
resolution: {integrity: sha512-X/XhAVKlpIxWPpgRTnlgZssJrF0m6YtRA0QDWgsBNT12uZM6LPRydR7ip405Y3t1LamD8cP2TZFEDZFBf5ApcA==}
1195
+
1196
+
cborg@4.0.7:
1197
+
resolution: {integrity: sha512-5h2n7973T4dkY2XLfHpwYR9IjeDSfolZibYtb8clW53BvvgTyq+X2EtwrjfhTAETrwaQOX4+lRua14/XjbZHaQ==}
1198
+
hasBin: true
1199
+
1200
+
chalk@2.4.2:
1201
+
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
1202
+
engines: {node: '>=4'}
1203
+
1204
+
chalk@4.1.2:
1205
+
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
1206
+
engines: {node: '>=10'}
1207
+
1208
+
chokidar@3.6.0:
1209
+
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
1210
+
engines: {node: '>= 8.10.0'}
1211
+
1212
+
color-convert@1.9.3:
1213
+
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
1214
+
1215
+
color-convert@2.0.1:
1216
+
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
1217
+
engines: {node: '>=7.0.0'}
1218
+
1219
+
color-name@1.1.3:
1220
+
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
1221
+
1222
+
color-name@1.1.4:
1223
+
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
1224
+
1225
+
commander@2.20.3:
1226
+
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
1227
+
1228
+
commander@4.1.1:
1229
+
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
1230
+
engines: {node: '>= 6'}
1231
+
1232
+
common-tags@1.8.2:
1233
+
resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==}
1234
+
engines: {node: '>=4.0.0'}
1235
+
1236
+
concat-map@0.0.1:
1237
+
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
1238
+
1239
+
convert-source-map@2.0.0:
1240
+
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
1241
+
1242
+
core-js-compat@3.37.1:
1243
+
resolution: {integrity: sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==}
1244
+
1245
+
cross-spawn@7.0.3:
1246
+
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
1247
+
engines: {node: '>= 8'}
1248
+
1249
+
crypto-random-string@2.0.0:
1250
+
resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==}
1251
+
engines: {node: '>=8'}
1252
+
1253
+
cssesc@3.0.0:
1254
+
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
1255
+
engines: {node: '>=4'}
1256
+
hasBin: true
1257
+
1258
+
csstype@3.1.3:
1259
+
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
1260
+
1261
+
data-view-buffer@1.0.1:
1262
+
resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==}
1263
+
engines: {node: '>= 0.4'}
1264
+
1265
+
data-view-byte-length@1.0.1:
1266
+
resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==}
1267
+
engines: {node: '>= 0.4'}
1268
+
1269
+
data-view-byte-offset@1.0.0:
1270
+
resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==}
1271
+
engines: {node: '>= 0.4'}
1272
+
1273
+
debug@4.3.4:
1274
+
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
1275
+
engines: {node: '>=6.0'}
1276
+
peerDependencies:
1277
+
supports-color: '*'
1278
+
peerDependenciesMeta:
1279
+
supports-color:
1280
+
optional: true
1281
+
1282
+
deepmerge@4.3.1:
1283
+
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
1284
+
engines: {node: '>=0.10.0'}
1285
+
1286
+
define-data-property@1.1.4:
1287
+
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
1288
+
engines: {node: '>= 0.4'}
1289
+
1290
+
define-properties@1.2.1:
1291
+
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
1292
+
engines: {node: '>= 0.4'}
1293
+
1294
+
didyoumean@1.2.2:
1295
+
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
1296
+
1297
+
dlv@1.1.3:
1298
+
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
1299
+
1300
+
eastasianwidth@0.2.0:
1301
+
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
1302
+
1303
+
ejs@3.1.10:
1304
+
resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==}
1305
+
engines: {node: '>=0.10.0'}
1306
+
hasBin: true
1307
+
1308
+
electron-to-chromium@1.4.783:
1309
+
resolution: {integrity: sha512-bT0jEz/Xz1fahQpbZ1D7LgmPYZ3iHVY39NcWWro1+hA2IvjiPeaXtfSqrQ+nXjApMvQRE2ASt1itSLRrebHMRQ==}
1310
+
1311
+
emoji-regex@8.0.0:
1312
+
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
1313
+
1314
+
emoji-regex@9.2.2:
1315
+
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
1316
+
1317
+
es-abstract@1.23.3:
1318
+
resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==}
1319
+
engines: {node: '>= 0.4'}
1320
+
1321
+
es-define-property@1.0.0:
1322
+
resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==}
1323
+
engines: {node: '>= 0.4'}
1324
+
1325
+
es-errors@1.3.0:
1326
+
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
1327
+
engines: {node: '>= 0.4'}
1328
+
1329
+
es-object-atoms@1.0.0:
1330
+
resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==}
1331
+
engines: {node: '>= 0.4'}
1332
+
1333
+
es-set-tostringtag@2.0.3:
1334
+
resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==}
1335
+
engines: {node: '>= 0.4'}
1336
+
1337
+
es-to-primitive@1.2.1:
1338
+
resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
1339
+
engines: {node: '>= 0.4'}
1340
+
1341
+
esbuild@0.20.2:
1342
+
resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==}
1343
+
engines: {node: '>=12'}
1344
+
hasBin: true
1345
+
1346
+
escalade@3.1.2:
1347
+
resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
1348
+
engines: {node: '>=6'}
1349
+
1350
+
escape-string-regexp@1.0.5:
1351
+
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
1352
+
engines: {node: '>=0.8.0'}
1353
+
1354
+
estree-walker@1.0.1:
1355
+
resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==}
1356
+
1357
+
estree-walker@2.0.2:
1358
+
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
1359
+
1360
+
esutils@2.0.3:
1361
+
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
1362
+
engines: {node: '>=0.10.0'}
1363
+
1364
+
fast-deep-equal@3.1.3:
1365
+
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
1366
+
1367
+
fast-glob@3.3.2:
1368
+
resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
1369
+
engines: {node: '>=8.6.0'}
1370
+
1371
+
fast-json-stable-stringify@2.1.0:
1372
+
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
1373
+
1374
+
fastq@1.17.1:
1375
+
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
1376
+
1377
+
filelist@1.0.4:
1378
+
resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
1379
+
1380
+
fill-range@7.1.1:
1381
+
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
1382
+
engines: {node: '>=8'}
1383
+
1384
+
for-each@0.3.3:
1385
+
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
1386
+
1387
+
foreground-child@3.1.1:
1388
+
resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
1389
+
engines: {node: '>=14'}
1390
+
1391
+
fraction.js@4.3.7:
1392
+
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
1393
+
1394
+
fs-extra@9.1.0:
1395
+
resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
1396
+
engines: {node: '>=10'}
1397
+
1398
+
fs.realpath@1.0.0:
1399
+
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
1400
+
1401
+
fsevents@2.3.3:
1402
+
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
1403
+
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
1404
+
os: [darwin]
1405
+
1406
+
function-bind@1.1.2:
1407
+
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
1408
+
1409
+
function.prototype.name@1.1.6:
1410
+
resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==}
1411
+
engines: {node: '>= 0.4'}
1412
+
1413
+
functions-have-names@1.2.3:
1414
+
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
1415
+
1416
+
gensync@1.0.0-beta.2:
1417
+
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
1418
+
engines: {node: '>=6.9.0'}
1419
+
1420
+
get-intrinsic@1.2.4:
1421
+
resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
1422
+
engines: {node: '>= 0.4'}
1423
+
1424
+
get-own-enumerable-property-symbols@3.0.2:
1425
+
resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==}
1426
+
1427
+
get-symbol-description@1.0.2:
1428
+
resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==}
1429
+
engines: {node: '>= 0.4'}
1430
+
1431
+
glob-parent@5.1.2:
1432
+
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
1433
+
engines: {node: '>= 6'}
1434
+
1435
+
glob-parent@6.0.2:
1436
+
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
1437
+
engines: {node: '>=10.13.0'}
1438
+
1439
+
glob@10.4.1:
1440
+
resolution: {integrity: sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==}
1441
+
engines: {node: '>=16 || 14 >=14.18'}
1442
+
hasBin: true
1443
+
1444
+
glob@7.2.3:
1445
+
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
1446
+
deprecated: Glob versions prior to v9 are no longer supported
1447
+
1448
+
globals@11.12.0:
1449
+
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
1450
+
engines: {node: '>=4'}
1451
+
1452
+
globalthis@1.0.4:
1453
+
resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
1454
+
engines: {node: '>= 0.4'}
1455
+
1456
+
gopd@1.0.1:
1457
+
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
1458
+
1459
+
graceful-fs@4.2.11:
1460
+
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
1461
+
1462
+
has-bigints@1.0.2:
1463
+
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
1464
+
1465
+
has-flag@3.0.0:
1466
+
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
1467
+
engines: {node: '>=4'}
1468
+
1469
+
has-flag@4.0.0:
1470
+
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
1471
+
engines: {node: '>=8'}
1472
+
1473
+
has-property-descriptors@1.0.2:
1474
+
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
1475
+
1476
+
has-proto@1.0.3:
1477
+
resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==}
1478
+
engines: {node: '>= 0.4'}
1479
+
1480
+
has-symbols@1.0.3:
1481
+
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
1482
+
engines: {node: '>= 0.4'}
1483
+
1484
+
has-tostringtag@1.0.2:
1485
+
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
1486
+
engines: {node: '>= 0.4'}
1487
+
1488
+
hasown@2.0.2:
1489
+
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
1490
+
engines: {node: '>= 0.4'}
1491
+
1492
+
html-entities@2.3.3:
1493
+
resolution: {integrity: sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==}
1494
+
1495
+
idb@7.1.1:
1496
+
resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==}
1497
+
1498
+
inflight@1.0.6:
1499
+
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
1500
+
deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
1501
+
1502
+
inherits@2.0.4:
1503
+
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
1504
+
1505
+
internal-slot@1.0.7:
1506
+
resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==}
1507
+
engines: {node: '>= 0.4'}
1508
+
1509
+
is-array-buffer@3.0.4:
1510
+
resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==}
1511
+
engines: {node: '>= 0.4'}
1512
+
1513
+
is-bigint@1.0.4:
1514
+
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
1515
+
1516
+
is-binary-path@2.1.0:
1517
+
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
1518
+
engines: {node: '>=8'}
1519
+
1520
+
is-boolean-object@1.1.2:
1521
+
resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
1522
+
engines: {node: '>= 0.4'}
1523
+
1524
+
is-builtin-module@3.2.1:
1525
+
resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==}
1526
+
engines: {node: '>=6'}
1527
+
1528
+
is-callable@1.2.7:
1529
+
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
1530
+
engines: {node: '>= 0.4'}
1531
+
1532
+
is-core-module@2.13.1:
1533
+
resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
1534
+
1535
+
is-data-view@1.0.1:
1536
+
resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==}
1537
+
engines: {node: '>= 0.4'}
1538
+
1539
+
is-date-object@1.0.5:
1540
+
resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
1541
+
engines: {node: '>= 0.4'}
1542
+
1543
+
is-extglob@2.1.1:
1544
+
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
1545
+
engines: {node: '>=0.10.0'}
1546
+
1547
+
is-fullwidth-code-point@3.0.0:
1548
+
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
1549
+
engines: {node: '>=8'}
1550
+
1551
+
is-glob@4.0.3:
1552
+
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
1553
+
engines: {node: '>=0.10.0'}
1554
+
1555
+
is-module@1.0.0:
1556
+
resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==}
1557
+
1558
+
is-negative-zero@2.0.3:
1559
+
resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
1560
+
engines: {node: '>= 0.4'}
1561
+
1562
+
is-number-object@1.0.7:
1563
+
resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
1564
+
engines: {node: '>= 0.4'}
1565
+
1566
+
is-number@7.0.0:
1567
+
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
1568
+
engines: {node: '>=0.12.0'}
1569
+
1570
+
is-obj@1.0.1:
1571
+
resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==}
1572
+
engines: {node: '>=0.10.0'}
1573
+
1574
+
is-regex@1.1.4:
1575
+
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
1576
+
engines: {node: '>= 0.4'}
1577
+
1578
+
is-regexp@1.0.0:
1579
+
resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==}
1580
+
engines: {node: '>=0.10.0'}
1581
+
1582
+
is-shared-array-buffer@1.0.3:
1583
+
resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==}
1584
+
engines: {node: '>= 0.4'}
1585
+
1586
+
is-stream@2.0.1:
1587
+
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
1588
+
engines: {node: '>=8'}
1589
+
1590
+
is-string@1.0.7:
1591
+
resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
1592
+
engines: {node: '>= 0.4'}
1593
+
1594
+
is-symbol@1.0.4:
1595
+
resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==}
1596
+
engines: {node: '>= 0.4'}
1597
+
1598
+
is-typed-array@1.1.13:
1599
+
resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==}
1600
+
engines: {node: '>= 0.4'}
1601
+
1602
+
is-weakref@1.0.2:
1603
+
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
1604
+
1605
+
is-what@4.1.16:
1606
+
resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==}
1607
+
engines: {node: '>=12.13'}
1608
+
1609
+
isarray@2.0.5:
1610
+
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
1611
+
1612
+
isexe@2.0.0:
1613
+
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
1614
+
1615
+
jackspeak@3.4.0:
1616
+
resolution: {integrity: sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==}
1617
+
engines: {node: '>=14'}
1618
+
1619
+
jake@10.9.1:
1620
+
resolution: {integrity: sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==}
1621
+
engines: {node: '>=10'}
1622
+
hasBin: true
1623
+
1624
+
jiti@1.21.3:
1625
+
resolution: {integrity: sha512-uy2bNX5zQ+tESe+TiC7ilGRz8AtRGmnJH55NC5S0nSUjvvvM2hJHmefHErugGXN4pNv4Qx7vLsnNw9qJ9mtIsw==}
1626
+
hasBin: true
1627
+
1628
+
js-tokens@4.0.0:
1629
+
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
1630
+
1631
+
jsesc@0.5.0:
1632
+
resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==}
1633
+
hasBin: true
1634
+
1635
+
jsesc@2.5.2:
1636
+
resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
1637
+
engines: {node: '>=4'}
1638
+
hasBin: true
1639
+
1640
+
json-schema-traverse@1.0.0:
1641
+
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
1642
+
1643
+
json-schema@0.4.0:
1644
+
resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
1645
+
1646
+
json5@2.2.3:
1647
+
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
1648
+
engines: {node: '>=6'}
1649
+
hasBin: true
1650
+
1651
+
jsonfile@6.1.0:
1652
+
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
1653
+
1654
+
jsonpointer@5.0.1:
1655
+
resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==}
1656
+
engines: {node: '>=0.10.0'}
1657
+
1658
+
leven@3.1.0:
1659
+
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
1660
+
engines: {node: '>=6'}
1661
+
1662
+
lilconfig@2.1.0:
1663
+
resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
1664
+
engines: {node: '>=10'}
1665
+
1666
+
lilconfig@3.1.1:
1667
+
resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==}
1668
+
engines: {node: '>=14'}
1669
+
1670
+
lines-and-columns@1.2.4:
1671
+
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
1672
+
1673
+
lodash.debounce@4.0.8:
1674
+
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
1675
+
1676
+
lodash.sortby@4.7.0:
1677
+
resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==}
1678
+
1679
+
lodash@4.17.21:
1680
+
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
1681
+
1682
+
lru-cache@10.2.2:
1683
+
resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==}
1684
+
engines: {node: 14 || >=16.14}
1685
+
1686
+
lru-cache@5.1.1:
1687
+
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
1688
+
1689
+
magic-string@0.25.9:
1690
+
resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
1691
+
1692
+
merge-anything@5.1.7:
1693
+
resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==}
1694
+
engines: {node: '>=12.13'}
1695
+
1696
+
merge2@1.4.1:
1697
+
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
1698
+
engines: {node: '>= 8'}
1699
+
1700
+
micromatch@4.0.7:
1701
+
resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==}
1702
+
engines: {node: '>=8.6'}
1703
+
1704
+
minimatch@3.1.2:
1705
+
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
1706
+
1707
+
minimatch@5.1.6:
1708
+
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
1709
+
engines: {node: '>=10'}
1710
+
1711
+
minimatch@9.0.4:
1712
+
resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==}
1713
+
engines: {node: '>=16 || 14 >=14.17'}
1714
+
1715
+
minipass@7.1.2:
1716
+
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
1717
+
engines: {node: '>=16 || 14 >=14.17'}
1718
+
1719
+
ms@2.1.2:
1720
+
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
1721
+
1722
+
mz@2.7.0:
1723
+
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
1724
+
1725
+
nanoid@3.3.7:
1726
+
resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
1727
+
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
1728
+
hasBin: true
1729
+
1730
+
nanoid@5.0.7:
1731
+
resolution: {integrity: sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==}
1732
+
engines: {node: ^18 || >=20}
1733
+
hasBin: true
1734
+
1735
+
node-releases@2.0.14:
1736
+
resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
1737
+
1738
+
normalize-path@3.0.0:
1739
+
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
1740
+
engines: {node: '>=0.10.0'}
1741
+
1742
+
normalize-range@0.1.2:
1743
+
resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
1744
+
engines: {node: '>=0.10.0'}
1745
+
1746
+
object-assign@4.1.1:
1747
+
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
1748
+
engines: {node: '>=0.10.0'}
1749
+
1750
+
object-hash@3.0.0:
1751
+
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
1752
+
engines: {node: '>= 6'}
1753
+
1754
+
object-inspect@1.13.1:
1755
+
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
1756
+
1757
+
object-keys@1.1.1:
1758
+
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
1759
+
engines: {node: '>= 0.4'}
1760
+
1761
+
object.assign@4.1.5:
1762
+
resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==}
1763
+
engines: {node: '>= 0.4'}
1764
+
1765
+
once@1.4.0:
1766
+
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
1767
+
1768
+
path-is-absolute@1.0.1:
1769
+
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
1770
+
engines: {node: '>=0.10.0'}
1771
+
1772
+
path-key@3.1.1:
1773
+
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
1774
+
engines: {node: '>=8'}
1775
+
1776
+
path-parse@1.0.7:
1777
+
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
1778
+
1779
+
path-scurry@1.11.1:
1780
+
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
1781
+
engines: {node: '>=16 || 14 >=14.18'}
1782
+
1783
+
picocolors@1.0.1:
1784
+
resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
1785
+
1786
+
picomatch@2.3.1:
1787
+
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
1788
+
engines: {node: '>=8.6'}
1789
+
1790
+
pify@2.3.0:
1791
+
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
1792
+
engines: {node: '>=0.10.0'}
1793
+
1794
+
pirates@4.0.6:
1795
+
resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
1796
+
engines: {node: '>= 6'}
1797
+
1798
+
possible-typed-array-names@1.0.0:
1799
+
resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==}
1800
+
engines: {node: '>= 0.4'}
1801
+
1802
+
postcss-import@15.1.0:
1803
+
resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
1804
+
engines: {node: '>=14.0.0'}
1805
+
peerDependencies:
1806
+
postcss: ^8.0.0
1807
+
1808
+
postcss-js@4.0.1:
1809
+
resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
1810
+
engines: {node: ^12 || ^14 || >= 16}
1811
+
peerDependencies:
1812
+
postcss: ^8.4.21
1813
+
1814
+
postcss-load-config@4.0.2:
1815
+
resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
1816
+
engines: {node: '>= 14'}
1817
+
peerDependencies:
1818
+
postcss: '>=8.0.9'
1819
+
ts-node: '>=9.0.0'
1820
+
peerDependenciesMeta:
1821
+
postcss:
1822
+
optional: true
1823
+
ts-node:
1824
+
optional: true
1825
+
1826
+
postcss-nested@6.0.1:
1827
+
resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==}
1828
+
engines: {node: '>=12.0'}
1829
+
peerDependencies:
1830
+
postcss: ^8.2.14
1831
+
1832
+
postcss-selector-parser@6.1.0:
1833
+
resolution: {integrity: sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==}
1834
+
engines: {node: '>=4'}
1835
+
1836
+
postcss-value-parser@4.2.0:
1837
+
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
1838
+
1839
+
postcss@8.4.38:
1840
+
resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
1841
+
engines: {node: ^10 || ^12 || >=14}
1842
+
1843
+
prettier-plugin-tailwindcss@0.6.3:
1844
+
resolution: {integrity: sha512-GeJ9bqXN4APAP0V5T2a1J/o6a50MWevEUCPWxijpdXFDQkBCoAfz4pQfv+YMXSqZ5GXLMDYio0mUOfrYL7gf4w==}
1845
+
engines: {node: '>=14.21.3'}
1846
+
peerDependencies:
1847
+
'@ianvs/prettier-plugin-sort-imports': '*'
1848
+
'@prettier/plugin-pug': '*'
1849
+
'@shopify/prettier-plugin-liquid': '*'
1850
+
'@trivago/prettier-plugin-sort-imports': '*'
1851
+
'@zackad/prettier-plugin-twig-melody': '*'
1852
+
prettier: ^3.0
1853
+
prettier-plugin-astro: '*'
1854
+
prettier-plugin-css-order: '*'
1855
+
prettier-plugin-import-sort: '*'
1856
+
prettier-plugin-jsdoc: '*'
1857
+
prettier-plugin-marko: '*'
1858
+
prettier-plugin-organize-attributes: '*'
1859
+
prettier-plugin-organize-imports: '*'
1860
+
prettier-plugin-sort-imports: '*'
1861
+
prettier-plugin-style-order: '*'
1862
+
prettier-plugin-svelte: '*'
1863
+
peerDependenciesMeta:
1864
+
'@ianvs/prettier-plugin-sort-imports':
1865
+
optional: true
1866
+
'@prettier/plugin-pug':
1867
+
optional: true
1868
+
'@shopify/prettier-plugin-liquid':
1869
+
optional: true
1870
+
'@trivago/prettier-plugin-sort-imports':
1871
+
optional: true
1872
+
'@zackad/prettier-plugin-twig-melody':
1873
+
optional: true
1874
+
prettier-plugin-astro:
1875
+
optional: true
1876
+
prettier-plugin-css-order:
1877
+
optional: true
1878
+
prettier-plugin-import-sort:
1879
+
optional: true
1880
+
prettier-plugin-jsdoc:
1881
+
optional: true
1882
+
prettier-plugin-marko:
1883
+
optional: true
1884
+
prettier-plugin-organize-attributes:
1885
+
optional: true
1886
+
prettier-plugin-organize-imports:
1887
+
optional: true
1888
+
prettier-plugin-sort-imports:
1889
+
optional: true
1890
+
prettier-plugin-style-order:
1891
+
optional: true
1892
+
prettier-plugin-svelte:
1893
+
optional: true
1894
+
1895
+
prettier@3.3.2:
1896
+
resolution: {integrity: sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==}
1897
+
engines: {node: '>=14'}
1898
+
hasBin: true
1899
+
1900
+
pretty-bytes@5.6.0:
1901
+
resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==}
1902
+
engines: {node: '>=6'}
1903
+
1904
+
pretty-bytes@6.1.1:
1905
+
resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==}
1906
+
engines: {node: ^14.13.1 || >=16.0.0}
1907
+
1908
+
punycode@2.3.1:
1909
+
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
1910
+
engines: {node: '>=6'}
1911
+
1912
+
queue-microtask@1.2.3:
1913
+
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
1914
+
1915
+
randombytes@2.1.0:
1916
+
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
1917
+
1918
+
read-cache@1.0.0:
1919
+
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
1920
+
1921
+
readdirp@3.6.0:
1922
+
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
1923
+
engines: {node: '>=8.10.0'}
1924
+
1925
+
regenerate-unicode-properties@10.1.1:
1926
+
resolution: {integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==}
1927
+
engines: {node: '>=4'}
1928
+
1929
+
regenerate@1.4.2:
1930
+
resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==}
1931
+
1932
+
regenerator-runtime@0.14.1:
1933
+
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
1934
+
1935
+
regenerator-transform@0.15.2:
1936
+
resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==}
1937
+
1938
+
regexp.prototype.flags@1.5.2:
1939
+
resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==}
1940
+
engines: {node: '>= 0.4'}
1941
+
1942
+
regexpu-core@5.3.2:
1943
+
resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==}
1944
+
engines: {node: '>=4'}
1945
+
1946
+
regjsparser@0.9.1:
1947
+
resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==}
1948
+
hasBin: true
1949
+
1950
+
require-from-string@2.0.2:
1951
+
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
1952
+
engines: {node: '>=0.10.0'}
1953
+
1954
+
resolve@1.22.8:
1955
+
resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
1956
+
hasBin: true
1957
+
1958
+
reusify@1.0.4:
1959
+
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
1960
+
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
1961
+
1962
+
rollup@2.79.1:
1963
+
resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==}
1964
+
engines: {node: '>=10.0.0'}
1965
+
hasBin: true
1966
+
1967
+
rollup@4.18.0:
1968
+
resolution: {integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==}
1969
+
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
1970
+
hasBin: true
1971
+
1972
+
run-parallel@1.2.0:
1973
+
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
1974
+
1975
+
safe-array-concat@1.1.2:
1976
+
resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==}
1977
+
engines: {node: '>=0.4'}
1978
+
1979
+
safe-buffer@5.2.1:
1980
+
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
1981
+
1982
+
safe-regex-test@1.0.3:
1983
+
resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==}
1984
+
engines: {node: '>= 0.4'}
1985
+
1986
+
semver@6.3.1:
1987
+
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
1988
+
hasBin: true
1989
+
1990
+
serialize-javascript@6.0.2:
1991
+
resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
1992
+
1993
+
seroval-plugins@1.0.7:
1994
+
resolution: {integrity: sha512-GO7TkWvodGp6buMEX9p7tNyIkbwlyuAWbI6G9Ec5bhcm7mQdu3JOK1IXbEUwb3FVzSc363GraG/wLW23NSavIw==}
1995
+
engines: {node: '>=10'}
1996
+
peerDependencies:
1997
+
seroval: ^1.0
1998
+
1999
+
seroval@1.0.7:
2000
+
resolution: {integrity: sha512-n6ZMQX5q0Vn19Zq7CIKNIo7E75gPkGCFUEqDpa8jgwpYr/vScjqnQ6H09t1uIiZ0ZSK0ypEGvrYK2bhBGWsGdw==}
2001
+
engines: {node: '>=10'}
2002
+
2003
+
set-function-length@1.2.2:
2004
+
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
2005
+
engines: {node: '>= 0.4'}
2006
+
2007
+
set-function-name@2.0.2:
2008
+
resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
2009
+
engines: {node: '>= 0.4'}
2010
+
2011
+
shebang-command@2.0.0:
2012
+
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
2013
+
engines: {node: '>=8'}
2014
+
2015
+
shebang-regex@3.0.0:
2016
+
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
2017
+
engines: {node: '>=8'}
2018
+
2019
+
side-channel@1.0.6:
2020
+
resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
2021
+
engines: {node: '>= 0.4'}
2022
+
2023
+
signal-exit@4.1.0:
2024
+
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
2025
+
engines: {node: '>=14'}
2026
+
2027
+
smob@1.5.0:
2028
+
resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==}
2029
+
2030
+
solid-floating-ui@0.2.1:
2031
+
resolution: {integrity: sha512-ZWVigPZjisSY7GkAKzZpDoY6Eb0NWSakaF7tut5jkW0ls9BB/bk7Jhsxd8l0JoU3KaxYH15GYcpuM0fU06kc/A==}
2032
+
engines: {node: '>=10'}
2033
+
peerDependencies:
2034
+
'@floating-ui/dom': ^1.0
2035
+
solid-js: ^1.3
2036
+
2037
+
solid-js@1.8.17:
2038
+
resolution: {integrity: sha512-E0FkUgv9sG/gEBWkHr/2XkBluHb1fkrHywUgA6o6XolPDCJ4g1HaLmQufcBBhiF36ee40q+HpG/vCZu7fLpI3Q==}
2039
+
2040
+
solid-refresh@0.6.3:
2041
+
resolution: {integrity: sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==}
2042
+
peerDependencies:
2043
+
solid-js: ^1.3
2044
+
2045
+
solid-textarea-autosize@0.0.5:
2046
+
resolution: {integrity: sha512-le/X+VLr4G3PEE/gZuKBDNqh5e9OoNiIYIN0fSebAuOABxJIY/Eh/9rjDEMB8DbPdeD/wHIVk4Wd9zBvFFIt1A==}
2047
+
peerDependencies:
2048
+
solid-js: '>=1.0.0'
2049
+
2050
+
source-map-js@1.2.0:
2051
+
resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
2052
+
engines: {node: '>=0.10.0'}
2053
+
2054
+
source-map-support@0.5.21:
2055
+
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
2056
+
2057
+
source-map@0.6.1:
2058
+
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
2059
+
engines: {node: '>=0.10.0'}
2060
+
2061
+
source-map@0.8.0-beta.0:
2062
+
resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
2063
+
engines: {node: '>= 8'}
2064
+
2065
+
sourcemap-codec@1.4.8:
2066
+
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
2067
+
deprecated: Please use @jridgewell/sourcemap-codec instead
2068
+
2069
+
string-width@4.2.3:
2070
+
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
2071
+
engines: {node: '>=8'}
2072
+
2073
+
string-width@5.1.2:
2074
+
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
2075
+
engines: {node: '>=12'}
2076
+
2077
+
string.prototype.matchall@4.0.11:
2078
+
resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==}
2079
+
engines: {node: '>= 0.4'}
2080
+
2081
+
string.prototype.trim@1.2.9:
2082
+
resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==}
2083
+
engines: {node: '>= 0.4'}
2084
+
2085
+
string.prototype.trimend@1.0.8:
2086
+
resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==}
2087
+
2088
+
string.prototype.trimstart@1.0.8:
2089
+
resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
2090
+
engines: {node: '>= 0.4'}
2091
+
2092
+
stringify-object@3.3.0:
2093
+
resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==}
2094
+
engines: {node: '>=4'}
2095
+
2096
+
strip-ansi@6.0.1:
2097
+
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
2098
+
engines: {node: '>=8'}
2099
+
2100
+
strip-ansi@7.1.0:
2101
+
resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
2102
+
engines: {node: '>=12'}
2103
+
2104
+
strip-comments@2.0.1:
2105
+
resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==}
2106
+
engines: {node: '>=10'}
2107
+
2108
+
sucrase@3.35.0:
2109
+
resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
2110
+
engines: {node: '>=16 || 14 >=14.17'}
2111
+
hasBin: true
2112
+
2113
+
supports-color@5.5.0:
2114
+
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
2115
+
engines: {node: '>=4'}
2116
+
2117
+
supports-color@7.2.0:
2118
+
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
2119
+
engines: {node: '>=8'}
2120
+
2121
+
supports-preserve-symlinks-flag@1.0.0:
2122
+
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
2123
+
engines: {node: '>= 0.4'}
2124
+
2125
+
tailwindcss@3.4.4:
2126
+
resolution: {integrity: sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A==}
2127
+
engines: {node: '>=14.0.0'}
2128
+
hasBin: true
2129
+
2130
+
temp-dir@2.0.0:
2131
+
resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==}
2132
+
engines: {node: '>=8'}
2133
+
2134
+
tempy@0.6.0:
2135
+
resolution: {integrity: sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==}
2136
+
engines: {node: '>=10'}
2137
+
2138
+
terser@5.31.1:
2139
+
resolution: {integrity: sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg==}
2140
+
engines: {node: '>=10'}
2141
+
hasBin: true
2142
+
2143
+
thenify-all@1.6.0:
2144
+
resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
2145
+
engines: {node: '>=0.8'}
2146
+
2147
+
thenify@3.3.1:
2148
+
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
2149
+
2150
+
to-fast-properties@2.0.0:
2151
+
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
2152
+
engines: {node: '>=4'}
2153
+
2154
+
to-regex-range@5.0.1:
2155
+
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
2156
+
engines: {node: '>=8.0'}
2157
+
2158
+
tr46@1.0.1:
2159
+
resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==}
2160
+
2161
+
ts-interface-checker@0.1.13:
2162
+
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
2163
+
2164
+
type-fest@0.16.0:
2165
+
resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==}
2166
+
engines: {node: '>=10'}
2167
+
2168
+
typed-array-buffer@1.0.2:
2169
+
resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==}
2170
+
engines: {node: '>= 0.4'}
2171
+
2172
+
typed-array-byte-length@1.0.1:
2173
+
resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==}
2174
+
engines: {node: '>= 0.4'}
2175
+
2176
+
typed-array-byte-offset@1.0.2:
2177
+
resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==}
2178
+
engines: {node: '>= 0.4'}
2179
+
2180
+
typed-array-length@1.0.6:
2181
+
resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==}
2182
+
engines: {node: '>= 0.4'}
2183
+
2184
+
typescript@5.4.5:
2185
+
resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==}
2186
+
engines: {node: '>=14.17'}
2187
+
hasBin: true
2188
+
2189
+
unbox-primitive@1.0.2:
2190
+
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
2191
+
2192
+
unicode-canonical-property-names-ecmascript@2.0.0:
2193
+
resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==}
2194
+
engines: {node: '>=4'}
2195
+
2196
+
unicode-match-property-ecmascript@2.0.0:
2197
+
resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==}
2198
+
engines: {node: '>=4'}
2199
+
2200
+
unicode-match-property-value-ecmascript@2.1.0:
2201
+
resolution: {integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==}
2202
+
engines: {node: '>=4'}
2203
+
2204
+
unicode-property-aliases-ecmascript@2.1.0:
2205
+
resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==}
2206
+
engines: {node: '>=4'}
2207
+
2208
+
unique-string@2.0.0:
2209
+
resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==}
2210
+
engines: {node: '>=8'}
2211
+
2212
+
universalify@2.0.1:
2213
+
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
2214
+
engines: {node: '>= 10.0.0'}
2215
+
2216
+
upath@1.2.0:
2217
+
resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==}
2218
+
engines: {node: '>=4'}
2219
+
2220
+
update-browserslist-db@1.0.16:
2221
+
resolution: {integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==}
2222
+
hasBin: true
2223
+
peerDependencies:
2224
+
browserslist: '>= 4.21.0'
2225
+
2226
+
uri-js@4.4.1:
2227
+
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
2228
+
2229
+
util-deprecate@1.0.2:
2230
+
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
2231
+
2232
+
validate-html-nesting@1.2.2:
2233
+
resolution: {integrity: sha512-hGdgQozCsQJMyfK5urgFcWEqsSSrK63Awe0t/IMR0bZ0QMtnuaiHzThW81guu3qx9abLi99NEuiaN6P9gVYsNg==}
2234
+
2235
+
vite-plugin-pwa@0.17.4:
2236
+
resolution: {integrity: sha512-j9iiyinFOYyof4Zk3Q+DtmYyDVBDAi6PuMGNGq6uGI0pw7E+LNm9e+nQ2ep9obMP/kjdWwzilqUrlfVRj9OobA==}
2237
+
engines: {node: '>=16.0.0'}
2238
+
peerDependencies:
2239
+
vite: ^3.1.0 || ^4.0.0 || ^5.0.0
2240
+
2241
+
vite-plugin-solid@2.10.2:
2242
+
resolution: {integrity: sha512-AOEtwMe2baBSXMXdo+BUwECC8IFHcKS6WQV/1NEd+Q7vHPap5fmIhLcAzr+DUJ04/KHx/1UBU0l1/GWP+rMAPQ==}
2243
+
peerDependencies:
2244
+
'@testing-library/jest-dom': ^5.16.6 || ^5.17.0 || ^6.*
2245
+
solid-js: ^1.7.2
2246
+
vite: ^3.0.0 || ^4.0.0 || ^5.0.0
2247
+
peerDependenciesMeta:
2248
+
'@testing-library/jest-dom':
2249
+
optional: true
2250
+
2251
+
vite@5.2.11:
2252
+
resolution: {integrity: sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==}
2253
+
engines: {node: ^18.0.0 || >=20.0.0}
2254
+
hasBin: true
2255
+
peerDependencies:
2256
+
'@types/node': ^18.0.0 || >=20.0.0
2257
+
less: '*'
2258
+
lightningcss: ^1.21.0
2259
+
sass: '*'
2260
+
stylus: '*'
2261
+
sugarss: '*'
2262
+
terser: ^5.4.0
2263
+
peerDependenciesMeta:
2264
+
'@types/node':
2265
+
optional: true
2266
+
less:
2267
+
optional: true
2268
+
lightningcss:
2269
+
optional: true
2270
+
sass:
2271
+
optional: true
2272
+
stylus:
2273
+
optional: true
2274
+
sugarss:
2275
+
optional: true
2276
+
terser:
2277
+
optional: true
2278
+
2279
+
vitefu@0.2.5:
2280
+
resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==}
2281
+
peerDependencies:
2282
+
vite: ^3.0.0 || ^4.0.0 || ^5.0.0
2283
+
peerDependenciesMeta:
2284
+
vite:
2285
+
optional: true
2286
+
2287
+
webidl-conversions@4.0.2:
2288
+
resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
2289
+
2290
+
whatwg-url@7.1.0:
2291
+
resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==}
2292
+
2293
+
which-boxed-primitive@1.0.2:
2294
+
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
2295
+
2296
+
which-typed-array@1.1.15:
2297
+
resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==}
2298
+
engines: {node: '>= 0.4'}
2299
+
2300
+
which@2.0.2:
2301
+
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
2302
+
engines: {node: '>= 8'}
2303
+
hasBin: true
2304
+
2305
+
workbox-background-sync@7.1.0:
2306
+
resolution: {integrity: sha512-rMbgrzueVWDFcEq1610YyDW71z0oAXLfdRHRQcKw4SGihkfOK0JUEvqWHFwA6rJ+6TClnMIn7KQI5PNN1XQXwQ==}
2307
+
2308
+
workbox-broadcast-update@7.1.0:
2309
+
resolution: {integrity: sha512-O36hIfhjej/c5ar95pO67k1GQw0/bw5tKP7CERNgK+JdxBANQhDmIuOXZTNvwb2IHBx9hj2kxvcDyRIh5nzOgQ==}
2310
+
2311
+
workbox-build@7.1.0:
2312
+
resolution: {integrity: sha512-F6R94XAxjB2j4ETMkP1EXKfjECOtDmyvt0vz3BzgWJMI68TNSXIVNkgatwUKBlPGOfy9n2F/4voYRNAhEvPJNg==}
2313
+
engines: {node: '>=16.0.0'}
2314
+
2315
+
workbox-cacheable-response@7.1.0:
2316
+
resolution: {integrity: sha512-iwsLBll8Hvua3xCuBB9h92+/e0wdsmSVgR2ZlvcfjepZWwhd3osumQB3x9o7flj+FehtWM2VHbZn8UJeBXXo6Q==}
2317
+
2318
+
workbox-core@7.1.0:
2319
+
resolution: {integrity: sha512-5KB4KOY8rtL31nEF7BfvU7FMzKT4B5TkbYa2tzkS+Peqj0gayMT9SytSFtNzlrvMaWgv6y/yvP9C0IbpFjV30Q==}
2320
+
2321
+
workbox-expiration@7.1.0:
2322
+
resolution: {integrity: sha512-m5DcMY+A63rJlPTbbBNtpJ20i3enkyOtSgYfv/l8h+D6YbbNiA0zKEkCUaMsdDlxggla1oOfRkyqTvl5Ni5KQQ==}
2323
+
2324
+
workbox-google-analytics@7.1.0:
2325
+
resolution: {integrity: sha512-FvE53kBQHfVTcZyczeBVRexhh7JTkyQ8HAvbVY6mXd2n2A7Oyz/9fIwnY406ZcDhvE4NFfKGjW56N4gBiqkrew==}
2326
+
2327
+
workbox-navigation-preload@7.1.0:
2328
+
resolution: {integrity: sha512-4wyAbo0vNI/X0uWNJhCMKxnPanNyhybsReMGN9QUpaePLTiDpKxPqFxl4oUmBNddPwIXug01eTSLVIFXimRG/A==}
2329
+
2330
+
workbox-precaching@7.1.0:
2331
+
resolution: {integrity: sha512-LyxzQts+UEpgtmfnolo0hHdNjoB7EoRWcF7EDslt+lQGd0lW4iTvvSe3v5JiIckQSB5KTW5xiCqjFviRKPj1zA==}
2332
+
2333
+
workbox-range-requests@7.1.0:
2334
+
resolution: {integrity: sha512-m7+O4EHolNs5yb/79CrnwPR/g/PRzMFYEdo01LqwixVnc/sbzNSvKz0d04OE3aMRel1CwAAZQheRsqGDwATgPQ==}
2335
+
2336
+
workbox-recipes@7.1.0:
2337
+
resolution: {integrity: sha512-NRrk4ycFN9BHXJB6WrKiRX3W3w75YNrNrzSX9cEZgFB5ubeGoO8s/SDmOYVrFYp9HMw6sh1Pm3eAY/1gVS8YLg==}
2338
+
2339
+
workbox-routing@7.1.0:
2340
+
resolution: {integrity: sha512-oOYk+kLriUY2QyHkIilxUlVcFqwduLJB7oRZIENbqPGeBP/3TWHYNNdmGNhz1dvKuw7aqvJ7CQxn27/jprlTdg==}
2341
+
2342
+
workbox-strategies@7.1.0:
2343
+
resolution: {integrity: sha512-/UracPiGhUNehGjRm/tLUQ+9PtWmCbRufWtV0tNrALuf+HZ4F7cmObSEK+E4/Bx1p8Syx2tM+pkIrvtyetdlew==}
2344
+
2345
+
workbox-streams@7.1.0:
2346
+
resolution: {integrity: sha512-WyHAVxRXBMfysM8ORwiZnI98wvGWTVAq/lOyBjf00pXFvG0mNaVz4Ji+u+fKa/mf1i2SnTfikoYKto4ihHeS6w==}
2347
+
2348
+
workbox-sw@7.1.0:
2349
+
resolution: {integrity: sha512-Hml/9+/njUXBglv3dtZ9WBKHI235AQJyLBV1G7EFmh4/mUdSQuXui80RtjDeVRrXnm/6QWgRUEHG3/YBVbxtsA==}
2350
+
2351
+
workbox-window@7.1.0:
2352
+
resolution: {integrity: sha512-ZHeROyqR+AS5UPzholQRDttLFqGMwP0Np8MKWAdyxsDETxq3qOAyXvqessc3GniohG6e0mAqSQyKOHmT8zPF7g==}
2353
+
2354
+
wrap-ansi@7.0.0:
2355
+
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
2356
+
engines: {node: '>=10'}
2357
+
2358
+
wrap-ansi@8.1.0:
2359
+
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
2360
+
engines: {node: '>=12'}
2361
+
2362
+
wrappy@1.0.2:
2363
+
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
2364
+
2365
+
yallist@3.1.1:
2366
+
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
2367
+
2368
+
yaml@2.4.3:
2369
+
resolution: {integrity: sha512-sntgmxj8o7DE7g/Qi60cqpLBA3HG3STcDA0kO+WfB05jEKhZMbY7umNm2rBpQvsmZ16/lPXCJGW2672dgOUkrg==}
2370
+
engines: {node: '>= 14'}
2371
+
hasBin: true
2372
+
2373
+
snapshots:
2374
+
2375
+
'@alloc/quick-lru@5.2.0': {}
2376
+
2377
+
'@ampproject/remapping@2.3.0':
2378
+
dependencies:
2379
+
'@jridgewell/gen-mapping': 0.3.5
2380
+
'@jridgewell/trace-mapping': 0.3.25
2381
+
2382
+
'@apideck/better-ajv-errors@0.3.6(ajv@8.14.0)':
2383
+
dependencies:
2384
+
ajv: 8.14.0
2385
+
json-schema: 0.4.0
2386
+
jsonpointer: 5.0.1
2387
+
leven: 3.1.0
2388
+
2389
+
'@babel/code-frame@7.24.6':
2390
+
dependencies:
2391
+
'@babel/highlight': 7.24.6
2392
+
picocolors: 1.0.1
2393
+
2394
+
'@babel/compat-data@7.24.6': {}
2395
+
2396
+
'@babel/core@7.24.6':
2397
+
dependencies:
2398
+
'@ampproject/remapping': 2.3.0
2399
+
'@babel/code-frame': 7.24.6
2400
+
'@babel/generator': 7.24.6
2401
+
'@babel/helper-compilation-targets': 7.24.6
2402
+
'@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.6)
2403
+
'@babel/helpers': 7.24.6
2404
+
'@babel/parser': 7.24.6
2405
+
'@babel/template': 7.24.6
2406
+
'@babel/traverse': 7.24.6
2407
+
'@babel/types': 7.24.6
2408
+
convert-source-map: 2.0.0
2409
+
debug: 4.3.4
2410
+
gensync: 1.0.0-beta.2
2411
+
json5: 2.2.3
2412
+
semver: 6.3.1
2413
+
transitivePeerDependencies:
2414
+
- supports-color
2415
+
2416
+
'@babel/generator@7.24.6':
2417
+
dependencies:
2418
+
'@babel/types': 7.24.6
2419
+
'@jridgewell/gen-mapping': 0.3.5
2420
+
'@jridgewell/trace-mapping': 0.3.25
2421
+
jsesc: 2.5.2
2422
+
2423
+
'@babel/helper-annotate-as-pure@7.24.6':
2424
+
dependencies:
2425
+
'@babel/types': 7.24.6
2426
+
2427
+
'@babel/helper-builder-binary-assignment-operator-visitor@7.24.6':
2428
+
dependencies:
2429
+
'@babel/types': 7.24.6
2430
+
2431
+
'@babel/helper-compilation-targets@7.24.6':
2432
+
dependencies:
2433
+
'@babel/compat-data': 7.24.6
2434
+
'@babel/helper-validator-option': 7.24.6
2435
+
browserslist: 4.23.0
2436
+
lru-cache: 5.1.1
2437
+
semver: 6.3.1
2438
+
2439
+
'@babel/helper-create-class-features-plugin@7.24.6(@babel/core@7.24.6)':
2440
+
dependencies:
2441
+
'@babel/core': 7.24.6
2442
+
'@babel/helper-annotate-as-pure': 7.24.6
2443
+
'@babel/helper-environment-visitor': 7.24.6
2444
+
'@babel/helper-function-name': 7.24.6
2445
+
'@babel/helper-member-expression-to-functions': 7.24.6
2446
+
'@babel/helper-optimise-call-expression': 7.24.6
2447
+
'@babel/helper-replace-supers': 7.24.6(@babel/core@7.24.6)
2448
+
'@babel/helper-skip-transparent-expression-wrappers': 7.24.6
2449
+
'@babel/helper-split-export-declaration': 7.24.6
2450
+
semver: 6.3.1
2451
+
2452
+
'@babel/helper-create-regexp-features-plugin@7.24.6(@babel/core@7.24.6)':
2453
+
dependencies:
2454
+
'@babel/core': 7.24.6
2455
+
'@babel/helper-annotate-as-pure': 7.24.6
2456
+
regexpu-core: 5.3.2
2457
+
semver: 6.3.1
2458
+
2459
+
'@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.24.6)':
2460
+
dependencies:
2461
+
'@babel/core': 7.24.6
2462
+
'@babel/helper-compilation-targets': 7.24.6
2463
+
'@babel/helper-plugin-utils': 7.24.6
2464
+
debug: 4.3.4
2465
+
lodash.debounce: 4.0.8
2466
+
resolve: 1.22.8
2467
+
transitivePeerDependencies:
2468
+
- supports-color
2469
+
2470
+
'@babel/helper-environment-visitor@7.24.6': {}
2471
+
2472
+
'@babel/helper-function-name@7.24.6':
2473
+
dependencies:
2474
+
'@babel/template': 7.24.6
2475
+
'@babel/types': 7.24.6
2476
+
2477
+
'@babel/helper-hoist-variables@7.24.6':
2478
+
dependencies:
2479
+
'@babel/types': 7.24.6
2480
+
2481
+
'@babel/helper-member-expression-to-functions@7.24.6':
2482
+
dependencies:
2483
+
'@babel/types': 7.24.6
2484
+
2485
+
'@babel/helper-module-imports@7.18.6':
2486
+
dependencies:
2487
+
'@babel/types': 7.24.6
2488
+
2489
+
'@babel/helper-module-imports@7.24.6':
2490
+
dependencies:
2491
+
'@babel/types': 7.24.6
2492
+
2493
+
'@babel/helper-module-transforms@7.24.6(@babel/core@7.24.6)':
2494
+
dependencies:
2495
+
'@babel/core': 7.24.6
2496
+
'@babel/helper-environment-visitor': 7.24.6
2497
+
'@babel/helper-module-imports': 7.24.6
2498
+
'@babel/helper-simple-access': 7.24.6
2499
+
'@babel/helper-split-export-declaration': 7.24.6
2500
+
'@babel/helper-validator-identifier': 7.24.6
2501
+
2502
+
'@babel/helper-optimise-call-expression@7.24.6':
2503
+
dependencies:
2504
+
'@babel/types': 7.24.6
2505
+
2506
+
'@babel/helper-plugin-utils@7.24.6': {}
2507
+
2508
+
'@babel/helper-remap-async-to-generator@7.24.6(@babel/core@7.24.6)':
2509
+
dependencies:
2510
+
'@babel/core': 7.24.6
2511
+
'@babel/helper-annotate-as-pure': 7.24.6
2512
+
'@babel/helper-environment-visitor': 7.24.6
2513
+
'@babel/helper-wrap-function': 7.24.6
2514
+
2515
+
'@babel/helper-replace-supers@7.24.6(@babel/core@7.24.6)':
2516
+
dependencies:
2517
+
'@babel/core': 7.24.6
2518
+
'@babel/helper-environment-visitor': 7.24.6
2519
+
'@babel/helper-member-expression-to-functions': 7.24.6
2520
+
'@babel/helper-optimise-call-expression': 7.24.6
2521
+
2522
+
'@babel/helper-simple-access@7.24.6':
2523
+
dependencies:
2524
+
'@babel/types': 7.24.6
2525
+
2526
+
'@babel/helper-skip-transparent-expression-wrappers@7.24.6':
2527
+
dependencies:
2528
+
'@babel/types': 7.24.6
2529
+
2530
+
'@babel/helper-split-export-declaration@7.24.6':
2531
+
dependencies:
2532
+
'@babel/types': 7.24.6
2533
+
2534
+
'@babel/helper-string-parser@7.24.6': {}
2535
+
2536
+
'@babel/helper-validator-identifier@7.24.6': {}
2537
+
2538
+
'@babel/helper-validator-option@7.24.6': {}
2539
+
2540
+
'@babel/helper-wrap-function@7.24.6':
2541
+
dependencies:
2542
+
'@babel/helper-function-name': 7.24.6
2543
+
'@babel/template': 7.24.6
2544
+
'@babel/types': 7.24.6
2545
+
2546
+
'@babel/helpers@7.24.6':
2547
+
dependencies:
2548
+
'@babel/template': 7.24.6
2549
+
'@babel/types': 7.24.6
2550
+
2551
+
'@babel/highlight@7.24.6':
2552
+
dependencies:
2553
+
'@babel/helper-validator-identifier': 7.24.6
2554
+
chalk: 2.4.2
2555
+
js-tokens: 4.0.0
2556
+
picocolors: 1.0.1
2557
+
2558
+
'@babel/parser@7.24.6':
2559
+
dependencies:
2560
+
'@babel/types': 7.24.6
2561
+
2562
+
'@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.6(@babel/core@7.24.6)':
2563
+
dependencies:
2564
+
'@babel/core': 7.24.6
2565
+
'@babel/helper-environment-visitor': 7.24.6
2566
+
'@babel/helper-plugin-utils': 7.24.6
2567
+
2568
+
'@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.6(@babel/core@7.24.6)':
2569
+
dependencies:
2570
+
'@babel/core': 7.24.6
2571
+
'@babel/helper-plugin-utils': 7.24.6
2572
+
2573
+
'@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.6(@babel/core@7.24.6)':
2574
+
dependencies:
2575
+
'@babel/core': 7.24.6
2576
+
'@babel/helper-plugin-utils': 7.24.6
2577
+
'@babel/helper-skip-transparent-expression-wrappers': 7.24.6
2578
+
'@babel/plugin-transform-optional-chaining': 7.24.6(@babel/core@7.24.6)
2579
+
2580
+
'@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.6(@babel/core@7.24.6)':
2581
+
dependencies:
2582
+
'@babel/core': 7.24.6
2583
+
'@babel/helper-environment-visitor': 7.24.6
2584
+
'@babel/helper-plugin-utils': 7.24.6
2585
+
2586
+
'@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.6)':
2587
+
dependencies:
2588
+
'@babel/core': 7.24.6
2589
+
2590
+
'@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.6)':
2591
+
dependencies:
2592
+
'@babel/core': 7.24.6
2593
+
'@babel/helper-plugin-utils': 7.24.6
2594
+
2595
+
'@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.6)':
2596
+
dependencies:
2597
+
'@babel/core': 7.24.6
2598
+
'@babel/helper-plugin-utils': 7.24.6
2599
+
2600
+
'@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.6)':
2601
+
dependencies:
2602
+
'@babel/core': 7.24.6
2603
+
'@babel/helper-plugin-utils': 7.24.6
2604
+
2605
+
'@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.6)':
2606
+
dependencies:
2607
+
'@babel/core': 7.24.6
2608
+
'@babel/helper-plugin-utils': 7.24.6
2609
+
2610
+
'@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.6)':
2611
+
dependencies:
2612
+
'@babel/core': 7.24.6
2613
+
'@babel/helper-plugin-utils': 7.24.6
2614
+
2615
+
'@babel/plugin-syntax-import-assertions@7.24.6(@babel/core@7.24.6)':
2616
+
dependencies:
2617
+
'@babel/core': 7.24.6
2618
+
'@babel/helper-plugin-utils': 7.24.6
2619
+
2620
+
'@babel/plugin-syntax-import-attributes@7.24.6(@babel/core@7.24.6)':
2621
+
dependencies:
2622
+
'@babel/core': 7.24.6
2623
+
'@babel/helper-plugin-utils': 7.24.6
2624
+
2625
+
'@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.6)':
2626
+
dependencies:
2627
+
'@babel/core': 7.24.6
2628
+
'@babel/helper-plugin-utils': 7.24.6
2629
+
2630
+
'@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.6)':
2631
+
dependencies:
2632
+
'@babel/core': 7.24.6
2633
+
'@babel/helper-plugin-utils': 7.24.6
2634
+
2635
+
'@babel/plugin-syntax-jsx@7.24.6(@babel/core@7.24.6)':
2636
+
dependencies:
2637
+
'@babel/core': 7.24.6
2638
+
'@babel/helper-plugin-utils': 7.24.6
2639
+
2640
+
'@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.6)':
2641
+
dependencies:
2642
+
'@babel/core': 7.24.6
2643
+
'@babel/helper-plugin-utils': 7.24.6
2644
+
2645
+
'@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.6)':
2646
+
dependencies:
2647
+
'@babel/core': 7.24.6
2648
+
'@babel/helper-plugin-utils': 7.24.6
2649
+
2650
+
'@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.6)':
2651
+
dependencies:
2652
+
'@babel/core': 7.24.6
2653
+
'@babel/helper-plugin-utils': 7.24.6
2654
+
2655
+
'@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.6)':
2656
+
dependencies:
2657
+
'@babel/core': 7.24.6
2658
+
'@babel/helper-plugin-utils': 7.24.6
2659
+
2660
+
'@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.6)':
2661
+
dependencies:
2662
+
'@babel/core': 7.24.6
2663
+
'@babel/helper-plugin-utils': 7.24.6
2664
+
2665
+
'@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.6)':
2666
+
dependencies:
2667
+
'@babel/core': 7.24.6
2668
+
'@babel/helper-plugin-utils': 7.24.6
2669
+
2670
+
'@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.6)':
2671
+
dependencies:
2672
+
'@babel/core': 7.24.6
2673
+
'@babel/helper-plugin-utils': 7.24.6
2674
+
2675
+
'@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.6)':
2676
+
dependencies:
2677
+
'@babel/core': 7.24.6
2678
+
'@babel/helper-plugin-utils': 7.24.6
2679
+
2680
+
'@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.6)':
2681
+
dependencies:
2682
+
'@babel/core': 7.24.6
2683
+
'@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.6)
2684
+
'@babel/helper-plugin-utils': 7.24.6
2685
+
2686
+
'@babel/plugin-transform-arrow-functions@7.24.6(@babel/core@7.24.6)':
2687
+
dependencies:
2688
+
'@babel/core': 7.24.6
2689
+
'@babel/helper-plugin-utils': 7.24.6
2690
+
2691
+
'@babel/plugin-transform-async-generator-functions@7.24.6(@babel/core@7.24.6)':
2692
+
dependencies:
2693
+
'@babel/core': 7.24.6
2694
+
'@babel/helper-environment-visitor': 7.24.6
2695
+
'@babel/helper-plugin-utils': 7.24.6
2696
+
'@babel/helper-remap-async-to-generator': 7.24.6(@babel/core@7.24.6)
2697
+
'@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.6)
2698
+
2699
+
'@babel/plugin-transform-async-to-generator@7.24.6(@babel/core@7.24.6)':
2700
+
dependencies:
2701
+
'@babel/core': 7.24.6
2702
+
'@babel/helper-module-imports': 7.24.6
2703
+
'@babel/helper-plugin-utils': 7.24.6
2704
+
'@babel/helper-remap-async-to-generator': 7.24.6(@babel/core@7.24.6)
2705
+
2706
+
'@babel/plugin-transform-block-scoped-functions@7.24.6(@babel/core@7.24.6)':
2707
+
dependencies:
2708
+
'@babel/core': 7.24.6
2709
+
'@babel/helper-plugin-utils': 7.24.6
2710
+
2711
+
'@babel/plugin-transform-block-scoping@7.24.6(@babel/core@7.24.6)':
2712
+
dependencies:
2713
+
'@babel/core': 7.24.6
2714
+
'@babel/helper-plugin-utils': 7.24.6
2715
+
2716
+
'@babel/plugin-transform-class-properties@7.24.6(@babel/core@7.24.6)':
2717
+
dependencies:
2718
+
'@babel/core': 7.24.6
2719
+
'@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.6)
2720
+
'@babel/helper-plugin-utils': 7.24.6
2721
+
2722
+
'@babel/plugin-transform-class-static-block@7.24.6(@babel/core@7.24.6)':
2723
+
dependencies:
2724
+
'@babel/core': 7.24.6
2725
+
'@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.6)
2726
+
'@babel/helper-plugin-utils': 7.24.6
2727
+
'@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.6)
2728
+
2729
+
'@babel/plugin-transform-classes@7.24.6(@babel/core@7.24.6)':
2730
+
dependencies:
2731
+
'@babel/core': 7.24.6
2732
+
'@babel/helper-annotate-as-pure': 7.24.6
2733
+
'@babel/helper-compilation-targets': 7.24.6
2734
+
'@babel/helper-environment-visitor': 7.24.6
2735
+
'@babel/helper-function-name': 7.24.6
2736
+
'@babel/helper-plugin-utils': 7.24.6
2737
+
'@babel/helper-replace-supers': 7.24.6(@babel/core@7.24.6)
2738
+
'@babel/helper-split-export-declaration': 7.24.6
2739
+
globals: 11.12.0
2740
+
2741
+
'@babel/plugin-transform-computed-properties@7.24.6(@babel/core@7.24.6)':
2742
+
dependencies:
2743
+
'@babel/core': 7.24.6
2744
+
'@babel/helper-plugin-utils': 7.24.6
2745
+
'@babel/template': 7.24.6
2746
+
2747
+
'@babel/plugin-transform-destructuring@7.24.6(@babel/core@7.24.6)':
2748
+
dependencies:
2749
+
'@babel/core': 7.24.6
2750
+
'@babel/helper-plugin-utils': 7.24.6
2751
+
2752
+
'@babel/plugin-transform-dotall-regex@7.24.6(@babel/core@7.24.6)':
2753
+
dependencies:
2754
+
'@babel/core': 7.24.6
2755
+
'@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.6)
2756
+
'@babel/helper-plugin-utils': 7.24.6
2757
+
2758
+
'@babel/plugin-transform-duplicate-keys@7.24.6(@babel/core@7.24.6)':
2759
+
dependencies:
2760
+
'@babel/core': 7.24.6
2761
+
'@babel/helper-plugin-utils': 7.24.6
2762
+
2763
+
'@babel/plugin-transform-dynamic-import@7.24.6(@babel/core@7.24.6)':
2764
+
dependencies:
2765
+
'@babel/core': 7.24.6
2766
+
'@babel/helper-plugin-utils': 7.24.6
2767
+
'@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.6)
2768
+
2769
+
'@babel/plugin-transform-exponentiation-operator@7.24.6(@babel/core@7.24.6)':
2770
+
dependencies:
2771
+
'@babel/core': 7.24.6
2772
+
'@babel/helper-builder-binary-assignment-operator-visitor': 7.24.6
2773
+
'@babel/helper-plugin-utils': 7.24.6
2774
+
2775
+
'@babel/plugin-transform-export-namespace-from@7.24.6(@babel/core@7.24.6)':
2776
+
dependencies:
2777
+
'@babel/core': 7.24.6
2778
+
'@babel/helper-plugin-utils': 7.24.6
2779
+
'@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.6)
2780
+
2781
+
'@babel/plugin-transform-for-of@7.24.6(@babel/core@7.24.6)':
2782
+
dependencies:
2783
+
'@babel/core': 7.24.6
2784
+
'@babel/helper-plugin-utils': 7.24.6
2785
+
'@babel/helper-skip-transparent-expression-wrappers': 7.24.6
2786
+
2787
+
'@babel/plugin-transform-function-name@7.24.6(@babel/core@7.24.6)':
2788
+
dependencies:
2789
+
'@babel/core': 7.24.6
2790
+
'@babel/helper-compilation-targets': 7.24.6
2791
+
'@babel/helper-function-name': 7.24.6
2792
+
'@babel/helper-plugin-utils': 7.24.6
2793
+
2794
+
'@babel/plugin-transform-json-strings@7.24.6(@babel/core@7.24.6)':
2795
+
dependencies:
2796
+
'@babel/core': 7.24.6
2797
+
'@babel/helper-plugin-utils': 7.24.6
2798
+
'@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.6)
2799
+
2800
+
'@babel/plugin-transform-literals@7.24.6(@babel/core@7.24.6)':
2801
+
dependencies:
2802
+
'@babel/core': 7.24.6
2803
+
'@babel/helper-plugin-utils': 7.24.6
2804
+
2805
+
'@babel/plugin-transform-logical-assignment-operators@7.24.6(@babel/core@7.24.6)':
2806
+
dependencies:
2807
+
'@babel/core': 7.24.6
2808
+
'@babel/helper-plugin-utils': 7.24.6
2809
+
'@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.6)
2810
+
2811
+
'@babel/plugin-transform-member-expression-literals@7.24.6(@babel/core@7.24.6)':
2812
+
dependencies:
2813
+
'@babel/core': 7.24.6
2814
+
'@babel/helper-plugin-utils': 7.24.6
2815
+
2816
+
'@babel/plugin-transform-modules-amd@7.24.6(@babel/core@7.24.6)':
2817
+
dependencies:
2818
+
'@babel/core': 7.24.6
2819
+
'@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.6)
2820
+
'@babel/helper-plugin-utils': 7.24.6
2821
+
2822
+
'@babel/plugin-transform-modules-commonjs@7.24.6(@babel/core@7.24.6)':
2823
+
dependencies:
2824
+
'@babel/core': 7.24.6
2825
+
'@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.6)
2826
+
'@babel/helper-plugin-utils': 7.24.6
2827
+
'@babel/helper-simple-access': 7.24.6
2828
+
2829
+
'@babel/plugin-transform-modules-systemjs@7.24.6(@babel/core@7.24.6)':
2830
+
dependencies:
2831
+
'@babel/core': 7.24.6
2832
+
'@babel/helper-hoist-variables': 7.24.6
2833
+
'@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.6)
2834
+
'@babel/helper-plugin-utils': 7.24.6
2835
+
'@babel/helper-validator-identifier': 7.24.6
2836
+
2837
+
'@babel/plugin-transform-modules-umd@7.24.6(@babel/core@7.24.6)':
2838
+
dependencies:
2839
+
'@babel/core': 7.24.6
2840
+
'@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.6)
2841
+
'@babel/helper-plugin-utils': 7.24.6
2842
+
2843
+
'@babel/plugin-transform-named-capturing-groups-regex@7.24.6(@babel/core@7.24.6)':
2844
+
dependencies:
2845
+
'@babel/core': 7.24.6
2846
+
'@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.6)
2847
+
'@babel/helper-plugin-utils': 7.24.6
2848
+
2849
+
'@babel/plugin-transform-new-target@7.24.6(@babel/core@7.24.6)':
2850
+
dependencies:
2851
+
'@babel/core': 7.24.6
2852
+
'@babel/helper-plugin-utils': 7.24.6
2853
+
2854
+
'@babel/plugin-transform-nullish-coalescing-operator@7.24.6(@babel/core@7.24.6)':
2855
+
dependencies:
2856
+
'@babel/core': 7.24.6
2857
+
'@babel/helper-plugin-utils': 7.24.6
2858
+
'@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.6)
2859
+
2860
+
'@babel/plugin-transform-numeric-separator@7.24.6(@babel/core@7.24.6)':
2861
+
dependencies:
2862
+
'@babel/core': 7.24.6
2863
+
'@babel/helper-plugin-utils': 7.24.6
2864
+
'@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.6)
2865
+
2866
+
'@babel/plugin-transform-object-rest-spread@7.24.6(@babel/core@7.24.6)':
2867
+
dependencies:
2868
+
'@babel/core': 7.24.6
2869
+
'@babel/helper-compilation-targets': 7.24.6
2870
+
'@babel/helper-plugin-utils': 7.24.6
2871
+
'@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.6)
2872
+
'@babel/plugin-transform-parameters': 7.24.6(@babel/core@7.24.6)
2873
+
2874
+
'@babel/plugin-transform-object-super@7.24.6(@babel/core@7.24.6)':
2875
+
dependencies:
2876
+
'@babel/core': 7.24.6
2877
+
'@babel/helper-plugin-utils': 7.24.6
2878
+
'@babel/helper-replace-supers': 7.24.6(@babel/core@7.24.6)
2879
+
2880
+
'@babel/plugin-transform-optional-catch-binding@7.24.6(@babel/core@7.24.6)':
2881
+
dependencies:
2882
+
'@babel/core': 7.24.6
2883
+
'@babel/helper-plugin-utils': 7.24.6
2884
+
'@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.6)
2885
+
2886
+
'@babel/plugin-transform-optional-chaining@7.24.6(@babel/core@7.24.6)':
2887
+
dependencies:
2888
+
'@babel/core': 7.24.6
2889
+
'@babel/helper-plugin-utils': 7.24.6
2890
+
'@babel/helper-skip-transparent-expression-wrappers': 7.24.6
2891
+
'@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.6)
2892
+
2893
+
'@babel/plugin-transform-parameters@7.24.6(@babel/core@7.24.6)':
2894
+
dependencies:
2895
+
'@babel/core': 7.24.6
2896
+
'@babel/helper-plugin-utils': 7.24.6
2897
+
2898
+
'@babel/plugin-transform-private-methods@7.24.6(@babel/core@7.24.6)':
2899
+
dependencies:
2900
+
'@babel/core': 7.24.6
2901
+
'@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.6)
2902
+
'@babel/helper-plugin-utils': 7.24.6
2903
+
2904
+
'@babel/plugin-transform-private-property-in-object@7.24.6(@babel/core@7.24.6)':
2905
+
dependencies:
2906
+
'@babel/core': 7.24.6
2907
+
'@babel/helper-annotate-as-pure': 7.24.6
2908
+
'@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.6)
2909
+
'@babel/helper-plugin-utils': 7.24.6
2910
+
'@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.6)
2911
+
2912
+
'@babel/plugin-transform-property-literals@7.24.6(@babel/core@7.24.6)':
2913
+
dependencies:
2914
+
'@babel/core': 7.24.6
2915
+
'@babel/helper-plugin-utils': 7.24.6
2916
+
2917
+
'@babel/plugin-transform-regenerator@7.24.6(@babel/core@7.24.6)':
2918
+
dependencies:
2919
+
'@babel/core': 7.24.6
2920
+
'@babel/helper-plugin-utils': 7.24.6
2921
+
regenerator-transform: 0.15.2
2922
+
2923
+
'@babel/plugin-transform-reserved-words@7.24.6(@babel/core@7.24.6)':
2924
+
dependencies:
2925
+
'@babel/core': 7.24.6
2926
+
'@babel/helper-plugin-utils': 7.24.6
2927
+
2928
+
'@babel/plugin-transform-shorthand-properties@7.24.6(@babel/core@7.24.6)':
2929
+
dependencies:
2930
+
'@babel/core': 7.24.6
2931
+
'@babel/helper-plugin-utils': 7.24.6
2932
+
2933
+
'@babel/plugin-transform-spread@7.24.6(@babel/core@7.24.6)':
2934
+
dependencies:
2935
+
'@babel/core': 7.24.6
2936
+
'@babel/helper-plugin-utils': 7.24.6
2937
+
'@babel/helper-skip-transparent-expression-wrappers': 7.24.6
2938
+
2939
+
'@babel/plugin-transform-sticky-regex@7.24.6(@babel/core@7.24.6)':
2940
+
dependencies:
2941
+
'@babel/core': 7.24.6
2942
+
'@babel/helper-plugin-utils': 7.24.6
2943
+
2944
+
'@babel/plugin-transform-template-literals@7.24.6(@babel/core@7.24.6)':
2945
+
dependencies:
2946
+
'@babel/core': 7.24.6
2947
+
'@babel/helper-plugin-utils': 7.24.6
2948
+
2949
+
'@babel/plugin-transform-typeof-symbol@7.24.6(@babel/core@7.24.6)':
2950
+
dependencies:
2951
+
'@babel/core': 7.24.6
2952
+
'@babel/helper-plugin-utils': 7.24.6
2953
+
2954
+
'@babel/plugin-transform-unicode-escapes@7.24.6(@babel/core@7.24.6)':
2955
+
dependencies:
2956
+
'@babel/core': 7.24.6
2957
+
'@babel/helper-plugin-utils': 7.24.6
2958
+
2959
+
'@babel/plugin-transform-unicode-property-regex@7.24.6(@babel/core@7.24.6)':
2960
+
dependencies:
2961
+
'@babel/core': 7.24.6
2962
+
'@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.6)
2963
+
'@babel/helper-plugin-utils': 7.24.6
2964
+
2965
+
'@babel/plugin-transform-unicode-regex@7.24.6(@babel/core@7.24.6)':
2966
+
dependencies:
2967
+
'@babel/core': 7.24.6
2968
+
'@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.6)
2969
+
'@babel/helper-plugin-utils': 7.24.6
2970
+
2971
+
'@babel/plugin-transform-unicode-sets-regex@7.24.6(@babel/core@7.24.6)':
2972
+
dependencies:
2973
+
'@babel/core': 7.24.6
2974
+
'@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.6)
2975
+
'@babel/helper-plugin-utils': 7.24.6
2976
+
2977
+
'@babel/preset-env@7.24.6(@babel/core@7.24.6)':
2978
+
dependencies:
2979
+
'@babel/compat-data': 7.24.6
2980
+
'@babel/core': 7.24.6
2981
+
'@babel/helper-compilation-targets': 7.24.6
2982
+
'@babel/helper-plugin-utils': 7.24.6
2983
+
'@babel/helper-validator-option': 7.24.6
2984
+
'@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.6(@babel/core@7.24.6)
2985
+
'@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.6(@babel/core@7.24.6)
2986
+
'@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.6(@babel/core@7.24.6)
2987
+
'@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.24.6(@babel/core@7.24.6)
2988
+
'@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.6)
2989
+
'@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.6)
2990
+
'@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.6)
2991
+
'@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.6)
2992
+
'@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.6)
2993
+
'@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.6)
2994
+
'@babel/plugin-syntax-import-assertions': 7.24.6(@babel/core@7.24.6)
2995
+
'@babel/plugin-syntax-import-attributes': 7.24.6(@babel/core@7.24.6)
2996
+
'@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.6)
2997
+
'@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.6)
2998
+
'@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.6)
2999
+
'@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.6)
3000
+
'@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.6)
3001
+
'@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.6)
3002
+
'@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.6)
3003
+
'@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.6)
3004
+
'@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.6)
3005
+
'@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.6)
3006
+
'@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.6)
3007
+
'@babel/plugin-transform-arrow-functions': 7.24.6(@babel/core@7.24.6)
3008
+
'@babel/plugin-transform-async-generator-functions': 7.24.6(@babel/core@7.24.6)
3009
+
'@babel/plugin-transform-async-to-generator': 7.24.6(@babel/core@7.24.6)
3010
+
'@babel/plugin-transform-block-scoped-functions': 7.24.6(@babel/core@7.24.6)
3011
+
'@babel/plugin-transform-block-scoping': 7.24.6(@babel/core@7.24.6)
3012
+
'@babel/plugin-transform-class-properties': 7.24.6(@babel/core@7.24.6)
3013
+
'@babel/plugin-transform-class-static-block': 7.24.6(@babel/core@7.24.6)
3014
+
'@babel/plugin-transform-classes': 7.24.6(@babel/core@7.24.6)
3015
+
'@babel/plugin-transform-computed-properties': 7.24.6(@babel/core@7.24.6)
3016
+
'@babel/plugin-transform-destructuring': 7.24.6(@babel/core@7.24.6)
3017
+
'@babel/plugin-transform-dotall-regex': 7.24.6(@babel/core@7.24.6)
3018
+
'@babel/plugin-transform-duplicate-keys': 7.24.6(@babel/core@7.24.6)
3019
+
'@babel/plugin-transform-dynamic-import': 7.24.6(@babel/core@7.24.6)
3020
+
'@babel/plugin-transform-exponentiation-operator': 7.24.6(@babel/core@7.24.6)
3021
+
'@babel/plugin-transform-export-namespace-from': 7.24.6(@babel/core@7.24.6)
3022
+
'@babel/plugin-transform-for-of': 7.24.6(@babel/core@7.24.6)
3023
+
'@babel/plugin-transform-function-name': 7.24.6(@babel/core@7.24.6)
3024
+
'@babel/plugin-transform-json-strings': 7.24.6(@babel/core@7.24.6)
3025
+
'@babel/plugin-transform-literals': 7.24.6(@babel/core@7.24.6)
3026
+
'@babel/plugin-transform-logical-assignment-operators': 7.24.6(@babel/core@7.24.6)
3027
+
'@babel/plugin-transform-member-expression-literals': 7.24.6(@babel/core@7.24.6)
3028
+
'@babel/plugin-transform-modules-amd': 7.24.6(@babel/core@7.24.6)
3029
+
'@babel/plugin-transform-modules-commonjs': 7.24.6(@babel/core@7.24.6)
3030
+
'@babel/plugin-transform-modules-systemjs': 7.24.6(@babel/core@7.24.6)
3031
+
'@babel/plugin-transform-modules-umd': 7.24.6(@babel/core@7.24.6)
3032
+
'@babel/plugin-transform-named-capturing-groups-regex': 7.24.6(@babel/core@7.24.6)
3033
+
'@babel/plugin-transform-new-target': 7.24.6(@babel/core@7.24.6)
3034
+
'@babel/plugin-transform-nullish-coalescing-operator': 7.24.6(@babel/core@7.24.6)
3035
+
'@babel/plugin-transform-numeric-separator': 7.24.6(@babel/core@7.24.6)
3036
+
'@babel/plugin-transform-object-rest-spread': 7.24.6(@babel/core@7.24.6)
3037
+
'@babel/plugin-transform-object-super': 7.24.6(@babel/core@7.24.6)
3038
+
'@babel/plugin-transform-optional-catch-binding': 7.24.6(@babel/core@7.24.6)
3039
+
'@babel/plugin-transform-optional-chaining': 7.24.6(@babel/core@7.24.6)
3040
+
'@babel/plugin-transform-parameters': 7.24.6(@babel/core@7.24.6)
3041
+
'@babel/plugin-transform-private-methods': 7.24.6(@babel/core@7.24.6)
3042
+
'@babel/plugin-transform-private-property-in-object': 7.24.6(@babel/core@7.24.6)
3043
+
'@babel/plugin-transform-property-literals': 7.24.6(@babel/core@7.24.6)
3044
+
'@babel/plugin-transform-regenerator': 7.24.6(@babel/core@7.24.6)
3045
+
'@babel/plugin-transform-reserved-words': 7.24.6(@babel/core@7.24.6)
3046
+
'@babel/plugin-transform-shorthand-properties': 7.24.6(@babel/core@7.24.6)
3047
+
'@babel/plugin-transform-spread': 7.24.6(@babel/core@7.24.6)
3048
+
'@babel/plugin-transform-sticky-regex': 7.24.6(@babel/core@7.24.6)
3049
+
'@babel/plugin-transform-template-literals': 7.24.6(@babel/core@7.24.6)
3050
+
'@babel/plugin-transform-typeof-symbol': 7.24.6(@babel/core@7.24.6)
3051
+
'@babel/plugin-transform-unicode-escapes': 7.24.6(@babel/core@7.24.6)
3052
+
'@babel/plugin-transform-unicode-property-regex': 7.24.6(@babel/core@7.24.6)
3053
+
'@babel/plugin-transform-unicode-regex': 7.24.6(@babel/core@7.24.6)
3054
+
'@babel/plugin-transform-unicode-sets-regex': 7.24.6(@babel/core@7.24.6)
3055
+
'@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.6)
3056
+
babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.24.6)
3057
+
babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.6)
3058
+
babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.24.6)
3059
+
core-js-compat: 3.37.1
3060
+
semver: 6.3.1
3061
+
transitivePeerDependencies:
3062
+
- supports-color
3063
+
3064
+
'@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.6)':
3065
+
dependencies:
3066
+
'@babel/core': 7.24.6
3067
+
'@babel/helper-plugin-utils': 7.24.6
3068
+
'@babel/types': 7.24.6
3069
+
esutils: 2.0.3
3070
+
3071
+
'@babel/regjsgen@0.8.0': {}
3072
+
3073
+
'@babel/runtime@7.24.6':
3074
+
dependencies:
3075
+
regenerator-runtime: 0.14.1
3076
+
3077
+
'@babel/template@7.24.6':
3078
+
dependencies:
3079
+
'@babel/code-frame': 7.24.6
3080
+
'@babel/parser': 7.24.6
3081
+
'@babel/types': 7.24.6
3082
+
3083
+
'@babel/traverse@7.24.6':
3084
+
dependencies:
3085
+
'@babel/code-frame': 7.24.6
3086
+
'@babel/generator': 7.24.6
3087
+
'@babel/helper-environment-visitor': 7.24.6
3088
+
'@babel/helper-function-name': 7.24.6
3089
+
'@babel/helper-hoist-variables': 7.24.6
3090
+
'@babel/helper-split-export-declaration': 7.24.6
3091
+
'@babel/parser': 7.24.6
3092
+
'@babel/types': 7.24.6
3093
+
debug: 4.3.4
3094
+
globals: 11.12.0
3095
+
transitivePeerDependencies:
3096
+
- supports-color
3097
+
3098
+
'@babel/types@7.24.6':
3099
+
dependencies:
3100
+
'@babel/helper-string-parser': 7.24.6
3101
+
'@babel/helper-validator-identifier': 7.24.6
3102
+
to-fast-properties: 2.0.0
3103
+
3104
+
'@esbuild/aix-ppc64@0.20.2':
3105
+
optional: true
3106
+
3107
+
'@esbuild/android-arm64@0.20.2':
3108
+
optional: true
3109
+
3110
+
'@esbuild/android-arm@0.20.2':
3111
+
optional: true
3112
+
3113
+
'@esbuild/android-x64@0.20.2':
3114
+
optional: true
3115
+
3116
+
'@esbuild/darwin-arm64@0.20.2':
3117
+
optional: true
3118
+
3119
+
'@esbuild/darwin-x64@0.20.2':
3120
+
optional: true
3121
+
3122
+
'@esbuild/freebsd-arm64@0.20.2':
3123
+
optional: true
3124
+
3125
+
'@esbuild/freebsd-x64@0.20.2':
3126
+
optional: true
3127
+
3128
+
'@esbuild/linux-arm64@0.20.2':
3129
+
optional: true
3130
+
3131
+
'@esbuild/linux-arm@0.20.2':
3132
+
optional: true
3133
+
3134
+
'@esbuild/linux-ia32@0.20.2':
3135
+
optional: true
3136
+
3137
+
'@esbuild/linux-loong64@0.20.2':
3138
+
optional: true
3139
+
3140
+
'@esbuild/linux-mips64el@0.20.2':
3141
+
optional: true
3142
+
3143
+
'@esbuild/linux-ppc64@0.20.2':
3144
+
optional: true
3145
+
3146
+
'@esbuild/linux-riscv64@0.20.2':
3147
+
optional: true
3148
+
3149
+
'@esbuild/linux-s390x@0.20.2':
3150
+
optional: true
3151
+
3152
+
'@esbuild/linux-x64@0.20.2':
3153
+
optional: true
3154
+
3155
+
'@esbuild/netbsd-x64@0.20.2':
3156
+
optional: true
3157
+
3158
+
'@esbuild/openbsd-x64@0.20.2':
3159
+
optional: true
3160
+
3161
+
'@esbuild/sunos-x64@0.20.2':
3162
+
optional: true
3163
+
3164
+
'@esbuild/win32-arm64@0.20.2':
3165
+
optional: true
3166
+
3167
+
'@esbuild/win32-ia32@0.20.2':
3168
+
optional: true
3169
+
3170
+
'@esbuild/win32-x64@0.20.2':
3171
+
optional: true
3172
+
3173
+
'@externdefs/solid-freeze@0.1.1(solid-js@1.8.17(patch_hash=wunpcbjxb5h4ujg4psj63uuluq))':
3174
+
dependencies:
3175
+
solid-js: 1.8.17(patch_hash=wunpcbjxb5h4ujg4psj63uuluq)
3176
+
3177
+
'@externdefs/solid-query@0.1.4(solid-js@1.8.17(patch_hash=wunpcbjxb5h4ujg4psj63uuluq))':
3178
+
dependencies:
3179
+
'@tanstack/query-core': 5.17.19(patch_hash=v3r5daycwz67li5mxwjnajy7bm)
3180
+
solid-js: 1.8.17(patch_hash=wunpcbjxb5h4ujg4psj63uuluq)
3181
+
3182
+
'@floating-ui/core@1.6.2':
3183
+
dependencies:
3184
+
'@floating-ui/utils': 0.2.2
3185
+
3186
+
'@floating-ui/dom@1.6.5':
3187
+
dependencies:
3188
+
'@floating-ui/core': 1.6.2
3189
+
'@floating-ui/utils': 0.2.2
3190
+
3191
+
'@floating-ui/utils@0.2.2': {}
3192
+
3193
+
'@isaacs/cliui@8.0.2':
3194
+
dependencies:
3195
+
string-width: 5.1.2
3196
+
string-width-cjs: string-width@4.2.3
3197
+
strip-ansi: 7.1.0
3198
+
strip-ansi-cjs: strip-ansi@6.0.1
3199
+
wrap-ansi: 8.1.0
3200
+
wrap-ansi-cjs: wrap-ansi@7.0.0
3201
+
3202
+
'@jridgewell/gen-mapping@0.3.5':
3203
+
dependencies:
3204
+
'@jridgewell/set-array': 1.2.1
3205
+
'@jridgewell/sourcemap-codec': 1.4.15
3206
+
'@jridgewell/trace-mapping': 0.3.25
3207
+
3208
+
'@jridgewell/resolve-uri@3.1.2': {}
3209
+
3210
+
'@jridgewell/set-array@1.2.1': {}
3211
+
3212
+
'@jridgewell/source-map@0.3.6':
3213
+
dependencies:
3214
+
'@jridgewell/gen-mapping': 0.3.5
3215
+
'@jridgewell/trace-mapping': 0.3.25
3216
+
3217
+
'@jridgewell/sourcemap-codec@1.4.15': {}
3218
+
3219
+
'@jridgewell/trace-mapping@0.3.25':
3220
+
dependencies:
3221
+
'@jridgewell/resolve-uri': 3.1.2
3222
+
'@jridgewell/sourcemap-codec': 1.4.15
3223
+
3224
+
'@jsr/mary__bluesky-client@0.5.23': {}
3225
+
3226
+
'@jsr/mary__events@0.1.0': {}
3227
+
3228
+
'@nodelib/fs.scandir@2.1.5':
3229
+
dependencies:
3230
+
'@nodelib/fs.stat': 2.0.5
3231
+
run-parallel: 1.2.0
3232
+
3233
+
'@nodelib/fs.stat@2.0.5': {}
3234
+
3235
+
'@nodelib/fs.walk@1.2.8':
3236
+
dependencies:
3237
+
'@nodelib/fs.scandir': 2.1.5
3238
+
fastq: 1.17.1
3239
+
3240
+
'@pkgjs/parseargs@0.11.0':
3241
+
optional: true
3242
+
3243
+
'@rollup/plugin-babel@5.3.1(@babel/core@7.24.6)(@types/babel__core@7.20.5)(rollup@2.79.1)':
3244
+
dependencies:
3245
+
'@babel/core': 7.24.6
3246
+
'@babel/helper-module-imports': 7.24.6
3247
+
'@rollup/pluginutils': 3.1.0(rollup@2.79.1)
3248
+
rollup: 2.79.1
3249
+
optionalDependencies:
3250
+
'@types/babel__core': 7.20.5
3251
+
3252
+
'@rollup/plugin-node-resolve@15.2.3(rollup@2.79.1)':
3253
+
dependencies:
3254
+
'@rollup/pluginutils': 5.1.0(rollup@2.79.1)
3255
+
'@types/resolve': 1.20.2
3256
+
deepmerge: 4.3.1
3257
+
is-builtin-module: 3.2.1
3258
+
is-module: 1.0.0
3259
+
resolve: 1.22.8
3260
+
optionalDependencies:
3261
+
rollup: 2.79.1
3262
+
3263
+
'@rollup/plugin-replace@2.4.2(rollup@2.79.1)':
3264
+
dependencies:
3265
+
'@rollup/pluginutils': 3.1.0(rollup@2.79.1)
3266
+
magic-string: 0.25.9
3267
+
rollup: 2.79.1
3268
+
3269
+
'@rollup/plugin-terser@0.4.4(rollup@2.79.1)':
3270
+
dependencies:
3271
+
serialize-javascript: 6.0.2
3272
+
smob: 1.5.0
3273
+
terser: 5.31.1
3274
+
optionalDependencies:
3275
+
rollup: 2.79.1
3276
+
3277
+
'@rollup/pluginutils@3.1.0(rollup@2.79.1)':
3278
+
dependencies:
3279
+
'@types/estree': 0.0.39
3280
+
estree-walker: 1.0.1
3281
+
picomatch: 2.3.1
3282
+
rollup: 2.79.1
3283
+
3284
+
'@rollup/pluginutils@5.1.0(rollup@2.79.1)':
3285
+
dependencies:
3286
+
'@types/estree': 1.0.5
3287
+
estree-walker: 2.0.2
3288
+
picomatch: 2.3.1
3289
+
optionalDependencies:
3290
+
rollup: 2.79.1
3291
+
3292
+
'@rollup/rollup-android-arm-eabi@4.18.0':
3293
+
optional: true
3294
+
3295
+
'@rollup/rollup-android-arm64@4.18.0':
3296
+
optional: true
3297
+
3298
+
'@rollup/rollup-darwin-arm64@4.18.0':
3299
+
optional: true
3300
+
3301
+
'@rollup/rollup-darwin-x64@4.18.0':
3302
+
optional: true
3303
+
3304
+
'@rollup/rollup-linux-arm-gnueabihf@4.18.0':
3305
+
optional: true
3306
+
3307
+
'@rollup/rollup-linux-arm-musleabihf@4.18.0':
3308
+
optional: true
3309
+
3310
+
'@rollup/rollup-linux-arm64-gnu@4.18.0':
3311
+
optional: true
3312
+
3313
+
'@rollup/rollup-linux-arm64-musl@4.18.0':
3314
+
optional: true
3315
+
3316
+
'@rollup/rollup-linux-powerpc64le-gnu@4.18.0':
3317
+
optional: true
3318
+
3319
+
'@rollup/rollup-linux-riscv64-gnu@4.18.0':
3320
+
optional: true
3321
+
3322
+
'@rollup/rollup-linux-s390x-gnu@4.18.0':
3323
+
optional: true
3324
+
3325
+
'@rollup/rollup-linux-x64-gnu@4.18.0':
3326
+
optional: true
3327
+
3328
+
'@rollup/rollup-linux-x64-musl@4.18.0':
3329
+
optional: true
3330
+
3331
+
'@rollup/rollup-win32-arm64-msvc@4.18.0':
3332
+
optional: true
3333
+
3334
+
'@rollup/rollup-win32-ia32-msvc@4.18.0':
3335
+
optional: true
3336
+
3337
+
'@rollup/rollup-win32-x64-msvc@4.18.0':
3338
+
optional: true
3339
+
3340
+
'@surma/rollup-plugin-off-main-thread@2.2.3':
3341
+
dependencies:
3342
+
ejs: 3.1.10
3343
+
json5: 2.2.3
3344
+
magic-string: 0.25.9
3345
+
string.prototype.matchall: 4.0.11
3346
+
3347
+
'@tanstack/query-core@5.17.19(patch_hash=v3r5daycwz67li5mxwjnajy7bm)': {}
3348
+
3349
+
'@types/babel__core@7.20.5':
3350
+
dependencies:
3351
+
'@babel/parser': 7.24.6
3352
+
'@babel/types': 7.24.6
3353
+
'@types/babel__generator': 7.6.8
3354
+
'@types/babel__template': 7.4.4
3355
+
'@types/babel__traverse': 7.20.6
3356
+
3357
+
'@types/babel__generator@7.6.8':
3358
+
dependencies:
3359
+
'@babel/types': 7.24.6
3360
+
3361
+
'@types/babel__template@7.4.4':
3362
+
dependencies:
3363
+
'@babel/parser': 7.24.6
3364
+
'@babel/types': 7.24.6
3365
+
3366
+
'@types/babel__traverse@7.20.6':
3367
+
dependencies:
3368
+
'@babel/types': 7.24.6
3369
+
3370
+
'@types/dom-close-watcher@1.0.0': {}
3371
+
3372
+
'@types/estree@0.0.39': {}
3373
+
3374
+
'@types/estree@1.0.5': {}
3375
+
3376
+
'@types/resolve@1.20.2': {}
3377
+
3378
+
'@types/trusted-types@2.0.7': {}
3379
+
3380
+
acorn@8.11.3: {}
3381
+
3382
+
ajv@8.14.0:
3383
+
dependencies:
3384
+
fast-deep-equal: 3.1.3
3385
+
json-schema-traverse: 1.0.0
3386
+
require-from-string: 2.0.2
3387
+
uri-js: 4.4.1
3388
+
3389
+
ansi-regex@5.0.1: {}
3390
+
3391
+
ansi-regex@6.0.1: {}
3392
+
3393
+
ansi-styles@3.2.1:
3394
+
dependencies:
3395
+
color-convert: 1.9.3
3396
+
3397
+
ansi-styles@4.3.0:
3398
+
dependencies:
3399
+
color-convert: 2.0.1
3400
+
3401
+
ansi-styles@6.2.1: {}
3402
+
3403
+
any-promise@1.3.0: {}
3404
+
3405
+
anymatch@3.1.3:
3406
+
dependencies:
3407
+
normalize-path: 3.0.0
3408
+
picomatch: 2.3.1
3409
+
3410
+
arg@5.0.2: {}
3411
+
3412
+
array-buffer-byte-length@1.0.1:
3413
+
dependencies:
3414
+
call-bind: 1.0.7
3415
+
is-array-buffer: 3.0.4
3416
+
3417
+
arraybuffer.prototype.slice@1.0.3:
3418
+
dependencies:
3419
+
array-buffer-byte-length: 1.0.1
3420
+
call-bind: 1.0.7
3421
+
define-properties: 1.2.1
3422
+
es-abstract: 1.23.3
3423
+
es-errors: 1.3.0
3424
+
get-intrinsic: 1.2.4
3425
+
is-array-buffer: 3.0.4
3426
+
is-shared-array-buffer: 1.0.3
3427
+
3428
+
async@3.2.5: {}
3429
+
3430
+
at-least-node@1.0.0: {}
3431
+
3432
+
autoprefixer@10.4.19(postcss@8.4.38):
3433
+
dependencies:
3434
+
browserslist: 4.23.0
3435
+
caniuse-lite: 1.0.30001623
3436
+
fraction.js: 4.3.7
3437
+
normalize-range: 0.1.2
3438
+
picocolors: 1.0.1
3439
+
postcss: 8.4.38
3440
+
postcss-value-parser: 4.2.0
3441
+
3442
+
available-typed-arrays@1.0.7:
3443
+
dependencies:
3444
+
possible-typed-array-names: 1.0.0
3445
+
3446
+
babel-plugin-jsx-dom-expressions@0.37.21(@babel/core@7.24.6):
3447
+
dependencies:
3448
+
'@babel/core': 7.24.6
3449
+
'@babel/helper-module-imports': 7.18.6
3450
+
'@babel/plugin-syntax-jsx': 7.24.6(@babel/core@7.24.6)
3451
+
'@babel/types': 7.24.6
3452
+
html-entities: 2.3.3
3453
+
validate-html-nesting: 1.2.2
3454
+
3455
+
babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.24.6):
3456
+
dependencies:
3457
+
'@babel/compat-data': 7.24.6
3458
+
'@babel/core': 7.24.6
3459
+
'@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.6)
3460
+
semver: 6.3.1
3461
+
transitivePeerDependencies:
3462
+
- supports-color
3463
+
3464
+
babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.24.6):
3465
+
dependencies:
3466
+
'@babel/core': 7.24.6
3467
+
'@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.6)
3468
+
core-js-compat: 3.37.1
3469
+
transitivePeerDependencies:
3470
+
- supports-color
3471
+
3472
+
babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.24.6):
3473
+
dependencies:
3474
+
'@babel/core': 7.24.6
3475
+
'@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.6)
3476
+
transitivePeerDependencies:
3477
+
- supports-color
3478
+
3479
+
babel-preset-solid@1.8.17(@babel/core@7.24.6):
3480
+
dependencies:
3481
+
'@babel/core': 7.24.6
3482
+
babel-plugin-jsx-dom-expressions: 0.37.21(@babel/core@7.24.6)
3483
+
3484
+
balanced-match@1.0.2: {}
3485
+
3486
+
binary-extensions@2.3.0: {}
3487
+
3488
+
brace-expansion@1.1.11:
3489
+
dependencies:
3490
+
balanced-match: 1.0.2
3491
+
concat-map: 0.0.1
3492
+
3493
+
brace-expansion@2.0.1:
3494
+
dependencies:
3495
+
balanced-match: 1.0.2
3496
+
3497
+
braces@3.0.3:
3498
+
dependencies:
3499
+
fill-range: 7.1.1
3500
+
3501
+
browserslist@4.23.0:
3502
+
dependencies:
3503
+
caniuse-lite: 1.0.30001623
3504
+
electron-to-chromium: 1.4.783
3505
+
node-releases: 2.0.14
3506
+
update-browserslist-db: 1.0.16(browserslist@4.23.0)
3507
+
3508
+
buffer-from@1.1.2: {}
3509
+
3510
+
builtin-modules@3.3.0: {}
3511
+
3512
+
call-bind@1.0.7:
3513
+
dependencies:
3514
+
es-define-property: 1.0.0
3515
+
es-errors: 1.3.0
3516
+
function-bind: 1.1.2
3517
+
get-intrinsic: 1.2.4
3518
+
set-function-length: 1.2.2
3519
+
3520
+
camelcase-css@2.0.1: {}
3521
+
3522
+
caniuse-lite@1.0.30001623: {}
3523
+
3524
+
cborg@4.0.7(patch_hash=dz6b6r6dc4jadjfng7vtgi53hy): {}
3525
+
3526
+
chalk@2.4.2:
3527
+
dependencies:
3528
+
ansi-styles: 3.2.1
3529
+
escape-string-regexp: 1.0.5
3530
+
supports-color: 5.5.0
3531
+
3532
+
chalk@4.1.2:
3533
+
dependencies:
3534
+
ansi-styles: 4.3.0
3535
+
supports-color: 7.2.0
3536
+
3537
+
chokidar@3.6.0:
3538
+
dependencies:
3539
+
anymatch: 3.1.3
3540
+
braces: 3.0.3
3541
+
glob-parent: 5.1.2
3542
+
is-binary-path: 2.1.0
3543
+
is-glob: 4.0.3
3544
+
normalize-path: 3.0.0
3545
+
readdirp: 3.6.0
3546
+
optionalDependencies:
3547
+
fsevents: 2.3.3
3548
+
3549
+
color-convert@1.9.3:
3550
+
dependencies:
3551
+
color-name: 1.1.3
3552
+
3553
+
color-convert@2.0.1:
3554
+
dependencies:
3555
+
color-name: 1.1.4
3556
+
3557
+
color-name@1.1.3: {}
3558
+
3559
+
color-name@1.1.4: {}
3560
+
3561
+
commander@2.20.3: {}
3562
+
3563
+
commander@4.1.1: {}
3564
+
3565
+
common-tags@1.8.2: {}
3566
+
3567
+
concat-map@0.0.1: {}
3568
+
3569
+
convert-source-map@2.0.0: {}
3570
+
3571
+
core-js-compat@3.37.1:
3572
+
dependencies:
3573
+
browserslist: 4.23.0
3574
+
3575
+
cross-spawn@7.0.3:
3576
+
dependencies:
3577
+
path-key: 3.1.1
3578
+
shebang-command: 2.0.0
3579
+
which: 2.0.2
3580
+
3581
+
crypto-random-string@2.0.0: {}
3582
+
3583
+
cssesc@3.0.0: {}
3584
+
3585
+
csstype@3.1.3: {}
3586
+
3587
+
data-view-buffer@1.0.1:
3588
+
dependencies:
3589
+
call-bind: 1.0.7
3590
+
es-errors: 1.3.0
3591
+
is-data-view: 1.0.1
3592
+
3593
+
data-view-byte-length@1.0.1:
3594
+
dependencies:
3595
+
call-bind: 1.0.7
3596
+
es-errors: 1.3.0
3597
+
is-data-view: 1.0.1
3598
+
3599
+
data-view-byte-offset@1.0.0:
3600
+
dependencies:
3601
+
call-bind: 1.0.7
3602
+
es-errors: 1.3.0
3603
+
is-data-view: 1.0.1
3604
+
3605
+
debug@4.3.4:
3606
+
dependencies:
3607
+
ms: 2.1.2
3608
+
3609
+
deepmerge@4.3.1: {}
3610
+
3611
+
define-data-property@1.1.4:
3612
+
dependencies:
3613
+
es-define-property: 1.0.0
3614
+
es-errors: 1.3.0
3615
+
gopd: 1.0.1
3616
+
3617
+
define-properties@1.2.1:
3618
+
dependencies:
3619
+
define-data-property: 1.1.4
3620
+
has-property-descriptors: 1.0.2
3621
+
object-keys: 1.1.1
3622
+
3623
+
didyoumean@1.2.2: {}
3624
+
3625
+
dlv@1.1.3: {}
3626
+
3627
+
eastasianwidth@0.2.0: {}
3628
+
3629
+
ejs@3.1.10:
3630
+
dependencies:
3631
+
jake: 10.9.1
3632
+
3633
+
electron-to-chromium@1.4.783: {}
3634
+
3635
+
emoji-regex@8.0.0: {}
3636
+
3637
+
emoji-regex@9.2.2: {}
3638
+
3639
+
es-abstract@1.23.3:
3640
+
dependencies:
3641
+
array-buffer-byte-length: 1.0.1
3642
+
arraybuffer.prototype.slice: 1.0.3
3643
+
available-typed-arrays: 1.0.7
3644
+
call-bind: 1.0.7
3645
+
data-view-buffer: 1.0.1
3646
+
data-view-byte-length: 1.0.1
3647
+
data-view-byte-offset: 1.0.0
3648
+
es-define-property: 1.0.0
3649
+
es-errors: 1.3.0
3650
+
es-object-atoms: 1.0.0
3651
+
es-set-tostringtag: 2.0.3
3652
+
es-to-primitive: 1.2.1
3653
+
function.prototype.name: 1.1.6
3654
+
get-intrinsic: 1.2.4
3655
+
get-symbol-description: 1.0.2
3656
+
globalthis: 1.0.4
3657
+
gopd: 1.0.1
3658
+
has-property-descriptors: 1.0.2
3659
+
has-proto: 1.0.3
3660
+
has-symbols: 1.0.3
3661
+
hasown: 2.0.2
3662
+
internal-slot: 1.0.7
3663
+
is-array-buffer: 3.0.4
3664
+
is-callable: 1.2.7
3665
+
is-data-view: 1.0.1
3666
+
is-negative-zero: 2.0.3
3667
+
is-regex: 1.1.4
3668
+
is-shared-array-buffer: 1.0.3
3669
+
is-string: 1.0.7
3670
+
is-typed-array: 1.1.13
3671
+
is-weakref: 1.0.2
3672
+
object-inspect: 1.13.1
3673
+
object-keys: 1.1.1
3674
+
object.assign: 4.1.5
3675
+
regexp.prototype.flags: 1.5.2
3676
+
safe-array-concat: 1.1.2
3677
+
safe-regex-test: 1.0.3
3678
+
string.prototype.trim: 1.2.9
3679
+
string.prototype.trimend: 1.0.8
3680
+
string.prototype.trimstart: 1.0.8
3681
+
typed-array-buffer: 1.0.2
3682
+
typed-array-byte-length: 1.0.1
3683
+
typed-array-byte-offset: 1.0.2
3684
+
typed-array-length: 1.0.6
3685
+
unbox-primitive: 1.0.2
3686
+
which-typed-array: 1.1.15
3687
+
3688
+
es-define-property@1.0.0:
3689
+
dependencies:
3690
+
get-intrinsic: 1.2.4
3691
+
3692
+
es-errors@1.3.0: {}
3693
+
3694
+
es-object-atoms@1.0.0:
3695
+
dependencies:
3696
+
es-errors: 1.3.0
3697
+
3698
+
es-set-tostringtag@2.0.3:
3699
+
dependencies:
3700
+
get-intrinsic: 1.2.4
3701
+
has-tostringtag: 1.0.2
3702
+
hasown: 2.0.2
3703
+
3704
+
es-to-primitive@1.2.1:
3705
+
dependencies:
3706
+
is-callable: 1.2.7
3707
+
is-date-object: 1.0.5
3708
+
is-symbol: 1.0.4
3709
+
3710
+
esbuild@0.20.2:
3711
+
optionalDependencies:
3712
+
'@esbuild/aix-ppc64': 0.20.2
3713
+
'@esbuild/android-arm': 0.20.2
3714
+
'@esbuild/android-arm64': 0.20.2
3715
+
'@esbuild/android-x64': 0.20.2
3716
+
'@esbuild/darwin-arm64': 0.20.2
3717
+
'@esbuild/darwin-x64': 0.20.2
3718
+
'@esbuild/freebsd-arm64': 0.20.2
3719
+
'@esbuild/freebsd-x64': 0.20.2
3720
+
'@esbuild/linux-arm': 0.20.2
3721
+
'@esbuild/linux-arm64': 0.20.2
3722
+
'@esbuild/linux-ia32': 0.20.2
3723
+
'@esbuild/linux-loong64': 0.20.2
3724
+
'@esbuild/linux-mips64el': 0.20.2
3725
+
'@esbuild/linux-ppc64': 0.20.2
3726
+
'@esbuild/linux-riscv64': 0.20.2
3727
+
'@esbuild/linux-s390x': 0.20.2
3728
+
'@esbuild/linux-x64': 0.20.2
3729
+
'@esbuild/netbsd-x64': 0.20.2
3730
+
'@esbuild/openbsd-x64': 0.20.2
3731
+
'@esbuild/sunos-x64': 0.20.2
3732
+
'@esbuild/win32-arm64': 0.20.2
3733
+
'@esbuild/win32-ia32': 0.20.2
3734
+
'@esbuild/win32-x64': 0.20.2
3735
+
3736
+
escalade@3.1.2: {}
3737
+
3738
+
escape-string-regexp@1.0.5: {}
3739
+
3740
+
estree-walker@1.0.1: {}
3741
+
3742
+
estree-walker@2.0.2: {}
3743
+
3744
+
esutils@2.0.3: {}
3745
+
3746
+
fast-deep-equal@3.1.3: {}
3747
+
3748
+
fast-glob@3.3.2:
3749
+
dependencies:
3750
+
'@nodelib/fs.stat': 2.0.5
3751
+
'@nodelib/fs.walk': 1.2.8
3752
+
glob-parent: 5.1.2
3753
+
merge2: 1.4.1
3754
+
micromatch: 4.0.7
3755
+
3756
+
fast-json-stable-stringify@2.1.0: {}
3757
+
3758
+
fastq@1.17.1:
3759
+
dependencies:
3760
+
reusify: 1.0.4
3761
+
3762
+
filelist@1.0.4:
3763
+
dependencies:
3764
+
minimatch: 5.1.6
3765
+
3766
+
fill-range@7.1.1:
3767
+
dependencies:
3768
+
to-regex-range: 5.0.1
3769
+
3770
+
for-each@0.3.3:
3771
+
dependencies:
3772
+
is-callable: 1.2.7
3773
+
3774
+
foreground-child@3.1.1:
3775
+
dependencies:
3776
+
cross-spawn: 7.0.3
3777
+
signal-exit: 4.1.0
3778
+
3779
+
fraction.js@4.3.7: {}
3780
+
3781
+
fs-extra@9.1.0:
3782
+
dependencies:
3783
+
at-least-node: 1.0.0
3784
+
graceful-fs: 4.2.11
3785
+
jsonfile: 6.1.0
3786
+
universalify: 2.0.1
3787
+
3788
+
fs.realpath@1.0.0: {}
3789
+
3790
+
fsevents@2.3.3:
3791
+
optional: true
3792
+
3793
+
function-bind@1.1.2: {}
3794
+
3795
+
function.prototype.name@1.1.6:
3796
+
dependencies:
3797
+
call-bind: 1.0.7
3798
+
define-properties: 1.2.1
3799
+
es-abstract: 1.23.3
3800
+
functions-have-names: 1.2.3
3801
+
3802
+
functions-have-names@1.2.3: {}
3803
+
3804
+
gensync@1.0.0-beta.2: {}
3805
+
3806
+
get-intrinsic@1.2.4:
3807
+
dependencies:
3808
+
es-errors: 1.3.0
3809
+
function-bind: 1.1.2
3810
+
has-proto: 1.0.3
3811
+
has-symbols: 1.0.3
3812
+
hasown: 2.0.2
3813
+
3814
+
get-own-enumerable-property-symbols@3.0.2: {}
3815
+
3816
+
get-symbol-description@1.0.2:
3817
+
dependencies:
3818
+
call-bind: 1.0.7
3819
+
es-errors: 1.3.0
3820
+
get-intrinsic: 1.2.4
3821
+
3822
+
glob-parent@5.1.2:
3823
+
dependencies:
3824
+
is-glob: 4.0.3
3825
+
3826
+
glob-parent@6.0.2:
3827
+
dependencies:
3828
+
is-glob: 4.0.3
3829
+
3830
+
glob@10.4.1:
3831
+
dependencies:
3832
+
foreground-child: 3.1.1
3833
+
jackspeak: 3.4.0
3834
+
minimatch: 9.0.4
3835
+
minipass: 7.1.2
3836
+
path-scurry: 1.11.1
3837
+
3838
+
glob@7.2.3:
3839
+
dependencies:
3840
+
fs.realpath: 1.0.0
3841
+
inflight: 1.0.6
3842
+
inherits: 2.0.4
3843
+
minimatch: 3.1.2
3844
+
once: 1.4.0
3845
+
path-is-absolute: 1.0.1
3846
+
3847
+
globals@11.12.0: {}
3848
+
3849
+
globalthis@1.0.4:
3850
+
dependencies:
3851
+
define-properties: 1.2.1
3852
+
gopd: 1.0.1
3853
+
3854
+
gopd@1.0.1:
3855
+
dependencies:
3856
+
get-intrinsic: 1.2.4
3857
+
3858
+
graceful-fs@4.2.11: {}
3859
+
3860
+
has-bigints@1.0.2: {}
3861
+
3862
+
has-flag@3.0.0: {}
3863
+
3864
+
has-flag@4.0.0: {}
3865
+
3866
+
has-property-descriptors@1.0.2:
3867
+
dependencies:
3868
+
es-define-property: 1.0.0
3869
+
3870
+
has-proto@1.0.3: {}
3871
+
3872
+
has-symbols@1.0.3: {}
3873
+
3874
+
has-tostringtag@1.0.2:
3875
+
dependencies:
3876
+
has-symbols: 1.0.3
3877
+
3878
+
hasown@2.0.2:
3879
+
dependencies:
3880
+
function-bind: 1.1.2
3881
+
3882
+
html-entities@2.3.3: {}
3883
+
3884
+
idb@7.1.1: {}
3885
+
3886
+
inflight@1.0.6:
3887
+
dependencies:
3888
+
once: 1.4.0
3889
+
wrappy: 1.0.2
3890
+
3891
+
inherits@2.0.4: {}
3892
+
3893
+
internal-slot@1.0.7:
3894
+
dependencies:
3895
+
es-errors: 1.3.0
3896
+
hasown: 2.0.2
3897
+
side-channel: 1.0.6
3898
+
3899
+
is-array-buffer@3.0.4:
3900
+
dependencies:
3901
+
call-bind: 1.0.7
3902
+
get-intrinsic: 1.2.4
3903
+
3904
+
is-bigint@1.0.4:
3905
+
dependencies:
3906
+
has-bigints: 1.0.2
3907
+
3908
+
is-binary-path@2.1.0:
3909
+
dependencies:
3910
+
binary-extensions: 2.3.0
3911
+
3912
+
is-boolean-object@1.1.2:
3913
+
dependencies:
3914
+
call-bind: 1.0.7
3915
+
has-tostringtag: 1.0.2
3916
+
3917
+
is-builtin-module@3.2.1:
3918
+
dependencies:
3919
+
builtin-modules: 3.3.0
3920
+
3921
+
is-callable@1.2.7: {}
3922
+
3923
+
is-core-module@2.13.1:
3924
+
dependencies:
3925
+
hasown: 2.0.2
3926
+
3927
+
is-data-view@1.0.1:
3928
+
dependencies:
3929
+
is-typed-array: 1.1.13
3930
+
3931
+
is-date-object@1.0.5:
3932
+
dependencies:
3933
+
has-tostringtag: 1.0.2
3934
+
3935
+
is-extglob@2.1.1: {}
3936
+
3937
+
is-fullwidth-code-point@3.0.0: {}
3938
+
3939
+
is-glob@4.0.3:
3940
+
dependencies:
3941
+
is-extglob: 2.1.1
3942
+
3943
+
is-module@1.0.0: {}
3944
+
3945
+
is-negative-zero@2.0.3: {}
3946
+
3947
+
is-number-object@1.0.7:
3948
+
dependencies:
3949
+
has-tostringtag: 1.0.2
3950
+
3951
+
is-number@7.0.0: {}
3952
+
3953
+
is-obj@1.0.1: {}
3954
+
3955
+
is-regex@1.1.4:
3956
+
dependencies:
3957
+
call-bind: 1.0.7
3958
+
has-tostringtag: 1.0.2
3959
+
3960
+
is-regexp@1.0.0: {}
3961
+
3962
+
is-shared-array-buffer@1.0.3:
3963
+
dependencies:
3964
+
call-bind: 1.0.7
3965
+
3966
+
is-stream@2.0.1: {}
3967
+
3968
+
is-string@1.0.7:
3969
+
dependencies:
3970
+
has-tostringtag: 1.0.2
3971
+
3972
+
is-symbol@1.0.4:
3973
+
dependencies:
3974
+
has-symbols: 1.0.3
3975
+
3976
+
is-typed-array@1.1.13:
3977
+
dependencies:
3978
+
which-typed-array: 1.1.15
3979
+
3980
+
is-weakref@1.0.2:
3981
+
dependencies:
3982
+
call-bind: 1.0.7
3983
+
3984
+
is-what@4.1.16: {}
3985
+
3986
+
isarray@2.0.5: {}
3987
+
3988
+
isexe@2.0.0: {}
3989
+
3990
+
jackspeak@3.4.0:
3991
+
dependencies:
3992
+
'@isaacs/cliui': 8.0.2
3993
+
optionalDependencies:
3994
+
'@pkgjs/parseargs': 0.11.0
3995
+
3996
+
jake@10.9.1:
3997
+
dependencies:
3998
+
async: 3.2.5
3999
+
chalk: 4.1.2
4000
+
filelist: 1.0.4
4001
+
minimatch: 3.1.2
4002
+
4003
+
jiti@1.21.3: {}
4004
+
4005
+
js-tokens@4.0.0: {}
4006
+
4007
+
jsesc@0.5.0: {}
4008
+
4009
+
jsesc@2.5.2: {}
4010
+
4011
+
json-schema-traverse@1.0.0: {}
4012
+
4013
+
json-schema@0.4.0: {}
4014
+
4015
+
json5@2.2.3: {}
4016
+
4017
+
jsonfile@6.1.0:
4018
+
dependencies:
4019
+
universalify: 2.0.1
4020
+
optionalDependencies:
4021
+
graceful-fs: 4.2.11
4022
+
4023
+
jsonpointer@5.0.1: {}
4024
+
4025
+
leven@3.1.0: {}
4026
+
4027
+
lilconfig@2.1.0: {}
4028
+
4029
+
lilconfig@3.1.1: {}
4030
+
4031
+
lines-and-columns@1.2.4: {}
4032
+
4033
+
lodash.debounce@4.0.8: {}
4034
+
4035
+
lodash.sortby@4.7.0: {}
4036
+
4037
+
lodash@4.17.21: {}
4038
+
4039
+
lru-cache@10.2.2: {}
4040
+
4041
+
lru-cache@5.1.1:
4042
+
dependencies:
4043
+
yallist: 3.1.1
4044
+
4045
+
magic-string@0.25.9:
4046
+
dependencies:
4047
+
sourcemap-codec: 1.4.8
4048
+
4049
+
merge-anything@5.1.7:
4050
+
dependencies:
4051
+
is-what: 4.1.16
4052
+
4053
+
merge2@1.4.1: {}
4054
+
4055
+
micromatch@4.0.7:
4056
+
dependencies:
4057
+
braces: 3.0.3
4058
+
picomatch: 2.3.1
4059
+
4060
+
minimatch@3.1.2:
4061
+
dependencies:
4062
+
brace-expansion: 1.1.11
4063
+
4064
+
minimatch@5.1.6:
4065
+
dependencies:
4066
+
brace-expansion: 2.0.1
4067
+
4068
+
minimatch@9.0.4:
4069
+
dependencies:
4070
+
brace-expansion: 2.0.1
4071
+
4072
+
minipass@7.1.2: {}
4073
+
4074
+
ms@2.1.2: {}
4075
+
4076
+
mz@2.7.0:
4077
+
dependencies:
4078
+
any-promise: 1.3.0
4079
+
object-assign: 4.1.1
4080
+
thenify-all: 1.6.0
4081
+
4082
+
nanoid@3.3.7: {}
4083
+
4084
+
nanoid@5.0.7: {}
4085
+
4086
+
node-releases@2.0.14: {}
4087
+
4088
+
normalize-path@3.0.0: {}
4089
+
4090
+
normalize-range@0.1.2: {}
4091
+
4092
+
object-assign@4.1.1: {}
4093
+
4094
+
object-hash@3.0.0: {}
4095
+
4096
+
object-inspect@1.13.1: {}
4097
+
4098
+
object-keys@1.1.1: {}
4099
+
4100
+
object.assign@4.1.5:
4101
+
dependencies:
4102
+
call-bind: 1.0.7
4103
+
define-properties: 1.2.1
4104
+
has-symbols: 1.0.3
4105
+
object-keys: 1.1.1
4106
+
4107
+
once@1.4.0:
4108
+
dependencies:
4109
+
wrappy: 1.0.2
4110
+
4111
+
path-is-absolute@1.0.1: {}
4112
+
4113
+
path-key@3.1.1: {}
4114
+
4115
+
path-parse@1.0.7: {}
4116
+
4117
+
path-scurry@1.11.1:
4118
+
dependencies:
4119
+
lru-cache: 10.2.2
4120
+
minipass: 7.1.2
4121
+
4122
+
picocolors@1.0.1: {}
4123
+
4124
+
picomatch@2.3.1: {}
4125
+
4126
+
pify@2.3.0: {}
4127
+
4128
+
pirates@4.0.6: {}
4129
+
4130
+
possible-typed-array-names@1.0.0: {}
4131
+
4132
+
postcss-import@15.1.0(postcss@8.4.38):
4133
+
dependencies:
4134
+
postcss: 8.4.38
4135
+
postcss-value-parser: 4.2.0
4136
+
read-cache: 1.0.0
4137
+
resolve: 1.22.8
4138
+
4139
+
postcss-js@4.0.1(postcss@8.4.38):
4140
+
dependencies:
4141
+
camelcase-css: 2.0.1
4142
+
postcss: 8.4.38
4143
+
4144
+
postcss-load-config@4.0.2(postcss@8.4.38):
4145
+
dependencies:
4146
+
lilconfig: 3.1.1
4147
+
yaml: 2.4.3
4148
+
optionalDependencies:
4149
+
postcss: 8.4.38
4150
+
4151
+
postcss-nested@6.0.1(postcss@8.4.38):
4152
+
dependencies:
4153
+
postcss: 8.4.38
4154
+
postcss-selector-parser: 6.1.0
4155
+
4156
+
postcss-selector-parser@6.1.0:
4157
+
dependencies:
4158
+
cssesc: 3.0.0
4159
+
util-deprecate: 1.0.2
4160
+
4161
+
postcss-value-parser@4.2.0: {}
4162
+
4163
+
postcss@8.4.38:
4164
+
dependencies:
4165
+
nanoid: 3.3.7
4166
+
picocolors: 1.0.1
4167
+
source-map-js: 1.2.0
4168
+
4169
+
prettier-plugin-tailwindcss@0.6.3(prettier@3.3.2):
4170
+
dependencies:
4171
+
prettier: 3.3.2
4172
+
4173
+
prettier@3.3.2: {}
4174
+
4175
+
pretty-bytes@5.6.0: {}
4176
+
4177
+
pretty-bytes@6.1.1: {}
4178
+
4179
+
punycode@2.3.1: {}
4180
+
4181
+
queue-microtask@1.2.3: {}
4182
+
4183
+
randombytes@2.1.0:
4184
+
dependencies:
4185
+
safe-buffer: 5.2.1
4186
+
4187
+
read-cache@1.0.0:
4188
+
dependencies:
4189
+
pify: 2.3.0
4190
+
4191
+
readdirp@3.6.0:
4192
+
dependencies:
4193
+
picomatch: 2.3.1
4194
+
4195
+
regenerate-unicode-properties@10.1.1:
4196
+
dependencies:
4197
+
regenerate: 1.4.2
4198
+
4199
+
regenerate@1.4.2: {}
4200
+
4201
+
regenerator-runtime@0.14.1: {}
4202
+
4203
+
regenerator-transform@0.15.2:
4204
+
dependencies:
4205
+
'@babel/runtime': 7.24.6
4206
+
4207
+
regexp.prototype.flags@1.5.2:
4208
+
dependencies:
4209
+
call-bind: 1.0.7
4210
+
define-properties: 1.2.1
4211
+
es-errors: 1.3.0
4212
+
set-function-name: 2.0.2
4213
+
4214
+
regexpu-core@5.3.2:
4215
+
dependencies:
4216
+
'@babel/regjsgen': 0.8.0
4217
+
regenerate: 1.4.2
4218
+
regenerate-unicode-properties: 10.1.1
4219
+
regjsparser: 0.9.1
4220
+
unicode-match-property-ecmascript: 2.0.0
4221
+
unicode-match-property-value-ecmascript: 2.1.0
4222
+
4223
+
regjsparser@0.9.1:
4224
+
dependencies:
4225
+
jsesc: 0.5.0
4226
+
4227
+
require-from-string@2.0.2: {}
4228
+
4229
+
resolve@1.22.8:
4230
+
dependencies:
4231
+
is-core-module: 2.13.1
4232
+
path-parse: 1.0.7
4233
+
supports-preserve-symlinks-flag: 1.0.0
4234
+
4235
+
reusify@1.0.4: {}
4236
+
4237
+
rollup@2.79.1:
4238
+
optionalDependencies:
4239
+
fsevents: 2.3.3
4240
+
4241
+
rollup@4.18.0:
4242
+
dependencies:
4243
+
'@types/estree': 1.0.5
4244
+
optionalDependencies:
4245
+
'@rollup/rollup-android-arm-eabi': 4.18.0
4246
+
'@rollup/rollup-android-arm64': 4.18.0
4247
+
'@rollup/rollup-darwin-arm64': 4.18.0
4248
+
'@rollup/rollup-darwin-x64': 4.18.0
4249
+
'@rollup/rollup-linux-arm-gnueabihf': 4.18.0
4250
+
'@rollup/rollup-linux-arm-musleabihf': 4.18.0
4251
+
'@rollup/rollup-linux-arm64-gnu': 4.18.0
4252
+
'@rollup/rollup-linux-arm64-musl': 4.18.0
4253
+
'@rollup/rollup-linux-powerpc64le-gnu': 4.18.0
4254
+
'@rollup/rollup-linux-riscv64-gnu': 4.18.0
4255
+
'@rollup/rollup-linux-s390x-gnu': 4.18.0
4256
+
'@rollup/rollup-linux-x64-gnu': 4.18.0
4257
+
'@rollup/rollup-linux-x64-musl': 4.18.0
4258
+
'@rollup/rollup-win32-arm64-msvc': 4.18.0
4259
+
'@rollup/rollup-win32-ia32-msvc': 4.18.0
4260
+
'@rollup/rollup-win32-x64-msvc': 4.18.0
4261
+
fsevents: 2.3.3
4262
+
4263
+
run-parallel@1.2.0:
4264
+
dependencies:
4265
+
queue-microtask: 1.2.3
4266
+
4267
+
safe-array-concat@1.1.2:
4268
+
dependencies:
4269
+
call-bind: 1.0.7
4270
+
get-intrinsic: 1.2.4
4271
+
has-symbols: 1.0.3
4272
+
isarray: 2.0.5
4273
+
4274
+
safe-buffer@5.2.1: {}
4275
+
4276
+
safe-regex-test@1.0.3:
4277
+
dependencies:
4278
+
call-bind: 1.0.7
4279
+
es-errors: 1.3.0
4280
+
is-regex: 1.1.4
4281
+
4282
+
semver@6.3.1: {}
4283
+
4284
+
serialize-javascript@6.0.2:
4285
+
dependencies:
4286
+
randombytes: 2.1.0
4287
+
4288
+
seroval-plugins@1.0.7(seroval@1.0.7):
4289
+
dependencies:
4290
+
seroval: 1.0.7
4291
+
4292
+
seroval@1.0.7: {}
4293
+
4294
+
set-function-length@1.2.2:
4295
+
dependencies:
4296
+
define-data-property: 1.1.4
4297
+
es-errors: 1.3.0
4298
+
function-bind: 1.1.2
4299
+
get-intrinsic: 1.2.4
4300
+
gopd: 1.0.1
4301
+
has-property-descriptors: 1.0.2
4302
+
4303
+
set-function-name@2.0.2:
4304
+
dependencies:
4305
+
define-data-property: 1.1.4
4306
+
es-errors: 1.3.0
4307
+
functions-have-names: 1.2.3
4308
+
has-property-descriptors: 1.0.2
4309
+
4310
+
shebang-command@2.0.0:
4311
+
dependencies:
4312
+
shebang-regex: 3.0.0
4313
+
4314
+
shebang-regex@3.0.0: {}
4315
+
4316
+
side-channel@1.0.6:
4317
+
dependencies:
4318
+
call-bind: 1.0.7
4319
+
es-errors: 1.3.0
4320
+
get-intrinsic: 1.2.4
4321
+
object-inspect: 1.13.1
4322
+
4323
+
signal-exit@4.1.0: {}
4324
+
4325
+
smob@1.5.0: {}
4326
+
4327
+
solid-floating-ui@0.2.1(@floating-ui/dom@1.6.5)(solid-js@1.8.17(patch_hash=wunpcbjxb5h4ujg4psj63uuluq)):
4328
+
dependencies:
4329
+
'@floating-ui/dom': 1.6.5
4330
+
solid-js: 1.8.17(patch_hash=wunpcbjxb5h4ujg4psj63uuluq)
4331
+
4332
+
solid-js@1.8.17(patch_hash=wunpcbjxb5h4ujg4psj63uuluq):
4333
+
dependencies:
4334
+
csstype: 3.1.3
4335
+
seroval: 1.0.7
4336
+
seroval-plugins: 1.0.7(seroval@1.0.7)
4337
+
4338
+
solid-refresh@0.6.3(solid-js@1.8.17(patch_hash=wunpcbjxb5h4ujg4psj63uuluq)):
4339
+
dependencies:
4340
+
'@babel/generator': 7.24.6
4341
+
'@babel/helper-module-imports': 7.24.6
4342
+
'@babel/types': 7.24.6
4343
+
solid-js: 1.8.17(patch_hash=wunpcbjxb5h4ujg4psj63uuluq)
4344
+
4345
+
solid-textarea-autosize@0.0.5(patch_hash=xoixqosplh7bmfbnvr4hschede)(solid-js@1.8.17(patch_hash=wunpcbjxb5h4ujg4psj63uuluq)):
4346
+
dependencies:
4347
+
solid-js: 1.8.17(patch_hash=wunpcbjxb5h4ujg4psj63uuluq)
4348
+
4349
+
source-map-js@1.2.0: {}
4350
+
4351
+
source-map-support@0.5.21:
4352
+
dependencies:
4353
+
buffer-from: 1.1.2
4354
+
source-map: 0.6.1
4355
+
4356
+
source-map@0.6.1: {}
4357
+
4358
+
source-map@0.8.0-beta.0:
4359
+
dependencies:
4360
+
whatwg-url: 7.1.0
4361
+
4362
+
sourcemap-codec@1.4.8: {}
4363
+
4364
+
string-width@4.2.3:
4365
+
dependencies:
4366
+
emoji-regex: 8.0.0
4367
+
is-fullwidth-code-point: 3.0.0
4368
+
strip-ansi: 6.0.1
4369
+
4370
+
string-width@5.1.2:
4371
+
dependencies:
4372
+
eastasianwidth: 0.2.0
4373
+
emoji-regex: 9.2.2
4374
+
strip-ansi: 7.1.0
4375
+
4376
+
string.prototype.matchall@4.0.11:
4377
+
dependencies:
4378
+
call-bind: 1.0.7
4379
+
define-properties: 1.2.1
4380
+
es-abstract: 1.23.3
4381
+
es-errors: 1.3.0
4382
+
es-object-atoms: 1.0.0
4383
+
get-intrinsic: 1.2.4
4384
+
gopd: 1.0.1
4385
+
has-symbols: 1.0.3
4386
+
internal-slot: 1.0.7
4387
+
regexp.prototype.flags: 1.5.2
4388
+
set-function-name: 2.0.2
4389
+
side-channel: 1.0.6
4390
+
4391
+
string.prototype.trim@1.2.9:
4392
+
dependencies:
4393
+
call-bind: 1.0.7
4394
+
define-properties: 1.2.1
4395
+
es-abstract: 1.23.3
4396
+
es-object-atoms: 1.0.0
4397
+
4398
+
string.prototype.trimend@1.0.8:
4399
+
dependencies:
4400
+
call-bind: 1.0.7
4401
+
define-properties: 1.2.1
4402
+
es-object-atoms: 1.0.0
4403
+
4404
+
string.prototype.trimstart@1.0.8:
4405
+
dependencies:
4406
+
call-bind: 1.0.7
4407
+
define-properties: 1.2.1
4408
+
es-object-atoms: 1.0.0
4409
+
4410
+
stringify-object@3.3.0:
4411
+
dependencies:
4412
+
get-own-enumerable-property-symbols: 3.0.2
4413
+
is-obj: 1.0.1
4414
+
is-regexp: 1.0.0
4415
+
4416
+
strip-ansi@6.0.1:
4417
+
dependencies:
4418
+
ansi-regex: 5.0.1
4419
+
4420
+
strip-ansi@7.1.0:
4421
+
dependencies:
4422
+
ansi-regex: 6.0.1
4423
+
4424
+
strip-comments@2.0.1: {}
4425
+
4426
+
sucrase@3.35.0:
4427
+
dependencies:
4428
+
'@jridgewell/gen-mapping': 0.3.5
4429
+
commander: 4.1.1
4430
+
glob: 10.4.1
4431
+
lines-and-columns: 1.2.4
4432
+
mz: 2.7.0
4433
+
pirates: 4.0.6
4434
+
ts-interface-checker: 0.1.13
4435
+
4436
+
supports-color@5.5.0:
4437
+
dependencies:
4438
+
has-flag: 3.0.0
4439
+
4440
+
supports-color@7.2.0:
4441
+
dependencies:
4442
+
has-flag: 4.0.0
4443
+
4444
+
supports-preserve-symlinks-flag@1.0.0: {}
4445
+
4446
+
tailwindcss@3.4.4:
4447
+
dependencies:
4448
+
'@alloc/quick-lru': 5.2.0
4449
+
arg: 5.0.2
4450
+
chokidar: 3.6.0
4451
+
didyoumean: 1.2.2
4452
+
dlv: 1.1.3
4453
+
fast-glob: 3.3.2
4454
+
glob-parent: 6.0.2
4455
+
is-glob: 4.0.3
4456
+
jiti: 1.21.3
4457
+
lilconfig: 2.1.0
4458
+
micromatch: 4.0.7
4459
+
normalize-path: 3.0.0
4460
+
object-hash: 3.0.0
4461
+
picocolors: 1.0.1
4462
+
postcss: 8.4.38
4463
+
postcss-import: 15.1.0(postcss@8.4.38)
4464
+
postcss-js: 4.0.1(postcss@8.4.38)
4465
+
postcss-load-config: 4.0.2(postcss@8.4.38)
4466
+
postcss-nested: 6.0.1(postcss@8.4.38)
4467
+
postcss-selector-parser: 6.1.0
4468
+
resolve: 1.22.8
4469
+
sucrase: 3.35.0
4470
+
transitivePeerDependencies:
4471
+
- ts-node
4472
+
4473
+
temp-dir@2.0.0: {}
4474
+
4475
+
tempy@0.6.0:
4476
+
dependencies:
4477
+
is-stream: 2.0.1
4478
+
temp-dir: 2.0.0
4479
+
type-fest: 0.16.0
4480
+
unique-string: 2.0.0
4481
+
4482
+
terser@5.31.1:
4483
+
dependencies:
4484
+
'@jridgewell/source-map': 0.3.6
4485
+
acorn: 8.11.3
4486
+
commander: 2.20.3
4487
+
source-map-support: 0.5.21
4488
+
4489
+
thenify-all@1.6.0:
4490
+
dependencies:
4491
+
thenify: 3.3.1
4492
+
4493
+
thenify@3.3.1:
4494
+
dependencies:
4495
+
any-promise: 1.3.0
4496
+
4497
+
to-fast-properties@2.0.0: {}
4498
+
4499
+
to-regex-range@5.0.1:
4500
+
dependencies:
4501
+
is-number: 7.0.0
4502
+
4503
+
tr46@1.0.1:
4504
+
dependencies:
4505
+
punycode: 2.3.1
4506
+
4507
+
ts-interface-checker@0.1.13: {}
4508
+
4509
+
type-fest@0.16.0: {}
4510
+
4511
+
typed-array-buffer@1.0.2:
4512
+
dependencies:
4513
+
call-bind: 1.0.7
4514
+
es-errors: 1.3.0
4515
+
is-typed-array: 1.1.13
4516
+
4517
+
typed-array-byte-length@1.0.1:
4518
+
dependencies:
4519
+
call-bind: 1.0.7
4520
+
for-each: 0.3.3
4521
+
gopd: 1.0.1
4522
+
has-proto: 1.0.3
4523
+
is-typed-array: 1.1.13
4524
+
4525
+
typed-array-byte-offset@1.0.2:
4526
+
dependencies:
4527
+
available-typed-arrays: 1.0.7
4528
+
call-bind: 1.0.7
4529
+
for-each: 0.3.3
4530
+
gopd: 1.0.1
4531
+
has-proto: 1.0.3
4532
+
is-typed-array: 1.1.13
4533
+
4534
+
typed-array-length@1.0.6:
4535
+
dependencies:
4536
+
call-bind: 1.0.7
4537
+
for-each: 0.3.3
4538
+
gopd: 1.0.1
4539
+
has-proto: 1.0.3
4540
+
is-typed-array: 1.1.13
4541
+
possible-typed-array-names: 1.0.0
4542
+
4543
+
typescript@5.4.5: {}
4544
+
4545
+
unbox-primitive@1.0.2:
4546
+
dependencies:
4547
+
call-bind: 1.0.7
4548
+
has-bigints: 1.0.2
4549
+
has-symbols: 1.0.3
4550
+
which-boxed-primitive: 1.0.2
4551
+
4552
+
unicode-canonical-property-names-ecmascript@2.0.0: {}
4553
+
4554
+
unicode-match-property-ecmascript@2.0.0:
4555
+
dependencies:
4556
+
unicode-canonical-property-names-ecmascript: 2.0.0
4557
+
unicode-property-aliases-ecmascript: 2.1.0
4558
+
4559
+
unicode-match-property-value-ecmascript@2.1.0: {}
4560
+
4561
+
unicode-property-aliases-ecmascript@2.1.0: {}
4562
+
4563
+
unique-string@2.0.0:
4564
+
dependencies:
4565
+
crypto-random-string: 2.0.0
4566
+
4567
+
universalify@2.0.1: {}
4568
+
4569
+
upath@1.2.0: {}
4570
+
4571
+
update-browserslist-db@1.0.16(browserslist@4.23.0):
4572
+
dependencies:
4573
+
browserslist: 4.23.0
4574
+
escalade: 3.1.2
4575
+
picocolors: 1.0.1
4576
+
4577
+
uri-js@4.4.1:
4578
+
dependencies:
4579
+
punycode: 2.3.1
4580
+
4581
+
util-deprecate@1.0.2: {}
4582
+
4583
+
validate-html-nesting@1.2.2: {}
4584
+
4585
+
vite-plugin-pwa@0.17.4(patch_hash=ve5hypcrajivuvoyst6zln6qyq)(@types/babel__core@7.20.5)(vite@5.2.11(patch_hash=rtipi3fkkgeet3kqyzne4ksswy)(terser@5.31.1)):
4586
+
dependencies:
4587
+
debug: 4.3.4
4588
+
fast-glob: 3.3.2
4589
+
pretty-bytes: 6.1.1
4590
+
vite: 5.2.11(patch_hash=rtipi3fkkgeet3kqyzne4ksswy)(terser@5.31.1)
4591
+
workbox-build: 7.1.0(@types/babel__core@7.20.5)
4592
+
workbox-window: 7.1.0
4593
+
transitivePeerDependencies:
4594
+
- '@types/babel__core'
4595
+
- supports-color
4596
+
4597
+
vite-plugin-solid@2.10.2(solid-js@1.8.17(patch_hash=wunpcbjxb5h4ujg4psj63uuluq))(vite@5.2.11(patch_hash=rtipi3fkkgeet3kqyzne4ksswy)(terser@5.31.1)):
4598
+
dependencies:
4599
+
'@babel/core': 7.24.6
4600
+
'@types/babel__core': 7.20.5
4601
+
babel-preset-solid: 1.8.17(@babel/core@7.24.6)
4602
+
merge-anything: 5.1.7
4603
+
solid-js: 1.8.17(patch_hash=wunpcbjxb5h4ujg4psj63uuluq)
4604
+
solid-refresh: 0.6.3(solid-js@1.8.17(patch_hash=wunpcbjxb5h4ujg4psj63uuluq))
4605
+
vite: 5.2.11(patch_hash=rtipi3fkkgeet3kqyzne4ksswy)(terser@5.31.1)
4606
+
vitefu: 0.2.5(vite@5.2.11(patch_hash=rtipi3fkkgeet3kqyzne4ksswy)(terser@5.31.1))
4607
+
transitivePeerDependencies:
4608
+
- supports-color
4609
+
4610
+
vite@5.2.11(patch_hash=rtipi3fkkgeet3kqyzne4ksswy)(terser@5.31.1):
4611
+
dependencies:
4612
+
esbuild: 0.20.2
4613
+
postcss: 8.4.38
4614
+
rollup: 4.18.0
4615
+
optionalDependencies:
4616
+
fsevents: 2.3.3
4617
+
terser: 5.31.1
4618
+
4619
+
vitefu@0.2.5(vite@5.2.11(patch_hash=rtipi3fkkgeet3kqyzne4ksswy)(terser@5.31.1)):
4620
+
optionalDependencies:
4621
+
vite: 5.2.11(patch_hash=rtipi3fkkgeet3kqyzne4ksswy)(terser@5.31.1)
4622
+
4623
+
webidl-conversions@4.0.2: {}
4624
+
4625
+
whatwg-url@7.1.0:
4626
+
dependencies:
4627
+
lodash.sortby: 4.7.0
4628
+
tr46: 1.0.1
4629
+
webidl-conversions: 4.0.2
4630
+
4631
+
which-boxed-primitive@1.0.2:
4632
+
dependencies:
4633
+
is-bigint: 1.0.4
4634
+
is-boolean-object: 1.1.2
4635
+
is-number-object: 1.0.7
4636
+
is-string: 1.0.7
4637
+
is-symbol: 1.0.4
4638
+
4639
+
which-typed-array@1.1.15:
4640
+
dependencies:
4641
+
available-typed-arrays: 1.0.7
4642
+
call-bind: 1.0.7
4643
+
for-each: 0.3.3
4644
+
gopd: 1.0.1
4645
+
has-tostringtag: 1.0.2
4646
+
4647
+
which@2.0.2:
4648
+
dependencies:
4649
+
isexe: 2.0.0
4650
+
4651
+
workbox-background-sync@7.1.0:
4652
+
dependencies:
4653
+
idb: 7.1.1
4654
+
workbox-core: 7.1.0
4655
+
4656
+
workbox-broadcast-update@7.1.0:
4657
+
dependencies:
4658
+
workbox-core: 7.1.0
4659
+
4660
+
workbox-build@7.1.0(@types/babel__core@7.20.5):
4661
+
dependencies:
4662
+
'@apideck/better-ajv-errors': 0.3.6(ajv@8.14.0)
4663
+
'@babel/core': 7.24.6
4664
+
'@babel/preset-env': 7.24.6(@babel/core@7.24.6)
4665
+
'@babel/runtime': 7.24.6
4666
+
'@rollup/plugin-babel': 5.3.1(@babel/core@7.24.6)(@types/babel__core@7.20.5)(rollup@2.79.1)
4667
+
'@rollup/plugin-node-resolve': 15.2.3(rollup@2.79.1)
4668
+
'@rollup/plugin-replace': 2.4.2(rollup@2.79.1)
4669
+
'@rollup/plugin-terser': 0.4.4(rollup@2.79.1)
4670
+
'@surma/rollup-plugin-off-main-thread': 2.2.3
4671
+
ajv: 8.14.0
4672
+
common-tags: 1.8.2
4673
+
fast-json-stable-stringify: 2.1.0
4674
+
fs-extra: 9.1.0
4675
+
glob: 7.2.3
4676
+
lodash: 4.17.21
4677
+
pretty-bytes: 5.6.0
4678
+
rollup: 2.79.1
4679
+
source-map: 0.8.0-beta.0
4680
+
stringify-object: 3.3.0
4681
+
strip-comments: 2.0.1
4682
+
tempy: 0.6.0
4683
+
upath: 1.2.0
4684
+
workbox-background-sync: 7.1.0
4685
+
workbox-broadcast-update: 7.1.0
4686
+
workbox-cacheable-response: 7.1.0
4687
+
workbox-core: 7.1.0
4688
+
workbox-expiration: 7.1.0
4689
+
workbox-google-analytics: 7.1.0
4690
+
workbox-navigation-preload: 7.1.0
4691
+
workbox-precaching: 7.1.0(patch_hash=uwqzx25dqx6gokakqgp7nxcupi)
4692
+
workbox-range-requests: 7.1.0
4693
+
workbox-recipes: 7.1.0
4694
+
workbox-routing: 7.1.0
4695
+
workbox-strategies: 7.1.0
4696
+
workbox-streams: 7.1.0
4697
+
workbox-sw: 7.1.0
4698
+
workbox-window: 7.1.0
4699
+
transitivePeerDependencies:
4700
+
- '@types/babel__core'
4701
+
- supports-color
4702
+
4703
+
workbox-cacheable-response@7.1.0:
4704
+
dependencies:
4705
+
workbox-core: 7.1.0
4706
+
4707
+
workbox-core@7.1.0: {}
4708
+
4709
+
workbox-expiration@7.1.0:
4710
+
dependencies:
4711
+
idb: 7.1.1
4712
+
workbox-core: 7.1.0
4713
+
4714
+
workbox-google-analytics@7.1.0:
4715
+
dependencies:
4716
+
workbox-background-sync: 7.1.0
4717
+
workbox-core: 7.1.0
4718
+
workbox-routing: 7.1.0
4719
+
workbox-strategies: 7.1.0
4720
+
4721
+
workbox-navigation-preload@7.1.0:
4722
+
dependencies:
4723
+
workbox-core: 7.1.0
4724
+
4725
+
workbox-precaching@7.1.0(patch_hash=uwqzx25dqx6gokakqgp7nxcupi):
4726
+
dependencies:
4727
+
workbox-core: 7.1.0
4728
+
workbox-routing: 7.1.0
4729
+
workbox-strategies: 7.1.0
4730
+
4731
+
workbox-range-requests@7.1.0:
4732
+
dependencies:
4733
+
workbox-core: 7.1.0
4734
+
4735
+
workbox-recipes@7.1.0:
4736
+
dependencies:
4737
+
workbox-cacheable-response: 7.1.0
4738
+
workbox-core: 7.1.0
4739
+
workbox-expiration: 7.1.0
4740
+
workbox-precaching: 7.1.0(patch_hash=uwqzx25dqx6gokakqgp7nxcupi)
4741
+
workbox-routing: 7.1.0
4742
+
workbox-strategies: 7.1.0
4743
+
4744
+
workbox-routing@7.1.0:
4745
+
dependencies:
4746
+
workbox-core: 7.1.0
4747
+
4748
+
workbox-strategies@7.1.0:
4749
+
dependencies:
4750
+
workbox-core: 7.1.0
4751
+
4752
+
workbox-streams@7.1.0:
4753
+
dependencies:
4754
+
workbox-core: 7.1.0
4755
+
workbox-routing: 7.1.0
4756
+
4757
+
workbox-sw@7.1.0: {}
4758
+
4759
+
workbox-window@7.1.0:
4760
+
dependencies:
4761
+
'@types/trusted-types': 2.0.7
4762
+
workbox-core: 7.1.0
4763
+
4764
+
wrap-ansi@7.0.0:
4765
+
dependencies:
4766
+
ansi-styles: 4.3.0
4767
+
string-width: 4.2.3
4768
+
strip-ansi: 6.0.1
4769
+
4770
+
wrap-ansi@8.1.0:
4771
+
dependencies:
4772
+
ansi-styles: 6.2.1
4773
+
string-width: 5.1.2
4774
+
strip-ansi: 7.1.0
4775
+
4776
+
wrappy@1.0.2: {}
4777
+
4778
+
yallist@3.1.1: {}
4779
+
4780
+
yaml@2.4.3: {}
+6
postcss.config.js
+6
postcss.config.js
+92
src/api/cache/post-shadow.ts
+92
src/api/cache/post-shadow.ts
···
1
+
import { batch, createSignal, onCleanup } from 'solid-js';
2
+
3
+
import type { AppBskyFeedDefs } from '@mary/bluesky-client/lexicons';
4
+
import { EventEmitter } from '@mary/events';
5
+
import type { QueryClient } from '@mary/solid-query';
6
+
7
+
import { findAllPostsInQueryData as findAllPostsInTimelineQueryData } from '../queries/timeline';
8
+
import { EQUALS_DEQUAL } from '../utils/dequal';
9
+
10
+
export interface PostShadow {
11
+
deleted?: boolean;
12
+
likeUri?: string;
13
+
repostUri?: string;
14
+
threadMuted?: boolean;
15
+
}
16
+
17
+
export interface PostShadowView {
18
+
deleted: boolean;
19
+
likeCount: number;
20
+
likeUri: string | undefined;
21
+
repostCount: number;
22
+
repostUri: string | undefined;
23
+
threadMuted: boolean;
24
+
}
25
+
26
+
const emitter = new EventEmitter<{ [uri: string]: () => void }>();
27
+
const shadows = new WeakMap<AppBskyFeedDefs.PostView, PostShadow>();
28
+
29
+
export const usePostShadow = (post: AppBskyFeedDefs.PostView) => {
30
+
const [view, setView] = createSignal(getPostShadow(post), EQUALS_DEQUAL);
31
+
32
+
onCleanup(emitter.on(post.uri, () => setView(getPostShadow(post))));
33
+
return view;
34
+
};
35
+
36
+
const getPostShadow = (post: AppBskyFeedDefs.PostView): PostShadowView => {
37
+
const shadow = shadows.get(post) ?? {};
38
+
39
+
let likeCount = post.likeCount ?? 0;
40
+
let repostCount = post.repostCount ?? 0;
41
+
42
+
if ('likeUri' in shadow) {
43
+
const wasLiked = !!post.viewer?.like;
44
+
const isLiked = !!shadow.likeUri;
45
+
46
+
if (wasLiked && !isLiked) {
47
+
likeCount--;
48
+
} else if (!wasLiked && isLiked) {
49
+
likeCount++;
50
+
}
51
+
52
+
likeCount = Math.max(0, likeCount);
53
+
}
54
+
55
+
if ('repostUri' in shadow) {
56
+
const wasReposted = !!post.viewer?.repost;
57
+
const isReposted = !!shadow.repostUri;
58
+
59
+
if (wasReposted && !isReposted) {
60
+
repostCount--;
61
+
} else if (!wasReposted && isReposted) {
62
+
repostCount++;
63
+
}
64
+
65
+
repostCount = Math.max(0, repostCount);
66
+
}
67
+
68
+
return {
69
+
deleted: shadow.deleted ?? false,
70
+
likeCount: likeCount,
71
+
likeUri: 'likeUri' in shadow ? shadow.likeUri : post.viewer?.like,
72
+
repostCount: repostCount,
73
+
repostUri: 'repostUri' in shadow ? shadow.repostUri : post.viewer?.repost,
74
+
threadMuted: ('threadMuted' in shadow ? shadow.threadMuted : post.viewer?.threadMuted) ?? false,
75
+
};
76
+
};
77
+
78
+
export const updatePostShadow = (queryClient: QueryClient, uri: string, value: Partial<PostShadow>) => {
79
+
for (const post of findPostsInCache(queryClient, uri)) {
80
+
shadows.set(post, { ...shadows.get(post), ...value });
81
+
}
82
+
83
+
batch(() => emitter.emit(uri));
84
+
};
85
+
86
+
export function* findPostsInCache(
87
+
queryClient: QueryClient,
88
+
uri: string,
89
+
includeQuote = false,
90
+
): Generator<AppBskyFeedDefs.PostView> {
91
+
yield* findAllPostsInTimelineQueryData(queryClient, uri, includeQuote);
92
+
}
+59
src/api/cache/profile-shadow.ts
+59
src/api/cache/profile-shadow.ts
···
1
+
import { batch, createSignal, onCleanup } from 'solid-js';
2
+
3
+
import type { AppBskyActorDefs, At } from '@mary/bluesky-client/lexicons';
4
+
import { EventEmitter } from '@mary/events';
5
+
import type { QueryClient } from '@mary/solid-query';
6
+
7
+
import { findAllProfilesInQueryData as findAllProfilesInProfileQueryData } from '../queries/profile';
8
+
import { findAllProfilesInQueryData as findAllProfilesInTimelineQueryData } from '../queries/timeline';
9
+
import { EQUALS_DEQUAL } from '../utils/dequal';
10
+
11
+
export interface ProfileShadow {
12
+
blockUri?: string;
13
+
followUri?: string;
14
+
muted?: boolean;
15
+
}
16
+
17
+
export interface ProfileShadowView {
18
+
blockUri: string | undefined;
19
+
followUri: string | undefined;
20
+
muted: boolean;
21
+
}
22
+
23
+
type AllProfileView =
24
+
| AppBskyActorDefs.ProfileView
25
+
| AppBskyActorDefs.ProfileViewBasic
26
+
| AppBskyActorDefs.ProfileViewDetailed;
27
+
28
+
const emitter = new EventEmitter<{ [uri: At.DID]: () => void }>();
29
+
const shadows = new WeakMap<AllProfileView, ProfileShadow>();
30
+
31
+
export const useProfileShadow = (profile: AllProfileView) => {
32
+
const [view, setView] = createSignal(getProfileShadow(profile), EQUALS_DEQUAL);
33
+
34
+
onCleanup(emitter.on(profile.did, () => setView(getProfileShadow(profile))));
35
+
return view;
36
+
};
37
+
38
+
const getProfileShadow = (profile: AllProfileView): ProfileShadowView => {
39
+
const shadow = shadows.get(profile) ?? {};
40
+
41
+
return {
42
+
blockUri: 'blockUri' in shadow ? shadow.blockUri : profile.viewer?.blocking,
43
+
followUri: 'followUri' in shadow ? shadow.followUri : profile.viewer?.following,
44
+
muted: ('muted' in shadow ? shadow.muted : profile.viewer?.muted) ?? false,
45
+
};
46
+
};
47
+
48
+
export const updateProfileShadow = (queryClient: QueryClient, did: At.DID, value: Partial<ProfileShadow>) => {
49
+
for (const profile of findProfilesInCache(queryClient, did)) {
50
+
shadows.set(profile, { ...shadows.get(profile), ...value });
51
+
}
52
+
53
+
batch(() => emitter.emit(did));
54
+
};
55
+
56
+
export function* findProfilesInCache(queryClient: QueryClient, did: At.DID): Generator<AllProfileView> {
57
+
yield* findAllProfilesInProfileQueryData(queryClient, did);
58
+
yield* findAllProfilesInTimelineQueryData(queryClient, did);
59
+
}
+14
src/api/cache/types.ts
+14
src/api/cache/types.ts
···
1
+
// This isn't a real property, but it prevents T being compatible with Shadow<T>.
2
+
declare const IsShadow: unique symbol;
3
+
4
+
export type Shadow<T> = T & { [IsShadow]: true };
5
+
6
+
export const castAsShadow = <T>(value: T): Shadow<T> => {
7
+
return value as any as Shadow<T>;
8
+
};
9
+
10
+
export interface PostCacheFindOptions {
11
+
uri?: string;
12
+
rootUri?: string;
13
+
includeQuote?: boolean;
14
+
}
+10
src/api/defaults.ts
+10
src/api/defaults.ts
···
1
+
import type { DataServer } from './types';
2
+
3
+
export const DEFAULT_APP_VIEW = 'https://public.api.bsky.app';
4
+
5
+
export const DEFAULT_DATA_SERVER: DataServer = {
6
+
name: 'Bluesky Social',
7
+
uri: 'https://bsky.social',
8
+
};
9
+
10
+
export const BLUESKY_MODERATION_DID = 'did:plc:ar7c4by46qjdydhdevvrndac';
+164
src/api/models/timeline.ts
+164
src/api/models/timeline.ts
···
1
+
import type { AppBskyActorDefs, AppBskyFeedDefs } from '@mary/bluesky-client/lexicons';
2
+
3
+
type Post = AppBskyFeedDefs.PostView;
4
+
type TimelineItem = AppBskyFeedDefs.FeedViewPost;
5
+
type ReplyRef = AppBskyFeedDefs.ReplyRef;
6
+
7
+
// EnsuredTimelineItem
8
+
export interface EnsuredReplyRef {
9
+
root: Post | undefined;
10
+
parent: Post | undefined;
11
+
grandparentAuthor: AppBskyActorDefs.ProfileViewBasic | undefined;
12
+
}
13
+
14
+
export const ensureReplyRef = (reply: ReplyRef | undefined): EnsuredReplyRef | undefined => {
15
+
if (reply) {
16
+
const root = reply.root;
17
+
const parent = reply.parent;
18
+
const grandparentAuthor = reply.grandparentAuthor;
19
+
20
+
// Thread started, or this is replying to a blocked user, skip this.
21
+
// Thread started by a blocked user, skip this.
22
+
if (
23
+
root.$type === 'app.bsky.feed.defs#blockedPost' ||
24
+
parent.$type === 'app.bsky.feed.defs#blockedPost'
25
+
) {
26
+
return;
27
+
}
28
+
29
+
return {
30
+
root: root.$type === 'app.bsky.feed.defs#postView' ? root : undefined,
31
+
parent: parent.$type === 'app.bsky.feed.defs#postView' ? parent : undefined,
32
+
grandparentAuthor: grandparentAuthor,
33
+
};
34
+
}
35
+
};
36
+
37
+
export interface EnsuredTimelineItem {
38
+
post: Post;
39
+
reply: EnsuredReplyRef | undefined;
40
+
reason: TimelineItem['reason'];
41
+
}
42
+
43
+
export const ensureTimelineItem = (item: TimelineItem): EnsuredTimelineItem => {
44
+
return {
45
+
post: item.post,
46
+
reply: ensureReplyRef(item.reply),
47
+
reason: item.reason,
48
+
};
49
+
};
50
+
// TimelineSlice
51
+
export interface TimelineSlice {
52
+
items: EnsuredTimelineItem[];
53
+
}
54
+
55
+
// UiTimelineItem
56
+
export interface UiTimelineItem extends EnsuredTimelineItem {
57
+
prev: boolean;
58
+
next: boolean;
59
+
}
60
+
61
+
export type SliceFilter = (slice: TimelineSlice) => boolean | TimelineSlice[];
62
+
export type PostFilter = (item: EnsuredTimelineItem) => boolean;
63
+
64
+
const isNextInThread = (slice: TimelineSlice, item: EnsuredTimelineItem) => {
65
+
const items = slice.items;
66
+
const last = items[items.length - 1];
67
+
68
+
const parent = item.reply?.parent;
69
+
70
+
return !!parent && last.post.cid == parent.cid;
71
+
};
72
+
73
+
const isFirstInThread = (slice: TimelineSlice, item: EnsuredTimelineItem) => {
74
+
const items = slice.items;
75
+
const first = items[0];
76
+
77
+
const parent = first.reply?.parent;
78
+
79
+
return !!parent && parent.cid === item.post.cid;
80
+
};
81
+
82
+
const isArray = Array.isArray;
83
+
84
+
export const createJoinedItems = (
85
+
arr: TimelineItem[],
86
+
filterSlice?: SliceFilter,
87
+
filterPost?: PostFilter,
88
+
): UiTimelineItem[] => {
89
+
let slices: TimelineSlice[] = [];
90
+
let jlen = 0;
91
+
92
+
// arrange the posts into connected slices
93
+
loop: for (let i = arr.length - 1; i >= 0; i--) {
94
+
const item = ensureTimelineItem(arr[i]);
95
+
96
+
if (filterPost && !filterPost(item)) {
97
+
continue;
98
+
}
99
+
100
+
// if we find a matching slice and it's currently not in front, then bump
101
+
// it to the front. this is so that new reply don't get buried away because
102
+
// there's multiple posts separating it and the parent post.
103
+
for (let j = 0; j < jlen; j++) {
104
+
const slice = slices[j];
105
+
106
+
if (isFirstInThread(slice, item)) {
107
+
slice.items.unshift(item);
108
+
109
+
if (j !== 0) {
110
+
slices.splice(j, 1);
111
+
slices.unshift(slice);
112
+
}
113
+
114
+
continue loop;
115
+
} else if (isNextInThread(slice, item)) {
116
+
slice.items.push(item);
117
+
118
+
if (j !== 0) {
119
+
slices.splice(j, 1);
120
+
slices.unshift(slice);
121
+
}
122
+
123
+
continue loop;
124
+
}
125
+
}
126
+
127
+
slices.unshift({ items: [item] });
128
+
jlen++;
129
+
}
130
+
131
+
if (filterSlice && jlen > 0) {
132
+
const unfiltered = slices;
133
+
slices = [];
134
+
135
+
for (let j = 0; j < jlen; j++) {
136
+
const slice = unfiltered[j];
137
+
const result = filterSlice(slice);
138
+
139
+
if (result) {
140
+
if (isArray(result)) {
141
+
for (let k = 0, klen = result.length; k < klen; k++) {
142
+
const slice = result[k];
143
+
slices.push(slice);
144
+
}
145
+
} else {
146
+
slices.push(slice);
147
+
}
148
+
}
149
+
}
150
+
}
151
+
152
+
return slices.flatMap((slice) => {
153
+
const arr = slice.items;
154
+
const len = arr.length;
155
+
156
+
return arr.map((item, idx): UiTimelineItem => {
157
+
return {
158
+
...item,
159
+
prev: idx !== 0,
160
+
next: idx !== len - 1,
161
+
};
162
+
});
163
+
});
164
+
};
+31
src/api/moderation/entities/post.ts
+31
src/api/moderation/entities/post.ts
···
1
+
import type { AppBskyFeedDefs, AppBskyFeedPost } from '@mary/bluesky-client/lexicons';
2
+
3
+
import type { ProfileShadowView } from '~/api/cache/profile-shadow';
4
+
import { unwrapPostEmbedText } from '~/api/utils/post';
5
+
6
+
import {
7
+
PreferenceWarn,
8
+
TargetContent,
9
+
decideLabelModeration,
10
+
decideMutedKeywordModeration,
11
+
type ModerationCause,
12
+
type ModerationOptions,
13
+
} from '..';
14
+
import { moderateProfile } from './profile';
15
+
16
+
export const moderatePost = (
17
+
post: AppBskyFeedDefs.PostView,
18
+
authorShadow: ProfileShadowView,
19
+
opts: ModerationOptions,
20
+
) => {
21
+
const author = post.author;
22
+
const record = post.record as AppBskyFeedPost.Record;
23
+
const text = record.text + unwrapPostEmbedText(record.embed);
24
+
25
+
const accu: ModerationCause[] = moderateProfile(author, authorShadow, opts);
26
+
27
+
decideLabelModeration(accu, TargetContent, post.labels, author.did, opts);
28
+
decideMutedKeywordModeration(accu, text, !!authorShadow.followUri, PreferenceWarn, opts);
29
+
30
+
return accu;
31
+
};
+38
src/api/moderation/entities/profile.ts
+38
src/api/moderation/entities/profile.ts
···
1
+
import type { AppBskyActorDefs } from '@mary/bluesky-client/lexicons';
2
+
3
+
import { type ProfileShadowView } from '~/api/cache/profile-shadow';
4
+
5
+
import {
6
+
TargetAccount,
7
+
TargetProfile,
8
+
decideLabelModeration,
9
+
decideMutedPermanentModeration,
10
+
decideMutedTemporaryModeration,
11
+
type ModerationCause,
12
+
type ModerationOptions,
13
+
} from '..';
14
+
15
+
type AllProfileView =
16
+
| AppBskyActorDefs.ProfileView
17
+
| AppBskyActorDefs.ProfileViewBasic
18
+
| AppBskyActorDefs.ProfileViewDetailed;
19
+
20
+
export const moderateProfile = (
21
+
profile: AllProfileView,
22
+
shadow: ProfileShadowView,
23
+
opts: ModerationOptions,
24
+
) => {
25
+
const accu: ModerationCause[] = [];
26
+
const did = profile.did;
27
+
28
+
const labels = profile.labels;
29
+
const profileLabels = labels?.filter((label) => label.uri.endsWith('/app.bsky.actor.profile/self'));
30
+
const accountLabels = labels?.filter((label) => !label.uri.endsWith('/app.bsky.actor.profile/self'));
31
+
32
+
decideLabelModeration(accu, TargetProfile, profileLabels, did, opts);
33
+
decideLabelModeration(accu, TargetAccount, accountLabels, did, opts);
34
+
decideMutedPermanentModeration(accu, shadow.muted);
35
+
decideMutedTemporaryModeration(accu, did, opts);
36
+
37
+
return accu;
38
+
};
+31
src/api/moderation/entities/quote.ts
+31
src/api/moderation/entities/quote.ts
···
1
+
import type { AppBskyEmbedRecord, AppBskyFeedPost } from '@mary/bluesky-client/lexicons';
2
+
3
+
import type { ProfileShadowView } from '~/api/cache/profile-shadow';
4
+
import { unwrapPostEmbedText } from '~/api/utils/post';
5
+
6
+
import {
7
+
PreferenceWarn,
8
+
TargetContent,
9
+
decideLabelModeration,
10
+
decideMutedKeywordModeration,
11
+
type ModerationCause,
12
+
type ModerationOptions,
13
+
} from '..';
14
+
import { moderateProfile } from './profile';
15
+
16
+
export const moderateQuote = (
17
+
quote: AppBskyEmbedRecord.ViewRecord,
18
+
authorShadow: ProfileShadowView,
19
+
opts: ModerationOptions,
20
+
) => {
21
+
const author = quote.author;
22
+
const record = quote.value as AppBskyFeedPost.Record;
23
+
const text = record.text + unwrapPostEmbedText(record.embed);
24
+
25
+
const accu: ModerationCause[] = moderateProfile(author, authorShadow, opts);
26
+
27
+
decideLabelModeration(accu, TargetContent, quote.labels, author.did, opts);
28
+
decideMutedKeywordModeration(accu, text, !!authorShadow.followUri, PreferenceWarn, opts);
29
+
30
+
return accu;
31
+
};
+619
src/api/moderation/index.ts
+619
src/api/moderation/index.ts
···
1
+
import type { At, ComAtprotoLabelDefs } from '@mary/bluesky-client/lexicons';
2
+
3
+
type Label = ComAtprotoLabelDefs.Label;
4
+
5
+
/** Ignore this label */
6
+
export const PreferenceIgnore = 1;
7
+
/** Warn when viewing content with this label present */
8
+
export const PreferenceWarn = 2;
9
+
/** Hide content if this label is present */
10
+
export const PreferenceHide = 3;
11
+
12
+
export type LabelPreference = 1 | 2 | 3;
13
+
export type KeywordPreference = 1 | 2 | 3;
14
+
15
+
/** Don't blur any parts of the content */
16
+
export const BlurNone = 0;
17
+
/** Only blur the media present in the content */
18
+
export const BlurMedia = 1;
19
+
/** Blur the entire content */
20
+
export const BlurContent = 2;
21
+
/** Special blur value, guaranteed blurring of profile and content */
22
+
export const BlurForced = 3;
23
+
24
+
export type LabelBlur = 0 | 1 | 2 | 3;
25
+
26
+
/** Don't inform the user */
27
+
export const SeverityNone = 0;
28
+
/** Lightly inform the user about this label's presence */
29
+
export const SeverityInform = 1;
30
+
/** Alert the user about this label's presence */
31
+
export const SeverityAlert = 2;
32
+
33
+
export type LabelSeverity = 0 | 1 | 2;
34
+
35
+
/** No flags are present */
36
+
export const FlagsNone = 0;
37
+
/** Don't allow blurred content to be expanded */
38
+
export const FlagsForced = 1 << 0;
39
+
/** Don't apply label to self */
40
+
export const FlagsNoSelf = 1 << 1;
41
+
/** Label is adult-only. */
42
+
export const FlagsAdultOnly = 1 << 2;
43
+
44
+
/** Label is intended for content */
45
+
export const TargetContent = 0;
46
+
/** Label is intended for profile itself */
47
+
export const TargetProfile = 1;
48
+
/** Label is intended for the whole account */
49
+
export const TargetAccount = 2;
50
+
51
+
export type LabelTarget = 0 | 1 | 2;
52
+
53
+
/** Concerns viewing a post in full */
54
+
export const ContextContentView = 0;
55
+
/** Concerns the media of a post */
56
+
export const ContextContentMedia = 1;
57
+
/** Concerns post feed */
58
+
export const ContextContentList = 2;
59
+
/** Concerns viewing a profile in full */
60
+
export const ContextProfileView = 3;
61
+
/** Concerns avatar and banner of a profile */
62
+
export const ContextProfileMedia = 4;
63
+
/** Concerns profile listing (follows, liked by, reposted by, etc...) */
64
+
export const ContextProfileList = 5;
65
+
66
+
export type ModerationContext = 0 | 1 | 2 | 3 | 4 | 5;
67
+
68
+
/** Label should cause blurring */
69
+
const BehaviorBlur = 0;
70
+
/** Label should cause blurring if it has the adult flag, fallback to alert/inform if it isn't */
71
+
const BehaviorBlurIfAdultOrAlert = 1;
72
+
/** Label should be alerted/informed */
73
+
const BehaviorAlertOrInform = 2;
74
+
75
+
type ModerationBehavior = 0 | 1 | 2;
76
+
77
+
export interface LabelLocale {
78
+
/** Locale code */
79
+
i: string;
80
+
/** Label name */
81
+
n: string;
82
+
/** Label description */
83
+
d: string;
84
+
}
85
+
86
+
export interface LabelDefinition {
87
+
/** Label identifier */
88
+
i: string;
89
+
/** Default preference value */
90
+
d: LabelPreference;
91
+
/** How the content should be blurred */
92
+
b: LabelBlur;
93
+
/** How the content should be informed */
94
+
s: LabelSeverity;
95
+
/** Additional flags for the label */
96
+
f: number;
97
+
/** Descriptions for the label */
98
+
l: LabelLocale[];
99
+
}
100
+
101
+
export type LabelDefinitionMapping = Record<string, LabelDefinition>;
102
+
export type LabelPreferenceMapping = Record<string, LabelPreference | undefined>;
103
+
104
+
export const GLOBAL_LABELS: LabelDefinitionMapping = {
105
+
'!hide': {
106
+
i: '!hide',
107
+
d: PreferenceHide,
108
+
b: BlurForced,
109
+
s: SeverityNone,
110
+
f: FlagsForced | FlagsNoSelf,
111
+
l: [{ i: 'en', n: `Hidden by moderators`, d: `` }],
112
+
},
113
+
'!warn': {
114
+
i: '!warn',
115
+
d: PreferenceWarn,
116
+
b: BlurForced,
117
+
s: SeverityAlert,
118
+
f: FlagsNoSelf,
119
+
l: [{ i: 'en', n: `Content warning`, d: `` }],
120
+
},
121
+
porn: {
122
+
i: 'porn',
123
+
d: PreferenceWarn,
124
+
b: BlurMedia,
125
+
s: SeverityNone,
126
+
f: FlagsAdultOnly,
127
+
l: [{ i: 'en', n: `Adult content`, d: `Erotic nudity or explicit sexual activity` }],
128
+
},
129
+
sexual: {
130
+
i: 'sexual',
131
+
d: PreferenceWarn,
132
+
b: BlurMedia,
133
+
s: SeverityNone,
134
+
f: FlagsAdultOnly,
135
+
l: [{ i: 'en', n: `Sexually suggestive`, d: `Not pornographic but sexual in nature` }],
136
+
},
137
+
'graphic-media': {
138
+
i: 'graphic-media',
139
+
d: PreferenceWarn,
140
+
b: BlurMedia,
141
+
s: SeverityNone,
142
+
f: FlagsAdultOnly,
143
+
l: [{ i: 'en', n: `Graphic media`, d: `Disturbing content` }],
144
+
},
145
+
nudity: {
146
+
i: 'nudity',
147
+
d: PreferenceWarn,
148
+
b: BlurMedia,
149
+
s: SeverityNone,
150
+
f: FlagsNone,
151
+
l: [{ i: 'en', n: `Nudity`, d: `Artistic or non-erotic nudity` }],
152
+
},
153
+
};
154
+
155
+
type LabelBehavioralMapping = {
156
+
[K in LabelBlur]: { [K in LabelTarget]: { [K in ModerationContext]?: ModerationBehavior } };
157
+
};
158
+
159
+
const LABEL_BEHAVIORAL_MAPPING: LabelBehavioralMapping = {
160
+
[BlurForced]: {
161
+
[TargetAccount]: {
162
+
[ContextProfileList]: BehaviorBlur,
163
+
[ContextProfileView]: BehaviorBlur,
164
+
[ContextContentList]: BehaviorBlur,
165
+
[ContextContentView]: BehaviorBlur,
166
+
},
167
+
[TargetProfile]: {
168
+
[ContextProfileList]: BehaviorBlur,
169
+
[ContextProfileView]: BehaviorBlur,
170
+
},
171
+
[TargetContent]: {
172
+
[ContextContentList]: BehaviorBlur,
173
+
[ContextContentView]: BehaviorBlur,
174
+
},
175
+
},
176
+
[BlurContent]: {
177
+
[TargetAccount]: {
178
+
[ContextProfileList]: BehaviorAlertOrInform,
179
+
[ContextProfileView]: BehaviorAlertOrInform,
180
+
[ContextContentList]: BehaviorBlur,
181
+
[ContextContentView]: BehaviorBlurIfAdultOrAlert,
182
+
},
183
+
[TargetProfile]: {
184
+
[ContextProfileList]: BehaviorAlertOrInform,
185
+
[ContextProfileView]: BehaviorAlertOrInform,
186
+
},
187
+
[TargetContent]: {
188
+
[ContextContentList]: BehaviorBlur,
189
+
[ContextContentView]: BehaviorBlurIfAdultOrAlert,
190
+
},
191
+
},
192
+
[BlurMedia]: {
193
+
[TargetAccount]: {
194
+
[ContextProfileList]: BehaviorAlertOrInform,
195
+
[ContextProfileView]: BehaviorAlertOrInform,
196
+
[ContextProfileMedia]: BehaviorBlur,
197
+
},
198
+
[TargetProfile]: {
199
+
[ContextProfileList]: BehaviorAlertOrInform,
200
+
[ContextProfileView]: BehaviorAlertOrInform,
201
+
[ContextProfileMedia]: BehaviorBlur,
202
+
},
203
+
[TargetContent]: {
204
+
[ContextContentMedia]: BehaviorBlur,
205
+
},
206
+
},
207
+
[BlurNone]: {
208
+
[TargetAccount]: {
209
+
[ContextProfileList]: BehaviorAlertOrInform,
210
+
[ContextProfileView]: BehaviorAlertOrInform,
211
+
[ContextContentList]: BehaviorAlertOrInform,
212
+
[ContextContentView]: BehaviorAlertOrInform,
213
+
},
214
+
[TargetProfile]: {
215
+
[ContextProfileList]: BehaviorAlertOrInform,
216
+
[ContextProfileView]: BehaviorAlertOrInform,
217
+
},
218
+
[TargetContent]: {
219
+
[ContextContentList]: BehaviorAlertOrInform,
220
+
[ContextContentView]: BehaviorAlertOrInform,
221
+
},
222
+
},
223
+
};
224
+
225
+
export const getLocalizedLabel = (label: LabelDefinition): LabelLocale => {
226
+
// Get English definitions first before giving up
227
+
const locales = label.l;
228
+
229
+
return locales.length > 0
230
+
? locales.find(({ i }) => i.split('-')[0] === 'en') || locales[0]
231
+
: { i: 'en', n: label.i, d: `` };
232
+
};
233
+
234
+
export const CauseLabel = 0;
235
+
export const CauseMutedPermanent = 1;
236
+
export const CauseMutedTemporary = 2;
237
+
export const CauseMutedKeyword = 3;
238
+
239
+
export type ModerationCauseType = 0 | 1 | 2 | 3;
240
+
241
+
interface BaseModerationCause {
242
+
/** Cause type */
243
+
t: ModerationCauseType;
244
+
/** Cause priority */
245
+
p: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
246
+
}
247
+
248
+
export interface LabelModerationCause {
249
+
t: typeof CauseLabel;
250
+
p: 1 | 2 | 5 | 7 | 8;
251
+
252
+
/** Target of the label */
253
+
k: LabelTarget;
254
+
255
+
/** Label source, self-label if empty */
256
+
s: ModerationLabeler | undefined;
257
+
/** Whether label definition is global or not (also true for nonexistent definitions) */
258
+
g: boolean;
259
+
260
+
/** Label object */
261
+
l: Label;
262
+
/** Label definition */
263
+
d: LabelDefinition;
264
+
/** User-set preference for this label */
265
+
v: LabelPreference;
266
+
}
267
+
268
+
export interface MutedPermanentModerationCause extends BaseModerationCause {
269
+
t: typeof CauseMutedPermanent;
270
+
p: 6;
271
+
}
272
+
273
+
export interface MutedTemporaryModerationCause extends BaseModerationCause {
274
+
t: typeof CauseMutedTemporary;
275
+
p: 6;
276
+
277
+
/** Temporary mute duration */
278
+
d: number;
279
+
}
280
+
281
+
export interface MutedKeywordModerationCause extends BaseModerationCause {
282
+
t: typeof CauseMutedKeyword;
283
+
p: 6;
284
+
285
+
/** Name of keyword muting in effect */
286
+
n: string;
287
+
/** User's set preference for this cause */
288
+
v: KeywordPreference;
289
+
}
290
+
291
+
export type ModerationCause =
292
+
| LabelModerationCause
293
+
| MutedPermanentModerationCause
294
+
| MutedTemporaryModerationCause
295
+
| MutedKeywordModerationCause;
296
+
297
+
export type KeywordFilterMatcher = [keyword: string, whole: boolean];
298
+
299
+
export interface KeywordFilter {
300
+
// Only these properties are ever used during actual filtering
301
+
name: string;
302
+
pref: KeywordPreference;
303
+
match: string;
304
+
noFollows: boolean;
305
+
306
+
// These are used in the preferences UI
307
+
id: string;
308
+
matchers: KeywordFilterMatcher[];
309
+
}
310
+
311
+
export interface ModerationLabeler {
312
+
/** DID of the labeler */
313
+
did: At.DID;
314
+
/** Profile details of the labeler */
315
+
profile: {
316
+
avatar?: string;
317
+
handle: string;
318
+
displayName?: string;
319
+
};
320
+
/** Labels that this service claims to provide */
321
+
provides: string[];
322
+
/** Labels defined by this service */
323
+
definitions: LabelDefinitionMapping;
324
+
}
325
+
326
+
export interface ModerationLabelerPreferences {
327
+
/** Whether it should apply takedowns from this labeler */
328
+
redact: boolean;
329
+
/** Whether it's privileged to hide content by default */
330
+
privileged: boolean;
331
+
/** Preferences for local-defined labels from this labeler */
332
+
labels: LabelPreferenceMapping;
333
+
}
334
+
335
+
export interface ModerationPreferences {
336
+
/** Preferences for global-defined labels */
337
+
labels: LabelPreferenceMapping;
338
+
/** Preferences for labels from subscribed labelers */
339
+
labelers: Record<At.DID, ModerationLabelerPreferences>;
340
+
/** Keyword filters */
341
+
keywords: KeywordFilter[];
342
+
343
+
/** List of users to hide reposts from */
344
+
hideReposts: At.DID[];
345
+
/** Mapping of users and how long they should be temporarily muted for */
346
+
tempMutes: Record<At.DID, number | undefined>;
347
+
}
348
+
349
+
export interface ModerationOptions {
350
+
_filtersCache?: [raw: string, match: RegExp][];
351
+
352
+
preferences: ModerationPreferences;
353
+
labelerDefinitions: Record<At.DID, ModerationLabeler>;
354
+
}
355
+
356
+
export const decideLabelModeration = (
357
+
accu: ModerationCause[],
358
+
target: LabelTarget,
359
+
labels: Label[] | undefined,
360
+
userDid: At.DID,
361
+
opts: ModerationOptions,
362
+
) => {
363
+
if (labels /* && labels.length > 0 */) {
364
+
const globalPrefs = opts.preferences.labels;
365
+
const labelerPrefs = opts.preferences.labelers;
366
+
const labelerDefinitions = opts.labelerDefinitions;
367
+
368
+
for (let i = 0, il = labels.length; i < il; i++) {
369
+
const label = labels[i];
370
+
371
+
const val = label.val;
372
+
const src = label.src;
373
+
374
+
const isSelfLabeled = src === userDid;
375
+
const isSystem = val[0] === '!';
376
+
377
+
let labeler: ModerationLabeler | undefined = labelerDefinitions[src];
378
+
let def: LabelDefinition | undefined = GLOBAL_LABELS[val];
379
+
let isGlobalDef = true;
380
+
381
+
if (!isSystem && labeler && val in labeler.definitions) {
382
+
isGlobalDef = false;
383
+
def = labeler.definitions[val];
384
+
}
385
+
386
+
// skip if:
387
+
// - we don't have the definitions for this label value
388
+
// - this is self-labeled, and the definition says it can't be used for it
389
+
// - the service isn't reporting that it's using this global label definition
390
+
if (
391
+
!def ||
392
+
(isSelfLabeled && def.f & FlagsNoSelf) ||
393
+
(labeler && isGlobalDef && !labeler.provides.includes(val))
394
+
) {
395
+
continue;
396
+
}
397
+
398
+
const labelerPref = labelerPrefs[src];
399
+
400
+
const defaultLabelPref = def.d;
401
+
let labelPref = (labelerPref?.labels ?? globalPrefs)[val];
402
+
403
+
if (labelPref === undefined) {
404
+
if (defaultLabelPref === PreferenceHide && labelerPref && !labelerPref.privileged) {
405
+
labelPref = PreferenceWarn;
406
+
} else {
407
+
labelPref = defaultLabelPref;
408
+
}
409
+
}
410
+
411
+
if (labelPref !== PreferenceHide && labelPref !== PreferenceWarn) {
412
+
continue;
413
+
}
414
+
415
+
let prio: LabelModerationCause['p'];
416
+
if (def.f & FlagsForced) {
417
+
prio = 1;
418
+
} else if (labelPref === PreferenceHide) {
419
+
prio = 2;
420
+
} else if (def.b === BlurContent) {
421
+
prio = def.f & FlagsAdultOnly ? 5 : 7;
422
+
} else if (def.b === BlurMedia) {
423
+
prio = 7;
424
+
} else {
425
+
if (def.s === SeverityNone) {
426
+
continue;
427
+
}
428
+
429
+
prio = 8;
430
+
}
431
+
432
+
accu.push({
433
+
t: CauseLabel,
434
+
p: prio,
435
+
436
+
k: target,
437
+
438
+
s: labeler,
439
+
g: isGlobalDef,
440
+
l: label,
441
+
d: def,
442
+
v: labelPref,
443
+
});
444
+
}
445
+
}
446
+
};
447
+
448
+
export const decideMutedPermanentModeration = (accu: ModerationCause[], muted: boolean | undefined) => {
449
+
if (muted) {
450
+
accu.push({ t: CauseMutedPermanent, p: 6 });
451
+
}
452
+
};
453
+
454
+
export const decideMutedTemporaryModeration = (
455
+
accu: ModerationCause[],
456
+
userDid: At.DID,
457
+
opts: ModerationOptions,
458
+
) => {
459
+
const duration = isProfileTempMuted(opts, userDid);
460
+
461
+
if (duration != null) {
462
+
accu.push({ t: CauseMutedTemporary, p: 6, d: duration });
463
+
}
464
+
};
465
+
466
+
const shouldAllowKeywordFilter = (filterPref: KeywordPreference, pref: KeywordPreference) => {
467
+
if (pref === PreferenceWarn) {
468
+
return filterPref === pref || filterPref === PreferenceHide;
469
+
}
470
+
471
+
if (pref === PreferenceHide) {
472
+
return filterPref === pref;
473
+
}
474
+
475
+
return false;
476
+
};
477
+
478
+
export const decideMutedKeywordModeration = (
479
+
accu: ModerationCause[],
480
+
text: string,
481
+
following: boolean,
482
+
pref: KeywordPreference,
483
+
opts: ModerationOptions,
484
+
) => {
485
+
const filters = opts.preferences.keywords;
486
+
487
+
let cache = opts._filtersCache;
488
+
let init = true;
489
+
490
+
for (let idx = 0, len = filters.length; idx < len; idx++) {
491
+
const filter = filters[idx];
492
+
493
+
if (!shouldAllowKeywordFilter(filter.pref, pref) || (following && filter.noFollows)) {
494
+
continue;
495
+
}
496
+
497
+
if (init) {
498
+
if (cache) {
499
+
cache.length = len;
500
+
} else {
501
+
cache = opts._filtersCache = new Array(len);
502
+
}
503
+
504
+
init = !init;
505
+
}
506
+
507
+
let match = filter.match;
508
+
509
+
let matcher: RegExp;
510
+
let cachedMatcher = cache![idx];
511
+
512
+
if (!cachedMatcher || cachedMatcher[0] !== match) {
513
+
cache![idx] = [match, (matcher = new RegExp(match, 'i'))];
514
+
} else {
515
+
matcher = cachedMatcher[1];
516
+
}
517
+
518
+
if (matcher.test(text)) {
519
+
accu.push({ t: CauseMutedKeyword, p: 6, n: filter.name, v: pref });
520
+
}
521
+
}
522
+
523
+
return accu;
524
+
};
525
+
526
+
export interface ModerationUI {
527
+
/** Whether it's overridable */
528
+
o: boolean;
529
+
530
+
/** Filters */
531
+
f: ModerationCause[];
532
+
/** Blurs */
533
+
b: ModerationCause[];
534
+
/** Alerts */
535
+
a: ModerationCause[];
536
+
/** Informs */
537
+
i: ModerationCause[];
538
+
}
539
+
540
+
export const getModerationUI = (causes: ModerationCause[] = [], context: ModerationContext): ModerationUI => {
541
+
const filters: ModerationCause[] = [];
542
+
const blurs: ModerationCause[] = [];
543
+
const alerts: ModerationCause[] = [];
544
+
const informs: ModerationCause[] = [];
545
+
546
+
let overridable = true;
547
+
548
+
for (let i = 0, il = causes.length; i < il; i++) {
549
+
const cause = causes[i];
550
+
const type = cause.t;
551
+
552
+
if (type === CauseLabel) {
553
+
const target = cause.k;
554
+
const def = cause.d;
555
+
556
+
const flags = def.f;
557
+
const blur = def.b;
558
+
const severity = def.s;
559
+
560
+
const behavior = LABEL_BEHAVIORAL_MAPPING[blur][target][context];
561
+
562
+
if (cause.v === PreferenceHide) {
563
+
if (
564
+
(context === ContextProfileList && target === TargetAccount) ||
565
+
(context === ContextContentList && (target === TargetContent || target === TargetAccount))
566
+
) {
567
+
filters.push(cause);
568
+
}
569
+
}
570
+
571
+
if (behavior === BehaviorBlur || (behavior === BehaviorBlurIfAdultOrAlert && flags & FlagsAdultOnly)) {
572
+
blurs.push(cause);
573
+
574
+
if (flags & FlagsForced) {
575
+
overridable = false;
576
+
}
577
+
} else if (behavior === BehaviorAlertOrInform || behavior === BehaviorBlurIfAdultOrAlert) {
578
+
if (severity === SeverityAlert) {
579
+
alerts.push(cause);
580
+
} else if (severity === SeverityInform) {
581
+
informs.push(cause);
582
+
}
583
+
}
584
+
} else if (type === CauseMutedKeyword) {
585
+
if (context === ContextContentList) {
586
+
if (cause.v === PreferenceHide) {
587
+
filters.push(cause);
588
+
}
589
+
590
+
blurs.push(cause);
591
+
}
592
+
} else if (type === CauseMutedTemporary || type === CauseMutedPermanent) {
593
+
if (context === ContextContentList) {
594
+
filters.push(cause);
595
+
}
596
+
597
+
if (context !== ContextContentMedia && context !== ContextProfileMedia) {
598
+
blurs.push(cause);
599
+
}
600
+
}
601
+
}
602
+
603
+
return {
604
+
o: overridable,
605
+
f: filters.sort(sortByPriority),
606
+
b: blurs.sort(sortByPriority),
607
+
a: alerts,
608
+
i: informs,
609
+
};
610
+
};
611
+
612
+
const sortByPriority = (a: ModerationCause, b: ModerationCause) => {
613
+
return a.p - b.p;
614
+
};
615
+
616
+
export const isProfileTempMuted = (opts: ModerationOptions, actor: At.DID): number | null => {
617
+
const date = opts.preferences.tempMutes[actor];
618
+
return date !== undefined && Date.now() < date ? date : null;
619
+
};
+107
src/api/moderation/labeler.ts
+107
src/api/moderation/labeler.ts
···
1
+
import type { AppBskyLabelerDefs } from '@mary/bluesky-client/lexicons';
2
+
3
+
import { mapDefined } from '~/lib/misc';
4
+
5
+
import {
6
+
BlurContent,
7
+
BlurMedia,
8
+
BlurNone,
9
+
FlagsAdultOnly,
10
+
FlagsNone,
11
+
PreferenceIgnore,
12
+
PreferenceWarn,
13
+
SeverityAlert,
14
+
SeverityInform,
15
+
SeverityNone,
16
+
type LabelBlur,
17
+
type LabelDefinitionMapping,
18
+
type LabelPreference,
19
+
type LabelSeverity,
20
+
type ModerationLabeler,
21
+
} from '.';
22
+
23
+
export const interpretLabelerDefinition = (
24
+
service: AppBskyLabelerDefs.LabelerViewDetailed,
25
+
): ModerationLabeler => {
26
+
const creator = service.creator;
27
+
const policies = service.policies;
28
+
29
+
const values = policies.labelValues;
30
+
31
+
const supported = new Set(values);
32
+
const defs: LabelDefinitionMapping = {};
33
+
34
+
// Sort the definitions as per labelValues
35
+
for (const def of policies.labelValueDefinitions || []) {
36
+
const id = def.identifier;
37
+
38
+
// - Skip system label
39
+
// - Skip if it's not even on labelValues
40
+
if (id[0] === '!' || !supported.has(id)) {
41
+
continue;
42
+
}
43
+
44
+
defs[id] = {
45
+
i: id,
46
+
d: convertPreferenceValue(def.defaultSetting),
47
+
b: convertBlurValue(def.blurs),
48
+
s: convertSeverityValue(def.severity),
49
+
f: def.adultOnly ? FlagsAdultOnly : FlagsNone,
50
+
l: mapDefined(def.locales, (locale) => {
51
+
try {
52
+
// Normalize locale codes
53
+
const parsed = new Intl.Locale(locale.lang);
54
+
55
+
return {
56
+
i: parsed.baseName,
57
+
n: locale.name,
58
+
d: locale.description,
59
+
};
60
+
} catch {}
61
+
}),
62
+
};
63
+
}
64
+
65
+
return {
66
+
did: creator.did,
67
+
profile: {
68
+
handle: creator.handle,
69
+
avatar: creator.avatar,
70
+
displayName: creator.displayName,
71
+
},
72
+
provides: values,
73
+
definitions: defs,
74
+
};
75
+
};
76
+
77
+
const convertPreferenceValue = (value: string | undefined): LabelPreference => {
78
+
if (value === 'warn' || value === 'hide') {
79
+
return PreferenceWarn;
80
+
}
81
+
82
+
return PreferenceIgnore;
83
+
};
84
+
85
+
const convertBlurValue = (value: string | undefined): LabelBlur => {
86
+
if (value === 'content') {
87
+
return BlurContent;
88
+
}
89
+
90
+
if (value === 'media') {
91
+
return BlurMedia;
92
+
}
93
+
94
+
return BlurNone;
95
+
};
96
+
97
+
const convertSeverityValue = (value: string | undefined): LabelSeverity => {
98
+
if (value === 'alert') {
99
+
return SeverityAlert;
100
+
}
101
+
102
+
if (value === 'inform') {
103
+
return SeverityInform;
104
+
}
105
+
106
+
return SeverityNone;
107
+
};
+25
src/api/queries/handle.ts
+25
src/api/queries/handle.ts
···
1
+
import { createQuery } from '@mary/solid-query';
2
+
3
+
import { useAgent } from '~/lib/states/agent';
4
+
5
+
export const useResolveHandleQuery = (handle: () => string) => {
6
+
const { rpc } = useAgent();
7
+
8
+
return createQuery(() => {
9
+
const $handle = handle();
10
+
11
+
return {
12
+
queryKey: ['resolve-handle', $handle],
13
+
async queryFn(ctx) {
14
+
const { data } = await rpc.get('com.atproto.identity.resolveHandle', {
15
+
signal: ctx.signal,
16
+
params: {
17
+
handle: $handle,
18
+
},
19
+
});
20
+
21
+
return data.did;
22
+
},
23
+
};
24
+
});
25
+
};
+46
src/api/queries/profile.ts
+46
src/api/queries/profile.ts
···
1
+
import type { AppBskyActorDefs, At } from '@mary/bluesky-client/lexicons';
2
+
import { QueryClient, createQuery, type QueryPersister } from '@mary/solid-query';
3
+
4
+
import { useAgent } from '~/lib/states/agent';
5
+
import { useSession } from '~/lib/states/session';
6
+
7
+
export const useProfileQuery = (did: () => At.DID | undefined, persister?: QueryPersister) => {
8
+
const { rpc } = useAgent();
9
+
const { currentAccount } = useSession();
10
+
11
+
return createQuery(() => {
12
+
const $did = did();
13
+
14
+
return {
15
+
queryKey: ['profile', $did],
16
+
enabled: $did !== undefined,
17
+
persister: persister as any,
18
+
async queryFn(ctx) {
19
+
const { data } = await rpc.get('app.bsky.actor.getProfile', {
20
+
signal: ctx.signal,
21
+
params: {
22
+
actor: $did!,
23
+
},
24
+
});
25
+
26
+
if (currentAccount !== undefined && currentAccount.did === $did) {
27
+
// Unset `knownFollowers` as we don't need that on our own profile.
28
+
data.viewer!.knownFollowers = undefined;
29
+
}
30
+
31
+
return data;
32
+
},
33
+
};
34
+
});
35
+
};
36
+
37
+
export function* findAllProfilesInQueryData(
38
+
queryClient: QueryClient,
39
+
did: At.DID,
40
+
): Generator<AppBskyActorDefs.ProfileViewDetailed> {
41
+
const data = queryClient.getQueryData<AppBskyActorDefs.ProfileViewDetailed>(['profile', did]);
42
+
43
+
if (data !== undefined && data.did === did) {
44
+
yield data;
45
+
}
46
+
}
+755
src/api/queries/timeline.ts
+755
src/api/queries/timeline.ts
···
1
+
import { createMemo, createRenderEffect, untrack } from 'solid-js';
2
+
3
+
import type { BskyXRPC } from '@mary/bluesky-client';
4
+
import type {
5
+
AppBskyActorDefs,
6
+
AppBskyEmbedRecord,
7
+
AppBskyFeedDefs,
8
+
AppBskyFeedGetTimeline,
9
+
AppBskyFeedPost,
10
+
At,
11
+
} from '@mary/bluesky-client/lexicons';
12
+
import {
13
+
QueryClient,
14
+
createInfiniteQuery,
15
+
createQuery,
16
+
useQueryClient,
17
+
type InfiniteData,
18
+
} from '@mary/solid-query';
19
+
20
+
import { assert } from '~/lib/invariant';
21
+
import { useAgent } from '~/lib/states/agent';
22
+
import { useModerationOptions } from '~/lib/states/moderation';
23
+
import { useSession } from '~/lib/states/session';
24
+
25
+
import {
26
+
createJoinedItems,
27
+
type EnsuredReplyRef,
28
+
type EnsuredTimelineItem,
29
+
type PostFilter,
30
+
type SliceFilter,
31
+
type TimelineSlice,
32
+
type UiTimelineItem,
33
+
} from '../models/timeline';
34
+
import {
35
+
ContextContentList,
36
+
PreferenceHide,
37
+
TargetContent,
38
+
decideLabelModeration,
39
+
decideMutedKeywordModeration,
40
+
getModerationUI,
41
+
type ModerationCause,
42
+
type ModerationOptions,
43
+
} from '../moderation';
44
+
45
+
import { EQUALS_DEQUAL } from '../utils/dequal';
46
+
import { embedViewRecordToPostView, getEmbeddedPost, unwrapPostEmbedText } from '../utils/post';
47
+
import { resetInfiniteData, wrapQuery } from '../utils/query';
48
+
import { parseAtUri } from '../utils/strings';
49
+
50
+
type PostRecord = AppBskyFeedPost.Record;
51
+
52
+
export interface FollowingTimelineParams {
53
+
type: 'following';
54
+
showReplies: 'follows' | boolean;
55
+
showReposts: boolean;
56
+
showQuotes: boolean;
57
+
}
58
+
59
+
export interface FeedTimelineParams {
60
+
type: 'feed';
61
+
uri: string;
62
+
showReplies: boolean;
63
+
showReposts: boolean;
64
+
showQuotes: boolean;
65
+
}
66
+
67
+
export interface ListTimelineParams {
68
+
type: 'list';
69
+
uri: string;
70
+
showReplies: boolean;
71
+
showQuotes: boolean;
72
+
}
73
+
74
+
export interface ProfileTimelineParams {
75
+
type: 'profile';
76
+
actor: At.DID;
77
+
tab: 'posts' | 'replies' | 'likes' | 'media';
78
+
}
79
+
80
+
export interface SearchTimelineParams {
81
+
type: 'search';
82
+
query: string;
83
+
sort: 'top' | 'latest';
84
+
}
85
+
86
+
export type TimelineParams =
87
+
| FeedTimelineParams
88
+
| FollowingTimelineParams
89
+
| ListTimelineParams
90
+
| ProfileTimelineParams
91
+
| SearchTimelineParams;
92
+
93
+
export interface TimelinePage {
94
+
cursor: string | undefined;
95
+
cid: string | undefined;
96
+
items: UiTimelineItem[];
97
+
}
98
+
99
+
export interface TimelineLatestResult {
100
+
cid: string | undefined;
101
+
}
102
+
103
+
const MAX_TIMELINE_POSTS = 50;
104
+
105
+
export function* findAllPostsInQueryData(
106
+
queryClient: QueryClient,
107
+
uri: string,
108
+
includeQuote = false,
109
+
): Generator<AppBskyFeedDefs.PostView> {
110
+
const entries = queryClient.getQueriesData<InfiniteData<TimelinePage>>({
111
+
queryKey: ['timeline'],
112
+
});
113
+
114
+
for (const [_key, data] of entries) {
115
+
if (data === undefined) {
116
+
continue;
117
+
}
118
+
119
+
for (const page of data.pages) {
120
+
for (const item of page.items) {
121
+
const post = item.post;
122
+
const reply = item.reply;
123
+
124
+
if (post.uri === uri) {
125
+
yield post;
126
+
}
127
+
128
+
if (reply !== undefined) {
129
+
const parent = reply.parent;
130
+
const root = reply.root;
131
+
132
+
if (parent !== undefined) {
133
+
if (parent.uri === uri) {
134
+
yield parent;
135
+
}
136
+
137
+
if (includeQuote) {
138
+
const embeddedPost = getEmbeddedPost(parent.embed);
139
+
if (embeddedPost && embeddedPost.uri === uri) {
140
+
yield embedViewRecordToPostView(embeddedPost);
141
+
}
142
+
}
143
+
}
144
+
145
+
if (root !== undefined) {
146
+
if (root.uri === uri) {
147
+
yield root;
148
+
}
149
+
150
+
if (includeQuote) {
151
+
const embeddedPost = getEmbeddedPost(root.embed);
152
+
if (embeddedPost && embeddedPost.uri === uri) {
153
+
yield embedViewRecordToPostView(embeddedPost);
154
+
}
155
+
}
156
+
}
157
+
}
158
+
}
159
+
}
160
+
}
161
+
}
162
+
163
+
export function* findAllProfilesInQueryData(
164
+
queryClient: QueryClient,
165
+
did: At.DID,
166
+
): Generator<AppBskyActorDefs.ProfileViewBasic> {
167
+
const entries = queryClient.getQueriesData<InfiniteData<TimelinePage>>({
168
+
queryKey: ['timeline'],
169
+
});
170
+
171
+
for (const [_key, data] of entries) {
172
+
if (data === undefined) {
173
+
continue;
174
+
}
175
+
176
+
for (const page of data.pages) {
177
+
for (const item of page.items) {
178
+
const post = item.post;
179
+
const reply = item.reply;
180
+
181
+
if (post.author.did === did) {
182
+
yield post.author;
183
+
}
184
+
185
+
if (reply !== undefined) {
186
+
const parent = reply.parent;
187
+
const root = reply.root;
188
+
189
+
if (parent !== undefined) {
190
+
if (parent.author.did === did) {
191
+
yield parent.author;
192
+
}
193
+
194
+
const embeddedPost = getEmbeddedPost(parent.embed);
195
+
if (embeddedPost && embeddedPost.author.did === did) {
196
+
yield embeddedPost.author;
197
+
}
198
+
}
199
+
200
+
if (root !== undefined) {
201
+
if (root.author.did === did) {
202
+
yield root.author;
203
+
}
204
+
205
+
const embeddedPost = getEmbeddedPost(root.embed);
206
+
if (embeddedPost && embeddedPost.author.did === did) {
207
+
yield embeddedPost.author;
208
+
}
209
+
}
210
+
}
211
+
}
212
+
}
213
+
}
214
+
}
215
+
216
+
export const useTimelineQuery = (_params: () => TimelineParams) => {
217
+
const getParams = createMemo(() => _params(), EQUALS_DEQUAL);
218
+
219
+
const { rpc } = useAgent();
220
+
const { currentAccount } = useSession();
221
+
const queryClient = useQueryClient();
222
+
const moderationOptions = useModerationOptions();
223
+
224
+
const limit = MAX_TIMELINE_POSTS;
225
+
226
+
const timeline = createInfiniteQuery(() => {
227
+
const params = getParams();
228
+
229
+
return {
230
+
queryKey: ['timeline', params],
231
+
initialPageParam: undefined,
232
+
getNextPageParam: (last) => last.cursor,
233
+
staleTime: Infinity,
234
+
structuralSharing: false,
235
+
queryFn: wrapQuery<TimelinePage, string | undefined>(async (ctx) => {
236
+
const uid = currentAccount?.did;
237
+
const moderation = moderationOptions();
238
+
239
+
const type = params.type;
240
+
241
+
let cursor = ctx.pageParam;
242
+
243
+
let sliceFilter: SliceFilter | undefined;
244
+
let postFilter: PostFilter | undefined;
245
+
246
+
if (type === 'following') {
247
+
assert(uid !== undefined);
248
+
249
+
sliceFilter = createHomeSliceFilter(uid, params.showReplies === 'follows');
250
+
251
+
postFilter = combine([
252
+
createHiddenRepostFilter(moderation),
253
+
createDuplicatePostFilter(),
254
+
255
+
!params.showReplies && createHideRepliesFilter(),
256
+
!params.showQuotes && createHideQuotesFilter(),
257
+
!params.showReposts && createHideRepostsFilter(),
258
+
259
+
createInvalidReplyFilter(),
260
+
createLabelPostFilter(moderation),
261
+
createTempMutePostFilter(uid, moderation),
262
+
]);
263
+
} else if (type === 'feed' || type === 'list') {
264
+
sliceFilter = createFeedSliceFilter();
265
+
266
+
postFilter = combine([
267
+
type === 'feed' && createHiddenRepostFilter(moderation),
268
+
createDuplicatePostFilter(),
269
+
270
+
!params.showReplies && createHideRepliesFilter(),
271
+
!params.showQuotes && createHideQuotesFilter(),
272
+
type === 'feed' && !params.showReposts && createHideRepostsFilter(),
273
+
274
+
createLabelPostFilter(moderation),
275
+
uid && createTempMutePostFilter(uid, moderation),
276
+
]);
277
+
} else if (type === 'profile') {
278
+
postFilter = createLabelPostFilter(moderation);
279
+
280
+
if (params.tab === 'posts') {
281
+
sliceFilter = createProfileSliceFilter(params.actor);
282
+
postFilter = combine([createInvalidReplyFilter(), createLabelPostFilter(moderation)]);
283
+
}
284
+
} else {
285
+
postFilter = createLabelPostFilter(moderation);
286
+
}
287
+
288
+
const timeline = await fetchPage(rpc, params, limit, cursor, ctx.signal);
289
+
290
+
const feed = timeline.feed;
291
+
const result = createJoinedItems(feed, sliceFilter, postFilter);
292
+
293
+
const page: TimelinePage = {
294
+
cursor: timeline.cursor,
295
+
cid: feed.length > 0 ? feed[0].post.cid : undefined,
296
+
items: result,
297
+
};
298
+
299
+
return page;
300
+
}),
301
+
};
302
+
});
303
+
304
+
const latest = createQuery<TimelineLatestResult>(() => {
305
+
const params = getParams();
306
+
const timelineData = timeline.data;
307
+
308
+
return {
309
+
queryKey: ['timeline-latest', params],
310
+
enabled: timelineData !== undefined,
311
+
staleTime: 30_000,
312
+
refetchOnWindowFocus: (query) => {
313
+
return !isTimelineStale(timelineData, query.state.data);
314
+
},
315
+
refetchInterval: (query) => {
316
+
if (!isTimelineStale(timelineData, query.state.data)) {
317
+
// 1 minute, or 5 minutes
318
+
return !document.hidden ? 60_000 : 5 * 60_000;
319
+
}
320
+
321
+
return false;
322
+
},
323
+
async queryFn(ctx): Promise<TimelineLatestResult> {
324
+
const timeline = await fetchPage(rpc, params, 1, undefined, ctx.signal);
325
+
const feed = timeline.feed;
326
+
327
+
return { cid: feed.length > 0 ? feed[0].post.cid : undefined };
328
+
},
329
+
};
330
+
});
331
+
332
+
const reset = () => {
333
+
const params = untrack(getParams);
334
+
335
+
resetInfiniteData(queryClient, ['timeline', params]);
336
+
timeline.refetch();
337
+
};
338
+
339
+
const isStale = () => {
340
+
return isTimelineStale(timeline.data, latest.data);
341
+
};
342
+
343
+
// This is a render effect such that changes to the timeline query immediately
344
+
// mutates the stale check query before it has the chance to react itself.
345
+
createRenderEffect((prev: typeof timeline.data | 0) => {
346
+
const next = timeline.data;
347
+
348
+
if (prev !== 0 && next) {
349
+
const pages = next.pages;
350
+
const length = pages.length;
351
+
352
+
// Only mutate stale check if the length is exactly 1, as in, we've just
353
+
// loaded the timeline, or refreshed it.
354
+
if (length === 1) {
355
+
// Untrack so we don't become dependent on this.
356
+
const params = untrack(getParams);
357
+
358
+
queryClient.setQueryData(
359
+
['timeline-latest', params],
360
+
{ cid: pages[0].cid },
361
+
{ updatedAt: timeline.dataUpdatedAt },
362
+
);
363
+
}
364
+
}
365
+
366
+
return next;
367
+
}, 0 as const);
368
+
369
+
return { timeline, reset, isStale };
370
+
};
371
+
372
+
const isTimelineStale = (
373
+
timelineData: InfiniteData<TimelinePage> | undefined,
374
+
latestData: TimelineLatestResult | undefined,
375
+
) => {
376
+
return latestData?.cid && timelineData ? latestData.cid !== timelineData.pages[0].cid : false;
377
+
};
378
+
379
+
//// Raw fetch
380
+
const fetchPage = async (
381
+
rpc: BskyXRPC,
382
+
params: TimelineParams,
383
+
limit: number,
384
+
cursor: string | undefined,
385
+
signal: AbortSignal,
386
+
): Promise<AppBskyFeedGetTimeline.Output> => {
387
+
const type = params.type;
388
+
389
+
if (type === 'following') {
390
+
const response = await rpc.get('app.bsky.feed.getTimeline', {
391
+
signal: signal,
392
+
params: {
393
+
algorithm: 'reverse-chronological',
394
+
cursor: cursor,
395
+
limit: limit,
396
+
},
397
+
});
398
+
399
+
return response.data;
400
+
} else if (type === 'feed') {
401
+
const response = await rpc.get('app.bsky.feed.getFeed', {
402
+
signal: signal,
403
+
headers: {
404
+
'accent-language': navigator.languages.join(','),
405
+
},
406
+
params: {
407
+
feed: params.uri,
408
+
cursor: cursor,
409
+
limit: limit,
410
+
},
411
+
});
412
+
413
+
return response.data;
414
+
} else if (type === 'list') {
415
+
const response = await rpc.get('app.bsky.feed.getListFeed', {
416
+
signal: signal,
417
+
params: {
418
+
list: params.uri,
419
+
cursor: cursor,
420
+
limit: limit,
421
+
},
422
+
});
423
+
424
+
return response.data;
425
+
} else if (type === 'profile') {
426
+
if (params.tab === 'likes') {
427
+
const response = await rpc.get('app.bsky.feed.getActorLikes', {
428
+
signal: signal,
429
+
params: {
430
+
actor: params.actor,
431
+
cursor: cursor,
432
+
limit: limit,
433
+
},
434
+
});
435
+
436
+
return response.data;
437
+
} else {
438
+
const response = await rpc.get('app.bsky.feed.getAuthorFeed', {
439
+
signal: signal,
440
+
params: {
441
+
actor: params.actor,
442
+
cursor: cursor,
443
+
limit: limit,
444
+
filter:
445
+
params.tab === 'media'
446
+
? 'posts_with_media'
447
+
: params.tab === 'replies'
448
+
? 'posts_with_replies'
449
+
: 'posts_and_author_threads',
450
+
},
451
+
});
452
+
453
+
return response.data;
454
+
}
455
+
} else if (type === 'search') {
456
+
const response = await rpc.get('app.bsky.feed.searchPosts', {
457
+
signal: signal,
458
+
params: {
459
+
sort: 'latest',
460
+
q: params.query,
461
+
cursor: cursor,
462
+
limit: limit,
463
+
},
464
+
});
465
+
466
+
const data = response.data;
467
+
468
+
return { cursor: data.cursor, feed: data.posts.map((view) => ({ post: view })) };
469
+
} else {
470
+
assert(false, `Unknown type: ${type}`);
471
+
}
472
+
};
473
+
474
+
/// Timeline filters
475
+
type FilterFn<T> = (data: T) => boolean;
476
+
477
+
const combine = <T>(filters: Array<undefined | false | FilterFn<T>>): FilterFn<T> | undefined => {
478
+
const filtered = filters.filter((filter): filter is FilterFn<T> => !!filter);
479
+
const len = filtered.length;
480
+
481
+
// if (len === 1) {
482
+
// return filtered[0];
483
+
// }
484
+
485
+
// if (len === 0) {
486
+
// return;
487
+
// }
488
+
489
+
return (data: T) => {
490
+
for (let idx = 0; idx < len; idx++) {
491
+
const filter = filtered[idx];
492
+
493
+
if (!filter(data)) {
494
+
return false;
495
+
}
496
+
}
497
+
498
+
return true;
499
+
};
500
+
};
501
+
502
+
//// Post filters
503
+
const createDuplicatePostFilter = (): PostFilter => {
504
+
const map: Record<string, boolean> = {};
505
+
506
+
return (item) => {
507
+
const uri = item.post.uri;
508
+
509
+
if (map[uri]) {
510
+
return false;
511
+
}
512
+
513
+
return (map[uri] = true);
514
+
};
515
+
};
516
+
517
+
const createInvalidReplyFilter = (): PostFilter => {
518
+
return (item) => {
519
+
// Don't allow posts that isn't being a hydrated with a reply when it should
520
+
return (
521
+
// Allow reposts
522
+
item.reason?.$type === 'app.bsky.feed.defs#reasonRepost' ||
523
+
// Allow posts with a timeline reply attached
524
+
item.reply?.parent !== undefined ||
525
+
// Allow posts whose record doesn't have the reply object
526
+
(item.post.record as PostRecord).reply === undefined
527
+
);
528
+
};
529
+
};
530
+
531
+
const createLabelPostFilter = (opts: ModerationOptions): PostFilter | undefined => {
532
+
return (item) => {
533
+
const post = item.post;
534
+
535
+
const author = post.author;
536
+
const record = post.record as PostRecord;
537
+
538
+
const isFollowing = !!author.viewer?.following;
539
+
const text = record.text + unwrapPostEmbedText(record.embed);
540
+
541
+
record.embed;
542
+
543
+
const accu: ModerationCause[] = [];
544
+
decideLabelModeration(accu, TargetContent, post.labels, post.author.did, opts);
545
+
decideMutedKeywordModeration(accu, text, isFollowing, PreferenceHide, opts);
546
+
547
+
const decision = getModerationUI(accu, ContextContentList);
548
+
549
+
return decision.f.length === 0;
550
+
};
551
+
};
552
+
553
+
const createHiddenRepostFilter = (opts: ModerationOptions): PostFilter | undefined => {
554
+
const hidden = opts.preferences.hideReposts;
555
+
556
+
if (hidden.length === 0) {
557
+
return;
558
+
}
559
+
560
+
return (item) => {
561
+
const reason = item.reason;
562
+
563
+
return !reason || reason.$type !== 'app.bsky.feed.defs#reasonRepost' || !hidden.includes(reason.by.did);
564
+
};
565
+
};
566
+
567
+
const createTempMutePostFilter = (uid: At.DID, opts: ModerationOptions): PostFilter | undefined => {
568
+
// We won't be checking if any of the temporary mutes are stale, those should
569
+
// be handled within the UI.
570
+
const mutes = opts.preferences.tempMutes;
571
+
const hasMutes = Object.keys(mutes).length !== 0;
572
+
573
+
if (!hasMutes) {
574
+
return;
575
+
}
576
+
577
+
return (item) => {
578
+
const reason = item.reason;
579
+
580
+
if (reason) {
581
+
const did = reason.by.did;
582
+
583
+
if (did !== uid && did in mutes) {
584
+
return false;
585
+
}
586
+
}
587
+
588
+
const did = item.post.author.did;
589
+
590
+
if (did !== uid && did in mutes) {
591
+
return false;
592
+
}
593
+
594
+
return true;
595
+
};
596
+
};
597
+
598
+
const createHideRepliesFilter = (): PostFilter => {
599
+
return (item) => {
600
+
const reason = item.reason;
601
+
602
+
return (
603
+
// Allow reposts
604
+
(reason !== undefined && reason.$type === 'app.bsky.feed.defs#reasonRepost') ||
605
+
// Allow posts that aren't a reply
606
+
(item.reply === undefined && (item.post.record as PostRecord).reply === undefined)
607
+
);
608
+
};
609
+
};
610
+
611
+
const createHideRepostsFilter = (): PostFilter => {
612
+
return (item) => {
613
+
const reason = item.reason;
614
+
615
+
// Allow posts with no reason, or the reasoning isn't a repost.
616
+
return reason === undefined || reason.$type !== 'app.bsky.feed.defs#reasonRepost';
617
+
};
618
+
};
619
+
620
+
const createHideQuotesFilter = (): PostFilter => {
621
+
return (item) => {
622
+
const post = item.post.record as PostRecord;
623
+
const record = getRecordEmbed(post.embed);
624
+
625
+
return record === undefined || parseAtUri(record.record.uri).collection === 'app.bsky.feed.post';
626
+
};
627
+
};
628
+
629
+
//// Slice filters
630
+
const createFeedSliceFilter = (): SliceFilter | undefined => {
631
+
return (slice) => {
632
+
const items = slice.items;
633
+
const first = items[0];
634
+
635
+
const reply = first.reply;
636
+
637
+
// Skip any posts that are in reply to a muted user
638
+
if (reply) {
639
+
for (const author of getReplyAuthors(reply)) {
640
+
if (!author) {
641
+
continue;
642
+
}
643
+
644
+
const viewer = author.viewer;
645
+
646
+
if (!viewer) {
647
+
// If this one doesn't have viewer state then none of them does.
648
+
break;
649
+
}
650
+
651
+
if (viewer.muted) {
652
+
return yankReposts(items);
653
+
}
654
+
}
655
+
}
656
+
657
+
return true;
658
+
};
659
+
};
660
+
661
+
const createHomeSliceFilter = (uid: At.DID, followsOnly: boolean): SliceFilter | undefined => {
662
+
return (slice) => {
663
+
const items = slice.items;
664
+
const first = items[0];
665
+
666
+
const reply = first.reply;
667
+
const reason = first.reason;
668
+
669
+
// Skip any posts that are in reply to non-followed user or a muted user
670
+
if (reply && (!reason || reason.$type !== 'app.bsky.feed.defs#reasonRepost')) {
671
+
for (const author of getReplyAuthors(reply)) {
672
+
if (!author) {
673
+
continue;
674
+
}
675
+
676
+
const viewer = author.viewer;
677
+
678
+
if (!viewer) {
679
+
// If this one doesn't have viewer state then none of them does.
680
+
break;
681
+
}
682
+
683
+
if (author.did !== uid && ((followsOnly && !viewer.following) || !!viewer.muted)) {
684
+
return yankReposts(items);
685
+
}
686
+
}
687
+
}
688
+
689
+
return true;
690
+
};
691
+
};
692
+
693
+
const createProfileSliceFilter = (did: At.DID): SliceFilter | undefined => {
694
+
return (slice) => {
695
+
const items = slice.items;
696
+
const first = items[0];
697
+
698
+
const reply = first.reply;
699
+
const reason = first.reason;
700
+
701
+
// Skip any posts that doesn't seem to look like a self-thread
702
+
if (reply && (!reason || reason.$type !== 'app.bsky.feed.defs#reasonRepost')) {
703
+
for (const author of getReplyAuthors(reply)) {
704
+
if (!author) {
705
+
continue;
706
+
}
707
+
708
+
if (author.did !== did) {
709
+
return yankReposts(items);
710
+
}
711
+
}
712
+
}
713
+
714
+
return true;
715
+
};
716
+
};
717
+
718
+
// Get the reposts out of the gutter
719
+
const yankReposts = (items: EnsuredTimelineItem[]): TimelineSlice[] | false => {
720
+
let slices: TimelineSlice[] | false = false;
721
+
let last: EnsuredTimelineItem[] | undefined;
722
+
723
+
for (let idx = 0, len = items.length; idx < len; idx++) {
724
+
const item = items[idx];
725
+
const reason = item.reason;
726
+
727
+
if (reason && reason.$type === 'app.bsky.feed.defs#reasonRepost') {
728
+
if (last) {
729
+
last.push(item);
730
+
} else {
731
+
(slices ||= []).push({ items: (last = [item]) });
732
+
}
733
+
} else {
734
+
last = undefined;
735
+
}
736
+
}
737
+
738
+
return slices;
739
+
};
740
+
741
+
const getReplyAuthors = (reply: EnsuredReplyRef) => {
742
+
return [reply.root?.author, reply.grandparentAuthor, reply.parent?.author];
743
+
};
744
+
745
+
const getRecordEmbed = (embed: PostRecord['embed']): AppBskyEmbedRecord.Main | undefined => {
746
+
if (embed) {
747
+
if (embed.$type === 'app.bsky.embed.record') {
748
+
return embed;
749
+
}
750
+
751
+
if (embed.$type === 'app.bsky.embed.recordWithMedia') {
752
+
return embed.record;
753
+
}
754
+
}
755
+
};
+83
src/api/richtext/segment.ts
+83
src/api/richtext/segment.ts
···
1
+
import type { AppBskyRichtextFacet } from '@mary/bluesky-client/lexicons';
2
+
3
+
import type { UnwrapArray } from '../utils/types';
4
+
5
+
const encoder = new TextEncoder();
6
+
const decoder = new TextDecoder();
7
+
8
+
interface UtfString {
9
+
u16: string;
10
+
u8: Uint8Array;
11
+
}
12
+
13
+
const createUtfString = (utf16: string): UtfString => {
14
+
return { u16: utf16, u8: encoder.encode(utf16) };
15
+
};
16
+
17
+
const getUtf8Length = (utf: UtfString) => {
18
+
return utf.u8.byteLength;
19
+
};
20
+
21
+
const sliceUtf8 = (utf: UtfString, start?: number, end?: number) => {
22
+
return decoder.decode(utf.u8.slice(start, end));
23
+
};
24
+
25
+
type Facet = AppBskyRichtextFacet.Main;
26
+
type FacetFeature = UnwrapArray<Facet['features']>;
27
+
28
+
export interface RichtextSegment {
29
+
text: string;
30
+
feature: FacetFeature | undefined;
31
+
}
32
+
33
+
const createRichtextSegment = (text: string, feature: FacetFeature | undefined): RichtextSegment => {
34
+
return { text: text, feature: feature };
35
+
};
36
+
37
+
export const segmentRichText = (rtText: string, facets: Facet[] | undefined): RichtextSegment[] => {
38
+
if (facets === undefined || facets.length === 0) {
39
+
return [createRichtextSegment(rtText, undefined)];
40
+
}
41
+
42
+
const text = createUtfString(rtText);
43
+
44
+
const segments: RichtextSegment[] = [];
45
+
const length = getUtf8Length(text);
46
+
47
+
const facetsLength = facets.length;
48
+
49
+
let textCursor = 0;
50
+
let facetCursor = 0;
51
+
52
+
do {
53
+
const facet = facets[facetCursor];
54
+
const { byteStart, byteEnd } = facet.index;
55
+
56
+
if (textCursor < byteStart) {
57
+
segments.push(createRichtextSegment(sliceUtf8(text, textCursor, byteStart), undefined));
58
+
} else if (textCursor > byteStart) {
59
+
facetCursor++;
60
+
continue;
61
+
}
62
+
63
+
if (byteStart < byteEnd) {
64
+
const subtext = sliceUtf8(text, byteStart, byteEnd);
65
+
const features = facet.features;
66
+
67
+
if (features.length === 0 || subtext.trim().length === 0) {
68
+
segments.push(createRichtextSegment(subtext, undefined));
69
+
} else {
70
+
segments.push(createRichtextSegment(subtext, features[0]));
71
+
}
72
+
}
73
+
74
+
textCursor = byteEnd;
75
+
facetCursor++;
76
+
} while (facetCursor < facetsLength);
77
+
78
+
if (textCursor < length) {
79
+
segments.push(createRichtextSegment(sliceUtf8(text, textCursor, length), undefined));
80
+
}
81
+
82
+
return segments;
83
+
};
+54
src/api/utils/dequal.ts
+54
src/api/utils/dequal.ts
···
1
+
const keys = Object.keys;
2
+
3
+
export const dequal = (a: any, b: any): boolean => {
4
+
let ctor: any;
5
+
let len: number;
6
+
7
+
if (a === b) {
8
+
return true;
9
+
}
10
+
11
+
if (a && b && (ctor = a.constructor) === b.constructor) {
12
+
if (ctor === Array) {
13
+
if ((len = a.length) === b.length) {
14
+
while (len--) {
15
+
if (!dequal(a[len], b[len])) {
16
+
return false;
17
+
}
18
+
}
19
+
}
20
+
21
+
return len === -1;
22
+
} else if (!ctor || ctor === Object) {
23
+
len = 0;
24
+
25
+
for (ctor in a) {
26
+
len++;
27
+
28
+
if (!(ctor in b) || !dequal(a[ctor], b[ctor])) {
29
+
return false;
30
+
}
31
+
}
32
+
33
+
return keys(b).length === len;
34
+
}
35
+
}
36
+
37
+
return a !== a && b !== b;
38
+
};
39
+
40
+
export const EQUALS_DEQUAL = { equals: dequal } as const;
41
+
42
+
export const sequal = (a: any[], b: any[]): boolean => {
43
+
let len = a.length;
44
+
45
+
if (len === b.length) {
46
+
while (len--) {
47
+
if (a[len] !== b[len]) {
48
+
return false;
49
+
}
50
+
}
51
+
}
52
+
53
+
return len === -1;
54
+
};
+102
src/api/utils/did-doc.ts
+102
src/api/utils/did-doc.ts
···
1
+
import { BskyXRPC, getPdsEndpoint, type DidDocument } from '@mary/bluesky-client';
2
+
import type { At } from '@mary/bluesky-client/lexicons';
3
+
4
+
import { DEFAULT_APP_VIEW } from '../defaults';
5
+
import type { DataServer } from '../types';
6
+
import { isDid } from './strings';
7
+
8
+
const HOST_RE = /^([a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*(?:\.[a-zA-Z]+))$/;
9
+
10
+
export type ResolutionErrorKind =
11
+
| 'DID_UNSUPPORTED'
12
+
| 'PLC_NOT_FOUND'
13
+
| 'PLC_UNREACHABLE'
14
+
| 'WEB_INVALID'
15
+
| 'WEB_NOT_FOUND'
16
+
| 'WEB_UNREACHABLE';
17
+
18
+
export class DidResolutionError extends Error {
19
+
message!: ResolutionErrorKind;
20
+
21
+
constructor(kind: ResolutionErrorKind) {
22
+
super(kind);
23
+
}
24
+
}
25
+
26
+
export const findDidDocument = async (identifier: string): Promise<DidDocument> => {
27
+
let did: At.DID;
28
+
29
+
if (isDid(identifier)) {
30
+
did = identifier;
31
+
} else {
32
+
const rpc = new BskyXRPC({ service: DEFAULT_APP_VIEW });
33
+
const response = await rpc.get('com.atproto.identity.resolveHandle', {
34
+
params: {
35
+
handle: identifier,
36
+
},
37
+
});
38
+
39
+
did = response.data.did;
40
+
}
41
+
42
+
const colon_index = did.indexOf(':', 4);
43
+
44
+
const type = did.slice(4, colon_index);
45
+
const ident = did.slice(colon_index + 1);
46
+
47
+
// 2. retrieve their DID documents
48
+
let doc: DidDocument;
49
+
50
+
if (type === 'plc') {
51
+
const response = await fetch(`https://plc.directory/${did}`);
52
+
53
+
if (response.status === 404) {
54
+
throw new DidResolutionError('PLC_NOT_FOUND');
55
+
} else if (!response.ok) {
56
+
throw new DidResolutionError('PLC_UNREACHABLE');
57
+
}
58
+
59
+
const json = await response.json();
60
+
61
+
doc = json as DidDocument;
62
+
} else if (type === 'web') {
63
+
if (!HOST_RE.test(ident)) {
64
+
throw new DidResolutionError('WEB_INVALID');
65
+
}
66
+
67
+
const response = await fetch(`https://${ident}/.well-known/did.json`);
68
+
69
+
if (response.status === 404) {
70
+
throw new DidResolutionError('WEB_NOT_FOUND');
71
+
} else if (!response.ok) {
72
+
throw new DidResolutionError('WEB_UNREACHABLE');
73
+
}
74
+
75
+
const json = await response.json();
76
+
77
+
doc = json as DidDocument;
78
+
} else {
79
+
throw new DidResolutionError('DID_UNSUPPORTED');
80
+
}
81
+
82
+
return doc;
83
+
};
84
+
85
+
export const getDataServer = (doc: DidDocument): DataServer | null => {
86
+
const pds = getPdsEndpoint(doc);
87
+
88
+
if (pds) {
89
+
// Check if this is bsky.social, and give it a nice name.
90
+
const url = new URL(pds);
91
+
const host = url.host;
92
+
93
+
const isBskySocial = host === 'bsky.social' || host.endsWith('.host.bsky.network');
94
+
95
+
return {
96
+
name: isBskySocial ? `Bluesky Social` : host,
97
+
uri: pds,
98
+
};
99
+
}
100
+
101
+
return null;
102
+
};
+22
src/api/utils/did.ts
+22
src/api/utils/did.ts
···
1
+
import type { BskyXRPC } from '@mary/bluesky-client';
2
+
import type { At } from '@mary/bluesky-client/lexicons';
3
+
4
+
import { isDid } from './strings';
5
+
6
+
const getDid = async (rpc: BskyXRPC, actor: string, signal?: AbortSignal) => {
7
+
let did: At.DID;
8
+
if (isDid(actor)) {
9
+
did = actor;
10
+
} else {
11
+
const response = await rpc.get('com.atproto.identity.resolveHandle', {
12
+
signal: signal,
13
+
params: { handle: actor },
14
+
});
15
+
16
+
did = response.data.did;
17
+
}
18
+
19
+
return did;
20
+
};
21
+
22
+
export default getDid;
+20
src/api/utils/error.ts
+20
src/api/utils/error.ts
···
1
+
import { XRPCError } from '@mary/bluesky-client/xrpc';
2
+
3
+
export const formatXRPCError = (err: XRPCError): string => {
4
+
const name = err.kind;
5
+
return (name ? name + ': ' : '') + err.message;
6
+
};
7
+
8
+
export const formatQueryError = (err: unknown) => {
9
+
if (err instanceof XRPCError) {
10
+
const error = err.kind;
11
+
12
+
if (error === 'InvalidToken' || error === 'ExpiredToken') {
13
+
return `Account session invalid, please sign in again`;
14
+
}
15
+
16
+
return formatXRPCError(err);
17
+
}
18
+
19
+
return '' + err;
20
+
};
+94
src/api/utils/post.ts
+94
src/api/utils/post.ts
···
1
+
import type {
2
+
AppBskyEmbedExternal,
3
+
AppBskyEmbedImages,
4
+
AppBskyEmbedRecord,
5
+
AppBskyFeedDefs,
6
+
AppBskyFeedPost,
7
+
Brand,
8
+
} from '@mary/bluesky-client/lexicons';
9
+
10
+
type RecordEmbed = AppBskyFeedPost.Record['embed'];
11
+
type ViewEmbed = AppBskyFeedDefs.PostView['embed'];
12
+
13
+
export const unwrapPostEmbedText = (embed: RecordEmbed | ViewEmbed): string => {
14
+
let str = '';
15
+
16
+
const media = getMediaEmbed(embed);
17
+
18
+
if (media) {
19
+
const type = media.$type;
20
+
21
+
if (type === 'app.bsky.embed.external' || type === 'app.bsky.embed.external#view') {
22
+
str += ` ` + media.external.title;
23
+
} else if (type === 'app.bsky.embed.images' || type === 'app.bsky.embed.images#view') {
24
+
const images = media.images;
25
+
26
+
for (let idx = 0, len = images.length; idx < len; idx++) {
27
+
str += ` ` + images[idx].alt;
28
+
}
29
+
}
30
+
}
31
+
32
+
return str;
33
+
};
34
+
35
+
type RecordMedia = AppBskyEmbedExternal.Main | AppBskyEmbedImages.Main;
36
+
type ViewMedia = AppBskyEmbedExternal.View | AppBskyEmbedImages.View;
37
+
38
+
const getMediaEmbed = (embed: RecordEmbed | ViewEmbed): Brand.Union<RecordMedia | ViewMedia> | undefined => {
39
+
if (embed) {
40
+
const type = embed.$type;
41
+
42
+
if (
43
+
type === 'app.bsky.embed.external' ||
44
+
type === 'app.bsky.embed.external#view' ||
45
+
type === 'app.bsky.embed.images' ||
46
+
type === 'app.bsky.embed.images#view'
47
+
) {
48
+
return embed;
49
+
} else if (type === 'app.bsky.embed.recordWithMedia' || type === 'app.bsky.embed.recordWithMedia#view') {
50
+
const media = embed.media;
51
+
const mediatype = media.$type;
52
+
53
+
if (
54
+
mediatype === 'app.bsky.embed.external' ||
55
+
mediatype === 'app.bsky.embed.external#view' ||
56
+
mediatype === 'app.bsky.embed.images' ||
57
+
mediatype === 'app.bsky.embed.images#view'
58
+
) {
59
+
return media;
60
+
}
61
+
}
62
+
}
63
+
};
64
+
65
+
export const getEmbeddedPost = (
66
+
embed: AppBskyFeedDefs.PostView['embed'],
67
+
): AppBskyEmbedRecord.ViewRecord | undefined => {
68
+
if (embed) {
69
+
if (embed.$type === 'app.bsky.embed.record#view') {
70
+
if (embed.record.$type === 'app.bsky.embed.record#viewRecord') {
71
+
return embed.record;
72
+
}
73
+
} else if (embed.$type === 'app.bsky.embed.recordWithMedia#view') {
74
+
if (embed.record.record.$type === 'app.bsky.embed.record#viewRecord') {
75
+
return embed.record.record;
76
+
}
77
+
}
78
+
}
79
+
};
80
+
81
+
export const embedViewRecordToPostView = (v: AppBskyEmbedRecord.ViewRecord): AppBskyFeedDefs.PostView => {
82
+
return {
83
+
uri: v.uri,
84
+
cid: v.cid,
85
+
author: v.author,
86
+
record: v.value,
87
+
indexedAt: v.indexedAt,
88
+
labels: v.labels,
89
+
embed: v.embeds?.[0],
90
+
likeCount: v.likeCount,
91
+
replyCount: v.replyCount,
92
+
repostCount: v.repostCount,
93
+
};
94
+
};
+40
src/api/utils/query.ts
+40
src/api/utils/query.ts
···
1
+
import {
2
+
type InfiniteData,
3
+
type QueryClient,
4
+
type QueryFunctionContext,
5
+
type QueryKey,
6
+
} from '@mary/solid-query';
7
+
8
+
export const resetInfiniteData = (client: QueryClient, key: QueryKey) => {
9
+
client.setQueryData<InfiniteData<unknown>>(key, (data) => {
10
+
if (data && data.pages.length > 1) {
11
+
return {
12
+
pages: data.pages.slice(0, 1),
13
+
pageParams: data.pageParams.slice(0, 1),
14
+
};
15
+
}
16
+
17
+
return data;
18
+
});
19
+
};
20
+
21
+
const errorMap = new WeakMap<WeakKey, { pageParam: any; direction: 'forward' | 'backward' }>();
22
+
23
+
export const wrapQuery = <TQueryData, TPageParam, TQueryKey extends QueryKey = QueryKey>(
24
+
fn: (ctx: QueryFunctionContext<TQueryKey, TPageParam>) => Promise<TQueryData>,
25
+
) => {
26
+
return async (ctx: QueryFunctionContext<TQueryKey, TPageParam>): Promise<TQueryData> => {
27
+
try {
28
+
return await fn(ctx);
29
+
} catch (err) {
30
+
// @ts-expect-error
31
+
errorMap.set(err as any, { pageParam: ctx.pageParam, direction: ctx.direction });
32
+
throw err;
33
+
}
34
+
};
35
+
};
36
+
37
+
export const getQueryErrorInfo = (err: unknown) => {
38
+
const info = errorMap.get(err as any);
39
+
return info;
40
+
};
+98
src/api/utils/strings.ts
+98
src/api/utils/strings.ts
···
1
+
import type { At } from '@mary/bluesky-client/lexicons';
2
+
3
+
import { assert } from '~/lib/invariant';
4
+
5
+
export const isDid = (value: string): value is At.DID => {
6
+
return value.startsWith('did:');
7
+
};
8
+
9
+
export const ATURI_RE =
10
+
/^at:\/\/(did:[a-z0-9:%-]+)\/([a-zA-Z0-9-.]+)\/([a-zA-Z0-9._~:@!$&%')(*+,;=-]+)(?:#(\/[a-zA-Z0-9._~:@!$&%')(*+,;=\-[\]/\\]*))?$/;
11
+
12
+
export const DID_RE = /^did:([a-z]+):([a-zA-Z0-9._:%-]*[a-zA-Z0-9._-])$/;
13
+
14
+
export interface AtUri {
15
+
repo: At.DID;
16
+
collection: string;
17
+
rkey: string;
18
+
fragment: string | undefined;
19
+
}
20
+
21
+
export const parseAtUri = (str: string): AtUri => {
22
+
const match = ATURI_RE.exec(str);
23
+
assert(match !== null);
24
+
25
+
return {
26
+
repo: match[1] as At.DID,
27
+
collection: match[2],
28
+
rkey: match[3],
29
+
fragment: match[4],
30
+
};
31
+
};
32
+
33
+
// Check for the existence of URL.parse and use that if available, removes the
34
+
// performance hit from try..catch blocks.
35
+
export const safeUrlParse =
36
+
'parse' in URL
37
+
? (text: string): URL | null => {
38
+
// @ts-expect-error
39
+
const url = URL.parse(text) as URL | null;
40
+
41
+
if (url !== null && (url.protocol === 'https:' || url.protocol === 'http:')) {
42
+
return url;
43
+
}
44
+
45
+
return null;
46
+
}
47
+
: (text: string): URL | null => {
48
+
try {
49
+
const url = new URL(text);
50
+
51
+
if (url.protocol === 'https:' || url.protocol === 'http:') {
52
+
return url;
53
+
}
54
+
} catch {}
55
+
56
+
return null;
57
+
};
58
+
59
+
const TRIM_HOST_RE = /^www\./;
60
+
const TRIM_URLTEXT_RE = /^\s*(https?:\/\/)?(?:www\.)?/;
61
+
// const PATH_MAX_LENGTH = 18;
62
+
63
+
export const isLinkValid = (uri: string, text: string) => {
64
+
const url = safeUrlParse(uri);
65
+
66
+
if (url === null) {
67
+
return false;
68
+
}
69
+
70
+
const expectedHost = buildHostPart(url);
71
+
const length = expectedHost.length;
72
+
73
+
const normalized = text.replace(TRIM_URLTEXT_RE, '').toLowerCase();
74
+
const normalizedLength = normalized.length;
75
+
76
+
const boundary = normalizedLength >= length ? normalized[length] : undefined;
77
+
78
+
return (
79
+
(boundary === undefined || boundary === '/' || boundary === '?' || boundary === '#') &&
80
+
normalized.startsWith(expectedHost)
81
+
);
82
+
};
83
+
84
+
const buildHostPart = (url: URL) => {
85
+
const username = url.username;
86
+
// const password = url.password;
87
+
88
+
const hostname = url.hostname.replace(TRIM_HOST_RE, '').toLowerCase();
89
+
const port = url.port;
90
+
91
+
// Perhaps might be best if we always warn on authentication being passed.
92
+
// const auth = username ? username + (password ? ':' + password : '') + '@' : '';
93
+
const auth = username ? '\0@@\0' : '';
94
+
95
+
const host = hostname + (port ? ':' + port : '');
96
+
97
+
return auth + host;
98
+
};
+1
src/api/utils/types.ts
+1
src/api/utils/types.ts
···
1
+
export type UnwrapArray<T> = T extends (infer V)[] ? V : never;
+3
src/api/utils/utils.ts
+3
src/api/utils/utils.ts
+1
src/assets/default-feed-avatar.svg
+1
src/assets/default-feed-avatar.svg
···
1
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path fill="#0070FF" d="M0 0h32v32H0z"/><path fill="#fff" d="M13.5 7.25c0-.691.559-1.25 1.25-1.25C20.965 6 26 11.035 26 17.25c0 .691-.559 1.25-1.25 1.25s-1.25-.559-1.25-1.25a8.75 8.75 0 0 0-8.75-8.75c-.691 0-1.25-.559-1.25-1.25Zm-5.133 7.367 4.067 4.067 1.109-1.11A1.249 1.249 0 0 1 14.75 16c.691 0 1.25.559 1.25 1.25a1.249 1.249 0 0 1-1.574 1.207l-1.11 1.11 4.067 4.066c.566.566.46 1.515-.285 1.808a8.158 8.158 0 0 1-2.973.559 8.124 8.124 0 0 1-7.563-11.098c.293-.742 1.243-.851 1.81-.285h-.005ZM14.75 9.75c4.14 0 7.5 3.36 7.5 7.5 0 .691-.559 1.25-1.25 1.25s-1.25-.559-1.25-1.25a5 5 0 0 0-5-5c-.691 0-1.25-.559-1.25-1.25s.559-1.25 1.25-1.25Z"/></svg>
+1
src/assets/default-labeler-avatar.svg
+1
src/assets/default-labeler-avatar.svg
···
1
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path fill="#6900FF" d="M0 0h32v32H0z"/><path fill="none" stroke="#fff" stroke-linejoin="round" stroke-width="2" d="M24 9.75 16 7 8 9.75v6.162c0 4.973 4 7.088 8 9.246 4-2.158 8-4.273 8-9.246V9.75Z"/></svg>
+1
src/assets/default-list-avatar.svg
+1
src/assets/default-list-avatar.svg
···
1
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path fill="#0070FF" d="M0 0h32v32H0z"/><path fill="#fff" d="M22.153 22.354a9.328 9.328 0 0 0 3.837-.491 3.076 3.076 0 0 0-4.802-2.79m.965 3.281a6.128 6.128 0 0 0-.965-3.28Zm-11.342-3.28a3.077 3.077 0 0 0-4.801 2.79 9.21 9.21 0 0 0 3.835.49m.966-3.28a6.127 6.127 0 0 0-.966 3.28Zm8.265-8.997a3.076 3.076 0 1 1-6.153 0 3.076 3.076 0 0 1 6.153 0Zm6.154 3.077a2.307 2.307 0 1 1-4.615 0 2.307 2.307 0 0 1 4.615 0Zm-13.847 0a2.307 2.307 0 1 1-4.614 0 2.307 2.307 0 0 1 4.614 0Z"/><path fill="#fff" d="M22 22c0 3.314-2.686 3.5-6 3.5s-6-.186-6-3.5a6 6 0 0 1 12 0Z"/></svg>
+1
src/assets/default-user-avatar.svg
+1
src/assets/default-user-avatar.svg
···
1
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#0070ff" d="M0 0h24v24H0z"/><circle cx="12" cy="9.5" r="3.5" fill="#fff"/><path fill="#fff" d="M12.058 22.784c-2.636 0-5.051-.948-6.921-2.522.53-2.274 3.397-4.012 6.853-4.012 3.504 0 6.401 1.786 6.874 4.107a10.71 10.71 0 0 1-6.806 2.427Z"/></svg>
+104
src/components/avatar.tsx
+104
src/components/avatar.tsx
···
1
+
import { Match, Switch } from 'solid-js';
2
+
3
+
import { ContextProfileMedia, getModerationUI, type ModerationCause } from '~/api/moderation';
4
+
5
+
import DefaultFeedAvatar from '~/assets/default-feed-avatar.svg?url';
6
+
import DefaultLabelerAvatar from '~/assets/default-labeler-avatar.svg?url';
7
+
import DefaultListAvatar from '~/assets/default-list-avatar.svg?url';
8
+
import DefaultUserAvatar from '~/assets/default-user-avatar.svg?url';
9
+
10
+
import { handleLinkClick } from './button';
11
+
12
+
const AVATARS = {
13
+
feed: DefaultFeedAvatar,
14
+
labeler: DefaultLabelerAvatar,
15
+
list: DefaultListAvatar,
16
+
user: DefaultUserAvatar,
17
+
};
18
+
19
+
export interface AvatarProps {
20
+
type: keyof typeof AVATARS;
21
+
src?: string;
22
+
moderation?: ModerationCause[];
23
+
title?: string;
24
+
disabled?: boolean;
25
+
href?: string;
26
+
onClick?: () => void;
27
+
28
+
class?: string;
29
+
size?: 'xs' | 'sm' | 'md' | 'lg';
30
+
}
31
+
32
+
const Avatar = (props: AvatarProps) => {
33
+
const hasModeration = 'moderation' in props;
34
+
35
+
const shouldBlurAvatar = hasModeration
36
+
? () => getModerationUI(props.moderation!, ContextProfileMedia).b.length > 0
37
+
: undefined;
38
+
39
+
return (
40
+
<Switch>
41
+
<Match when={!props.disabled && props.href}>
42
+
{(href) => (
43
+
<a
44
+
href={href()}
45
+
title={props.title}
46
+
onClick={handleLinkClick(() => props.onClick)}
47
+
class={avatarClassNames(props, true)}
48
+
>
49
+
{renderAvatar(props.type, props.src, shouldBlurAvatar)}
50
+
</a>
51
+
)}
52
+
</Match>
53
+
54
+
<Match when={!props.disabled && props.onClick} keyed>
55
+
{(onClick) => (
56
+
<button title={props.title} onClick={onClick} class={avatarClassNames(props, true)}>
57
+
{renderAvatar(props.type, props.src, shouldBlurAvatar)}
58
+
</button>
59
+
)}
60
+
</Match>
61
+
62
+
<Match when>
63
+
<div title={props.title} class={avatarClassNames(props, false)}>
64
+
{renderAvatar(props.type, props.src, shouldBlurAvatar)}
65
+
</div>
66
+
</Match>
67
+
</Switch>
68
+
);
69
+
};
70
+
71
+
export default Avatar;
72
+
73
+
const renderAvatar = (type: keyof typeof AVATARS, src: string | undefined, shouldBlur?: () => boolean) => {
74
+
return (
75
+
<img
76
+
src={/* @once */ src ?? AVATARS[type]}
77
+
class={`h-full w-full` + (src && shouldBlur?.() ? ` blur` : ``)}
78
+
/>
79
+
);
80
+
};
81
+
82
+
const avatarClassNames = ({ size = 'md', class: className }: AvatarProps, interactive: boolean): string => {
83
+
let cn = `shrink-0 overflow-hidden rounded-full bg-c-contrast-200`;
84
+
85
+
if (interactive) {
86
+
cn += ` hover:opacity-80`;
87
+
}
88
+
89
+
if (size === 'md') {
90
+
cn += ` h-9 w-9`;
91
+
} else if (size === 'sm') {
92
+
cn += ` h-6 w-6`;
93
+
} else if (size === 'xs') {
94
+
cn += ` h-5 w-5`;
95
+
} else if (size === 'lg') {
96
+
cn += ` h-10 w-10`;
97
+
}
98
+
99
+
if (className) {
100
+
return `${cn} ${className}`;
101
+
} else {
102
+
return cn;
103
+
}
104
+
};
+27
src/components/circular-progress.tsx
+27
src/components/circular-progress.tsx
···
1
+
export interface CircularProgressProps {
2
+
size?: number;
3
+
}
4
+
5
+
const CircularProgress = (props: CircularProgressProps) => {
6
+
return (
7
+
<svg
8
+
viewBox="0 0 32 32"
9
+
class="animate-spin"
10
+
style={`height:${props.size ?? 24}px;width:${props.size ?? 24}px`}
11
+
>
12
+
<circle cx="16" cy="16" fill="none" r="14" stroke-width="4" class="stroke-c-primary-500 opacity-20" />
13
+
<circle
14
+
cx="16"
15
+
cy="16"
16
+
fill="none"
17
+
r="14"
18
+
stroke-width="4"
19
+
stroke-dasharray="80px"
20
+
stroke-dashoffset="60px"
21
+
class="stroke-c-primary-500"
22
+
/>
23
+
</svg>
24
+
);
25
+
};
26
+
27
+
export default CircularProgress;
+159
src/components/dialog.tsx
+159
src/components/dialog.tsx
···
1
+
import { type ParentProps } from 'solid-js';
2
+
3
+
import { useModalContext } from '~/globals/modals';
4
+
import { useMediaQuery } from '~/lib/hooks/media-query';
5
+
6
+
import { useModalClose } from '~/lib/hooks/modal-close';
7
+
import { useTheme } from '~/lib/states/theme';
8
+
9
+
import { Fieldset } from './fieldset';
10
+
import IconButton from './icon-button';
11
+
import CrossLargeOutlinedIcon from './icons-central/cross-large-outline';
12
+
13
+
const DialogBackdrop = () => {
14
+
const theme = useTheme();
15
+
16
+
return (
17
+
<div
18
+
class={`fixed inset-0 z-0` + (theme.currentTheme === 'light' ? ` bg-t-black/40` : ` bg-t-blue-low/40`)}
19
+
></div>
20
+
);
21
+
};
22
+
23
+
export { DialogBackdrop as Backdrop };
24
+
25
+
export interface DialogContainerProps extends ParentProps {
26
+
fullHeight?: boolean;
27
+
centered?: boolean;
28
+
maxWidth?: 'sm' | 'md';
29
+
disabled?: boolean;
30
+
}
31
+
32
+
const DialogContainer = (props: DialogContainerProps) => {
33
+
const { close, isActive } = useModalContext();
34
+
35
+
const isDesktop = useMediaQuery('(width >= 688px) and (height >= 500px)');
36
+
const isDisabled = () => !!props.disabled;
37
+
38
+
const containerRef = (node: HTMLElement): void => {
39
+
useModalClose(node, close, () => isActive() && !isDisabled());
40
+
};
41
+
42
+
return (
43
+
<Fieldset standalone disabled={isDisabled()}>
44
+
<div
45
+
ref={containerRef}
46
+
role="dialog"
47
+
class={containerClasses(isDesktop, props)}
48
+
style={{ '--dialog-width': getMaxDialogWidth(props) }}
49
+
>
50
+
<div class="mx-auto flex h-full min-h-0 w-full max-w-[var(--dialog-width)] flex-col overflow-hidden">
51
+
{props.children}
52
+
</div>
53
+
</div>
54
+
</Fieldset>
55
+
);
56
+
};
57
+
58
+
const getMaxDialogWidth = ({ maxWidth = 'md' }: DialogContainerProps) => {
59
+
if (maxWidth === 'md') {
60
+
return `600px`;
61
+
} else if (maxWidth === 'sm') {
62
+
return `446px`;
63
+
}
64
+
};
65
+
66
+
const containerClasses = (isDesktop: () => boolean, props: DialogContainerProps): string => {
67
+
var cn = `z-1 bg-c-contrast-0`;
68
+
69
+
if (isDesktop()) {
70
+
cn += ` a-dialog-desktop w-full max-w-[var(--dialog-width)] rounded-xl`;
71
+
72
+
if (props.fullHeight) {
73
+
cn += ` grow`;
74
+
}
75
+
76
+
if (props.centered) {
77
+
cn += ` my-auto`;
78
+
} else {
79
+
cn += ` mt-11`;
80
+
}
81
+
} else {
82
+
cn += ` h-full w-full grow`;
83
+
}
84
+
85
+
return cn;
86
+
};
87
+
88
+
export { DialogContainer as Container };
89
+
90
+
export interface DialogHeaderProps extends ParentProps {}
91
+
92
+
const DialogHeader = (props: DialogHeaderProps) => {
93
+
return <div class="flex h-13 shrink-0 items-center justify-between gap-4 px-2.5">{props.children}</div>;
94
+
};
95
+
96
+
export { DialogHeader as Header };
97
+
98
+
export interface DialogHeadingProps {
99
+
title?: string;
100
+
subtitle?: string;
101
+
}
102
+
103
+
const DialogHeading = (props: DialogHeadingProps) => {
104
+
return (
105
+
<div class="flex min-w-0 grow flex-col gap-0.5">
106
+
<p class="text-base font-bold empty:hidden">{props.title}</p>
107
+
</div>
108
+
);
109
+
};
110
+
111
+
export { DialogHeading as Heading };
112
+
113
+
export interface DialogHeaderAccessoryProps extends ParentProps {}
114
+
115
+
const DialogHeaderAccessory = (props: DialogHeaderAccessoryProps) => {
116
+
return <div class="flex shrink-0 gap-2 empty:hidden">{props.children}</div>;
117
+
};
118
+
119
+
export { DialogHeaderAccessory as HeaderAccessory };
120
+
121
+
const DialogClose = () => {
122
+
const { close } = useModalContext();
123
+
return <IconButton title="Close dialog" icon={CrossLargeOutlinedIcon} onClick={close} />;
124
+
};
125
+
126
+
export { DialogClose as Close };
127
+
128
+
export interface DialogBodyProps extends ParentProps {
129
+
unpadded?: boolean;
130
+
class?: string;
131
+
}
132
+
133
+
const DialogBody = (props: DialogBodyProps) => {
134
+
return (
135
+
<div class={`grow overflow-y-auto` + (!props.unpadded ? ` p-4` : ``) + ` ` + (props.class ?? '')}>
136
+
{props.children}
137
+
</div>
138
+
);
139
+
};
140
+
141
+
export { DialogBody as Body };
142
+
143
+
export interface DialogActionsProps extends ParentProps {}
144
+
145
+
const DialogActions = (props: DialogActionsProps) => {
146
+
const isDesktop = useMediaQuery('(width >= 688px) and (height >= 500px)');
147
+
148
+
return (
149
+
<div
150
+
class={
151
+
`flex shrink-0 gap-4 px-4 py-5` + (isDesktop() ? ` items-center justify-end` : ` flex-col-reverse`)
152
+
}
153
+
>
154
+
{props.children}
155
+
</div>
156
+
);
157
+
};
158
+
159
+
export { DialogActions as Actions };
+31
src/components/divider.tsx
+31
src/components/divider.tsx
···
1
+
type Gutter = false | 'sm' | 'md';
2
+
3
+
export interface DividerProps {
4
+
gutter?: Gutter;
5
+
gutterTop?: Gutter;
6
+
gutterBottom?: Gutter;
7
+
}
8
+
9
+
const Divider = (props: DividerProps) => {
10
+
return <hr class={dividerClassNames(props)} />;
11
+
};
12
+
13
+
const dividerClassNames = ({ gutter = false, gutterBottom = gutter, gutterTop = gutter }: DividerProps) => {
14
+
let cn = `border-c-contrast-200`;
15
+
16
+
if (gutterBottom === 'sm') {
17
+
cn += ` mb-1`;
18
+
} else if (gutterBottom === 'md') {
19
+
cn += ` mb-3`;
20
+
}
21
+
22
+
if (gutterTop === 'sm') {
23
+
cn += ` mt-1`;
24
+
} else if (gutterTop === 'md') {
25
+
cn += ` mt-3`;
26
+
}
27
+
28
+
return cn;
29
+
};
30
+
31
+
export default Divider;
+124
src/components/embeds/embed.tsx
+124
src/components/embeds/embed.tsx
···
1
+
import type {
2
+
AppBskyEmbedExternal,
3
+
AppBskyEmbedImages,
4
+
AppBskyEmbedRecord,
5
+
AppBskyFeedDefs,
6
+
Brand,
7
+
} from '@mary/bluesky-client/lexicons';
8
+
9
+
import { ContextContentMedia, getModerationUI, type ModerationCause } from '~/api/moderation';
10
+
import { parseAtUri } from '~/api/utils/strings';
11
+
12
+
import ContentHider from '../moderation/content-hider';
13
+
14
+
import ImageEmbed from './image-embed';
15
+
import QuoteEmbed from './quote-embed';
16
+
17
+
export interface EmbedProps {
18
+
/** Expected to be static */
19
+
embed: NonNullable<AppBskyFeedDefs.PostView['embed']>;
20
+
/** Expected to be static */
21
+
gutterTop?: boolean;
22
+
/** Expected to be static */
23
+
large?: boolean;
24
+
moderation?: ModerationCause[];
25
+
}
26
+
27
+
const Embed = (props: EmbedProps) => {
28
+
const embed = props.embed;
29
+
const gutterTop = props.gutterTop;
30
+
const large = props.large;
31
+
32
+
const type = embed.$type;
33
+
34
+
return (
35
+
<div class={`flex flex-col gap-3` + (gutterTop ? ` mt-3` : ``)}>
36
+
{type === 'app.bsky.embed.recordWithMedia#view' ? (
37
+
<>
38
+
<MediaEmbed embed={/* @once */ embed.media} moderation={props.moderation} />
39
+
<RecordEmbed embed={/* @once */ embed.record} large={large} />
40
+
</>
41
+
) : type !== 'app.bsky.embed.record#view' ? (
42
+
<MediaEmbed embed={embed} moderation={props.moderation} />
43
+
) : (
44
+
<RecordEmbed embed={embed} large={large} />
45
+
)}
46
+
</div>
47
+
);
48
+
};
49
+
50
+
export default Embed;
51
+
52
+
interface MediaEmbedProps {
53
+
/** Expected to be static */
54
+
embed: Brand.Union<AppBskyEmbedExternal.View | AppBskyEmbedImages.View>;
55
+
moderation?: ModerationCause[];
56
+
}
57
+
58
+
const MediaEmbed = (props: MediaEmbedProps) => {
59
+
return (
60
+
<ContentHider
61
+
ui={getModerationUI(props.moderation, ContextContentMedia)}
62
+
childContainerClass="flex flex-col mt-1.5"
63
+
children={(() => {
64
+
const embed = props.embed;
65
+
const type = embed.$type;
66
+
67
+
if (type === 'app.bsky.embed.images#view') {
68
+
return <ImageEmbed embed={embed} interactive />;
69
+
}
70
+
71
+
return renderEmpty(`Unsupported media`);
72
+
})()}
73
+
/>
74
+
);
75
+
};
76
+
77
+
interface RecordEmbedProps {
78
+
/** Expected to be static */
79
+
embed: AppBskyEmbedRecord.View;
80
+
/** Expected to be static */
81
+
large?: boolean;
82
+
}
83
+
84
+
const RecordEmbed = (props: RecordEmbedProps) => {
85
+
const embed = props.embed;
86
+
const large = props.large;
87
+
88
+
const record = embed.record;
89
+
const type = record.$type;
90
+
91
+
if (type === 'app.bsky.embed.record#viewNotFound' || type === 'app.bsky.embed.record#viewBlocked') {
92
+
const { collection } = parseAtUri(record.uri);
93
+
94
+
if (
95
+
collection === 'app.bsky.feed.post' &&
96
+
type === 'app.bsky.embed.record#viewBlocked' &&
97
+
record.author.viewer?.blocking
98
+
) {
99
+
return renderEmpty(`Blocking`);
100
+
}
101
+
102
+
return renderEmpty(`This post is unavailable`);
103
+
}
104
+
105
+
if (type === 'app.bsky.embed.record#viewRecord') {
106
+
return <QuoteEmbed quote={record} large={large} interactive />;
107
+
}
108
+
109
+
if (type === 'app.bsky.feed.defs#generatorView') {
110
+
}
111
+
112
+
if (type === 'app.bsky.graph.defs#listView') {
113
+
}
114
+
115
+
return renderEmpty(`Unsupported record`);
116
+
};
117
+
118
+
const renderEmpty = (msg: string) => {
119
+
return (
120
+
<div class="rounded-md border border-c-contrast-200 p-3">
121
+
<p class="text-sm text-c-contrast-600">{msg}</p>
122
+
</div>
123
+
);
124
+
};
+134
src/components/embeds/image-embed.tsx
+134
src/components/embeds/image-embed.tsx
···
1
+
import type { AppBskyEmbedImages } from '@mary/bluesky-client/lexicons';
2
+
3
+
export interface ImageEmbedProps {
4
+
/** Expected to be static */
5
+
embed: AppBskyEmbedImages.View;
6
+
blur?: boolean;
7
+
/** Expected to be static */
8
+
borderless?: boolean;
9
+
/** Expected to be static */
10
+
interactive?: boolean;
11
+
}
12
+
13
+
const enum RenderMode {
14
+
MULTIPLE,
15
+
MULTIPLE_SQUARE,
16
+
STANDALONE,
17
+
STANDALONE_RATIO,
18
+
}
19
+
20
+
const ImageEmbed = (props: ImageEmbedProps) => {
21
+
const { embed, borderless, interactive } = props;
22
+
23
+
const images = embed.images;
24
+
const length = images.length;
25
+
26
+
const hasStandaloneImage = interactive ? length === 1 && images[0].aspectRatio !== undefined : false;
27
+
28
+
const render = (image: AppBskyEmbedImages.ViewImage, mode: RenderMode) => {
29
+
const { alt, thumb, aspectRatio } = image;
30
+
31
+
// FIXME: with STANDALONE_RATIO, we are resizing the image to make it fit
32
+
// the container with our given constraints, but this doesn't work when the
33
+
// image hasn't had its metadata loaded yet, the browser will snap to the
34
+
// smallest possible size for our layout.
35
+
36
+
// clients will typically just shove the actual resolution info to the
37
+
// `aspectRatio` field, but we can't rely on that as it could send
38
+
// simplified ratios instead.
39
+
40
+
// so what we'll do here is to just have an empty <div> sized to the device
41
+
// screen width and height. there's no issue with keeping the <div> around,
42
+
// so we'll do just that.
43
+
44
+
let cn: string | undefined;
45
+
let ratio: string | undefined;
46
+
47
+
if (mode === RenderMode.MULTIPLE) {
48
+
cn = `min-h-0 grow basis-0 overflow-hidden`;
49
+
} else if (mode === RenderMode.MULTIPLE_SQUARE) {
50
+
cn = `aspect-square overflow-hidden`;
51
+
} else if (mode === RenderMode.STANDALONE) {
52
+
cn = `aspect-video overflow-hidden`;
53
+
} else if (mode === RenderMode.STANDALONE_RATIO) {
54
+
cn = `max-h-80 min-h-16 min-w-16 max-w-full overflow-hidden`;
55
+
ratio = `${aspectRatio!.width}/${aspectRatio!.height}`;
56
+
}
57
+
58
+
return (
59
+
<div class={`relative ` + cn} style={{ 'aspect-ratio': ratio }}>
60
+
<img
61
+
src={thumb}
62
+
title={alt}
63
+
class={
64
+
`h-full w-full object-cover text-[0px]` +
65
+
(interactive ? ` cursor-pointer` : ``) +
66
+
// prettier-ignore
67
+
(props.blur ? ` scale-110` + (!borderless ? ` blur` : ` blur-lg`) : ``)
68
+
}
69
+
/>
70
+
71
+
{/* @once */ mode === RenderMode.STANDALONE_RATIO && <div class="h-screen w-screen"></div>}
72
+
73
+
{interactive && alt && (
74
+
<button
75
+
class="text-white absolute bottom-0 left-0 m-2 h-5 rounded bg-t-black/70 px-1 text-xs font-medium"
76
+
title="Show image description"
77
+
>
78
+
ALT
79
+
</button>
80
+
)}
81
+
</div>
82
+
);
83
+
};
84
+
85
+
return (
86
+
<div
87
+
class={
88
+
`bg-c-contrast-0 ` +
89
+
(!borderless ? ` overflow-hidden rounded-md border border-c-contrast-200` : ``) +
90
+
(hasStandaloneImage ? ` max-w-full self-start` : ``)
91
+
}
92
+
>
93
+
{length === 4 ? (
94
+
<div class="flex gap-0.5">
95
+
<div class="flex grow basis-0 flex-col gap-0.5">
96
+
{/* @once */ render(images[0], RenderMode.MULTIPLE_SQUARE)}
97
+
{/* @once */ render(images[2], RenderMode.MULTIPLE_SQUARE)}
98
+
</div>
99
+
100
+
<div class="flex grow basis-0 flex-col gap-0.5">
101
+
{/* @once */ render(images[1], RenderMode.MULTIPLE_SQUARE)}
102
+
{/* @once */ render(images[3], RenderMode.MULTIPLE_SQUARE)}
103
+
</div>
104
+
</div>
105
+
) : length === 3 ? (
106
+
<div class="flex gap-0.5">
107
+
<div class="flex aspect-square grow-2 basis-0 flex-col gap-0.5">
108
+
{/* @once */ render(images[0], RenderMode.MULTIPLE)}
109
+
</div>
110
+
111
+
<div class="flex grow basis-0 flex-col gap-0.5">
112
+
{/* @once */ render(images[1], RenderMode.MULTIPLE_SQUARE)}
113
+
{/* @once */ render(images[2], RenderMode.MULTIPLE_SQUARE)}
114
+
</div>
115
+
</div>
116
+
) : length === 2 ? (
117
+
<div class="flex aspect-video gap-0.5">
118
+
<div class="flex grow basis-0 flex-col gap-0.5">
119
+
{/* @once */ render(images[0], RenderMode.MULTIPLE)}
120
+
</div>
121
+
<div class="flex grow basis-0 flex-col gap-0.5">
122
+
{/* @once */ render(images[1], RenderMode.MULTIPLE)}
123
+
</div>
124
+
</div>
125
+
) : hasStandaloneImage ? (
126
+
<>{/* @once */ render(images[0], RenderMode.STANDALONE_RATIO)}</>
127
+
) : length === 1 ? (
128
+
<>{/* @once */ render(images[0], RenderMode.STANDALONE)}</>
129
+
) : null}
130
+
</div>
131
+
);
132
+
};
133
+
134
+
export default ImageEmbed;
+113
src/components/embeds/quote-embed.tsx
+113
src/components/embeds/quote-embed.tsx
···
1
+
import { createMemo, type JSX } from 'solid-js';
2
+
3
+
import type {
4
+
AppBskyEmbedImages,
5
+
AppBskyEmbedRecord,
6
+
AppBskyFeedDefs,
7
+
AppBskyFeedPost,
8
+
} from '@mary/bluesky-client/lexicons';
9
+
10
+
import { useProfileShadow } from '~/api/cache/profile-shadow';
11
+
import { moderateQuote } from '~/api/moderation/entities/quote';
12
+
import { parseAtUri } from '~/api/utils/strings';
13
+
14
+
import { useModerationOptions } from '~/lib/states/moderation';
15
+
16
+
import { ContextContentMedia, getModerationUI } from '~/api/moderation';
17
+
import Avatar from '../avatar';
18
+
import { handleLinkNavigation } from '../button';
19
+
import TimeAgo from '../time-ago';
20
+
import ImageEmbed from './image-embed';
21
+
22
+
export interface QuoteEmbedProps {
23
+
/** Expected to be static */
24
+
quote: AppBskyEmbedRecord.ViewRecord;
25
+
/** Expected to be static */
26
+
interactive?: boolean;
27
+
/** Expected to be static. Whether it should show a large UI for image embeds */
28
+
large?: boolean;
29
+
}
30
+
31
+
const QuoteEmbed = ({ quote, interactive, large }: QuoteEmbedProps) => {
32
+
const record = quote.value as AppBskyFeedPost.Record;
33
+
const author = quote.author;
34
+
const authorShadow = useProfileShadow(author);
35
+
36
+
const uri = parseAtUri(quote.uri);
37
+
const href = `/${author.did}/${uri.rkey}`;
38
+
39
+
const text = record.text.trim();
40
+
const image = getPostImage(quote.embeds?.[0]);
41
+
42
+
const moderationOptions = useModerationOptions();
43
+
const moderation = createMemo(() => moderateQuote(quote, authorShadow(), moderationOptions()));
44
+
45
+
const showLargeImages = image && (large || !text);
46
+
const shouldBlurImage = () => getModerationUI(moderation(), ContextContentMedia).b.length !== 0;
47
+
48
+
return (
49
+
<a
50
+
href={interactive ? href : undefined}
51
+
onClick={interactive ? handleLinkNavigation : undefined}
52
+
class={
53
+
`overflow-hidden rounded-md border border-c-contrast-200` +
54
+
(interactive ? ` hover:bg-c-contrast-25` : ``)
55
+
}
56
+
>
57
+
<div class="mx-3 mt-3 flex min-w-0 text-sm text-c-contrast-600">
58
+
<Avatar type="user" src={/* @once */ author.avatar} size="xs" class="mr-2" />
59
+
60
+
<span class="flex max-w-full gap-1 overflow-hidden text-ellipsis whitespace-nowrap text-left">
61
+
<bdi class="overflow-hidden text-ellipsis">
62
+
<span class="font-bold text-c-contrast-900">
63
+
{/* @once */ author.displayName || author.handle}
64
+
</span>
65
+
</bdi>
66
+
<span class="block overflow-hidden text-ellipsis whitespace-nowrap">
67
+
@{/* @once */ author.handle}
68
+
</span>
69
+
</span>
70
+
71
+
<span class="px-1">·</span>
72
+
73
+
<span class="whitespace-nowrap">
74
+
<TimeAgo value={/* @once */ quote.indexedAt}>
75
+
{(relative, _absolute) => relative as unknown as JSX.Element}
76
+
</TimeAgo>
77
+
</span>
78
+
</div>
79
+
80
+
{text ? (
81
+
<div class="flex items-start">
82
+
{image && !large && (
83
+
<div class="mb-3 ml-3 mt-2 grow basis-0">
84
+
<ImageEmbed embed={image} blur={shouldBlurImage()} />
85
+
</div>
86
+
)}
87
+
88
+
<div class="mx-3 mb-3 mt-2 line-clamp-6 min-w-0 grow-4 basis-0 whitespace-pre-wrap break-words text-sm empty:hidden">
89
+
{text}
90
+
</div>
91
+
</div>
92
+
) : (
93
+
<div class="mt-3"></div>
94
+
)}
95
+
96
+
{showLargeImages && <ImageEmbed embed={image} borderless blur={shouldBlurImage()} />}
97
+
</a>
98
+
);
99
+
};
100
+
101
+
export default QuoteEmbed;
102
+
103
+
const getPostImage = (embed: AppBskyFeedDefs.PostView['embed']): AppBskyEmbedImages.View | undefined => {
104
+
if (embed) {
105
+
if (embed.$type === 'app.bsky.embed.images#view') {
106
+
return embed;
107
+
}
108
+
109
+
if (embed.$type === 'app.bsky.embed.recordWithMedia#view') {
110
+
return getPostImage(embed.media);
111
+
}
112
+
}
113
+
};
+25
src/components/error-view.tsx
+25
src/components/error-view.tsx
···
1
+
import { formatQueryError } from '~/api/utils/error';
2
+
3
+
import Button from './button';
4
+
5
+
export interface ErrorViewProps {
6
+
error: unknown;
7
+
onRetry?: () => void;
8
+
}
9
+
10
+
const ErrorView = (props: ErrorViewProps) => {
11
+
return (
12
+
<div class="p-4">
13
+
<div class="mb-4 text-sm">
14
+
<p class="font-bold">Something went wrong</p>
15
+
<p class="text-muted-fg">{formatQueryError(props.error)}</p>
16
+
</div>
17
+
18
+
<Button onClick={props.onRetry} variant="primary">
19
+
Try again
20
+
</Button>
21
+
</div>
22
+
);
23
+
};
24
+
25
+
export default ErrorView;
+28
src/components/fab.tsx
+28
src/components/fab.tsx
···
1
+
import type { Component } from 'solid-js';
2
+
3
+
export interface FABProps {
4
+
label: string;
5
+
icon: Component;
6
+
onClick?: () => void;
7
+
}
8
+
9
+
const FAB = (props: FABProps) => {
10
+
return (
11
+
<button title={props.label} class={fabClassNames()}>
12
+
{(() => {
13
+
const Icon = props.icon;
14
+
return <Icon />;
15
+
})()}
16
+
</button>
17
+
);
18
+
};
19
+
20
+
export default FAB;
21
+
22
+
const fabClassNames = (): string => {
23
+
let cn = `flex h-12 w-12 items-center justify-center`;
24
+
25
+
cn += ` bg-c-primary-500 text-c-white hover:bg-c-primary-600`;
26
+
27
+
return cn;
28
+
};
+121
src/components/feeds/post-actions.tsx
+121
src/components/feeds/post-actions.tsx
···
1
+
import type { AppBskyFeedDefs } from '@mary/bluesky-client/lexicons';
2
+
import { useQueryClient } from '@mary/solid-query';
3
+
4
+
import { updatePostShadow, type PostShadowView } from '~/api/cache/post-shadow';
5
+
6
+
import { openModal } from '~/globals/modals';
7
+
8
+
import * as Menu from '../menu';
9
+
import HeartOutlinedIcon from '../icons-central/heart-outline';
10
+
import HeartSolidIcon from '../icons-central/heart-solid';
11
+
import RepeatOutlinedIcon from '../icons-central/repeat-outline';
12
+
import ReplyOutlinedIcon from '../icons-central/reply-outline';
13
+
import ShareOutlinedIcon from '../icons-central/share-outline';
14
+
import WriteOutlinedIcon from '../icons-central/write-outline';
15
+
16
+
export interface PostActionsProps {
17
+
/** Expected to be static */
18
+
post: AppBskyFeedDefs.PostView;
19
+
shadow: PostShadowView;
20
+
}
21
+
22
+
const PostActions = (props: PostActionsProps) => {
23
+
const queryClient = useQueryClient();
24
+
25
+
const post = props.post;
26
+
27
+
const replyCount = post.replyCount ?? 0;
28
+
const isLiked = () => !!props.shadow.likeUri;
29
+
const isReposted = () => !!props.shadow.repostUri;
30
+
31
+
const toggleLike = () => {
32
+
updatePostShadow(queryClient, post.uri, { likeUri: isLiked() ? undefined : `pending` });
33
+
};
34
+
35
+
const toggleRepost = () => {
36
+
updatePostShadow(queryClient, post.uri, { repostUri: isReposted() ? undefined : `pending` });
37
+
};
38
+
39
+
return (
40
+
<div class="mt-3 flex items-center text-c-contrast-600">
41
+
<div class="min-w-0 grow basis-0">
42
+
<button class={`group flex max-w-full grow basis-0 items-end gap-0.5`}>
43
+
<div class="-my-1.5 -ml-2 flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-base group-hover:bg-c-contrast-50">
44
+
<ReplyOutlinedIcon />
45
+
</div>
46
+
47
+
<span class="overflow-hidden text-ellipsis whitespace-nowrap pr-2 text-de">{replyCount}</span>
48
+
</button>
49
+
</div>
50
+
51
+
<div class="min-w-0 grow basis-0">
52
+
<button
53
+
onClick={(ev) => {
54
+
const anchor = ev.currentTarget;
55
+
56
+
openModal(({ close }) => (
57
+
<Menu.Container anchor={anchor} placement="bottom">
58
+
<Menu.Item
59
+
icon={RepeatOutlinedIcon}
60
+
label={!isReposted() ? `Repost` : `Undo repost`}
61
+
onClick={() => {
62
+
close();
63
+
toggleRepost();
64
+
}}
65
+
/>
66
+
67
+
<Menu.Item
68
+
icon={WriteOutlinedIcon}
69
+
label="Quote"
70
+
onClick={() => {
71
+
close();
72
+
}}
73
+
/>
74
+
</Menu.Container>
75
+
));
76
+
}}
77
+
class={
78
+
`group flex max-w-full grow basis-0 items-end gap-0.5` +
79
+
(isReposted() ? ` text-c-positive-600` : ``)
80
+
}
81
+
>
82
+
<div class="-my-1.5 -ml-2 flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-base group-hover:bg-c-contrast-50">
83
+
<RepeatOutlinedIcon />
84
+
</div>
85
+
86
+
<span class="overflow-hidden text-ellipsis whitespace-nowrap pr-2 text-de">
87
+
{props.shadow.repostCount}
88
+
</span>
89
+
</button>
90
+
</div>
91
+
92
+
<div class="min-w-0 grow basis-0">
93
+
<button
94
+
onClick={toggleLike}
95
+
class={
96
+
`group flex max-w-full grow basis-0 items-end gap-0.5` + (isLiked() ? ` text-c-negative-400` : ``)
97
+
}
98
+
>
99
+
<div class="-my-1.5 -ml-2 flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-base group-hover:bg-c-contrast-50">
100
+
{(() => {
101
+
const Icon = !isLiked() ? HeartOutlinedIcon : HeartSolidIcon;
102
+
return <Icon />;
103
+
})()}
104
+
</div>
105
+
106
+
<span class="overflow-hidden text-ellipsis whitespace-nowrap pr-2 text-de">
107
+
{props.shadow.likeCount}
108
+
</span>
109
+
</button>
110
+
</div>
111
+
112
+
<div class="shrink-0">
113
+
<button class="-mx-2 -my-1.5 flex h-8 w-8 items-center justify-center rounded-full text-base hover:bg-c-contrast-50">
114
+
<ShareOutlinedIcon />
115
+
</button>
116
+
</div>
117
+
</div>
118
+
);
119
+
};
120
+
121
+
export default PostActions;
+111
src/components/feeds/post-feed-item.tsx
+111
src/components/feeds/post-feed-item.tsx
···
1
+
import { createMemo } from 'solid-js';
2
+
3
+
import type { AppBskyFeedPost, At } from '@mary/bluesky-client/lexicons';
4
+
5
+
import { usePostShadow } from '~/api/cache/post-shadow';
6
+
import { useProfileShadow } from '~/api/cache/profile-shadow';
7
+
import type { UiTimelineItem } from '~/api/models/timeline';
8
+
import { moderatePost } from '~/api/moderation/entities/post';
9
+
import { parseAtUri } from '~/api/utils/strings';
10
+
11
+
import { useModerationOptions } from '~/lib/states/moderation';
12
+
13
+
import Avatar from '../avatar';
14
+
import { handleLinkNavigation } from '../button';
15
+
import RepeatOutlinedIcon from '../icons-central/repeat-outline';
16
+
import RichText from '../rich-text';
17
+
18
+
import PostActions from './post-actions';
19
+
import PostMeta from './post-meta';
20
+
import PostReplyContext from './post-reply-context';
21
+
import Embed from '../embeds/embed';
22
+
23
+
export interface PostFeedItemProps {
24
+
/** Expected to be static */
25
+
item: UiTimelineItem;
26
+
timelineDid?: At.DID;
27
+
}
28
+
29
+
const PostFeedItem = ({ item, timelineDid }: PostFeedItemProps) => {
30
+
const moderationOptions = useModerationOptions();
31
+
32
+
const { post, reason, next, prev } = item;
33
+
34
+
const author = post.author;
35
+
const record = post.record as AppBskyFeedPost.Record;
36
+
const embed = post.embed;
37
+
38
+
const shadow = usePostShadow(post);
39
+
const authorShadow = useProfileShadow(author);
40
+
41
+
const uri = parseAtUri(post.uri);
42
+
const authorHref = `/${author.did}`;
43
+
const href = `/${author.did}/${uri.rkey}`;
44
+
45
+
const moderation = createMemo(() => moderatePost(post, authorShadow(), moderationOptions()));
46
+
47
+
return (
48
+
<div hidden={shadow().deleted} class={`relative border-c-contrast-200 px-4` + (!next ? ` border-b` : ``)}>
49
+
<div class="relative flex flex-col pb-1 pt-2">
50
+
{prev && (
51
+
<div class="flex w-9 flex-col items-center">
52
+
<div class="absolute bottom-1 top-0 grow border-l-2 border-c-contrast-200" />
53
+
</div>
54
+
)}
55
+
56
+
{/* @once */ renderReason(reason)}
57
+
</div>
58
+
59
+
<div class="flex gap-3">
60
+
<div class="flex shrink-0 flex-col items-center">
61
+
<Avatar
62
+
type={/* @once */ author.associated?.labeler ? 'labeler' : 'user'}
63
+
src={/* @once */ author.avatar}
64
+
href={authorHref}
65
+
moderation={moderation()}
66
+
/>
67
+
68
+
{next && <div class="mt-1 grow border-l-2 border-c-contrast-200" />}
69
+
</div>
70
+
71
+
<div class="min-w-0 grow pb-3">
72
+
<PostMeta post={post} href={href} authorHref={authorHref} gutterBottom />
73
+
<PostReplyContext item={item} />
74
+
75
+
<RichText text={/* @once */ record.text} facets={/* @once */ record.facets} clipped />
76
+
{embed && <Embed embed={embed} moderation={moderation()} gutterTop />}
77
+
78
+
<PostActions post={post} shadow={shadow()} />
79
+
</div>
80
+
</div>
81
+
</div>
82
+
);
83
+
};
84
+
85
+
export default PostFeedItem;
86
+
87
+
const renderReason = (reason: UiTimelineItem['reason']) => {
88
+
if (reason) {
89
+
const type = reason.$type;
90
+
91
+
if (type === 'app.bsky.feed.defs#reasonRepost') {
92
+
const by = reason.by;
93
+
const did = by.did;
94
+
const name = by.displayName || by.handle;
95
+
96
+
return (
97
+
<div class="flex items-center gap-3 text-de text-c-contrast-600">
98
+
<div class="flex w-9 shrink-0 justify-end">
99
+
<RepeatOutlinedIcon class="text-sm" />
100
+
</div>
101
+
<a href={`/${did}`} onClick={handleLinkNavigation} class="flex min-w-0 font-medium hover:underline">
102
+
<span dir="auto" class="overflow-hidden text-ellipsis whitespace-nowrap">
103
+
{name}
104
+
</span>
105
+
<span class="shrink-0 whitespace-pre"> Reposted</span>
106
+
</a>
107
+
</div>
108
+
);
109
+
}
110
+
}
111
+
};
+66
src/components/feeds/post-meta.tsx
+66
src/components/feeds/post-meta.tsx
···
1
+
import type { AppBskyFeedDefs } from '@mary/bluesky-client/lexicons';
2
+
3
+
import { handleLinkNavigation } from '../button';
4
+
import MoreHorizOutlinedIcon from '../icons-central/more-horiz-outline';
5
+
import TimeAgo from '../time-ago';
6
+
7
+
export interface PostMetaProps {
8
+
/** Expected to be static */
9
+
post: AppBskyFeedDefs.PostView;
10
+
authorHref: string;
11
+
href: string;
12
+
gutterBottom?: boolean;
13
+
}
14
+
15
+
const PostMeta = ({ post, authorHref, href, gutterBottom }: PostMetaProps) => {
16
+
const author = post.author;
17
+
18
+
const displayName = author.displayName;
19
+
const handle = author.handle;
20
+
const indexedAt = post.indexedAt;
21
+
22
+
return (
23
+
<div
24
+
class={`flex items-center justify-between gap-4 text-c-contrast-600` + (gutterBottom ? ` mb-0.5` : ``)}
25
+
>
26
+
<div class="flex items-center overflow-hidden text-sm">
27
+
<a
28
+
href={authorHref}
29
+
onClick={handleLinkNavigation}
30
+
class="flex max-w-full gap-1 overflow-hidden text-ellipsis whitespace-nowrap text-left"
31
+
>
32
+
{displayName && (
33
+
<bdi class="overflow-hidden text-ellipsis font-bold text-c-contrast-900 hover:underline">
34
+
{displayName}
35
+
</bdi>
36
+
)}
37
+
38
+
<span class="block overflow-hidden text-ellipsis whitespace-nowrap">@{handle}</span>
39
+
</a>
40
+
41
+
<span class="px-1">·</span>
42
+
43
+
<TimeAgo value={indexedAt}>
44
+
{(relative, absolute) => (
45
+
<a
46
+
title={absolute()}
47
+
href={href}
48
+
onClick={handleLinkNavigation}
49
+
class="whitespace-nowrap hover:underline"
50
+
>
51
+
{relative()}
52
+
</a>
53
+
)}
54
+
</TimeAgo>
55
+
</div>
56
+
57
+
<div class="shrink-0">
58
+
<button class="-mx-2 -my-1.5 flex h-8 w-8 items-center justify-center rounded-full text-base hover:bg-c-contrast-50">
59
+
<MoreHorizOutlinedIcon />
60
+
</button>
61
+
</div>
62
+
</div>
63
+
);
64
+
};
65
+
66
+
export default PostMeta;
+40
src/components/feeds/post-reply-context.tsx
+40
src/components/feeds/post-reply-context.tsx
···
1
+
import type { AppBskyFeedPost } from '@mary/bluesky-client/lexicons';
2
+
import type { UiTimelineItem } from '~/api/models/timeline';
3
+
4
+
export interface PostReplyContextProps {
5
+
/** Expected to be static */
6
+
item: UiTimelineItem;
7
+
}
8
+
9
+
const PostReplyContext = (props: PostReplyContextProps) => {
10
+
const { post, reply, prev } = props.item;
11
+
12
+
if (!prev) {
13
+
const parent = reply?.parent;
14
+
if (parent) {
15
+
const author = parent.author;
16
+
const did = author.did;
17
+
const handle = author.handle;
18
+
19
+
return (
20
+
<div class="mb-0.5 flex text-sm text-c-contrast-500">
21
+
<span class="shrink-0 whitespace-pre">Replying to </span>
22
+
<a
23
+
dir="auto"
24
+
href={`/${did}`}
25
+
class="overflow-hidden text-ellipsis whitespace-nowrap text-c-primary-400 hover:underline"
26
+
>
27
+
@{handle}
28
+
</a>
29
+
</div>
30
+
);
31
+
}
32
+
33
+
const record = post.record as AppBskyFeedPost.Record;
34
+
if (record.reply) {
35
+
return <div class="mb-0.5 text-sm text-c-contrast-500">Replying to unknown</div>;
36
+
}
37
+
}
38
+
};
39
+
40
+
export default PostReplyContext;
+42
src/components/fieldset.tsx
+42
src/components/fieldset.tsx
···
1
+
import { createContext, createMemo, useContext, type ParentProps } from 'solid-js';
2
+
3
+
export interface FieldsetContext {
4
+
readonly disabled: boolean;
5
+
}
6
+
7
+
const DEFAULT_FIELDSET: FieldsetContext = {
8
+
disabled: false,
9
+
};
10
+
11
+
const Context = createContext<FieldsetContext>(DEFAULT_FIELDSET);
12
+
13
+
export const useFieldset = (): FieldsetContext => {
14
+
return useContext(Context);
15
+
};
16
+
17
+
export interface FieldsetProps extends ParentProps {
18
+
standalone?: boolean;
19
+
disabled?: boolean;
20
+
}
21
+
22
+
export const Fieldset = (props: FieldsetProps) => {
23
+
let context: FieldsetContext;
24
+
25
+
if (!('disabled' in props) && props.standalone) {
26
+
context = DEFAULT_FIELDSET;
27
+
} else {
28
+
const parent = useFieldset();
29
+
30
+
const isDisabled = createMemo((): boolean => {
31
+
return (!props.standalone && parent.disabled) || !!props.disabled;
32
+
});
33
+
34
+
context = {
35
+
get disabled() {
36
+
return isDisabled();
37
+
},
38
+
};
39
+
}
40
+
41
+
return <Context.Provider value={context}>{props.children}</Context.Provider>;
42
+
};
+15
src/components/icons-central/_icon.tsx
+15
src/components/icons-central/_icon.tsx
···
1
+
import { type ComponentProps, type JSX } from 'solid-js';
2
+
import { spread } from 'solid-js/web';
3
+
4
+
/*#__NO_SIDE_EFFECTS__*/
5
+
export const createIcon = (path: () => JSX.Element) => {
6
+
// @ts-expect-error
7
+
return Icon.bind(path);
8
+
};
9
+
10
+
function Icon(this: () => Element, props: ComponentProps<'svg'>) {
11
+
const svg = this();
12
+
spread(svg, props, true, true);
13
+
14
+
return svg;
15
+
}
+10
src/components/icons-central/add-outline.tsx
+10
src/components/icons-central/add-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
// plus-large
4
+
const AddOutlinedIcon = createIcon(() => (
5
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
6
+
<path stroke="currentColor" stroke-linecap="square" stroke-width="2" d="M12 4v8m0 0v8m0-8H4m8 0h8" />
7
+
</svg>
8
+
));
9
+
10
+
export default AddOutlinedIcon;
+9
src/components/icons-central/arrow-left-outline.tsx
+9
src/components/icons-central/arrow-left-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
const ArrowLeftOutlinedIcon = createIcon(() => (
4
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
5
+
<path stroke="currentColor" stroke-linecap="square" stroke-width="2" d="m10 6-6 6 6 6m-5-6h15" />
6
+
</svg>
7
+
));
8
+
9
+
export default ArrowLeftOutlinedIcon;
+13
src/components/icons-central/bell-outline.tsx
+13
src/components/icons-central/bell-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
const BellOutlinedIcon = createIcon(() => (
4
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
5
+
<path
6
+
stroke="currentColor"
7
+
stroke-width="2"
8
+
d="M8.22 18.5C8.976 19.994 10.386 21 12 21c1.615 0 3.025-1.006 3.78-2.5M20 18l-1.207-9.053a6.853 6.853 0 0 0-13.586 0L4 18h16Z"
9
+
/>
10
+
</svg>
11
+
));
12
+
13
+
export default BellOutlinedIcon;
+14
src/components/icons-central/bell-solid.tsx
+14
src/components/icons-central/bell-solid.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
const BellSolidIcon = createIcon(() => (
4
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
5
+
<path
6
+
fill="currentColor"
7
+
fill-rule="evenodd"
8
+
d="M12 2a7.853 7.853 0 0 0-7.784 6.815L2.858 19h4.496c.904 1.748 2.607 3 4.646 3 2.039 0 3.742-1.252 4.646-3h4.496L19.784 8.815A7.853 7.853 0 0 0 12 2Zm2.222 17H9.778c.61.637 1.399 1 2.222 1s1.613-.363 2.222-1Z"
9
+
clip-rule="evenodd"
10
+
/>
11
+
</svg>
12
+
));
13
+
14
+
export default BellSolidIcon;
+14
src/components/icons-central/bullet-list-outline.tsx
+14
src/components/icons-central/bullet-list-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
const BulletListOutlinedIcon = createIcon(() => (
4
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
5
+
<path
6
+
stroke="currentColor"
7
+
stroke-linecap="square"
8
+
stroke-width="2"
9
+
d="M13 17h7M13 7h7M8 7a2 2 0 1 1-4 0 2 2 0 0 1 4 0Zm0 10a2 2 0 1 1-4 0 2 2 0 0 1 4 0Z"
10
+
/>
11
+
</svg>
12
+
));
13
+
14
+
export default BulletListOutlinedIcon;
+15
src/components/icons-central/chevron-right-outline.tsx
+15
src/components/icons-central/chevron-right-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
// chevron-right-small
4
+
const ChevronRightOutlinedIcon = createIcon(() => (
5
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
6
+
<path
7
+
fill="currentColor"
8
+
fill-rule="evenodd"
9
+
d="M10 6.586 15.414 12 10 17.414 8.586 16l4-4-4-4L10 6.586Z"
10
+
clip-rule="evenodd"
11
+
/>
12
+
</svg>
13
+
));
14
+
15
+
export default ChevronRightOutlinedIcon;
+14
src/components/icons-central/circle-check-solid.tsx
+14
src/components/icons-central/circle-check-solid.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
const CircleCheckSolidIcon = createIcon(() => (
4
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
5
+
<path
6
+
fill="currentColor"
7
+
fill-rule="evenodd"
8
+
d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10Zm-1.426-5.512 5.833-7.129-1.548-1.266-4.433 5.418L8.5 11.586 7.086 13l3.488 3.488Z"
9
+
clip-rule="evenodd"
10
+
/>
11
+
</svg>
12
+
));
13
+
14
+
export default CircleCheckSolidIcon;
+9
src/components/icons-central/cross-large-outline.tsx
+9
src/components/icons-central/cross-large-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
const CrossLargeOutlinedIcon = createIcon(() => (
4
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
5
+
<path stroke="currentColor" stroke-linecap="square" stroke-width="2" d="m5 5 14 14m0-14L5 19" />
6
+
</svg>
7
+
));
8
+
9
+
export default CrossLargeOutlinedIcon;
+10
src/components/icons-central/filter-outline.tsx
+10
src/components/icons-central/filter-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
// filter-1
4
+
const FilterOutlinedIcon = createIcon(() => (
5
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
6
+
<path stroke="currentColor" stroke-width="2" d="M20 4H4v4l6 6v7l4-1v-6l6-6V4Z" />
7
+
</svg>
8
+
));
9
+
10
+
export default FilterOutlinedIcon;
+15
src/components/icons-central/gear-outline.tsx
+15
src/components/icons-central/gear-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
// settings-gear-2
4
+
const GearOutlinedIcon = createIcon(() => (
5
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
6
+
<path
7
+
stroke="currentColor"
8
+
stroke-width="2"
9
+
d="m9.3 5.7-2.925-.675-1.35 1.35L5.7 9.3 3 11.1v1.8l2.7 1.8-.675 2.925 1.35 1.35L9.3 18.3l1.8 2.7h1.8l1.8-2.7 2.925.675 1.35-1.35L18.3 14.7l2.7-1.8v-1.8l-2.7-1.8.675-2.925-1.35-1.35L14.7 5.7 12.9 3h-1.8L9.3 5.7Z"
10
+
/>
11
+
<path stroke="currentColor" stroke-width="2" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
12
+
</svg>
13
+
));
14
+
15
+
export default GearOutlinedIcon;
+14
src/components/icons-central/hashtag-outline.tsx
+14
src/components/icons-central/hashtag-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
const HashtagOutlinedIcon = createIcon(() => (
4
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
5
+
<path
6
+
stroke="currentColor"
7
+
stroke-linecap="square"
8
+
stroke-width="2"
9
+
d="M9 4 7 20M17 4l-2 16M4 8h16m0 8H4"
10
+
/>
11
+
</svg>
12
+
));
13
+
14
+
export default HashtagOutlinedIcon;
+14
src/components/icons-central/heart-outline.tsx
+14
src/components/icons-central/heart-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
// heart-2
4
+
const HeartOutlinedIcon = createIcon(() => (
5
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
6
+
<path
7
+
stroke="currentColor"
8
+
stroke-width="2"
9
+
d="M12 5.768c6.162-6.25 16.725 5.358 0 14.732C-4.725 11.126 5.838-.482 12 5.768Z"
10
+
/>
11
+
</svg>
12
+
));
13
+
14
+
export default HeartOutlinedIcon;
+13
src/components/icons-central/heart-solid.tsx
+13
src/components/icons-central/heart-solid.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
// heart-2
4
+
const HeartSolidIcon = createIcon(() => (
5
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
6
+
<path
7
+
fill="currentColor"
8
+
d="M12.489 21.372c8.528-4.78 10.626-10.47 9.022-14.47-.779-1.941-2.414-3.333-4.342-3.763-1.697-.378-3.552.003-5.169 1.287-1.617-1.284-3.472-1.665-5.17-1.287-1.927.43-3.562 1.822-4.34 3.764-1.605 4 .493 9.69 9.021 14.47l.489.274.489-.274Z"
9
+
/>
10
+
</svg>
11
+
));
12
+
13
+
export default HeartSolidIcon;
+10
src/components/icons-central/home-outline.tsx
+10
src/components/icons-central/home-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
// home-open
4
+
const HomeOutlinedIcon = createIcon(() => (
5
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
6
+
<path stroke="currentColor" stroke-width="2" d="M20 20V9l-8-6.5L4 9v11h6v-6h4v6h6Z" />
7
+
</svg>
8
+
));
9
+
10
+
export default HomeOutlinedIcon;
+10
src/components/icons-central/home-solid.tsx
+10
src/components/icons-central/home-solid.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
// home-open
4
+
const HomeSolidIcon = createIcon(() => (
5
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
6
+
<path fill="currentColor" d="m21 8.524-9-7.312-9 7.312V21h7v-7h4v7h7V8.524Z" />
7
+
</svg>
8
+
));
9
+
10
+
export default HomeSolidIcon;
+21
src/components/icons-central/info-outline.tsx
+21
src/components/icons-central/info-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
// info-circle
4
+
const InfoOutlinedIcon = createIcon(() => (
5
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
6
+
<path
7
+
stroke="currentColor"
8
+
stroke-linecap="square"
9
+
stroke-width="2"
10
+
d="M11 11h1v5m9-4a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
11
+
/>
12
+
<path
13
+
fill="currentColor"
14
+
stroke="currentColor"
15
+
stroke-width=".5"
16
+
d="M11.5 7.25h-.25v1.5h1.5v-1.5H11.5Z"
17
+
/>
18
+
</svg>
19
+
));
20
+
21
+
export default InfoOutlinedIcon;
+15
src/components/icons-central/leave-outline.tsx
+15
src/components/icons-central/leave-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
// arrow-box-left
4
+
const LeaveOutlinedIcon = createIcon(() => (
5
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
6
+
<path
7
+
stroke="currentColor"
8
+
stroke-linecap="square"
9
+
stroke-width="2"
10
+
d="M11 20H4V4h7m-2 8h10m-3.5 4.5L20 12l-4.5-4.5"
11
+
/>
12
+
</svg>
13
+
));
14
+
15
+
export default LeaveOutlinedIcon;
+14
src/components/icons-central/magnifying-glass-outline.tsx
+14
src/components/icons-central/magnifying-glass-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
const MagnifyingGlassOutlinedIcon = createIcon(() => (
4
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
5
+
<path
6
+
stroke="currentColor"
7
+
stroke-linecap="square"
8
+
stroke-width="2"
9
+
d="m20 20-3.95-3.95M18 11a7 7 0 1 1-14 0 7 7 0 0 1 14 0Z"
10
+
/>
11
+
</svg>
12
+
));
13
+
14
+
export default MagnifyingGlassOutlinedIcon;
+13
src/components/icons-central/mail-outline.tsx
+13
src/components/icons-central/mail-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
// mail-3
4
+
const MailOutlinedIcon = createIcon(() => (
5
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
6
+
<path
7
+
fill="currentColor"
8
+
d="M21 5h1V4h-1v1Zm0 14v1h1v-1h-1ZM3 19H2v1h1v-1ZM3 5V4H2v1h1Zm9 8-.447.894.447.224.447-.224L12 13Zm8-8v14h2V5h-2Zm1 13H3v2h18v-2ZM4 19V5H2v14h2ZM3 6h18V4H3v2Zm9.447 6.106L3.735 7.75l-.894 1.789 8.712 4.355.894-1.788Zm7.818-4.356-8.712 4.356.894 1.788L21.16 9.54l-.894-1.79Z"
9
+
/>
10
+
</svg>
11
+
));
12
+
13
+
export default MailOutlinedIcon;
+11
src/components/icons-central/mail-solid.tsx
+11
src/components/icons-central/mail-solid.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
// mail-3
4
+
const MailSolidIcon = createIcon(() => (
5
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
6
+
<path fill="currentColor" d="M2 6.882V4h20v2.882l-10 5-10-5Z" />
7
+
<path fill="currentColor" d="M2 9.118V20h20V9.118l-10 5-10-5Z" />
8
+
</svg>
9
+
));
10
+
11
+
export default MailSolidIcon;
+15
src/components/icons-central/more-horiz-outline.tsx
+15
src/components/icons-central/more-horiz-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
// dot-grid-1x3-horizontal
4
+
const MoreHorizOutlinedIcon = createIcon(() => (
5
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
6
+
<path
7
+
fill="currentColor"
8
+
fill-rule="evenodd"
9
+
d="M2 12a2 2 0 1 1 4 0 2 2 0 0 1-4 0Zm8 0a2 2 0 1 1 4 0 2 2 0 0 1-4 0Zm8 0a2 2 0 1 1 4 0 2 2 0 0 1-4 0Z"
10
+
clip-rule="evenodd"
11
+
/>
12
+
</svg>
13
+
));
14
+
15
+
export default MoreHorizOutlinedIcon;
+13
src/components/icons-central/person-outline.tsx
+13
src/components/icons-central/person-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
// people
4
+
const PersonOutlinedIcon = createIcon(() => (
5
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
6
+
<path
7
+
fill="currentColor"
8
+
d="m4.5 20-.996-.094L3.402 21H4.5v-1Zm15 0v1h1.098l-.102-1.094L19.5 20Zm-5-13.5A2.5 2.5 0 0 1 12 9v2a4.5 4.5 0 0 0 4.5-4.5h-2ZM12 9a2.5 2.5 0 0 1-2.5-2.5h-2A4.5 4.5 0 0 0 12 11V9ZM9.5 6.5A2.5 2.5 0 0 1 12 4V2a4.5 4.5 0 0 0-4.5 4.5h2ZM12 4a2.5 2.5 0 0 1 2.5 2.5h2A4.5 4.5 0 0 0 12 2v2ZM5.496 20.094C5.82 16.63 8.377 14 12 14v-2c-4.758 0-8.083 3.521-8.496 7.906l1.992.188ZM12 14c3.623 0 6.179 2.63 6.504 6.094l1.992-.188C20.083 15.521 16.758 12 12 12v2Zm-7.5 7h15v-2h-15v2Z"
9
+
/>
10
+
</svg>
11
+
));
12
+
13
+
export default PersonOutlinedIcon;
+13
src/components/icons-central/person-remove-outline.tsx
+13
src/components/icons-central/person-remove-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
// people-remove
4
+
const PersonRemoveOutlinedIcon = createIcon(() => (
5
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
6
+
<path
7
+
fill="currentColor"
8
+
d="m4.5 20-.996-.094L3.402 21H4.5v-1Zm16.207-3.293.707-.707L20 14.586l-.707.707 1.414 1.414Zm-5.414 2.586-.707.707L16 21.414l.707-.707-1.414-1.414Zm1.414-4L16 14.586 14.586 16l.707.707 1.414-1.414Zm2.586 5.414.707.707L21.414 20l-.707-.707-1.414 1.414ZM14.5 6.5A2.5 2.5 0 0 1 12 9v2a4.5 4.5 0 0 0 4.5-4.5h-2ZM12 9a2.5 2.5 0 0 1-2.5-2.5h-2A4.5 4.5 0 0 0 12 11V9ZM9.5 6.5A2.5 2.5 0 0 1 12 4V2a4.5 4.5 0 0 0-4.5 4.5h2ZM12 4a2.5 2.5 0 0 1 2.5 2.5h2A4.5 4.5 0 0 0 12 2v2ZM5.496 20.094C5.82 16.63 8.377 14 12 14v-2c-4.758 0-8.083 3.521-8.496 7.906l1.992.188ZM4.5 21H13v-2H4.5v2Zm7.5-7c.621 0 1.206.077 1.748.218l.504-1.936A8.931 8.931 0 0 0 12 12v2Zm7.293 1.293-2 2 1.414 1.414 2-2-1.414-1.414Zm-2 2-2 2 1.414 1.414 2-2-1.414-1.414Zm-2-.586 2 2 1.414-1.414-2-2-1.414 1.414Zm2 2 2 2 1.414-1.414-2-2-1.414 1.414Z"
9
+
/>
10
+
</svg>
11
+
));
12
+
13
+
export default PersonRemoveOutlinedIcon;
+15
src/components/icons-central/problem-outline.tsx
+15
src/components/icons-central/problem-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
// triangle-exclamation
4
+
const ProblemOutlinedIcon = createIcon(() => (
5
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
6
+
<path
7
+
stroke="currentColor"
8
+
stroke-linecap="square"
9
+
stroke-width="2"
10
+
d="M12 16v-.01M12 10v3m0-10L2.5 19h19L12 3Z"
11
+
/>
12
+
</svg>
13
+
));
14
+
15
+
export default ProblemOutlinedIcon;
+15
src/components/icons-central/repeat-outline.tsx
+15
src/components/icons-central/repeat-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
// arrows-repeat-right-left
4
+
const RepeatOutlinedIcon = createIcon(() => (
5
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
6
+
<path
7
+
stroke="currentColor"
8
+
stroke-linecap="square"
9
+
stroke-width="2"
10
+
d="m17 3 3 3-3 3M7 21l-3-3 3-3m-2 3h15v-5M4 11V6h15"
11
+
/>
12
+
</svg>
13
+
));
14
+
15
+
export default RepeatOutlinedIcon;
+15
src/components/icons-central/reply-outline.tsx
+15
src/components/icons-central/reply-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
// bubble-2
4
+
const ReplyOutlinedIcon = createIcon(() => (
5
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
6
+
<path
7
+
stroke="currentColor"
8
+
stroke-linecap="square"
9
+
stroke-width="2"
10
+
d="M3.002 4h18v14h-9l-5 3v-3h-4V4Z"
11
+
/>
12
+
</svg>
13
+
));
14
+
15
+
export default ReplyOutlinedIcon;
+14
src/components/icons-central/shield-outline.tsx
+14
src/components/icons-central/shield-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
const ShieldOutlinedIcon = createIcon(() => (
4
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
5
+
<path
6
+
stroke="currentColor"
7
+
stroke-linecap="square"
8
+
stroke-width="2"
9
+
d="M20 5.75 12 3 4 5.75v6.162c0 4.973 4 7.088 8 9.246 4-2.158 8-4.273 8-9.246V5.75Z"
10
+
/>
11
+
</svg>
12
+
));
13
+
14
+
export default ShieldOutlinedIcon;
+13
src/components/icons-central/write-outline.tsx
+13
src/components/icons-central/write-outline.tsx
···
1
+
import { createIcon } from './_icon';
2
+
3
+
// edit-big
4
+
const WriteOutlinedIcon = createIcon(() => (
5
+
<svg width="1em" height="1em" fill="none" viewBox="0 0 24 24">
6
+
<path
7
+
fill="currentColor"
8
+
d="M20 20v1h1v-1h-1ZM4 20H3v1h1v-1ZM4 4V3H3v1h1Zm7 1h1V3h-1v2Zm10 8v-1h-2v1h2ZM9 12l-.707-.707-.293.293V12h1Zm0 3H8v1h1v-1Zm3 0v1h.414l.293-.293L12 15Zm6-12 .707-.707L18 1.586l-.707.707L18 3Zm3 3 .707.707.707-.707-.707-.707L21 6Zm-1 13H4v2h16v-2ZM5 20V4H3v16h2ZM4 5h7V3H4v2Zm15 8v7h2v-7h-2ZM8 12v3h2v-3H8Zm1 4h3v-2H9v2Zm.707-3.293 9-9-1.414-1.414-9 9 1.414 1.414Zm7.586-9 3 3 1.414-1.414-3-3-1.414 1.414Zm3 1.586-9 9 1.414 1.414 9-9-1.414-1.414Z"
9
+
/>
10
+
</svg>
11
+
));
12
+
13
+
export default WriteOutlinedIcon;
+38
src/components/inline-link.tsx
+38
src/components/inline-link.tsx
···
1
+
import type { ParentProps } from 'solid-js';
2
+
import { useFieldset } from './fieldset';
3
+
4
+
export interface InlineLinkProps extends ParentProps {
5
+
href?: string;
6
+
disabled?: boolean;
7
+
onClick?: () => void;
8
+
}
9
+
10
+
const InlineLink = (props: InlineLinkProps) => {
11
+
const fieldset = useFieldset();
12
+
const isDisabled = (): boolean => fieldset.disabled || !!props.disabled;
13
+
14
+
return (
15
+
<button
16
+
type="button"
17
+
disabled={isDisabled()}
18
+
onClick={props.onClick}
19
+
class={inlineLinkClassNames(isDisabled)}
20
+
>
21
+
{props.children}
22
+
</button>
23
+
);
24
+
};
25
+
26
+
const inlineLinkClassNames = (isDisabled: () => boolean): string => {
27
+
var cn = `text-left text-de hover:underline`;
28
+
29
+
if (!isDisabled()) {
30
+
cn += ` text-c-primary-400`;
31
+
} else {
32
+
cn += ` text-c-primary-700`;
33
+
}
34
+
35
+
return cn;
36
+
};
37
+
38
+
export default InlineLink;
+114
src/components/list.tsx
+114
src/components/list.tsx
···
1
+
import { For, Match, Switch, untrack, type JSX } from 'solid-js';
2
+
3
+
import { getQueryErrorInfo } from '~/api/utils/query';
4
+
5
+
import { ifIntersect } from '~/lib/element-refs';
6
+
7
+
import CircularProgress from './circular-progress';
8
+
import ErrorView from './error-view';
9
+
10
+
export interface ListProps<T> {
11
+
data?: T[];
12
+
error?: unknown;
13
+
render: (item: T, index: () => number) => JSX.Element;
14
+
fallback?: JSX.Element;
15
+
manualScroll?: boolean;
16
+
hasNewData?: boolean;
17
+
hasNextPage?: boolean;
18
+
isRefreshing?: boolean;
19
+
isFetchingNextPage?: boolean;
20
+
onEndReached?: () => void;
21
+
onRefresh?: () => void;
22
+
}
23
+
24
+
const List = <T,>(props: ListProps<T>) => {
25
+
const render = props.render;
26
+
27
+
const onEndReached = props.onEndReached;
28
+
const onRefresh = props.onRefresh;
29
+
30
+
return (
31
+
<div class="flex flex-col">
32
+
<Switch>
33
+
<Match when={props.isRefreshing}>
34
+
<div class="grid h-13 shrink-0 place-items-center border-b border-c-contrast-200">
35
+
<CircularProgress />
36
+
</div>
37
+
</Match>
38
+
39
+
<Match when={props.hasNewData}>
40
+
<button
41
+
onClick={onRefresh}
42
+
class="grid h-13 shrink-0 place-items-center border-b border-c-contrast-200 text-sm text-c-primary-400 hover:bg-c-contrast-25"
43
+
>
44
+
Show new items
45
+
</button>
46
+
</Match>
47
+
</Switch>
48
+
49
+
<For
50
+
each={props.data}
51
+
fallback={
52
+
(() => {
53
+
if (props.manualScroll || !props.hasNextPage) {
54
+
return untrack(() => props.fallback);
55
+
}
56
+
}) as unknown as JSX.Element
57
+
}
58
+
>
59
+
{render}
60
+
</For>
61
+
62
+
<Switch>
63
+
<Match when={props.isRefreshing}>{null}</Match>
64
+
65
+
<Match when={props.error}>
66
+
{(err) => (
67
+
<ErrorView
68
+
error={err()}
69
+
onRetry={() => {
70
+
const info = getQueryErrorInfo(err());
71
+
72
+
if (info && info.pageParam === undefined) {
73
+
onRefresh?.();
74
+
} else {
75
+
onEndReached?.();
76
+
}
77
+
}}
78
+
/>
79
+
)}
80
+
</Match>
81
+
82
+
<Match when={props.manualScroll && !props.isFetchingNextPage && props.hasNextPage}>
83
+
<button
84
+
onClick={onEndReached}
85
+
class="grid h-13 shrink-0 place-items-center text-sm text-c-primary-400 hover:bg-c-contrast-25"
86
+
>
87
+
Show more
88
+
</button>
89
+
</Match>
90
+
91
+
<Match when={props.isFetchingNextPage || props.hasNextPage}>
92
+
<div
93
+
ref={(node) => {
94
+
if (onEndReached) {
95
+
ifIntersect(node, () => !props.isFetchingNextPage && props.hasNextPage, onEndReached);
96
+
}
97
+
}}
98
+
class="grid h-13 shrink-0 place-items-center"
99
+
>
100
+
<CircularProgress />
101
+
</div>
102
+
</Match>
103
+
104
+
<Match when={props.data}>
105
+
<div class="grid h-13 shrink-0 place-items-center">
106
+
<p class="text-sm text-c-contrast-400">End of list</p>
107
+
</div>
108
+
</Match>
109
+
</Switch>
110
+
</div>
111
+
);
112
+
};
113
+
114
+
export default List;
+5
src/components/main/manage-account-dialog-lazy.tsx
+5
src/components/main/manage-account-dialog-lazy.tsx
+109
src/components/main/manage-account-dialog.tsx
+109
src/components/main/manage-account-dialog.tsx
···
1
+
import { For } from 'solid-js';
2
+
3
+
import { useProfileQuery } from '~/api/queries/profile';
4
+
5
+
import { closeAllModals, openModal } from '~/globals/modals';
6
+
7
+
import type { AccountData } from '~/lib/preferences/sessions';
8
+
import { useSession } from '~/lib/states/session';
9
+
10
+
import Avatar from '../avatar';
11
+
import * as Dialog from '../dialog';
12
+
import Divider from '../divider';
13
+
import CircleCheckSolidIcon from '../icons-central/circle-check-solid';
14
+
15
+
import SignInDialogLazy from './sign-in-dialog-lazy';
16
+
17
+
const ManageAccountDialog = () => {
18
+
const { currentAccount, getAccounts, resumeSession } = useSession();
19
+
20
+
const switchAccount = async (account: AccountData) => {
21
+
resumeSession(account);
22
+
closeAllModals();
23
+
};
24
+
25
+
return (
26
+
<>
27
+
<Dialog.Backdrop />
28
+
<Dialog.Container maxWidth="sm" fullHeight>
29
+
<Dialog.Header>
30
+
<Dialog.HeaderAccessory>
31
+
<Dialog.Close />
32
+
</Dialog.HeaderAccessory>
33
+
34
+
<Dialog.Heading title="Manage accounts" />
35
+
</Dialog.Header>
36
+
<Dialog.Body unpadded>
37
+
<div class="flex flex-col">
38
+
<CurrentAccountItem />
39
+
<For each={getAccounts().filter((account) => account.did !== currentAccount!.did)}>
40
+
{(account) => <AccountItem account={account} onClick={() => switchAccount(account)} />}
41
+
</For>
42
+
</div>
43
+
44
+
<Divider gutterTop="md" />
45
+
46
+
<div class="flex flex-col">
47
+
<button
48
+
onClick={() => {
49
+
openModal(() => <SignInDialogLazy />);
50
+
}}
51
+
class="px-4 py-3 text-left text-sm text-c-primary-400 hover:bg-c-primary-975"
52
+
>
53
+
Add new account
54
+
</button>
55
+
56
+
<button class="px-4 py-3 text-left text-sm text-c-negative-400 hover:bg-c-negative-975">
57
+
Sign out of all accounts
58
+
</button>
59
+
</div>
60
+
</Dialog.Body>
61
+
</Dialog.Container>
62
+
</>
63
+
);
64
+
};
65
+
66
+
export default ManageAccountDialog;
67
+
68
+
const CurrentAccountItem = () => {
69
+
const { currentAccount } = useSession();
70
+
const profile = useProfileQuery(() => currentAccount!.did);
71
+
72
+
return (
73
+
<div class="flex gap-4 px-4 py-3">
74
+
<Avatar type="user" src={profile.data?.avatar} class="mt-0.5" />
75
+
76
+
<div class="min-w-0 grow self-center text-sm">
77
+
<p class="overflow-hidden text-ellipsis whitespace-nowrap font-bold empty:hidden">
78
+
{profile.data?.displayName}
79
+
</p>
80
+
<p class="overflow-hidden text-ellipsis whitespace-nowrap text-de text-c-contrast-600">
81
+
{'@' + (profile.data?.handle ?? currentAccount!.data.session.handle)}
82
+
</p>
83
+
</div>
84
+
85
+
<div class="mt-2.5 shrink-0 text-lg text-c-positive-600">
86
+
<CircleCheckSolidIcon />
87
+
</div>
88
+
</div>
89
+
);
90
+
};
91
+
92
+
const AccountItem = ({ account, onClick }: { account: AccountData; onClick?: () => void }) => {
93
+
const profile = useProfileQuery(() => account.did);
94
+
95
+
return (
96
+
<button onClick={onClick} class="flex gap-4 px-4 py-3 text-left hover:bg-c-contrast-25">
97
+
<Avatar type="user" src={profile.data?.avatar} class="mt-0.5" />
98
+
99
+
<div class="min-w-0 grow self-center text-sm">
100
+
<p class="overflow-hidden text-ellipsis whitespace-nowrap font-bold empty:hidden">
101
+
{profile.data?.displayName}
102
+
</p>
103
+
<p class="overflow-hidden text-ellipsis whitespace-nowrap text-de text-c-contrast-600">
104
+
{'@' + (profile.data?.handle ?? account.session.handle)}
105
+
</p>
106
+
</div>
107
+
</button>
108
+
);
109
+
};
+82
src/components/main/modal-renderer.tsx
+82
src/components/main/modal-renderer.tsx
···
1
+
import { For, Suspense, onCleanup } from 'solid-js';
2
+
3
+
import { INTERNAL_ModalContext, INTERNAL_modals, closeModal, type ModalContext } from '~/globals/modals';
4
+
5
+
import * as Dialog from '../dialog';
6
+
import CircularProgress from '../circular-progress';
7
+
8
+
let isScrollbarSizeDetermined = false;
9
+
10
+
const ModalRenderer = () => {
11
+
return (
12
+
<For each={INTERNAL_modals()}>
13
+
{({ id, render }) => {
14
+
const context: ModalContext = {
15
+
id: id,
16
+
isActive(): boolean {
17
+
const array = INTERNAL_modals();
18
+
const last = array[array.length - 1];
19
+
return last !== undefined && last.id === id;
20
+
},
21
+
close(): void {
22
+
return closeModal(id);
23
+
},
24
+
};
25
+
26
+
// Restore focus
27
+
{
28
+
const focused = document.activeElement;
29
+
if (focused !== null && focused !== document.body) {
30
+
onCleanup(() => {
31
+
queueMicrotask(() => {
32
+
if (document.contains(focused)) {
33
+
(focused as any).focus();
34
+
}
35
+
});
36
+
});
37
+
}
38
+
}
39
+
40
+
// Determine scrollbar size
41
+
if (!isScrollbarSizeDetermined) {
42
+
determineScrollbarSize();
43
+
isScrollbarSizeDetermined = true;
44
+
}
45
+
46
+
return (
47
+
<INTERNAL_ModalContext.Provider value={context}>
48
+
<div
49
+
inert={!context.isActive()}
50
+
class="fixed inset-0 flex flex-col items-center justify-start overflow-hidden"
51
+
data-modal
52
+
>
53
+
<Suspense fallback={<FallbackLoader />}>{render(context)}</Suspense>
54
+
</div>
55
+
</INTERNAL_ModalContext.Provider>
56
+
);
57
+
}}
58
+
</For>
59
+
);
60
+
};
61
+
62
+
export default ModalRenderer;
63
+
64
+
const FallbackLoader = () => {
65
+
return (
66
+
<>
67
+
<Dialog.Backdrop />
68
+
<div class="grid grow place-items-center">
69
+
<CircularProgress />
70
+
</div>
71
+
</>
72
+
);
73
+
};
74
+
75
+
const determineScrollbarSize = () => {
76
+
const docEl = document.documentElement;
77
+
78
+
const documentWidth = docEl.clientWidth;
79
+
const scrollbarSize = Math.abs(window.innerWidth - documentWidth);
80
+
81
+
docEl.style.setProperty('--sb-width', `${scrollbarSize}px`);
82
+
};
+5
src/components/main/sign-in-dialog-lazy.tsx
+5
src/components/main/sign-in-dialog-lazy.tsx
+444
src/components/main/sign-in-dialog.tsx
+444
src/components/main/sign-in-dialog.tsx
···
1
+
import { Match, Switch, batch, createSignal } from 'solid-js';
2
+
3
+
import { XRPCError } from '@mary/bluesky-client/xrpc';
4
+
import { createMutation } from '@mary/solid-query';
5
+
6
+
import { DEFAULT_DATA_SERVER } from '~/api/defaults';
7
+
import type { DataServer } from '~/api/types';
8
+
import { DidResolutionError, findDidDocument, getDataServer } from '~/api/utils/did-doc';
9
+
import { isDid } from '~/api/utils/strings';
10
+
11
+
import { closeAllModals } from '~/globals/modals';
12
+
13
+
import { autofocusIfEnabled, autofocusOnMutation, modelText } from '~/lib/input-refs';
14
+
import { useSession } from '~/lib/states/session';
15
+
16
+
import Button from '../button';
17
+
import * as Dialog from '../dialog';
18
+
import { Fieldset } from '../fieldset';
19
+
import InlineLink from '../inline-link';
20
+
import TextInput from '../text-input';
21
+
22
+
type View =
23
+
| { type: 'handle_initial' }
24
+
| { type: 'handle_password' }
25
+
| { type: 'email_initial' }
26
+
| { type: 'otp'; from: 'handle' | 'email' };
27
+
28
+
type TargetedError = { target: 'identifier' | 'password' | 'otp'; msg: string };
29
+
30
+
const SignInDialog = () => {
31
+
const session = useSession();
32
+
33
+
const [view, setView] = createSignal<View>({ type: 'handle_initial' });
34
+
35
+
const [service, setService] = createSignal(DEFAULT_DATA_SERVER);
36
+
const [identifier, setIdentifier] = createSignal('');
37
+
const [password, setPassword] = createSignal('');
38
+
const [otp, setOtp] = createSignal('');
39
+
40
+
const [error, setError] = createSignal<TargetedError>();
41
+
42
+
const pdsMutation = createMutation(() => {
43
+
return {
44
+
async mutationFn({ identifier }: { identifier: string }) {
45
+
const didDoc = await findDidDocument(identifier);
46
+
const service = getDataServer(didDoc);
47
+
if (!service) {
48
+
throw new Error(`PDS_NOT_FOUND`);
49
+
}
50
+
51
+
return { didDoc, service };
52
+
},
53
+
onSuccess({ service }) {
54
+
setTimeout(() => {
55
+
batch(() => {
56
+
setView({ type: 'handle_password' });
57
+
setService(service);
58
+
});
59
+
}, 0);
60
+
},
61
+
onError(error, { identifier }) {
62
+
let msg = `Unknown error, try again later`;
63
+
64
+
if (error instanceof DidResolutionError) {
65
+
const type = error.message;
66
+
67
+
if (type === 'DID_UNSUPPORTED') {
68
+
if (isDid(identifier)) {
69
+
msg = `Unsupported DID method`;
70
+
} else {
71
+
msg = `Account uses an unsupported DID method`;
72
+
}
73
+
} else if (type === 'PLC_NOT_FOUND') {
74
+
msg = `DID not found in PLC directory`;
75
+
} else if (type === 'PLC_UNREACHABLE') {
76
+
msg = `Can't reach PLC directory right now, try again later`;
77
+
} else if (type === 'WEB_INVALID') {
78
+
msg = `Specified did:web is invalid`;
79
+
} else if (type === 'WEB_NOT_FOUND') {
80
+
msg = `Can't find your account, did you type it correctly?`;
81
+
} else if (type === 'WEB_UNREACHABLE') {
82
+
msg = `Can't reach your DID document right now, try again later`;
83
+
}
84
+
} else if (error instanceof XRPCError) {
85
+
const err = error.kind;
86
+
87
+
if (error.message === 'Unable to resolve handle') {
88
+
msg = `Can't find your account, did you type it correctly?`;
89
+
} else if (err === 'InvalidRequest') {
90
+
msg = `That doesn't seem right, did you type it correctly?`;
91
+
}
92
+
} else if (error instanceof Error) {
93
+
if (error.message === 'PDS_NOT_FOUND') {
94
+
msg = `Account is not attached to a hosting provider`;
95
+
}
96
+
}
97
+
98
+
setError({ target: 'identifier', msg });
99
+
},
100
+
};
101
+
});
102
+
103
+
const loginMutation = createMutation(() => ({
104
+
async mutationFn({
105
+
service,
106
+
identifier,
107
+
password,
108
+
authFactorToken,
109
+
}: {
110
+
from: 'handle' | 'email';
111
+
service: DataServer;
112
+
identifier: string;
113
+
password: string;
114
+
authFactorToken: string | undefined;
115
+
}) {
116
+
await session.login({
117
+
service: service.uri,
118
+
identifier: identifier,
119
+
password: password,
120
+
authFactorToken: authFactorToken,
121
+
});
122
+
},
123
+
onSuccess() {
124
+
closeAllModals();
125
+
},
126
+
onError(error: unknown, { from }) {
127
+
let msg = `Unknown error, try again later`;
128
+
129
+
if (error instanceof XRPCError) {
130
+
const err = error.kind;
131
+
132
+
if (err === 'AuthFactorTokenRequired') {
133
+
setView({ type: 'otp', from: from });
134
+
return;
135
+
} else if (err === 'AuthenticationRequired') {
136
+
msg = `Invalid password`;
137
+
} else if (err === 'AccountTakedown') {
138
+
msg = `Your account has been taken down`;
139
+
}
140
+
} else if (error instanceof DOMException) {
141
+
if (error.name === 'AbortError') {
142
+
msg = `Login attempt aborted, try again`;
143
+
}
144
+
}
145
+
146
+
setError({ target: 'password', msg });
147
+
},
148
+
}));
149
+
150
+
return (
151
+
<>
152
+
<Dialog.Backdrop />
153
+
<Dialog.Container maxWidth="sm" centered disabled={loginMutation.isPending}>
154
+
<form
155
+
class="contents"
156
+
onsubmit={(ev) => {
157
+
const $view = view();
158
+
159
+
ev.preventDefault();
160
+
161
+
batch(() => {
162
+
setError(undefined);
163
+
164
+
if ($view.type === 'handle_initial') {
165
+
pdsMutation.mutate({ identifier: identifier() });
166
+
} else if ($view.type === 'handle_password') {
167
+
loginMutation.mutate({
168
+
from: 'handle',
169
+
service: service(),
170
+
identifier: identifier(),
171
+
password: password(),
172
+
authFactorToken: undefined,
173
+
});
174
+
} else if ($view.type === 'email_initial') {
175
+
loginMutation.mutate({
176
+
from: 'email',
177
+
service: service(),
178
+
identifier: identifier(),
179
+
password: password(),
180
+
authFactorToken: undefined,
181
+
});
182
+
} else if ($view.type === 'otp') {
183
+
loginMutation.mutate({
184
+
from: $view.from,
185
+
service: service(),
186
+
identifier: identifier(),
187
+
password: password(),
188
+
authFactorToken: formatEmailOtpCode(otp()),
189
+
});
190
+
}
191
+
});
192
+
}}
193
+
>
194
+
<Dialog.Header>
195
+
<Dialog.HeaderAccessory>
196
+
<Dialog.Close />
197
+
</Dialog.HeaderAccessory>
198
+
</Dialog.Header>
199
+
200
+
<Fieldset disabled={pdsMutation.isPending}>
201
+
<Dialog.Body class="flex flex-col gap-6">
202
+
<Switch>
203
+
<Match when={view().type === 'handle_initial'}>
204
+
<div class="flex flex-col gap-1">
205
+
<h2 class="text-2xl font-bold">Sign in</h2>
206
+
<h3 class="text-base text-c-contrast-800">To begin, enter your Bluesky handle or DID</h3>
207
+
</div>
208
+
209
+
<div class="flex flex-col gap-4">
210
+
<TextInput
211
+
ref={(node) => {
212
+
autofocusOnMutation(node, pdsMutation);
213
+
modelText(node, identifier, setIdentifier);
214
+
}}
215
+
autocomplete="username"
216
+
pattern="([a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*(?:\.[a-zA-Z]+))|did:[a-z]+:[a-zA-Z0-9._\-]+"
217
+
required
218
+
label="Bluesky handle or DID"
219
+
placeholder="paul.bsky.social"
220
+
error={(() => {
221
+
const $error = error();
222
+
if ($error && $error.target === 'identifier') {
223
+
return $error.msg;
224
+
}
225
+
})()}
226
+
/>
227
+
228
+
<input
229
+
ref={(node) => {
230
+
modelText(node, password, setPassword);
231
+
}}
232
+
type="password"
233
+
autocomplete="current-password"
234
+
hidden
235
+
/>
236
+
237
+
<div class="flex flex-col gap-2">
238
+
<InlineLink
239
+
onClick={() => {
240
+
batch(() => {
241
+
setView({ type: 'email_initial' });
242
+
243
+
setError();
244
+
245
+
setService(DEFAULT_DATA_SERVER);
246
+
setIdentifier('');
247
+
setPassword('');
248
+
});
249
+
}}
250
+
>
251
+
Sign in with email address instead
252
+
</InlineLink>
253
+
</div>
254
+
</div>
255
+
</Match>
256
+
257
+
<Match when={view().type === 'handle_password'}>
258
+
<div class="flex flex-col gap-1">
259
+
<h2 class="text-2xl font-bold">Enter your password</h2>
260
+
</div>
261
+
262
+
<div class="flex flex-col gap-4">
263
+
<TextInput
264
+
disabled
265
+
autocomplete="username"
266
+
label="Bluesky handle or DID"
267
+
value={identifier()}
268
+
/>
269
+
270
+
<TextInput
271
+
ref={(node) => {
272
+
autofocusOnMutation(node, loginMutation);
273
+
modelText(node, password, setPassword);
274
+
}}
275
+
type="password"
276
+
autocomplete="current-password"
277
+
required
278
+
label="Password"
279
+
error={(() => {
280
+
const $error = error();
281
+
if ($error && $error.target === 'password') {
282
+
return $error.msg;
283
+
}
284
+
})()}
285
+
/>
286
+
287
+
<div class="flex flex-col gap-2">
288
+
<InlineLink
289
+
onClick={() => {
290
+
batch(() => {
291
+
setView({ type: 'handle_initial' });
292
+
293
+
setError();
294
+
setPassword('');
295
+
});
296
+
}}
297
+
>
298
+
Sign in with another account
299
+
</InlineLink>
300
+
</div>
301
+
</div>
302
+
</Match>
303
+
304
+
<Match when={view().type === 'email_initial'}>
305
+
<div class="flex flex-col gap-1">
306
+
<h2 class="text-2xl font-bold">Sign in</h2>
307
+
</div>
308
+
309
+
<div class="flex flex-col gap-4">
310
+
<TextInput
311
+
ref={(node) => {
312
+
autofocusIfEnabled(node, () => true);
313
+
modelText(node, identifier, setIdentifier);
314
+
}}
315
+
type="email"
316
+
required
317
+
label="Email address"
318
+
placeholder="emma@contoso.com"
319
+
error={(() => {
320
+
const $error = error();
321
+
if ($error && $error.target === 'identifier') {
322
+
return $error.msg;
323
+
}
324
+
})()}
325
+
/>
326
+
327
+
<TextInput
328
+
ref={(node) => {
329
+
autofocusOnMutation(node, loginMutation, false);
330
+
modelText(node, password, setPassword);
331
+
}}
332
+
type="password"
333
+
autocomplete="current-password"
334
+
required
335
+
label="Password"
336
+
error={(() => {
337
+
const $error = error();
338
+
if ($error && $error.target === 'password') {
339
+
return $error.msg;
340
+
}
341
+
})()}
342
+
/>
343
+
344
+
<div class="flex flex-col gap-2">
345
+
<InlineLink
346
+
onClick={() => {
347
+
batch(() => {
348
+
setView({ type: 'handle_initial' });
349
+
350
+
setError();
351
+
setIdentifier('');
352
+
setPassword('');
353
+
});
354
+
}}
355
+
>
356
+
Sign in with Bluesky handle instead
357
+
</InlineLink>
358
+
</div>
359
+
</div>
360
+
</Match>
361
+
362
+
<Match
363
+
when={(() => {
364
+
const $view = view();
365
+
if ($view.type === 'otp') {
366
+
return $view;
367
+
}
368
+
})()}
369
+
keyed
370
+
>
371
+
{({ from }) => (
372
+
<>
373
+
<div class="flex flex-col gap-1">
374
+
<h2 class="text-2xl font-bold">Enter verification code</h2>
375
+
<h3 class="max-w-84 text-base text-c-contrast-800">
376
+
Check your inbox for an email containing the code and enter it here
377
+
</h3>
378
+
</div>
379
+
380
+
<div class="flex flex-col gap-4">
381
+
<TextInput
382
+
ref={(node) => {
383
+
autofocusOnMutation(node, loginMutation);
384
+
modelText(node, otp, setOtp);
385
+
}}
386
+
autocomplete="one-time-code"
387
+
required
388
+
label="Verification code"
389
+
placeholder="AAAAA-BBBBB"
390
+
error={(() => {
391
+
const $error = error();
392
+
if ($error && $error.target === 'otp') {
393
+
return $error.msg;
394
+
}
395
+
})()}
396
+
/>
397
+
398
+
<InlineLink
399
+
onClick={() => {
400
+
batch(() => {
401
+
setView({ type: `${from}_initial` });
402
+
403
+
setError();
404
+
setOtp('');
405
+
});
406
+
}}
407
+
>
408
+
Sign in with another account
409
+
</InlineLink>
410
+
</div>
411
+
</>
412
+
)}
413
+
</Match>
414
+
</Switch>
415
+
</Dialog.Body>
416
+
417
+
<Dialog.Actions>
418
+
<Button type="submit" variant="primary" size="md">
419
+
{(() => {
420
+
const $view = view();
421
+
if ($view.type === 'handle_initial' || $view.type === 'otp') {
422
+
return `Continue`;
423
+
}
424
+
425
+
return `Sign in`;
426
+
})()}
427
+
</Button>
428
+
</Dialog.Actions>
429
+
</Fieldset>
430
+
</form>
431
+
</Dialog.Container>
432
+
</>
433
+
);
434
+
};
435
+
436
+
export default SignInDialog;
437
+
438
+
const formatEmailOtpCode = (code: string): string | undefined => {
439
+
if (code.length === 0) {
440
+
return undefined;
441
+
}
442
+
443
+
return (code.includes('-') ? code : code.slice(0, 5) + '-' + code.slice(5)).toUpperCase();
444
+
};
+113
src/components/moderation/content-hider.tsx
+113
src/components/moderation/content-hider.tsx
···
1
+
import { createSignal, type Component, type JSX, type ParentProps } from 'solid-js';
2
+
3
+
import {
4
+
CauseLabel,
5
+
CauseMutedKeyword,
6
+
CauseMutedPermanent,
7
+
CauseMutedTemporary,
8
+
SeverityAlert,
9
+
getLocalizedLabel,
10
+
type ModerationCause,
11
+
type ModerationCauseType,
12
+
type ModerationLabeler,
13
+
type ModerationUI,
14
+
} from '~/api/moderation';
15
+
16
+
import FilterOutlinedIcon from '../icons-central/filter-outline';
17
+
import InfoOutlinedIcon from '../icons-central/info-outline';
18
+
import PersonRemoveOutlinedIcon from '../icons-central/person-remove-outline';
19
+
import ProblemOutlinedIcon from '../icons-central/problem-outline';
20
+
21
+
export interface ContentHiderProps extends ParentProps {
22
+
ui: ModerationUI | undefined;
23
+
ignoreMute?: boolean;
24
+
class?: string;
25
+
childContainerClass?: string;
26
+
}
27
+
28
+
const ContentHider = (props: ContentHiderProps) => {
29
+
return (() => {
30
+
const ui = props.ui;
31
+
const blur = ui?.b[0];
32
+
33
+
if (!blur || (props.ignoreMute && isOnlyMuted(ui.b))) {
34
+
return <div class={`flex flex-col ` + props.class}>{props.children}</div>;
35
+
}
36
+
37
+
const [override, setOverride] = createSignal(false);
38
+
39
+
const type = blur.t;
40
+
41
+
let Icon: Component;
42
+
let title: string;
43
+
let forced: boolean | undefined;
44
+
45
+
if (type === CauseLabel) {
46
+
const def = blur.d;
47
+
const severity = def.s;
48
+
49
+
Icon = severity === SeverityAlert ? ProblemOutlinedIcon : InfoOutlinedIcon;
50
+
title = getLocalizedLabel(def).n;
51
+
forced = !ui.o;
52
+
} else if (type === CauseMutedKeyword) {
53
+
Icon = FilterOutlinedIcon;
54
+
title = blur.n;
55
+
} else if (type === CauseMutedTemporary) {
56
+
Icon = PersonRemoveOutlinedIcon;
57
+
title = `Silenced user`;
58
+
} else {
59
+
Icon = PersonRemoveOutlinedIcon;
60
+
title = `Muted user`;
61
+
}
62
+
63
+
return (
64
+
<div class={`flex flex-col ` + props.class}>
65
+
<button
66
+
disabled={forced}
67
+
onClick={() => setOverride((next) => !next)}
68
+
class="flex h-11 w-full items-center gap-3 self-stretch rounded-md bg-c-contrast-25 px-3 text-c-contrast-900 hover:bg-c-contrast-25"
69
+
>
70
+
<div class="shrink-0 text-lg text-c-contrast-600">
71
+
<Icon />
72
+
</div>
73
+
<span class="grow overflow-hidden text-ellipsis whitespace-nowrap text-left text-sm font-medium">
74
+
{title}
75
+
</span>
76
+
77
+
<span hidden={forced} class="text-de font-medium text-c-contrast-600">
78
+
{!override() ? `Show` : `Hide`}
79
+
</span>
80
+
</button>
81
+
82
+
{(() => {
83
+
if (type === CauseLabel && !override()) {
84
+
return null;
85
+
}
86
+
})()}
87
+
88
+
{(() => {
89
+
if (override()) {
90
+
return <div class={props.childContainerClass}>{props.children}</div>;
91
+
}
92
+
})()}
93
+
</div>
94
+
);
95
+
}) as unknown as JSX.Element;
96
+
};
97
+
98
+
export default ContentHider;
99
+
100
+
const renderLabelSource = (source: ModerationLabeler) => {
101
+
const profile = source.profile;
102
+
103
+
if (profile) {
104
+
return profile.displayName || `@${profile.handle}`;
105
+
}
106
+
107
+
return source.did;
108
+
};
109
+
110
+
const isOnlyMuted = (causes: ModerationCause[]) => {
111
+
let t: ModerationCauseType;
112
+
return causes.every((c) => (t = c.t) === CauseMutedTemporary || t === CauseMutedPermanent);
113
+
};
+78
src/components/page.tsx
+78
src/components/page.tsx
···
1
+
import type { ParentProps } from 'solid-js';
2
+
3
+
import { useProfileQuery } from '~/api/queries/profile';
4
+
5
+
import { openModal } from '~/globals/modals';
6
+
7
+
import { useAgent } from '~/lib/states/agent';
8
+
import { useSession } from '~/lib/states/session';
9
+
10
+
import Avatar from './avatar';
11
+
import IconButton from './icon-button';
12
+
import MenuOutlinedIcon from './icons-central/menu-outline';
13
+
import MainSidebarLazy from './main/main-sidebar';
14
+
15
+
export interface PageHeaderProps extends ParentProps {}
16
+
17
+
const PageHeader = (props: PageHeaderProps) => {
18
+
return (
19
+
<>
20
+
<div class="sticky top-0 z-1 flex h-13 w-full max-w-md shrink-0 items-center justify-between gap-4 bg-c-contrast-0 px-2.5">
21
+
{props.children}
22
+
</div>
23
+
</>
24
+
);
25
+
};
26
+
27
+
export { PageHeader as Header };
28
+
29
+
export interface PageHeadingProps {
30
+
title?: string;
31
+
subtitle?: string;
32
+
}
33
+
34
+
const PageHeading = (props: PageHeadingProps) => {
35
+
return (
36
+
<div class="flex min-w-0 grow flex-col gap-0.5">
37
+
<p class="overflow-hidden text-ellipsis whitespace-nowrap text-base font-bold leading-5">
38
+
{props.title}
39
+
</p>
40
+
</div>
41
+
);
42
+
};
43
+
44
+
export { PageHeading as Heading };
45
+
46
+
export interface PageHeaderAccessoryProps extends ParentProps {}
47
+
48
+
const PageHeaderAccessory = (props: PageHeaderAccessoryProps) => {
49
+
return <div class="flex shrink-0 gap-2 empty:hidden">{props.children}</div>;
50
+
};
51
+
52
+
export { PageHeaderAccessory as HeaderAccessory };
53
+
54
+
export interface PageMainMenuProps {}
55
+
56
+
const PageMainMenu = ({}: PageMainMenuProps) => {
57
+
const { currentAccount } = useSession();
58
+
const { persister } = useAgent();
59
+
60
+
return (
61
+
<IconButton
62
+
title="Open main menu"
63
+
icon={() => {
64
+
if (currentAccount) {
65
+
const profile = useProfileQuery(() => currentAccount.did, persister);
66
+
return <Avatar type="user" src={profile.data?.avatar} size="sm" />;
67
+
}
68
+
69
+
return <MenuOutlinedIcon />;
70
+
}}
71
+
onClick={() => {
72
+
openModal(() => <MainSidebarLazy />);
73
+
}}
74
+
/>
75
+
);
76
+
};
77
+
78
+
export { PageMainMenu as MainMenu };
+146
src/components/rich-text.tsx
+146
src/components/rich-text.tsx
···
1
+
import type { JSX } from 'solid-js';
2
+
3
+
import type { AppBskyRichtextFacet } from '@mary/bluesky-client/lexicons';
4
+
5
+
import { segmentRichText } from '~/api/richtext/segment';
6
+
7
+
import { handleLinkNavigation } from './button';
8
+
import { isLinkValid, safeUrlParse } from '~/api/utils/strings';
9
+
10
+
export interface RichTextProps {
11
+
text: string;
12
+
facets?: AppBskyRichtextFacet.Main[];
13
+
clipped?: boolean;
14
+
}
15
+
16
+
const EMOJI_RE = /^(\p{Emoji}\ufe0f|\p{Emoji_Presentation}){1,8}$/u;
17
+
18
+
const RichText = (props: RichTextProps) => {
19
+
return (() => {
20
+
const text = props.text;
21
+
const facets = props.facets;
22
+
23
+
let nodes: JSX.Element;
24
+
let large = false;
25
+
26
+
if (facets !== undefined && facets.length !== 0) {
27
+
const segments = segmentRichText(text, facets);
28
+
29
+
nodes = [];
30
+
31
+
for (let idx = 0, len = segments.length; idx < len; idx++) {
32
+
const segment = segments[idx];
33
+
const subtext = segment.text;
34
+
const feature = segment.feature;
35
+
36
+
let to: string | undefined;
37
+
let external = false;
38
+
39
+
if (feature) {
40
+
const type = feature.$type;
41
+
42
+
if (type === 'app.bsky.richtext.facet#link') {
43
+
const uri = feature.uri;
44
+
const redirect = findLinkRedirect(uri);
45
+
46
+
if (redirect === null) {
47
+
to = uri;
48
+
external = true;
49
+
} else {
50
+
to = redirect;
51
+
}
52
+
} else if (type === 'app.bsky.richtext.facet#mention') {
53
+
to = `/${feature.did}`;
54
+
} else if (type === 'app.bsky.richtext.facet#tag') {
55
+
to = `/topics/${feature.tag}`;
56
+
}
57
+
}
58
+
59
+
if (to !== undefined) {
60
+
if (!external) {
61
+
nodes.push(
62
+
<a href={to} onClick={handleLinkNavigation} class="text-c-primary-400 hover:underline">
63
+
{subtext}
64
+
</a>,
65
+
);
66
+
} else {
67
+
nodes.push(
68
+
<a
69
+
target="_blank"
70
+
href={to}
71
+
onClick={handleUnsafeLinkNavigation}
72
+
onAuxClick={handleUnsafeLinkNavigation}
73
+
class="text-c-primary-400 hover:underline"
74
+
>
75
+
{subtext}
76
+
</a>,
77
+
);
78
+
}
79
+
} else {
80
+
nodes.push(subtext);
81
+
}
82
+
}
83
+
} else {
84
+
nodes = text;
85
+
large = EMOJI_RE.test(text);
86
+
}
87
+
88
+
return (
89
+
<p
90
+
class={
91
+
`whitespace-pre-wrap break-words` +
92
+
(!large ? ` text-sm` : ` text-lg`) +
93
+
(props.clipped ? ` line-clamp-[12]` : ``)
94
+
}
95
+
>
96
+
{nodes}
97
+
</p>
98
+
);
99
+
}) as unknown as JSX.Element;
100
+
};
101
+
102
+
export default RichText;
103
+
104
+
const handleUnsafeLinkNavigation = (ev: MouseEvent) => {
105
+
if (ev.defaultPrevented || (ev.type === 'auxclick' && (ev as MouseEvent).button !== 1)) {
106
+
return;
107
+
}
108
+
109
+
const anchor = ev.currentTarget as HTMLAnchorElement;
110
+
const href = anchor.href;
111
+
112
+
if (isLinkValid(href, anchor.textContent ?? '')) {
113
+
}
114
+
};
115
+
116
+
const findLinkRedirect = (uri: string): string | null => {
117
+
const url = safeUrlParse(uri);
118
+
119
+
if (url === null) {
120
+
return null;
121
+
}
122
+
123
+
const host = url.host;
124
+
const pathname = url.pathname;
125
+
let match: RegExpExecArray | null | undefined;
126
+
127
+
if (host === 'bsky.app') {
128
+
if ((match = /^\/profile\/(?=.+[:.])([^/]+)\/?$/.exec(pathname))) {
129
+
return `/${match[1]}`;
130
+
}
131
+
132
+
if ((match = /^\/profile\/(?=.+[:.])([^/]+)\/post\/([^/]{13})\/?$/.exec(pathname))) {
133
+
return `/${match[1]}/${match[2]}`;
134
+
}
135
+
136
+
if ((match = /^\/profile\/(?=.+[:.])([^/]+)\/lists\/([^/]+)\/?$/.exec(pathname))) {
137
+
return `/${match[1]}/lists/${match[2]}`;
138
+
}
139
+
140
+
if ((match = /^\/profile\/(?=.+[:.])([^/]+)\/feed\/([^/]+)\/?$/.exec(pathname))) {
141
+
return `/${match[1]}/feeds/${match[2]}`;
142
+
}
143
+
}
144
+
145
+
return null;
146
+
};
+76
src/components/text-input.tsx
+76
src/components/text-input.tsx
···
1
+
import { createId } from '~/lib/hooks/id';
2
+
3
+
import { useFieldset } from './fieldset';
4
+
5
+
export interface TextInputProps {
6
+
ref?: (node: HTMLInputElement) => void;
7
+
label?: string;
8
+
type?: 'text' | 'email' | 'password' | 'search' | 'tel' | 'url';
9
+
autocomplete?:
10
+
| 'off'
11
+
| 'on'
12
+
| 'name'
13
+
| 'email'
14
+
| 'username'
15
+
| 'current-password'
16
+
| 'new-password'
17
+
| 'one-time-code';
18
+
pattern?: string;
19
+
required?: boolean;
20
+
disabled?: boolean;
21
+
placeholder?: string;
22
+
error?: string | null | undefined | false;
23
+
value?: string;
24
+
onInput?: (ev: InputEvent) => void;
25
+
}
26
+
27
+
const TextInput = (props: TextInputProps) => {
28
+
const fieldset = useFieldset();
29
+
const id = createId();
30
+
31
+
const hasValue = 'value' in props;
32
+
const isDisabled = () => fieldset.disabled || !!props.disabled;
33
+
34
+
return (
35
+
<div class="flex flex-col gap-2">
36
+
<label
37
+
for={id}
38
+
class={
39
+
`text-sm font-medium empty:hidden` +
40
+
(!isDisabled() ? ` text-c-contrast-900` : ` text-c-contrast-700`)
41
+
}
42
+
>
43
+
{props.label}
44
+
</label>
45
+
<input
46
+
ref={props.ref}
47
+
id={id}
48
+
type={props.type || 'text'}
49
+
autocomplete={props.autocomplete}
50
+
pattern={props.pattern}
51
+
required={props.required}
52
+
disabled={isDisabled()}
53
+
value={hasValue ? props.value : ''}
54
+
onInput={props.onInput}
55
+
placeholder={props.placeholder}
56
+
class={buttonClassNames(isDisabled)}
57
+
/>
58
+
59
+
{props.error && <p class="text-de text-c-negative-300">{props.error}</p>}
60
+
</div>
61
+
);
62
+
};
63
+
64
+
const buttonClassNames = (isDisabled: () => boolean): string => {
65
+
let cn = `rounded bg-c-black px-3 py-2 text-sm leading-6 outline-2 -outline-offset-2 outline-c-primary-400 focus:outline`;
66
+
67
+
if (!isDisabled()) {
68
+
cn += ` border border-c-contrast-300 text-c-contrast-900 placeholder:text-c-contrast-400`;
69
+
} else {
70
+
cn += ` border border-c-contrast-100 text-c-contrast-600 placeholder:text-c-contrast-400`;
71
+
}
72
+
73
+
return cn;
74
+
};
75
+
76
+
export default TextInput;
+47
src/components/time-ago.tsx
+47
src/components/time-ago.tsx
···
1
+
import { createRenderEffect, createSignal, type Accessor, type JSX } from 'solid-js';
2
+
3
+
import { formatAbsDateTime, formatReltime } from '~/lib/intl/time';
4
+
5
+
export interface TimeAgoProps {
6
+
value: string | number;
7
+
/** Expected to be static */
8
+
absolute?: (time: number) => string;
9
+
/** Expected to be static */
10
+
relative?: (time: number) => string;
11
+
children: (relative: Accessor<string>, absolute: Accessor<string>) => JSX.Element;
12
+
}
13
+
14
+
const [watch, tick] = createSignal<void>(undefined, { equals: false });
15
+
16
+
const tickForward = () => {
17
+
tick();
18
+
setTimeout(() => requestIdleCallback(tickForward), 60_000);
19
+
};
20
+
21
+
const TimeAgo = (props: TimeAgoProps) => {
22
+
const formatAbsolute = props.absolute ?? formatAbsDateTime;
23
+
const formatRelative = props.relative ?? formatReltime;
24
+
25
+
const [absolute, setAbsolute] = createSignal('');
26
+
const [relative, setRelative] = createSignal('');
27
+
28
+
createRenderEffect(() => {
29
+
const time = toInt(props.value);
30
+
31
+
setAbsolute(formatAbsolute(time));
32
+
33
+
createRenderEffect(() => {
34
+
watch();
35
+
return setRelative(formatRelative(time));
36
+
});
37
+
});
38
+
39
+
return props.children(relative, absolute);
40
+
};
41
+
42
+
const toInt = (date: string | number): number => {
43
+
return typeof date !== 'number' ? new Date(date).getTime() : date;
44
+
};
45
+
46
+
export default TimeAgo;
47
+
tickForward();
+139
src/components/virtual-item.tsx
+139
src/components/virtual-item.tsx
···
1
+
import { createEffect, createRenderEffect, createSignal, onCleanup, runWithOwner, type JSX } from 'solid-js';
2
+
3
+
import { UNSAFE_useViewContext } from '~/lib/navigation/router';
4
+
import { intersectionObserver, resizeObserver } from '~/lib/observer';
5
+
6
+
const createVirtualStore = (ctx: ReturnType<typeof UNSAFE_useViewContext>) => {
7
+
return runWithOwner(ctx.owner, () => {
8
+
const active = ctx.active;
9
+
let disabled = false;
10
+
11
+
createRenderEffect(() => {
12
+
if (!active()) {
13
+
disabled = true;
14
+
}
15
+
});
16
+
17
+
createEffect(() => {
18
+
if (active()) {
19
+
disabled = false;
20
+
}
21
+
});
22
+
23
+
return {
24
+
get disabled() {
25
+
return disabled;
26
+
},
27
+
};
28
+
})!;
29
+
};
30
+
31
+
const virtualStoreMap = new WeakMap<
32
+
ReturnType<typeof UNSAFE_useViewContext>,
33
+
ReturnType<typeof createVirtualStore>
34
+
>();
35
+
36
+
const getVirtualStore = (ctx: ReturnType<typeof UNSAFE_useViewContext>) => {
37
+
let store = virtualStoreMap.get(ctx);
38
+
if (store === undefined) {
39
+
virtualStoreMap.set(ctx, (store = createVirtualStore(ctx)));
40
+
}
41
+
42
+
return store;
43
+
};
44
+
45
+
export interface VirtualItemProps {
46
+
estimateHeight?: number;
47
+
children?: JSX.Element;
48
+
}
49
+
50
+
const VirtualItem = (props: VirtualItemProps) => {
51
+
let _entry: IntersectionObserverEntry | undefined;
52
+
let _height: number | undefined;
53
+
let _intersecting = false;
54
+
55
+
const store = getVirtualStore(UNSAFE_useViewContext());
56
+
const estimateHeight = props.estimateHeight;
57
+
58
+
const [intersecting, setIntersecting] = createSignal(_intersecting);
59
+
const [storedHeight, setStoredHeight] = createSignal(estimateHeight);
60
+
61
+
const shouldHide = () => !intersecting() && (estimateHeight ?? storedHeight()) !== undefined;
62
+
63
+
const handleIntersect = (nextEntry: IntersectionObserverEntry) => {
64
+
_entry = undefined;
65
+
66
+
if (store.disabled) {
67
+
return;
68
+
}
69
+
70
+
const prev = _intersecting;
71
+
const next = nextEntry.isIntersecting;
72
+
73
+
if (!prev && next) {
74
+
// hidden -> visible
75
+
setIntersecting((_intersecting = next));
76
+
} else if (prev && !next) {
77
+
// visible -> hidden
78
+
// unmounting is cheap, but we don't need to immediately unmount it, say
79
+
// for scenarios where layout is still being figured out and we don't
80
+
// actually know where the virtual container is gonna end up.
81
+
82
+
_entry = nextEntry;
83
+
84
+
requestIdleCallback(() => {
85
+
// bail out if it's no longer us.
86
+
if (_entry !== nextEntry) {
87
+
return;
88
+
}
89
+
90
+
_entry = undefined;
91
+
setIntersecting((_intersecting = next));
92
+
});
93
+
}
94
+
};
95
+
96
+
const handleResize = (nextEntry: ResizeObserverEntry) => {
97
+
if (!_intersecting || store.disabled) {
98
+
return;
99
+
}
100
+
101
+
const contentRect = nextEntry.contentRect;
102
+
const nextHeight = ((contentRect.height * 1000) | 0) / 1000;
103
+
104
+
if (nextHeight !== _height) {
105
+
setStoredHeight((_height = nextHeight));
106
+
}
107
+
};
108
+
109
+
return (
110
+
<article
111
+
ref={startMeasure}
112
+
class="shrink-0"
113
+
style={{
114
+
contain: 'content',
115
+
height: shouldHide() ? `${_height ?? storedHeight()}px` : undefined,
116
+
}}
117
+
prop:$onintersect={handleIntersect}
118
+
prop:$onresize={handleResize}
119
+
>
120
+
{(() => {
121
+
if (!shouldHide()) {
122
+
return props.children;
123
+
}
124
+
})()}
125
+
</article>
126
+
);
127
+
};
128
+
129
+
export default VirtualItem;
130
+
131
+
const startMeasure = (node: HTMLElement) => {
132
+
intersectionObserver.observe(node);
133
+
resizeObserver.observe(node);
134
+
135
+
onCleanup(() => {
136
+
intersectionObserver.unobserve(node);
137
+
resizeObserver.unobserve(node);
138
+
});
139
+
};
+1
src/globals/broadcast.ts
+1
src/globals/broadcast.ts
···
1
+
export const focusBroadcast = new BroadcastChannel('focus');
+10
src/globals/events.ts
+10
src/globals/events.ts
···
1
+
import { EventEmitter } from '@mary/events';
2
+
3
+
export const globalEvents = new EventEmitter<{
4
+
// Current session has expired
5
+
sessionexpired(): void;
6
+
// User has published a post
7
+
postpublished(): void;
8
+
// User tried navigating to the same main page they're already in
9
+
softreset(): void;
10
+
}>();
+7
src/globals/locales.ts
+7
src/globals/locales.ts
+58
src/globals/modals.tsx
+58
src/globals/modals.tsx
···
1
+
import { createContext, createSignal, useContext, type JSX } from 'solid-js';
2
+
import { assert } from '~/lib/invariant';
3
+
4
+
type ModalRenderer = (context: ModalContext) => JSX.Element;
5
+
6
+
export interface ModalState {
7
+
id: number;
8
+
render: ModalRenderer;
9
+
}
10
+
11
+
const [modals, _setModals] = createSignal<ModalState[]>([]);
12
+
let _id = 0;
13
+
14
+
export const hasModals = (): boolean => {
15
+
return modals().length !== 0;
16
+
};
17
+
18
+
export const openModal = (fn: ModalRenderer): number => {
19
+
const id = _id++;
20
+
21
+
_setModals(($modals) => $modals.concat({ id, render: fn }));
22
+
return id;
23
+
};
24
+
25
+
export const closeModal = (id: number): void => {
26
+
_setModals(($modals) => {
27
+
const index = $modals.findIndex((v) => v.id === id);
28
+
29
+
if (index === -1) {
30
+
return $modals;
31
+
}
32
+
33
+
return $modals.toSpliced(index, 1);
34
+
});
35
+
};
36
+
37
+
export const closeAllModals = (): void => {
38
+
_setModals([]);
39
+
};
40
+
41
+
export interface ModalContext {
42
+
id: number;
43
+
/** Whether this dialog is currently the top-most dialog presented */
44
+
isActive(): boolean;
45
+
/** Close this dialog */
46
+
close(): void;
47
+
}
48
+
49
+
const Context = createContext<ModalContext>();
50
+
51
+
export const useModalContext = (): ModalContext => {
52
+
const context = useContext(Context);
53
+
assert(context !== undefined, `Expected useModalContext to be used under a modal`);
54
+
55
+
return context;
56
+
};
57
+
58
+
export { Context as INTERNAL_ModalContext, modals as INTERNAL_modals };
+37
src/globals/preferences.ts
+37
src/globals/preferences.ts
···
1
+
import { createRoot } from 'solid-js';
2
+
3
+
import type { GlobalPreferenceSchema } from '~/lib/preferences/global';
4
+
import type { SessionPreferenceSchema } from '~/lib/preferences/sessions';
5
+
6
+
import { createReactiveLocalStorage } from '~/lib/signals/storage';
7
+
8
+
export const sessions = createRoot(() => {
9
+
return createReactiveLocalStorage<SessionPreferenceSchema>('sessions', (version, prev) => {
10
+
if (version === 0) {
11
+
return {
12
+
$version: 1,
13
+
active: undefined,
14
+
accounts: [],
15
+
};
16
+
}
17
+
18
+
return prev;
19
+
});
20
+
});
21
+
22
+
export const global = createRoot(() => {
23
+
return createReactiveLocalStorage<GlobalPreferenceSchema>('global', (version, prev) => {
24
+
if (version === 0) {
25
+
const prefs: GlobalPreferenceSchema = {
26
+
$version: 1,
27
+
ui: {
28
+
theme: 'system',
29
+
},
30
+
};
31
+
32
+
return prefs;
33
+
}
34
+
35
+
return prev;
36
+
});
37
+
});
+35
src/lib/element-refs.ts
+35
src/lib/element-refs.ts
···
1
+
import { createEffect } from 'solid-js';
2
+
3
+
import { intersectionObserver } from './observer';
4
+
5
+
export const ifIntersect = (
6
+
node: HTMLElement,
7
+
enabled: () => boolean | undefined,
8
+
onIntersect: () => void,
9
+
) => {
10
+
createEffect((setup: boolean) => {
11
+
if (enabled()) {
12
+
if (!setup) {
13
+
// @ts-expect-error
14
+
node.$onintersect = (entry: IntersectionObserverEntry) => {
15
+
if (entry.isIntersecting) {
16
+
onIntersect();
17
+
}
18
+
};
19
+
20
+
intersectionObserver.observe(node);
21
+
return true;
22
+
}
23
+
} else {
24
+
if (setup) {
25
+
// @ts-expect-error
26
+
node.$onintersect = undefined;
27
+
28
+
intersectionObserver.unobserve(node);
29
+
return false;
30
+
}
31
+
}
32
+
33
+
return setup;
34
+
}, false);
35
+
};
+22
src/lib/hooks/abortable.ts
+22
src/lib/hooks/abortable.ts
···
1
+
import { onCleanup } from 'solid-js';
2
+
3
+
type Abortable = [signal: () => AbortSignal, cleanup: () => void];
4
+
5
+
export const makeAbortable = (): Abortable => {
6
+
let controller: AbortController | undefined;
7
+
8
+
const cleanup = () => {
9
+
return controller?.abort();
10
+
};
11
+
12
+
const signal = () => {
13
+
cleanup();
14
+
15
+
controller = new AbortController();
16
+
return controller.signal;
17
+
};
18
+
19
+
onCleanup(cleanup);
20
+
21
+
return [signal, cleanup];
22
+
};
+23
src/lib/hooks/debounced-value.ts
+23
src/lib/hooks/debounced-value.ts
···
1
+
import { createEffect, createSignal, onCleanup, type Accessor } from 'solid-js';
2
+
3
+
export const createDebouncedValue = <T>(
4
+
accessor: Accessor<T>,
5
+
delay: number,
6
+
equals?: false | ((prev: T, next: T) => boolean),
7
+
): Accessor<T> => {
8
+
const initial = accessor();
9
+
const [state, setState] = createSignal(initial, { equals });
10
+
11
+
createEffect((prev: T) => {
12
+
const next = accessor();
13
+
14
+
if (prev !== next) {
15
+
const timeout = setTimeout(() => setState(() => next), delay);
16
+
onCleanup(() => clearTimeout(timeout));
17
+
}
18
+
19
+
return next;
20
+
}, initial);
21
+
22
+
return state;
23
+
};
+11
src/lib/hooks/derived-signal.ts
+11
src/lib/hooks/derived-signal.ts
···
1
+
import { createRenderEffect, createSignal, type Accessor, type Signal } from 'solid-js';
2
+
3
+
export const createDerivedSignal = <T>(accessor: Accessor<T>): Signal<T> => {
4
+
const [state, setState] = createSignal<T>();
5
+
6
+
createRenderEffect(() => {
7
+
setState(accessor);
8
+
});
9
+
10
+
return [state, setState] as Signal<T>;
11
+
};
+23
src/lib/hooks/escape.ts
+23
src/lib/hooks/escape.ts
···
1
+
import { createEffect } from 'solid-js';
2
+
import { createEventListener } from './event-listener';
3
+
4
+
export const useEscape = (callback: () => void, enabled: () => boolean) => {
5
+
createEffect(() => {
6
+
if (!enabled()) {
7
+
return;
8
+
}
9
+
10
+
createEventListener(window, 'keydown', (ev) => {
11
+
if (ev.key === 'Escape' && !ev.defaultPrevented) {
12
+
ev.preventDefault();
13
+
14
+
const focused = document.activeElement;
15
+
if (focused !== null && focused !== document.body) {
16
+
(focused as any).blur();
17
+
}
18
+
19
+
callback();
20
+
}
21
+
});
22
+
});
23
+
};
+42
src/lib/hooks/event-listener.ts
+42
src/lib/hooks/event-listener.ts
···
1
+
import { onCleanup } from 'solid-js';
2
+
3
+
type UnknownFunction = (...args: any[]) => any;
4
+
5
+
type InferEventType<TTarget> = TTarget extends {
6
+
// we infer from 2 overloads which are super common for event targets in the DOM lib
7
+
// we "prioritize" the first one as the first one is always more specific
8
+
addEventListener(type: infer P, ...args: any): void;
9
+
// we can ignore the second one as it's usually just a fallback that allows bare `string` here
10
+
// we use `infer P2` over `any` as we really don't care about this type value
11
+
// and we don't want to accidentally fail a type assignability check, remember that `any` isn't assignable to `never`
12
+
addEventListener(type: infer P2, ...args: any): void;
13
+
}
14
+
? P & string
15
+
: never;
16
+
17
+
type InferEvent<TTarget, TType extends string> = `on${TType}` extends keyof TTarget
18
+
? Parameters<Extract<TTarget[`on${TType}`], UnknownFunction>>[0]
19
+
: Event;
20
+
21
+
// For listener objects, the handleEvent function has the object as the `this` binding
22
+
type ListenerObject<TEvent extends Event> = {
23
+
handleEvent(this: ListenerObject<TEvent>, event: TEvent): void;
24
+
};
25
+
26
+
// event listeners can be an object or a function
27
+
export type Listener<TTarget extends EventTarget, TType extends string> =
28
+
| ListenerObject<InferEvent<TTarget, TType>>
29
+
| { (this: TTarget, ev: InferEvent<TTarget, TType>): void };
30
+
31
+
export const createEventListener = <
32
+
TTarget extends EventTarget,
33
+
TType extends InferEventType<TTarget> | (string & {}),
34
+
>(
35
+
target: TTarget,
36
+
type: TType,
37
+
listener: Listener<TTarget, TType>,
38
+
options?: boolean | AddEventListenerOptions,
39
+
) => {
40
+
onCleanup(target.removeEventListener.bind(target, type, listener, options));
41
+
target.addEventListener(type, listener, options);
42
+
};
+5
src/lib/hooks/id.ts
+5
src/lib/hooks/id.ts
+41
src/lib/hooks/media-query.ts
+41
src/lib/hooks/media-query.ts
···
1
+
import { createSignal, onCleanup, type Accessor } from 'solid-js';
2
+
3
+
interface MediaStore {
4
+
/** State backing */
5
+
a: Accessor<boolean>;
6
+
/** Amount of subscriptions */
7
+
n: number;
8
+
/** Cleanup function */
9
+
c: () => void;
10
+
}
11
+
12
+
const map: Record<string, MediaStore> = {};
13
+
14
+
/*#__NO_SIDE_EFFECTS__*/
15
+
export const useMediaQuery = (query: string): Accessor<boolean> => {
16
+
let media = map[query];
17
+
18
+
if (!media) {
19
+
const matcher = window.matchMedia(query);
20
+
const [state, setState] = createSignal(matcher.matches);
21
+
22
+
const callback = () => setState(matcher.matches);
23
+
matcher.onchange = callback;
24
+
25
+
media = map[query] = {
26
+
n: 0,
27
+
a: state,
28
+
c: () => {
29
+
if (--media.n < 1) {
30
+
matcher.onchange = null;
31
+
delete map[query];
32
+
}
33
+
},
34
+
};
35
+
}
36
+
37
+
media.n++;
38
+
onCleanup(media.c);
39
+
40
+
return media.a;
41
+
};
+72
src/lib/hooks/modal-close.ts
+72
src/lib/hooks/modal-close.ts
···
1
+
import { createEffect, onCleanup } from 'solid-js';
2
+
3
+
import { createEventListener } from './event-listener';
4
+
5
+
const isCloseWatcherAvailable = typeof CloseWatcher !== 'undefined';
6
+
7
+
export const useModalClose = (container: HTMLElement, callback: () => void, enabled: () => boolean) => {
8
+
createEffect(() => {
9
+
if (!enabled()) {
10
+
return;
11
+
}
12
+
13
+
// Close modal if clicks happen outside of container
14
+
let initialTarget: HTMLElement | null = null;
15
+
16
+
createEventListener(document, 'pointerdown', (ev) => {
17
+
// We'd like to know where the click initially started from, not where the
18
+
// click ended up, this prevents closing the modal prematurely from the
19
+
// user (accidentally) overshooting their mouse cursor.
20
+
21
+
initialTarget = ev.target as HTMLElement | null;
22
+
});
23
+
24
+
createEventListener(
25
+
document,
26
+
'click',
27
+
() => {
28
+
// Don't do anything if `initialTarget` is somehow missing
29
+
if (!initialTarget) {
30
+
return;
31
+
}
32
+
33
+
// Unset `initialTarget` now that we're here
34
+
const target = initialTarget;
35
+
initialTarget = null;
36
+
37
+
// Don't do anything if `target` is inside `container`
38
+
if (container.contains(target)) {
39
+
return;
40
+
}
41
+
42
+
// Call back since this click happened outside `container`.
43
+
callback();
44
+
},
45
+
true,
46
+
);
47
+
48
+
// Start a close watcher if available, otherwise, listen to Escape key only
49
+
if (isCloseWatcherAvailable) {
50
+
const watcher = new CloseWatcher();
51
+
watcher.oncancel = (ev) => {
52
+
ev.preventDefault();
53
+
close();
54
+
};
55
+
56
+
onCleanup(() => watcher.close());
57
+
} else {
58
+
createEventListener(window, 'keydown', (ev) => {
59
+
if (ev.key === 'Escape' && !ev.defaultPrevented) {
60
+
ev.preventDefault();
61
+
62
+
const focused = document.activeElement;
63
+
if (focused !== null && focused !== document.body) {
64
+
(focused as any).blur();
65
+
}
66
+
67
+
callback();
68
+
}
69
+
});
70
+
}
71
+
});
72
+
};
+45
src/lib/hooks/outside-click.ts
+45
src/lib/hooks/outside-click.ts
···
1
+
import { createEffect } from 'solid-js';
2
+
3
+
import { createEventListener } from './event-listener';
4
+
5
+
export const useOutsideClick = (container: HTMLElement, callback: () => void, enabled: () => boolean) => {
6
+
createEffect(() => {
7
+
if (!enabled()) {
8
+
return;
9
+
}
10
+
11
+
let initialTarget: HTMLElement | null = null;
12
+
13
+
createEventListener(document, 'pointerdown', (ev) => {
14
+
// We'd like to know where the click initially started from, not where the
15
+
// click ended up, this prevents closing the modal prematurely from the
16
+
// user (accidentally) overshooting their mouse cursor.
17
+
18
+
initialTarget = ev.target as HTMLElement | null;
19
+
});
20
+
21
+
createEventListener(
22
+
document,
23
+
'click',
24
+
() => {
25
+
// Don't do anything if `initialTarget` is somehow missing
26
+
if (!initialTarget) {
27
+
return;
28
+
}
29
+
30
+
// Unset `initialTarget` now that we're here
31
+
const target = initialTarget;
32
+
initialTarget = null;
33
+
34
+
// Don't do anything if `target` is inside `container`
35
+
if (container.contains(target)) {
36
+
return;
37
+
}
38
+
39
+
// Call back since this click happened outside `container`.
40
+
callback();
41
+
},
42
+
true,
43
+
);
44
+
});
45
+
};
+44
src/lib/input-refs.ts
+44
src/lib/input-refs.ts
···
1
+
import { createEffect, createRenderEffect } from 'solid-js';
2
+
3
+
import type { CreateMutationResult } from '@mary/solid-query';
4
+
5
+
type FocusableElement = HTMLButtonElement | HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;
6
+
type TextInput = HTMLInputElement | HTMLTextAreaElement;
7
+
8
+
export const autofocusOnMutation = (
9
+
node: FocusableElement,
10
+
mutation: CreateMutationResult<any, any, any, any>,
11
+
first = true,
12
+
) => {
13
+
// Render effects are not affected by <Suspense>
14
+
createRenderEffect((first: boolean) => {
15
+
if (mutation.isError || first) {
16
+
setTimeout(() => node.focus(), 0);
17
+
}
18
+
19
+
return false;
20
+
}, first);
21
+
};
22
+
23
+
export const autofocusIfEnabled = (node: FocusableElement, enabled: () => boolean) => {
24
+
// Render effects are not affected by <Suspense>
25
+
createRenderEffect(() => {
26
+
if (enabled()) {
27
+
setTimeout(() => node.focus(), 0);
28
+
}
29
+
});
30
+
};
31
+
32
+
export const modelText = (node: TextInput, getter: () => string, setter: (next: string) => void) => {
33
+
let current: string | undefined;
34
+
35
+
createEffect(() => {
36
+
if (current !== (current = getter())) {
37
+
node.value = current;
38
+
}
39
+
});
40
+
41
+
node.addEventListener('input', (ev) => {
42
+
setter((current = node.value));
43
+
});
44
+
};
+44
src/lib/interaction.ts
+44
src/lib/interaction.ts
···
1
+
export const isMac = /^Mac/i.test(navigator.platform);
2
+
3
+
const DEFAULT_EXCLUSION = 'a, button, img, video, dialog, [role=button]';
4
+
export const INTERACTION_TAGS = 'a, button, [role=button]';
5
+
6
+
export const hasSelectionRange = () => {
7
+
const selection = window.getSelection();
8
+
return selection !== null && selection.type === 'Range';
9
+
};
10
+
11
+
export const isElementClicked = (ev: Event, exclusion = DEFAULT_EXCLUSION) => {
12
+
const target = ev.currentTarget as HTMLElement;
13
+
const path = ev.composedPath() as HTMLElement[];
14
+
15
+
if (
16
+
!path.includes(target) ||
17
+
(ev.type === 'keydown' && (ev as KeyboardEvent).key !== 'Enter') ||
18
+
(ev.type === 'auxclick' && (ev as MouseEvent).button !== 1)
19
+
) {
20
+
return false;
21
+
}
22
+
23
+
for (let idx = 0, len = path.length; idx < len; idx++) {
24
+
const node = path[idx];
25
+
26
+
if (node == target) {
27
+
break;
28
+
}
29
+
30
+
if (node.matches(exclusion)) {
31
+
return false;
32
+
}
33
+
}
34
+
35
+
return !hasSelectionRange();
36
+
};
37
+
38
+
export const isElementAltClicked = (ev: MouseEvent | KeyboardEvent) => {
39
+
return ev.type === 'auxclick' || isCtrlKeyPressed(ev);
40
+
};
41
+
42
+
export const isCtrlKeyPressed = (ev: MouseEvent | KeyboardEvent) => {
43
+
return isMac ? ev.metaKey : ev.ctrlKey;
44
+
};
+87
src/lib/intl/time.ts
+87
src/lib/intl/time.ts
···
1
+
let startOfYear = 0;
2
+
let endOfYear = 0;
3
+
4
+
const SECOND = 1e3;
5
+
const NOW = SECOND * 10;
6
+
const MINUTE = SECOND * 60;
7
+
const HOUR = MINUTE * 60;
8
+
const DAY = HOUR * 24;
9
+
const WEEK = DAY * 7;
10
+
// const MONTH = WEEK * 4;
11
+
// const YEAR = MONTH * 12;
12
+
13
+
const absWithYearFormat = new Intl.DateTimeFormat('en-US', { dateStyle: 'medium' });
14
+
const absFormat = new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric' });
15
+
const absTimeFormat = new Intl.DateTimeFormat('en-US', { dateStyle: 'long', timeStyle: 'short' });
16
+
17
+
const formatters: Record<string, Intl.NumberFormat> = {};
18
+
const getNow = Date.now;
19
+
20
+
export const formatReltime = (time: number): string => {
21
+
const now = getNow();
22
+
const delta = now - time;
23
+
24
+
if (delta < 0 || delta > WEEK) {
25
+
if (now > endOfYear) {
26
+
const date = new Date();
27
+
28
+
date.setMonth(0, 1);
29
+
date.setHours(0, 0, 0);
30
+
startOfYear = date.getTime();
31
+
32
+
date.setFullYear(date.getFullYear() + 1, 0, 0);
33
+
date.setHours(23, 59, 59, 999);
34
+
endOfYear = date.getTime();
35
+
}
36
+
37
+
// if it happened this year, don't show the year.
38
+
if (time >= startOfYear && time <= endOfYear) {
39
+
return absFormat.format(time);
40
+
}
41
+
42
+
return absWithYearFormat.format(time);
43
+
}
44
+
45
+
if (delta < NOW) {
46
+
return `now`;
47
+
}
48
+
49
+
{
50
+
let value: number;
51
+
let unit: Intl.RelativeTimeFormatUnit;
52
+
53
+
if (delta < MINUTE) {
54
+
value = Math.floor(delta / SECOND);
55
+
unit = 'second';
56
+
} else if (delta < HOUR) {
57
+
value = Math.floor(delta / MINUTE);
58
+
unit = 'minute';
59
+
} else if (delta < DAY) {
60
+
value = Math.floor(delta / HOUR);
61
+
unit = 'hour';
62
+
} else {
63
+
// use rounding, this handles the following scenario:
64
+
// - 2024-02-13T09:00Z <- 2024-02-15T07:00Z = 2d
65
+
value = Math.round(delta / DAY);
66
+
unit = 'day';
67
+
}
68
+
69
+
const formatter = (formatters[unit] ||= new Intl.NumberFormat('en-US', {
70
+
style: 'unit',
71
+
unit: unit,
72
+
unitDisplay: 'narrow',
73
+
}));
74
+
75
+
return formatter.format(Math.abs(value));
76
+
}
77
+
};
78
+
79
+
export const formatAbsDate = (time: string | number) => {
80
+
const date = new Date(time);
81
+
return absWithYearFormat.format(date);
82
+
};
83
+
84
+
export const formatAbsDateTime = (time: string | number) => {
85
+
const date = new Date(time);
86
+
return absTimeFormat.format(date);
87
+
};
+15
src/lib/invariant.ts
+15
src/lib/invariant.ts
···
1
+
export function assert(condition: any, message?: string): asserts condition {
2
+
if (import.meta.env.DEV && !condition) {
3
+
throw new Error(`Assertion failed` + message ? `: ${message}` : ``);
4
+
}
5
+
}
6
+
7
+
export function assertStrong(condition: any, message?: string): asserts condition {
8
+
if (!condition) {
9
+
if (import.meta.env.DEV) {
10
+
throw new Error(`Assertion failed` + message ? `: ${message}` : ``);
11
+
}
12
+
13
+
throw new Error(`Assertion failed`);
14
+
}
15
+
}
+28
src/lib/misc.ts
+28
src/lib/misc.ts
···
1
+
import { createMemo, untrack } from 'solid-js';
2
+
3
+
export const mapDefined = <T, R>(array: T[], mapper: (value: T) => R | undefined): R[] => {
4
+
var mapped: R[] = [];
5
+
6
+
var idx = 0;
7
+
var len = array.length;
8
+
var temp: R | undefined;
9
+
10
+
for (; idx < len; idx++) {
11
+
if ((temp = mapper(array[idx])) !== undefined) {
12
+
mapped.push(temp);
13
+
}
14
+
}
15
+
16
+
return mapped;
17
+
};
18
+
19
+
export const on = <T, R>(accessor: () => T, callback: (value: T) => R): (() => R) => {
20
+
return () => {
21
+
const value = accessor();
22
+
return untrack(() => callback(value));
23
+
};
24
+
};
25
+
26
+
export const memoizedOn = <T, R>(accessor: () => T, callback: (value: T) => R): (() => R) => {
27
+
return createMemo(on(accessor, callback));
28
+
};
+41
src/lib/observer.ts
+41
src/lib/observer.ts
···
1
+
const intersectionCallback: IntersectionObserverCallback = (entries, observer) => {
2
+
for (let idx = 0, len = entries.length; idx < len; idx++) {
3
+
const entry = entries[idx];
4
+
5
+
const target = entry.target as any;
6
+
const listener = target.$onintersect;
7
+
8
+
if (listener) {
9
+
listener(entry);
10
+
} else {
11
+
observer.unobserve(target);
12
+
}
13
+
}
14
+
};
15
+
16
+
const resizeCallback: ResizeObserverCallback = (entries, observer) => {
17
+
for (let idx = 0, len = entries.length; idx < len; idx++) {
18
+
const entry = entries[idx];
19
+
20
+
const target = entry.target as any;
21
+
const listener = target.$onresize;
22
+
23
+
if (listener) {
24
+
listener(entry);
25
+
} else {
26
+
observer.unobserve(target);
27
+
}
28
+
}
29
+
};
30
+
31
+
export const intersectionObserver = new IntersectionObserver(intersectionCallback);
32
+
export const resizeObserver = new ResizeObserver(resizeCallback);
33
+
34
+
declare module 'solid-js' {
35
+
namespace JSX {
36
+
interface ExplicitProperties {
37
+
$onintersect: (entry: IntersectionObserverEntry) => void;
38
+
$onresize: (entry: ResizeObserverEntry) => void;
39
+
}
40
+
}
41
+
}
+35
src/lib/preferences/account.ts
+35
src/lib/preferences/account.ts
···
1
+
import type { At } from '@mary/bluesky-client/lexicons';
2
+
3
+
import type { ModerationLabeler, ModerationPreferences } from '~/api/moderation';
4
+
5
+
export interface PerAccountPreferenceSchema {
6
+
$version: 1;
7
+
feeds: SavedFeed[];
8
+
language: LanguagePreferences;
9
+
moderation: ModerationPreferences;
10
+
}
11
+
12
+
export interface ModerationLabelerPreferences {
13
+
updated: number;
14
+
definitions: Record<At.DID, ModerationLabeler>;
15
+
}
16
+
17
+
export interface SavedFeed {
18
+
readonly uri: string;
19
+
pinned: boolean;
20
+
info: SavedFeedInfo;
21
+
avatar?: string;
22
+
indexedAt?: string;
23
+
}
24
+
25
+
export interface SavedFeedInfo {
26
+
name: string;
27
+
avatar?: string;
28
+
acceptsInteraction?: boolean;
29
+
indexedAt?: string;
30
+
}
31
+
32
+
export interface LanguagePreferences {
33
+
/** Default language to use when composing a new post */
34
+
defaultPostLanguage: 'none' | 'system' | (string & {});
35
+
}
+8
src/lib/preferences/global.ts
+8
src/lib/preferences/global.ts
+19
src/lib/preferences/sessions.ts
+19
src/lib/preferences/sessions.ts
···
1
+
import type { AtpSessionData } from '@mary/bluesky-client';
2
+
import type { At } from '@mary/bluesky-client/lexicons';
3
+
4
+
export interface SessionPreferenceSchema {
5
+
$version: 1;
6
+
active: At.DID | undefined;
7
+
accounts: AccountData[];
8
+
}
9
+
10
+
export interface AccountData {
11
+
/** Account DID */
12
+
readonly did: At.DID;
13
+
/** Account's PDS or entryway (bsky.social instances) */
14
+
service: string;
15
+
/** Account's session data, from `BskyAuth` */
16
+
session: AtpSessionData;
17
+
/** Whether an account has a defined scope, from app passwords. */
18
+
scope: 'limited' | 'privileged' | undefined;
19
+
}
+36
src/lib/signals/index.ts
+36
src/lib/signals/index.ts
···
1
+
import { type Accessor, type Setter, type SignalOptions, createSignal, untrack } from 'solid-js';
2
+
3
+
// Solid's createSignal is pretty clunky to carry around as it returns an array
4
+
// that is expected to be destructured, this Signal class serves as a wrapper.
5
+
6
+
export class Signal<T> {
7
+
#get: Accessor<T>;
8
+
#set: Setter<T>;
9
+
10
+
constructor(value: T, options?: SignalOptions<T>) {
11
+
const impl = createSignal(value, options);
12
+
this.#get = impl[0];
13
+
this.#set = impl[1];
14
+
}
15
+
16
+
get value() {
17
+
return this.#get();
18
+
}
19
+
20
+
set value(next: T) {
21
+
// @ts-expect-error
22
+
this.#set(typeof next === 'function' ? () => next : next);
23
+
}
24
+
25
+
peek() {
26
+
return untrack(this.#get);
27
+
}
28
+
}
29
+
30
+
export interface ReadonlySignal<T> extends Signal<T> {
31
+
readonly value: T;
32
+
}
33
+
34
+
export const signal = <T>(value: T, options?: SignalOptions<T>) => {
35
+
return new Signal(value, options);
36
+
};
+51
src/lib/signals/storage.ts
+51
src/lib/signals/storage.ts
···
1
+
import { createEffect } from 'solid-js';
2
+
import { createMutable, modifyMutable, reconcile, type StoreNode } from 'solid-js/store';
3
+
import { createEventListener } from '../hooks/event-listener';
4
+
5
+
type MigrateFn<T> = (version: number, prev: any) => T;
6
+
7
+
/** Useful for knowing whether an effect occured by external writes */
8
+
export let isExternalWriting = false;
9
+
10
+
const parse = <T>(raw: string | null, migrate: MigrateFn<T>): T => {
11
+
if (raw !== null) {
12
+
try {
13
+
const persisted = JSON.parse(raw);
14
+
15
+
if (persisted != null) {
16
+
return migrate(persisted.$version || 0, persisted);
17
+
}
18
+
} catch {}
19
+
}
20
+
21
+
return migrate(0, null);
22
+
};
23
+
24
+
export const createReactiveLocalStorage = <T extends StoreNode>(name: string, migrate: MigrateFn<T>) => {
25
+
const mutable = createMutable<T>(parse(localStorage.getItem(name), migrate));
26
+
27
+
createEffect((inited) => {
28
+
const json = JSON.stringify(mutable);
29
+
30
+
if (inited && !isExternalWriting) {
31
+
localStorage.setItem(name, json);
32
+
}
33
+
34
+
return true;
35
+
}, false);
36
+
37
+
createEventListener(window, 'storage', (ev) => {
38
+
if (ev.key === name) {
39
+
// Prevent our own effects from running, since this is already persisted.
40
+
41
+
try {
42
+
isExternalWriting = true;
43
+
modifyMutable(mutable, reconcile(parse(ev.newValue, migrate), { merge: true }));
44
+
} finally {
45
+
isExternalWriting = false;
46
+
}
47
+
}
48
+
});
49
+
50
+
return mutable;
51
+
};
+70
src/lib/states/agent.tsx
+70
src/lib/states/agent.tsx
···
1
+
import { createContext, createMemo, useContext, type JSX, type ParentProps } from 'solid-js';
2
+
3
+
import { BskyXRPC, type BskyAuth } from '@mary/bluesky-client';
4
+
import { QueryClient, QueryClientProvider } from '@mary/solid-query';
5
+
6
+
import { assert } from '../invariant';
7
+
import { memoizedOn } from '../misc';
8
+
import { createQueryPersister } from '../utils/query-storage';
9
+
10
+
import { useSession } from './session';
11
+
12
+
export interface AgentContext {
13
+
rpc: BskyXRPC;
14
+
auth: BskyAuth | null;
15
+
persister: ReturnType<typeof createQueryPersister>;
16
+
}
17
+
18
+
const Context = createContext<AgentContext>();
19
+
20
+
export const AgentProvider = (props: ParentProps) => {
21
+
const session = useSession();
22
+
23
+
const agent = createMemo((): AgentContext => {
24
+
const currentAccount = session.currentAccount;
25
+
26
+
if (currentAccount) {
27
+
return {
28
+
rpc: currentAccount.rpc,
29
+
auth: currentAccount.auth,
30
+
persister: createQueryPersister({ name: `queryCache-${currentAccount.did}` }),
31
+
};
32
+
}
33
+
34
+
return {
35
+
rpc: new BskyXRPC({ service: 'https://public.api.bsky.app' }),
36
+
auth: null,
37
+
persister: createQueryPersister({ name: `queryCache-public` }),
38
+
};
39
+
});
40
+
41
+
return memoizedOn(agent, ($agent) => {
42
+
// Always use a new QueryClient when the agent changes,
43
+
// this way we don't need to manually reset on switching accounts.
44
+
const queryClient = new QueryClient({
45
+
defaultOptions: {
46
+
queries: {
47
+
gcTime: 2_000,
48
+
staleTime: 30_000,
49
+
refetchOnReconnect: false,
50
+
refetchOnWindowFocus: false,
51
+
retry: false,
52
+
},
53
+
},
54
+
});
55
+
56
+
return (
57
+
<QueryClientProvider client={queryClient}>
58
+
<Context.Provider value={$agent}>{props.children}</Context.Provider>
59
+
</QueryClientProvider>
60
+
);
61
+
}) as unknown as JSX.Element;
62
+
};
63
+
64
+
// Safe to destructure when under <AgentProvider>
65
+
export const useAgent = (): AgentContext => {
66
+
const agent = useContext(Context);
67
+
assert(agent !== undefined, `Expected useAgent to be called under <AgentProvider>`);
68
+
69
+
return agent;
70
+
};
+106
src/lib/states/moderation.tsx
+106
src/lib/states/moderation.tsx
···
1
+
import { createContext, createMemo, useContext, type ParentProps } from 'solid-js';
2
+
import { unwrap } from 'solid-js/store';
3
+
4
+
import type { AppBskyLabelerDefs, At } from '@mary/bluesky-client/lexicons';
5
+
import { createQueries } from '@mary/solid-query';
6
+
7
+
import { BLUESKY_MODERATION_DID } from '~/api/defaults';
8
+
import type { ModerationLabeler, ModerationOptions, ModerationPreferences } from '~/api/moderation';
9
+
import { interpretLabelerDefinition } from '~/api/moderation/labeler';
10
+
11
+
import { assert } from '~/lib/invariant';
12
+
import { mapDefined } from '~/lib/misc';
13
+
import { createBatchedFetch } from '~/lib/utils/batch-fetch';
14
+
15
+
import { useAgent } from './agent';
16
+
import { useSession } from './session';
17
+
18
+
type Labeler = AppBskyLabelerDefs.LabelerViewDetailed;
19
+
20
+
const DEFAULT_MODERATION_PREFERENCES: ModerationPreferences = {
21
+
hideReposts: [],
22
+
keywords: [],
23
+
labelers: {
24
+
[BLUESKY_MODERATION_DID]: {
25
+
redact: true,
26
+
privileged: true,
27
+
labels: {},
28
+
},
29
+
},
30
+
labels: {},
31
+
tempMutes: {},
32
+
};
33
+
34
+
const Context = createContext<() => ModerationOptions>();
35
+
36
+
export const ModerationProvider = (props: ParentProps) => {
37
+
const { rpc, persister } = useAgent();
38
+
const { currentAccount } = useSession();
39
+
40
+
const modPreferences = createMemo(() => {
41
+
if (!currentAccount) {
42
+
return DEFAULT_MODERATION_PREFERENCES;
43
+
}
44
+
45
+
return currentAccount.preferences.moderation;
46
+
});
47
+
48
+
const fetchLabeler = createBatchedFetch<At.DID, At.DID, ModerationLabeler>({
49
+
limit: 20,
50
+
timeout: 1,
51
+
idFromQuery: (query) => query,
52
+
idFromData: (data) => data.did,
53
+
async fetch(dids) {
54
+
const { data } = await rpc.get('app.bsky.labeler.getServices', {
55
+
params: {
56
+
dids: dids,
57
+
detailed: true,
58
+
},
59
+
});
60
+
61
+
const views = data.views as Labeler[];
62
+
63
+
return views.map((view) => interpretLabelerDefinition(view));
64
+
},
65
+
});
66
+
67
+
const labelerDefs = createQueries(() => {
68
+
return {
69
+
queries: Object.keys(modPreferences().labelers).map((_did) => {
70
+
const did = _did as At.DID;
71
+
72
+
return {
73
+
queryKey: ['labeler-definition', did],
74
+
queryFn: () => fetchLabeler(did),
75
+
staleTime: 21600000, // 6 hours
76
+
gcTime: 86400000, // 24 hours
77
+
refetchOnWindowFocus: true,
78
+
persister: persister,
79
+
};
80
+
}),
81
+
combine(results) {
82
+
const defs = mapDefined(results, (result) => result.data);
83
+
const fields = Object.fromEntries(defs.map((def) => [def.did, def]));
84
+
85
+
return fields as Record<At.DID, ModerationLabeler>;
86
+
},
87
+
};
88
+
});
89
+
90
+
const modOptions = createMemo((prev?: ModerationOptions): ModerationOptions => {
91
+
return {
92
+
_filtersCache: prev?._filtersCache,
93
+
preferences: unwrap(modPreferences()),
94
+
labelerDefinitions: labelerDefs(),
95
+
};
96
+
});
97
+
98
+
return <Context.Provider value={modOptions}>{props.children}</Context.Provider>;
99
+
};
100
+
101
+
export const useModerationOptions = (): (() => ModerationOptions) => {
102
+
const options = useContext(Context);
103
+
assert(options !== undefined, `Expected useModerationOptions to be used under <ModerationProvider>`);
104
+
105
+
return options;
106
+
};
+258
src/lib/states/session.tsx
+258
src/lib/states/session.tsx
···
1
+
import {
2
+
batch,
3
+
createContext,
4
+
createEffect,
5
+
createRoot,
6
+
createSignal,
7
+
untrack,
8
+
useContext,
9
+
type ParentProps,
10
+
} from 'solid-js';
11
+
import { unwrap } from 'solid-js/store';
12
+
13
+
import { BskyAuth, BskyMod, BskyXRPC, type AtpAccessJwt, type BskyAuthOptions } from '@mary/bluesky-client';
14
+
import type { At } from '@mary/bluesky-client/lexicons';
15
+
import { decodeJwt } from '@mary/bluesky-client/utils/jwt';
16
+
17
+
import { BLUESKY_MODERATION_DID } from '~/api/defaults';
18
+
19
+
import { globalEvents } from '~/globals/events';
20
+
import { sessions } from '~/globals/preferences';
21
+
22
+
import { makeAbortable } from '../hooks/abortable';
23
+
import type { PerAccountPreferenceSchema } from '../preferences/account';
24
+
import type { AccountData } from '../preferences/sessions';
25
+
import { createReactiveLocalStorage, isExternalWriting } from '../signals/storage';
26
+
27
+
import { assert } from '../invariant';
28
+
29
+
interface LoginOptions {
30
+
service: string;
31
+
identifier: string;
32
+
password: string;
33
+
authFactorToken?: string;
34
+
}
35
+
36
+
export interface CurrentAccountState {
37
+
readonly did: At.DID;
38
+
readonly data: AccountData;
39
+
readonly preferences: PerAccountPreferenceSchema;
40
+
41
+
readonly rpc: BskyXRPC;
42
+
readonly auth: BskyAuth;
43
+
readonly _cleanup: () => void;
44
+
}
45
+
46
+
export interface SessionContext {
47
+
readonly currentAccount: CurrentAccountState | undefined;
48
+
49
+
getAccounts(): AccountData[];
50
+
resumeSession(account: AccountData): Promise<void>;
51
+
removeAccount(account: AccountData): void;
52
+
53
+
login(opts: LoginOptions): Promise<void>;
54
+
logout(): void;
55
+
}
56
+
57
+
const Context = createContext<SessionContext>();
58
+
59
+
export const SessionProvider = (props: ParentProps) => {
60
+
const [getSignal] = makeAbortable();
61
+
const [state, _setState] = createSignal<CurrentAccountState>();
62
+
63
+
const replaceState = (next: CurrentAccountState | undefined) => {
64
+
_setState((prev) => {
65
+
prev?._cleanup();
66
+
return next;
67
+
});
68
+
};
69
+
70
+
const createAccountState = (account: AccountData, rpc: BskyXRPC, auth: BskyAuth): CurrentAccountState => {
71
+
return createRoot((cleanup): CurrentAccountState => {
72
+
const preferences = createAccountPreferences(account.did);
73
+
const mod = new BskyMod(rpc);
74
+
75
+
createEffect(() => {
76
+
const entries = Object.entries(preferences.moderation.labelers);
77
+
mod.labelers = entries.map(([did, info]) => ({ did: did as At.DID, redact: info.redact }));
78
+
});
79
+
80
+
return {
81
+
did: account.did,
82
+
data: account,
83
+
preferences: preferences,
84
+
85
+
auth: auth,
86
+
rpc: rpc,
87
+
_cleanup: cleanup,
88
+
};
89
+
}, null);
90
+
};
91
+
92
+
const getAuthOptions = (): BskyAuthOptions => {
93
+
return {
94
+
onExpired() {
95
+
globalEvents.emit('sessionexpired');
96
+
},
97
+
onRefresh(session) {
98
+
const did = session.did;
99
+
const existing = sessions.accounts.find((acc) => acc.did === did);
100
+
101
+
if (existing) {
102
+
batch(() => Object.assign(existing.session, session));
103
+
}
104
+
},
105
+
};
106
+
};
107
+
108
+
const context: SessionContext = {
109
+
get currentAccount() {
110
+
return state();
111
+
},
112
+
113
+
getAccounts(): AccountData[] {
114
+
return sessions.accounts;
115
+
},
116
+
async resumeSession(account: AccountData): Promise<void> {
117
+
const signal = getSignal();
118
+
const session = unwrap(account.session);
119
+
120
+
const rpc = new BskyXRPC({ service: account.service });
121
+
const auth = new BskyAuth(rpc, getAuthOptions());
122
+
123
+
await auth.resume(session);
124
+
signal.throwIfAborted();
125
+
126
+
batch(() => {
127
+
sessions.active = account.did;
128
+
sessions.accounts = [account, ...sessions.accounts.filter((acc) => acc.did !== session.did)];
129
+
replaceState(createAccountState(account, rpc, auth));
130
+
});
131
+
},
132
+
removeAccount(account: AccountData): void {
133
+
const $state = untrack(state);
134
+
const isLoggedIn = $state !== undefined && $state.did === account.did;
135
+
136
+
batch(() => {
137
+
if (isLoggedIn) {
138
+
replaceState(undefined);
139
+
}
140
+
});
141
+
},
142
+
143
+
async login(opts: LoginOptions): Promise<void> {
144
+
const signal = getSignal();
145
+
146
+
const rpc = new BskyXRPC({ service: opts.service });
147
+
const auth = new BskyAuth(rpc, getAuthOptions());
148
+
149
+
await auth.login({ identifier: opts.identifier, password: opts.password, code: opts.authFactorToken });
150
+
signal.throwIfAborted();
151
+
152
+
const session = auth.session!;
153
+
const sessionJwt = decodeJwt(session.accessJwt) as AtpAccessJwt;
154
+
155
+
const scope = sessionJwt.scope;
156
+
let accountScope: AccountData['scope'];
157
+
if (scope === 'com.atproto.appPass') {
158
+
accountScope = 'limited';
159
+
} else if (scope === 'com.atproto.appPassPrivileged') {
160
+
accountScope = 'privileged';
161
+
}
162
+
163
+
const account: AccountData = {
164
+
did: session.did,
165
+
service: opts.service,
166
+
session: session,
167
+
scope: accountScope,
168
+
};
169
+
170
+
batch(() => {
171
+
sessions.active = account.did;
172
+
sessions.accounts = [account, ...sessions.accounts.filter((acc) => acc.did !== session.did)];
173
+
replaceState(createAccountState(account, rpc, auth));
174
+
});
175
+
},
176
+
logout(): void {
177
+
const $state = untrack(state);
178
+
if ($state !== undefined) {
179
+
this.removeAccount($state.data);
180
+
}
181
+
},
182
+
};
183
+
184
+
createEffect(() => {
185
+
const active = sessions.active;
186
+
const activeAccount = active && sessions.accounts.find((acc) => acc.did === active);
187
+
188
+
// Only run this on external changes
189
+
if (isExternalWriting) {
190
+
const untrackedState = untrack(state);
191
+
192
+
if (active) {
193
+
if (active !== untrackedState?.did) {
194
+
// Current active account doesn't match what we have
195
+
196
+
// Still logged in, so log out of this one
197
+
if (untrackedState) {
198
+
replaceState(undefined);
199
+
}
200
+
201
+
// Account data exists, try to login as that account
202
+
if (activeAccount) {
203
+
context.resumeSession(activeAccount);
204
+
}
205
+
} else if (activeAccount) {
206
+
// It's likely that this external write occured due to session changes
207
+
// so update it to whatever it is now
208
+
untrackedState.auth.session = unwrap(activeAccount.session);
209
+
}
210
+
} else if (untrackedState) {
211
+
// No active account yet we have a session, log out
212
+
replaceState(undefined);
213
+
}
214
+
}
215
+
});
216
+
217
+
return <Context.Provider value={context}>{props.children}</Context.Provider>;
218
+
};
219
+
220
+
// Safe to destructure when under <AgentProvider>
221
+
export const useSession = (): SessionContext => {
222
+
const session = useContext(Context);
223
+
assert(session !== undefined, `Expected useSession to be called under <SessionProvider>`);
224
+
225
+
return session;
226
+
};
227
+
228
+
const createAccountPreferences = (did: At.DID) => {
229
+
const key = `account-${did}`;
230
+
return createReactiveLocalStorage<PerAccountPreferenceSchema>(key, (version, prev) => {
231
+
if (version === 0) {
232
+
const obj: PerAccountPreferenceSchema = {
233
+
$version: 1,
234
+
feeds: [],
235
+
language: {
236
+
defaultPostLanguage: 'system',
237
+
},
238
+
moderation: {
239
+
hideReposts: [],
240
+
keywords: [],
241
+
labelers: {
242
+
[BLUESKY_MODERATION_DID]: {
243
+
redact: true,
244
+
privileged: false,
245
+
labels: {},
246
+
},
247
+
},
248
+
labels: {},
249
+
tempMutes: {},
250
+
},
251
+
};
252
+
253
+
return obj;
254
+
}
255
+
256
+
return prev;
257
+
});
258
+
};
+52
src/lib/states/theme.tsx
+52
src/lib/states/theme.tsx
···
1
+
import { createContext, createRenderEffect, createSignal, useContext, type ParentProps } from 'solid-js';
2
+
3
+
import * as preferences from '~/globals/preferences';
4
+
5
+
import { useMediaQuery } from '../hooks/media-query';
6
+
import { assert } from '../invariant';
7
+
8
+
type Theme = 'light' | 'dark';
9
+
10
+
export interface ThemeContext {
11
+
readonly currentTheme: Theme;
12
+
}
13
+
14
+
const Context = createContext<ThemeContext>();
15
+
16
+
export const useTheme = (): ThemeContext => {
17
+
const context = useContext(Context);
18
+
assert(context !== undefined, `Expected useTheme to be used under ThemeProvider`);
19
+
20
+
return context;
21
+
};
22
+
23
+
export const ThemeProvider = (props: ParentProps) => {
24
+
const [theme, setTheme] = createSignal<Theme>('light');
25
+
26
+
const context: ThemeContext = {
27
+
get currentTheme() {
28
+
return theme();
29
+
},
30
+
};
31
+
32
+
createRenderEffect(() => {
33
+
const theme = preferences.global.ui.theme;
34
+
35
+
if (theme === 'system') {
36
+
const isDark = useMediaQuery('(prefers-color-scheme: dark)');
37
+
createRenderEffect(() => setTheme(!isDark() ? 'light' : 'dark'));
38
+
} else {
39
+
setTheme(theme);
40
+
}
41
+
});
42
+
43
+
createRenderEffect(() => {
44
+
const cl = document.documentElement.classList;
45
+
const $theme = theme();
46
+
47
+
cl.toggle('theme-light', $theme === 'light');
48
+
cl.toggle('theme-dark', $theme === 'dark');
49
+
});
50
+
51
+
return <Context.Provider value={context}>{props.children}</Context.Provider>;
52
+
};
+16
src/lib/styles.ts
+16
src/lib/styles.ts
···
1
+
export const clsx = (classes: (string | false | null | undefined | 0)[]): string => {
2
+
var result = '';
3
+
var subsequent = false;
4
+
var temp: string | false | null | undefined | 0;
5
+
6
+
for (var idx = 0, len = classes.length; idx < len; idx++) {
7
+
if ((temp = classes[idx])) {
8
+
subsequent && (result += ' ');
9
+
result += temp;
10
+
11
+
subsequent = true;
12
+
}
13
+
}
14
+
15
+
return result;
16
+
};
+106
src/lib/utils/batch-fetch.ts
+106
src/lib/utils/batch-fetch.ts
···
1
+
// we would sometimes rely on fetching multiple individual posts, and it would
2
+
// be preferrable if it can be batched.
3
+
4
+
type Promisable<T> = T | Promise<T>;
5
+
6
+
export type QueryId = string | number;
7
+
8
+
export interface BatchedFetchOptions<Query, Id extends QueryId, Data> {
9
+
limit: number;
10
+
timeout: number;
11
+
fetch: (queries: Query[]) => Promisable<Data[]>;
12
+
key?: (query: Query) => string | number;
13
+
idFromQuery: (query: Query) => Id;
14
+
idFromData: (data: Data) => Id;
15
+
}
16
+
17
+
interface BatchedFetchMap<Query, Id, Data> {
18
+
key: string | number | undefined;
19
+
timeout: any;
20
+
queries: Query[];
21
+
pending: Map<Id, PromiseWithResolvers<Data>>;
22
+
}
23
+
24
+
export class ResourceMissingError extends Error {
25
+
name = 'ResourceMissingError';
26
+
}
27
+
28
+
/*#__NO_SIDE_EFFECTS__*/
29
+
export const createBatchedFetch = <Query, Id extends QueryId, Data>(
30
+
options: BatchedFetchOptions<Query, Id, Data>,
31
+
) => {
32
+
const { limit, timeout, fetch, key: _key, idFromData, idFromQuery } = options;
33
+
34
+
let curr: BatchedFetchMap<Query, Id, Data> | undefined;
35
+
36
+
return (query: Query): Promise<Data> => {
37
+
const id = idFromQuery(query);
38
+
const key = _key?.(query);
39
+
40
+
let map = curr;
41
+
42
+
if (!map || map.queries.length >= limit || map.key !== key) {
43
+
map = curr = {
44
+
key,
45
+
timeout: undefined,
46
+
queries: [],
47
+
pending: new Map(),
48
+
};
49
+
}
50
+
51
+
let deferred = map.pending.get(id);
52
+
53
+
if (!deferred) {
54
+
deferred = Promise.withResolvers<Data>();
55
+
56
+
map.queries.push(query);
57
+
map.pending.set(id, deferred);
58
+
}
59
+
60
+
clearTimeout(map.timeout);
61
+
62
+
map.timeout = setTimeout(() => {
63
+
if (curr === map) {
64
+
curr = undefined;
65
+
}
66
+
67
+
perform(map!, fetch, idFromData);
68
+
}, timeout);
69
+
70
+
return deferred.promise;
71
+
};
72
+
};
73
+
74
+
const perform = async <Query, Id extends QueryId, Data>(
75
+
map: BatchedFetchMap<Query, Id, Data>,
76
+
fetch: (queries: Query[]) => Promisable<Data[]>,
77
+
idFromData: (data: Data) => Id,
78
+
) => {
79
+
const queries = map.queries;
80
+
const pending = map.pending;
81
+
82
+
let errored = false;
83
+
84
+
try {
85
+
const dataset = await fetch(queries);
86
+
87
+
for (const data of dataset) {
88
+
const id = idFromData(data);
89
+
const deferred = pending.get(id);
90
+
91
+
deferred?.resolve(data);
92
+
}
93
+
} catch (error) {
94
+
errored = true;
95
+
96
+
for (const deferred of pending.values()) {
97
+
deferred.reject(error);
98
+
}
99
+
} finally {
100
+
if (!errored) {
101
+
for (const deferred of pending.values()) {
102
+
deferred.reject(new ResourceMissingError());
103
+
}
104
+
}
105
+
}
106
+
};
+122
src/lib/utils/query-storage.ts
+122
src/lib/utils/query-storage.ts
···
1
+
import type { Query, QueryFunctionContext, QueryKey } from '@mary/solid-query';
2
+
3
+
import { createEventListener } from '../hooks/event-listener';
4
+
5
+
export interface QueryPersistOptions {
6
+
name: string;
7
+
key?: string;
8
+
maxAge?: number;
9
+
}
10
+
11
+
export const createQueryPersister = ({
12
+
name,
13
+
key = '',
14
+
maxAge = 1000 * 60 * 60 * 24, // 24 hours
15
+
}: QueryPersistOptions) => {
16
+
let storage: QueryPersistenceSchema = {};
17
+
18
+
const write = () => {
19
+
localStorage.setItem(name, JSON.stringify(storage));
20
+
};
21
+
22
+
const read = (raw: string | null) => {
23
+
storage = JSON.parse(raw ?? '{}') as any;
24
+
25
+
let now = Date.now();
26
+
let written = false;
27
+
28
+
for (const queryHash in storage) {
29
+
const persisted = storage[queryHash];
30
+
const persistedKey = persisted.key;
31
+
32
+
const age = now - persisted.dataUpdatedAt;
33
+
34
+
const expired = age > maxAge;
35
+
const mismatch = persistedKey !== key;
36
+
37
+
if (expired || mismatch) {
38
+
delete storage[queryHash];
39
+
written = true;
40
+
}
41
+
}
42
+
43
+
if (written) {
44
+
write();
45
+
}
46
+
};
47
+
48
+
const persist = async <T, TQueryKey extends QueryKey>(
49
+
queryFn: (context: QueryFunctionContext<TQueryKey>) => T | Promise<T>,
50
+
context: QueryFunctionContext<TQueryKey>,
51
+
query: Query,
52
+
) => {
53
+
const hash = query.queryHash;
54
+
55
+
{
56
+
const persisted = storage[hash];
57
+
58
+
if (persisted) {
59
+
const persistedKey = persisted.key;
60
+
const dataUpdatedAt = persisted.dataUpdatedAt;
61
+
62
+
const age = Date.now() - dataUpdatedAt;
63
+
64
+
const expired = age > maxAge;
65
+
const mismatch = persistedKey !== key;
66
+
67
+
if (expired || mismatch) {
68
+
delete storage[hash];
69
+
write();
70
+
} else if (query.state.data === undefined || dataUpdatedAt > query.state.dataUpdatedAt) {
71
+
// Just after restoring we want to get fresh data from the server if it's stale
72
+
setTimeout(() => {
73
+
// Set proper updatedAt, since resolving in the first pass overrides those values
74
+
query.setState({ dataUpdatedAt: dataUpdatedAt });
75
+
76
+
if (query.isStale()) {
77
+
query.fetch();
78
+
}
79
+
}, 0);
80
+
81
+
return persisted.data as T;
82
+
}
83
+
}
84
+
}
85
+
86
+
const result = await queryFn(context);
87
+
88
+
{
89
+
setTimeout(() => {
90
+
const state = query.state;
91
+
92
+
storage[hash] = {
93
+
key: key,
94
+
data: state.data,
95
+
dataUpdatedAt: state.dataUpdatedAt,
96
+
};
97
+
98
+
write();
99
+
}, 0);
100
+
}
101
+
102
+
return result;
103
+
};
104
+
105
+
read(localStorage.getItem(name));
106
+
107
+
createEventListener(window, 'storage', (ev) => {
108
+
if (ev.key === name) {
109
+
read(ev.newValue);
110
+
}
111
+
});
112
+
113
+
return persist;
114
+
};
115
+
116
+
interface QueryPersistenceSchema {
117
+
[queryHash: string]: {
118
+
key: string;
119
+
data: unknown;
120
+
dataUpdatedAt: number;
121
+
};
122
+
}
+83
src/main.tsx
+83
src/main.tsx
···
1
+
/* @refresh reload */
2
+
3
+
import './styles/app.css';
4
+
5
+
import { createSignal, onMount, type JSX } from 'solid-js';
6
+
import { render } from 'solid-js/web';
7
+
8
+
import * as navigation from './globals/navigation';
9
+
import * as preferences from './globals/preferences';
10
+
11
+
import { memoizedOn } from './lib/misc';
12
+
import { configureRouter } from './lib/navigation/router';
13
+
14
+
import type { AccountData } from './lib/preferences/sessions';
15
+
16
+
import { AgentProvider } from './lib/states/agent';
17
+
import { ModerationProvider } from './lib/states/moderation';
18
+
import { SessionProvider, useSession } from './lib/states/session';
19
+
import { ThemeProvider } from './lib/states/theme';
20
+
21
+
import ModalRenderer from './components/main/modal-renderer';
22
+
import routes from './routes';
23
+
import Shell from './shell';
24
+
25
+
// Configure routing
26
+
configureRouter({
27
+
history: navigation.history,
28
+
logger: navigation.logger,
29
+
routes: routes,
30
+
});
31
+
32
+
const InnerApp = () => {
33
+
const [ready, setReady] = createSignal(false);
34
+
const session = useSession();
35
+
36
+
onMount(() => {
37
+
const resumeAccount = async (account: AccountData | undefined) => {
38
+
try {
39
+
if (account) {
40
+
await session.resumeSession(account);
41
+
}
42
+
} finally {
43
+
setReady(true);
44
+
}
45
+
};
46
+
47
+
{
48
+
const { active, accounts } = preferences.sessions;
49
+
const account = active && accounts.find((acc) => acc.did === active);
50
+
51
+
resumeAccount(account);
52
+
}
53
+
});
54
+
55
+
return memoizedOn(ready, ($ready) => {
56
+
if (!$ready) {
57
+
return;
58
+
}
59
+
60
+
return (
61
+
<AgentProvider>
62
+
{/* Anything under <AgentProvider> gets remounted on account changes */}
63
+
<ModerationProvider>
64
+
<Shell />
65
+
<ModalRenderer />
66
+
</ModerationProvider>
67
+
</AgentProvider>
68
+
);
69
+
}) as unknown as JSX.Element;
70
+
};
71
+
72
+
const App = () => {
73
+
return (
74
+
<ThemeProvider>
75
+
<SessionProvider>
76
+
<InnerApp />
77
+
</SessionProvider>
78
+
</ThemeProvider>
79
+
);
80
+
};
81
+
82
+
// Render the app
83
+
render(App, document.body);
+57
src/routes.ts
+57
src/routes.ts
···
1
+
import { lazy } from 'solid-js';
2
+
import type { RouteDefinition } from './lib/navigation/router';
3
+
4
+
const routes: RouteDefinition[] = [
5
+
{
6
+
path: '/',
7
+
component: lazy(() => import('./views/main/home')),
8
+
meta: {
9
+
name: 'Home',
10
+
main: true,
11
+
// public: true,
12
+
},
13
+
},
14
+
{
15
+
path: '/search',
16
+
component: lazy(() => import('./views/main/search')),
17
+
meta: {
18
+
name: 'Search',
19
+
main: true,
20
+
// public: true,
21
+
},
22
+
},
23
+
{
24
+
path: '/notifications',
25
+
component: lazy(() => import('./views/main/notifications')),
26
+
meta: {
27
+
name: 'Notifications',
28
+
main: true,
29
+
},
30
+
},
31
+
{
32
+
path: '/messages',
33
+
component: lazy(() => import('./views/main/messages')),
34
+
meta: {
35
+
name: 'Messages',
36
+
main: true,
37
+
},
38
+
},
39
+
{
40
+
path: '/feeds',
41
+
component: lazy(() => import('./views/main/feeds')),
42
+
meta: {
43
+
name: 'Feeds',
44
+
main: true,
45
+
},
46
+
},
47
+
48
+
{
49
+
path: '*',
50
+
component: lazy(() => import('./views/not-found')),
51
+
meta: {
52
+
public: true,
53
+
},
54
+
},
55
+
];
56
+
57
+
export default routes;
+169
src/shell.tsx
+169
src/shell.tsx
···
1
+
import { Suspense, lazy, type Accessor, type Component, type ComponentProps } from 'solid-js';
2
+
3
+
import { globalEvents } from './globals/events';
4
+
import { hasModals } from './globals/modals';
5
+
import { history } from './globals/navigation';
6
+
7
+
import { RouterView, useMatchedRoute, type MatchedRouteState } from './lib/navigation/router';
8
+
import { useSession } from './lib/states/session';
9
+
10
+
import BellOutlinedIcon from './components/icons-central/bell-outline';
11
+
import BellSolidIcon from './components/icons-central/bell-solid';
12
+
import HashtagOutlinedIcon from './components/icons-central/hashtag-outline';
13
+
import HomeOutlinedIcon from './components/icons-central/home-outline';
14
+
import HomeSolidIcon from './components/icons-central/home-solid';
15
+
import MagnifyingGlassOutlinedIcon from './components/icons-central/magnifying-glass-outline';
16
+
import MailOutlinedIcon from './components/icons-central/mail-outline';
17
+
import MailSolidIcon from './components/icons-central/mail-solid';
18
+
19
+
const SignedOutView = lazy(() => import('./views/_signed-out'));
20
+
21
+
const Shell = () => {
22
+
const { currentAccount } = useSession();
23
+
24
+
// Will always match because we've set a 404 handler.
25
+
const route = useMatchedRoute() as Accessor<MatchedRouteState>;
26
+
27
+
return (
28
+
<div
29
+
inert={hasModals()}
30
+
class="relative z-0 mx-auto flex min-h-screen max-w-md flex-col-reverse border-c-contrast-200 sm:border-x"
31
+
>
32
+
{!!(currentAccount && route().def.meta?.main) && <NavBar route={route} />}
33
+
34
+
<div class="z-0 flex min-h-0 grow flex-col overflow-clip">
35
+
<RouterView
36
+
render={({ def }) => {
37
+
return (
38
+
<Suspense
39
+
children={(() => {
40
+
if (!currentAccount && !def.meta?.public) {
41
+
return <SignedOutView />;
42
+
}
43
+
44
+
return <def.component />;
45
+
})()}
46
+
/>
47
+
);
48
+
}}
49
+
/>
50
+
</div>
51
+
</div>
52
+
);
53
+
};
54
+
55
+
export default Shell;
56
+
57
+
const enum MainTabs {
58
+
HOME = 'Home',
59
+
SEARCH = 'Search',
60
+
NOTIFICATIONS = 'Notifications',
61
+
MESSAGES = 'Messages',
62
+
FEEDS = 'Feeds',
63
+
}
64
+
65
+
const MainTabsRoutes = {
66
+
[MainTabs.HOME]: '/',
67
+
[MainTabs.SEARCH]: '/search',
68
+
[MainTabs.NOTIFICATIONS]: '/notifications',
69
+
[MainTabs.MESSAGES]: '/messages',
70
+
[MainTabs.FEEDS]: '/feeds',
71
+
};
72
+
73
+
const NavBar = ({ route }: { route: Accessor<MatchedRouteState> }) => {
74
+
const active = () => route().def.meta?.name;
75
+
76
+
const bindClick = (to: MainTabs) => {
77
+
return () => {
78
+
const from = active();
79
+
80
+
if (from === to) {
81
+
window.scrollTo({ top: 0, behavior: 'smooth' });
82
+
globalEvents.emit('softreset');
83
+
return;
84
+
}
85
+
86
+
const fromHome = !!(history.location.state as any)?.fromHome;
87
+
const href = MainTabsRoutes[to];
88
+
89
+
if (to === MainTabs.HOME && fromHome) {
90
+
history.back();
91
+
return;
92
+
}
93
+
94
+
history.navigate(href, {
95
+
replace: from !== MainTabs.HOME,
96
+
state: {
97
+
// inherit `fromHome` state
98
+
fromHome: fromHome || from === MainTabs.HOME,
99
+
},
100
+
});
101
+
};
102
+
};
103
+
104
+
return (
105
+
<>
106
+
<div class="sticky bottom-0 z-1 flex h-13 w-full max-w-md shrink-0 items-stretch border-t border-c-contrast-200 bg-c-contrast-0">
107
+
<NavItem
108
+
label="Home"
109
+
active={active() === MainTabs.HOME}
110
+
onClick={bindClick(MainTabs.HOME)}
111
+
icon={HomeOutlinedIcon}
112
+
iconActive={HomeSolidIcon}
113
+
/>
114
+
<NavItem
115
+
label="Search"
116
+
active={active() === MainTabs.SEARCH}
117
+
onClick={bindClick(MainTabs.SEARCH)}
118
+
icon={MagnifyingGlassOutlinedIcon}
119
+
/>
120
+
<NavItem
121
+
label="Notifications"
122
+
active={active() === MainTabs.NOTIFICATIONS}
123
+
onClick={bindClick(MainTabs.NOTIFICATIONS)}
124
+
icon={BellOutlinedIcon}
125
+
iconActive={BellSolidIcon}
126
+
/>
127
+
<NavItem
128
+
label="Direct Messages"
129
+
active={active() === MainTabs.MESSAGES}
130
+
onClick={bindClick(MainTabs.MESSAGES)}
131
+
icon={MailOutlinedIcon}
132
+
iconActive={MailSolidIcon}
133
+
/>
134
+
<NavItem
135
+
label="Feeds"
136
+
active={active() === MainTabs.FEEDS}
137
+
onClick={bindClick(MainTabs.FEEDS)}
138
+
icon={HashtagOutlinedIcon}
139
+
/>
140
+
</div>
141
+
</>
142
+
);
143
+
};
144
+
145
+
type IconComponent = Component<ComponentProps<'svg'>>;
146
+
147
+
interface NavItemProps {
148
+
active?: boolean;
149
+
label: string;
150
+
icon: IconComponent;
151
+
iconActive?: IconComponent;
152
+
onClick?: () => void;
153
+
}
154
+
155
+
const NavItem = (props: NavItemProps) => {
156
+
const InactiveIcon = props.icon;
157
+
const ActiveIcon = props.iconActive;
158
+
159
+
return (
160
+
<button title={props.label} onClick={props.onClick} class="grid grow basis-0 place-items-center">
161
+
{(() => {
162
+
const active = props.active;
163
+
164
+
const Icon = active && ActiveIcon ? ActiveIcon : InactiveIcon;
165
+
return <Icon class={`text-2xl` + (active && !ActiveIcon ? ` stroke-3 stroke-c-contrast-900` : ``)} />;
166
+
})()}
167
+
</button>
168
+
);
169
+
};
+214
src/styles/app.css
+214
src/styles/app.css
···
1
+
@tailwind base;
2
+
@tailwind components;
3
+
@tailwind utilities;
4
+
5
+
@layer base {
6
+
body {
7
+
@apply overflow-y-scroll bg-c-contrast-0 text-c-contrast-900;
8
+
}
9
+
10
+
body:has(div[data-modal]) {
11
+
@apply overflow-hidden pr-[--sb-width];
12
+
}
13
+
14
+
::selection {
15
+
@apply bg-c-primary-600 text-c-white;
16
+
}
17
+
18
+
.a-dialog-desktop {
19
+
max-height: min(100vh - 88px, 652px);
20
+
}
21
+
}
22
+
23
+
@layer base {
24
+
:root {
25
+
--t-black: 0 0% 0%;
26
+
27
+
--t-gray-0: 0 0% 100%;
28
+
--t-gray-25: 0 0% 95.3%;
29
+
--t-gray-50: 0 0% 90.6%;
30
+
--t-gray-100: 0 0% 85.9%;
31
+
--t-gray-200: 0 0% 81.2%;
32
+
--t-gray-300: 0 0% 71.8%;
33
+
--t-gray-400: 0 0% 62.4%;
34
+
--t-gray-500: 0 0% 53%;
35
+
--t-gray-600: 0 0% 43.6%;
36
+
--t-gray-700: 0 0% 34.2%;
37
+
--t-gray-800: 0 0% 24.8%;
38
+
--t-gray-900: 0 0% 20.1%;
39
+
--t-gray-950: 0 0% 15.4%;
40
+
--t-gray-975: 0 0% 10.7%;
41
+
--t-gray-1000: 0 0% 6%;
42
+
43
+
--t-blue-25: 211 99% 97%;
44
+
--t-blue-50: 211 99% 95%;
45
+
--t-blue-100: 211 99% 90%;
46
+
--t-blue-200: 211 99% 80%;
47
+
--t-blue-300: 211 99% 70%;
48
+
--t-blue-400: 211 99% 60%;
49
+
--t-blue-500: 211 99% 53%;
50
+
--t-blue-600: 211 99% 42%;
51
+
--t-blue-700: 211 99% 34%;
52
+
--t-blue-800: 211 99% 26%;
53
+
--t-blue-900: 211 99% 18%;
54
+
--t-blue-950: 211 99% 10%;
55
+
--t-blue-975: 211 99% 7%;
56
+
--t-blue-low: 214.99 13% 44%;
57
+
58
+
--t-green-25: 152 82% 97%;
59
+
--t-green-50: 152 82% 95%;
60
+
--t-green-100: 152 82% 90%;
61
+
--t-green-200: 152 82% 80%;
62
+
--t-green-300: 152 82% 70%;
63
+
--t-green-400: 152 82% 60%;
64
+
--t-green-500: 152 82% 50%;
65
+
--t-green-600: 152 82% 42%;
66
+
--t-green-700: 152 82% 34%;
67
+
--t-green-800: 152 82% 26%;
68
+
--t-green-900: 152 82% 18%;
69
+
--t-green-950: 152 82% 10%;
70
+
--t-green-975: 152 82% 7%;
71
+
72
+
--t-red-25: 346 91% 97%;
73
+
--t-red-50: 346 91% 95%;
74
+
--t-red-100: 346 91% 90%;
75
+
--t-red-200: 346 91% 80%;
76
+
--t-red-300: 346 91% 70%;
77
+
--t-red-400: 346 91% 60%;
78
+
--t-red-500: 346 91% 50%;
79
+
--t-red-600: 346 91% 42%;
80
+
--t-red-700: 346 91% 34%;
81
+
--t-red-800: 346 91% 26%;
82
+
--t-red-900: 346 91% 18%;
83
+
--t-red-950: 346 91% 10%;
84
+
--t-red-975: 346 91% 7%;
85
+
}
86
+
87
+
.theme-light {
88
+
color-scheme: light;
89
+
90
+
--c-white: var(--t-gray-0);
91
+
--c-black: var(--t-gray-1000);
92
+
93
+
--c-contrast-0: var(--t-gray-0);
94
+
--c-contrast-25: var(--t-gray-25);
95
+
--c-contrast-50: var(--t-gray-50);
96
+
--c-contrast-100: var(--t-gray-100);
97
+
--c-contrast-200: var(--t-gray-200);
98
+
--c-contrast-300: var(--t-gray-300);
99
+
--c-contrast-400: var(--t-gray-400);
100
+
--c-contrast-500: var(--t-gray-500);
101
+
--c-contrast-600: var(--t-gray-600);
102
+
--c-contrast-700: var(--t-gray-700);
103
+
--c-contrast-800: var(--t-gray-800);
104
+
--c-contrast-900: var(--t-gray-900);
105
+
--c-contrast-950: var(--t-gray-950);
106
+
--c-contrast-975: var(--t-gray-975);
107
+
108
+
--c-primary-25: var(--t-blue-25);
109
+
--c-primary-50: var(--t-blue-50);
110
+
--c-primary-100: var(--t-blue-100);
111
+
--c-primary-200: var(--t-blue-200);
112
+
--c-primary-300: var(--t-blue-300);
113
+
--c-primary-400: var(--t-blue-400);
114
+
--c-primary-500: var(--t-blue-500);
115
+
--c-primary-600: var(--t-blue-600);
116
+
--c-primary-700: var(--t-blue-700);
117
+
--c-primary-800: var(--t-blue-800);
118
+
--c-primary-900: var(--t-blue-900);
119
+
--c-primary-950: var(--t-blue-950);
120
+
--c-primary-975: var(--t-blue-975);
121
+
122
+
--c-positive-25: var(--t-green-25);
123
+
--c-positive-50: var(--t-green-50);
124
+
--c-positive-100: var(--t-green-100);
125
+
--c-positive-200: var(--t-green-200);
126
+
--c-positive-300: var(--t-green-300);
127
+
--c-positive-400: var(--t-green-400);
128
+
--c-positive-500: var(--t-green-500);
129
+
--c-positive-600: var(--t-green-600);
130
+
--c-positive-700: var(--t-green-700);
131
+
--c-positive-800: var(--t-green-800);
132
+
--c-positive-900: var(--t-green-900);
133
+
--c-positive-950: var(--t-green-950);
134
+
--c-positive-975: var(--t-green-975);
135
+
136
+
--c-negative-25: var(--t-red-25);
137
+
--c-negative-50: var(--t-red-50);
138
+
--c-negative-100: var(--t-red-100);
139
+
--c-negative-200: var(--t-red-200);
140
+
--c-negative-300: var(--t-red-300);
141
+
--c-negative-400: var(--t-red-400);
142
+
--c-negative-500: var(--t-red-500);
143
+
--c-negative-600: var(--t-red-600);
144
+
--c-negative-700: var(--t-red-700);
145
+
--c-negative-800: var(--t-red-800);
146
+
--c-negative-900: var(--t-red-900);
147
+
--c-negative-950: var(--t-red-950);
148
+
--c-negative-975: var(--t-red-975);
149
+
}
150
+
151
+
.theme-dark {
152
+
color-scheme: dark;
153
+
154
+
--c-white: var(--t-gray-0);
155
+
--c-black: var(--t-black);
156
+
157
+
--c-contrast-0: var(--t-black);
158
+
--c-contrast-25: var(--t-gray-1000);
159
+
--c-contrast-50: var(--t-gray-975);
160
+
--c-contrast-100: var(--t-gray-950);
161
+
--c-contrast-200: var(--t-gray-900);
162
+
--c-contrast-300: var(--t-gray-800);
163
+
--c-contrast-400: var(--t-gray-700);
164
+
--c-contrast-500: var(--t-gray-600);
165
+
--c-contrast-600: var(--t-gray-500);
166
+
--c-contrast-700: var(--t-gray-400);
167
+
--c-contrast-800: var(--t-gray-300);
168
+
--c-contrast-900: var(--t-gray-200);
169
+
--c-contrast-950: var(--t-gray-100);
170
+
--c-contrast-975: var(--t-gray-50);
171
+
172
+
--c-primary-25: var(--t-blue-25);
173
+
--c-primary-50: var(--t-blue-50);
174
+
--c-primary-100: var(--t-blue-100);
175
+
--c-primary-200: var(--t-blue-200);
176
+
--c-primary-300: var(--t-blue-300);
177
+
--c-primary-400: var(--t-blue-400);
178
+
--c-primary-500: var(--t-blue-500);
179
+
--c-primary-600: var(--t-blue-600);
180
+
--c-primary-700: var(--t-blue-700);
181
+
--c-primary-800: var(--t-blue-800);
182
+
--c-primary-900: var(--t-blue-900);
183
+
--c-primary-950: var(--t-blue-950);
184
+
--c-primary-975: var(--t-blue-975);
185
+
186
+
--c-positive-25: var(--t-green-25);
187
+
--c-positive-50: var(--t-green-50);
188
+
--c-positive-100: var(--t-green-100);
189
+
--c-positive-200: var(--t-green-200);
190
+
--c-positive-300: var(--t-green-300);
191
+
--c-positive-400: var(--t-green-400);
192
+
--c-positive-500: var(--t-green-500);
193
+
--c-positive-600: var(--t-green-600);
194
+
--c-positive-700: var(--t-green-700);
195
+
--c-positive-800: var(--t-green-800);
196
+
--c-positive-900: var(--t-green-900);
197
+
--c-positive-950: var(--t-green-950);
198
+
--c-positive-975: var(--t-green-975);
199
+
200
+
--c-negative-25: var(--t-red-25);
201
+
--c-negative-50: var(--t-red-50);
202
+
--c-negative-100: var(--t-red-100);
203
+
--c-negative-200: var(--t-red-200);
204
+
--c-negative-300: var(--t-red-300);
205
+
--c-negative-400: var(--t-red-400);
206
+
--c-negative-500: var(--t-red-500);
207
+
--c-negative-600: var(--t-red-600);
208
+
--c-negative-700: var(--t-red-700);
209
+
--c-negative-800: var(--t-red-800);
210
+
--c-negative-900: var(--t-red-900);
211
+
--c-negative-950: var(--t-red-950);
212
+
--c-negative-975: var(--t-red-975);
213
+
}
214
+
}
+31
src/views/_signed-out/index.tsx
+31
src/views/_signed-out/index.tsx
···
1
+
import Button from '~/components/button';
2
+
import { openModal } from '~/globals/modals';
3
+
4
+
import SignInDialogLazy from '~/components/main/sign-in-dialog-lazy';
5
+
6
+
const SignedOutPage = () => {
7
+
return (
8
+
<>
9
+
<div class="flex grow flex-col items-center justify-center gap-1">
10
+
<p class="text-lg">here's where i would've put a logo</p>
11
+
<p class="text-2xl font-medium">IF I HAD ONE</p>
12
+
</div>
13
+
<div class="flex shrink-0 flex-col gap-4 p-6">
14
+
<Button
15
+
onClick={() => {
16
+
openModal(() => <SignInDialogLazy />);
17
+
}}
18
+
variant="primary"
19
+
size="md"
20
+
>
21
+
Sign in
22
+
</Button>
23
+
<Button size="md" disabled>
24
+
Create account
25
+
</Button>
26
+
</div>
27
+
</>
28
+
);
29
+
};
30
+
31
+
export default SignedOutPage;
+5
src/views/main/feeds.tsx
+5
src/views/main/feeds.tsx
+63
src/views/main/home.tsx
+63
src/views/main/home.tsx
···
1
+
import { useTimelineQuery } from '~/api/queries/timeline';
2
+
import PostFeedItem from '~/components/feeds/post-feed-item';
3
+
4
+
import IconButton from '~/components/icon-button';
5
+
import ChevronRightOutlinedIcon from '~/components/icons-central/chevron-right-outline';
6
+
import GearOutlinedIcon from '~/components/icons-central/gear-outline';
7
+
import List from '~/components/list';
8
+
import * as Page from '~/components/page';
9
+
import VirtualItem from '~/components/virtual-item';
10
+
11
+
const HomePage = () => {
12
+
const { timeline, isStale, reset } = useTimelineQuery(() => {
13
+
return {
14
+
type: 'following',
15
+
showQuotes: true,
16
+
showReplies: 'follows',
17
+
showReposts: true,
18
+
};
19
+
});
20
+
21
+
return (
22
+
<>
23
+
<Page.Header>
24
+
<Page.HeaderAccessory>
25
+
<Page.MainMenu />
26
+
</Page.HeaderAccessory>
27
+
28
+
<div class="flex min-w-0 grow">
29
+
<button class="-mx-2 flex items-center gap-1 overflow-hidden rounded px-2 py-1 hover:bg-c-contrast-50">
30
+
<span class="overflow-hidden text-ellipsis whitespace-nowrap text-base font-bold">
31
+
{'Following'}
32
+
</span>
33
+
<ChevronRightOutlinedIcon class="-mr-1 shrink-0 rotate-90 text-lg text-c-contrast-600" />
34
+
</button>
35
+
</div>
36
+
37
+
<Page.HeaderAccessory>
38
+
<IconButton title="Home settings" icon={GearOutlinedIcon} />
39
+
</Page.HeaderAccessory>
40
+
</Page.Header>
41
+
42
+
<List
43
+
data={timeline.data?.pages.flatMap((page) => page.items)}
44
+
error={timeline.error}
45
+
render={(item) => {
46
+
return (
47
+
<VirtualItem estimateHeight={99}>
48
+
<PostFeedItem item={item} />
49
+
</VirtualItem>
50
+
);
51
+
}}
52
+
hasNewData={isStale()}
53
+
hasNextPage={timeline.hasNextPage}
54
+
isFetchingNextPage={timeline.isFetchingNextPage || timeline.isLoading}
55
+
isRefreshing={timeline.isRefetching}
56
+
onEndReached={() => timeline.fetchNextPage()}
57
+
onRefresh={reset}
58
+
/>
59
+
</>
60
+
);
61
+
};
62
+
63
+
export default HomePage;
+5
src/views/main/messages.tsx
+5
src/views/main/messages.tsx
+17
src/views/main/notifications.tsx
+17
src/views/main/notifications.tsx
···
1
+
import * as Page from '~/components/page';
2
+
3
+
const NotificationsPage = () => {
4
+
return (
5
+
<>
6
+
<Page.Header>
7
+
<Page.HeaderAccessory>
8
+
<Page.MainMenu />
9
+
</Page.HeaderAccessory>
10
+
11
+
<Page.Heading title="Notifications" />
12
+
</Page.Header>
13
+
</>
14
+
);
15
+
};
16
+
17
+
export default NotificationsPage;
+5
src/views/main/search.tsx
+5
src/views/main/search.tsx
+33
src/views/not-found.tsx
+33
src/views/not-found.tsx
···
1
+
import Button from '~/components/button';
2
+
import { history, logger } from '~/globals/navigation';
3
+
4
+
const NotFoundPage = () => {
5
+
return (
6
+
<>
7
+
<div class="p-4">
8
+
<h2 class="text-lg font-bold">Page not found</h2>
9
+
<p class="mb-4 text-sm">
10
+
We're sorry, but the link you followed might be broken, or the page may have been removed.
11
+
</p>
12
+
13
+
<div class="mb-4">
14
+
<Button
15
+
onClick={() => {
16
+
if (logger.canGoBack) {
17
+
history.back();
18
+
} else {
19
+
history.navigate('/', { replace: true });
20
+
}
21
+
}}
22
+
size="sm"
23
+
variant="primary"
24
+
>
25
+
Go back
26
+
</Button>
27
+
</div>
28
+
</div>
29
+
</>
30
+
);
31
+
};
32
+
33
+
export default NotFoundPage;
+2
src/vite-env.d.ts
+2
src/vite-env.d.ts
+232
tailwind.config.js
+232
tailwind.config.js
···
1
+
import plugin from 'tailwindcss/plugin';
2
+
3
+
/** @type {import('tailwindcss').Config} */
4
+
export default {
5
+
content: ['./src/**/*.tsx'],
6
+
theme: {
7
+
extend: {
8
+
fontSize: {
9
+
de: ['0.8125rem', '1.25rem'],
10
+
},
11
+
zIndex: {
12
+
1: '1',
13
+
2: '2',
14
+
},
15
+
spacing: {
16
+
0.75: '0.1875rem',
17
+
7.5: '1.875rem',
18
+
9.5: '2.375rem',
19
+
13: '3.25rem',
20
+
17: '4.24rem',
21
+
22: '5.5rem',
22
+
30: '7.5rem',
23
+
84: '21rem',
24
+
120: '30rem',
25
+
},
26
+
borderWidth: {
27
+
3: '3px',
28
+
},
29
+
minWidth: {
30
+
14: '3.5rem',
31
+
16: '4rem',
32
+
},
33
+
minHeight: {
34
+
16: '4rem',
35
+
},
36
+
maxHeight: {
37
+
141: '35.25rem',
38
+
'50vh': '50vh',
39
+
},
40
+
flexGrow: {
41
+
2: '2',
42
+
4: '4',
43
+
},
44
+
aspectRatio: {
45
+
banner: '3 / 1',
46
+
},
47
+
keyframes: {
48
+
indeterminate: {
49
+
'0%': {
50
+
translate: '-100%',
51
+
},
52
+
'100%': {
53
+
translate: '400%',
54
+
},
55
+
},
56
+
},
57
+
animation: {
58
+
indeterminate: 'indeterminate 1s linear infinite',
59
+
},
60
+
boxShadow: {
61
+
menu: 'rgba(var(--primary) / 0.2) 0px 0px 15px, rgba(var(--primary) / 0.15) 0px 0px 3px 1px',
62
+
},
63
+
dropShadow: {
64
+
DEFAULT: ['0 1px 2px rgb(0 0 0 / .3)', '0 1px 1px rgb(0 0 0 / .1)'],
65
+
},
66
+
},
67
+
fontFamily: {
68
+
sans: `"Roboto", ui-sans-serif, sans-serif, "Noto Color Emoji", "Twemoji Mozilla"`,
69
+
mono: `"JetBrains Mono NL", ui-monospace, monospace`,
70
+
},
71
+
colors: {
72
+
transparent: 'transparent',
73
+
c: {
74
+
white: 'hsl(var(--c-white))',
75
+
black: 'hsl(var(--c-black))',
76
+
contrast: {
77
+
0: 'hsl(var(--c-contrast-0))',
78
+
25: 'hsl(var(--c-contrast-25))',
79
+
50: 'hsl(var(--c-contrast-50))',
80
+
100: 'hsl(var(--c-contrast-100))',
81
+
200: 'hsl(var(--c-contrast-200))',
82
+
300: 'hsl(var(--c-contrast-300))',
83
+
400: 'hsl(var(--c-contrast-400))',
84
+
500: 'hsl(var(--c-contrast-500))',
85
+
600: 'hsl(var(--c-contrast-600))',
86
+
700: 'hsl(var(--c-contrast-700))',
87
+
800: 'hsl(var(--c-contrast-800))',
88
+
900: 'hsl(var(--c-contrast-900))',
89
+
950: 'hsl(var(--c-contrast-950))',
90
+
975: 'hsl(var(--c-contrast-975))',
91
+
},
92
+
primary: {
93
+
25: 'hsl(var(--c-primary-25))',
94
+
50: 'hsl(var(--c-primary-50))',
95
+
100: 'hsl(var(--c-primary-100))',
96
+
200: 'hsl(var(--c-primary-200))',
97
+
300: 'hsl(var(--c-primary-300))',
98
+
400: 'hsl(var(--c-primary-400))',
99
+
500: 'hsl(var(--c-primary-500))',
100
+
600: 'hsl(var(--c-primary-600))',
101
+
700: 'hsl(var(--c-primary-700))',
102
+
800: 'hsl(var(--c-primary-800))',
103
+
900: 'hsl(var(--c-primary-900))',
104
+
950: 'hsl(var(--c-primary-950))',
105
+
975: 'hsl(var(--c-primary-975))',
106
+
},
107
+
positive: {
108
+
25: 'hsl(var(--c-positive-25))',
109
+
50: 'hsl(var(--c-positive-50))',
110
+
100: 'hsl(var(--c-positive-100))',
111
+
200: 'hsl(var(--c-positive-200))',
112
+
300: 'hsl(var(--c-positive-300))',
113
+
400: 'hsl(var(--c-positive-400))',
114
+
500: 'hsl(var(--c-positive-500))',
115
+
600: 'hsl(var(--c-positive-600))',
116
+
700: 'hsl(var(--c-positive-700))',
117
+
800: 'hsl(var(--c-positive-800))',
118
+
900: 'hsl(var(--c-positive-900))',
119
+
950: 'hsl(var(--c-positive-950))',
120
+
975: 'hsl(var(--c-positive-975))',
121
+
},
122
+
negative: {
123
+
25: 'hsl(var(--c-negative-25))',
124
+
50: 'hsl(var(--c-negative-50))',
125
+
100: 'hsl(var(--c-negative-100))',
126
+
200: 'hsl(var(--c-negative-200))',
127
+
300: 'hsl(var(--c-negative-300))',
128
+
400: 'hsl(var(--c-negative-400))',
129
+
500: 'hsl(var(--c-negative-500))',
130
+
600: 'hsl(var(--c-negative-600))',
131
+
700: 'hsl(var(--c-negative-700))',
132
+
800: 'hsl(var(--c-negative-800))',
133
+
900: 'hsl(var(--c-negative-900))',
134
+
950: 'hsl(var(--c-negative-950))',
135
+
975: 'hsl(var(--c-negative-975))',
136
+
},
137
+
},
138
+
t: {
139
+
black: 'hsl(var(--t-black))',
140
+
gray: {
141
+
0: 'hsl(var(--t-gray-0))',
142
+
25: 'hsl(var(--t-gray-25))',
143
+
50: 'hsl(var(--t-gray-50))',
144
+
100: 'hsl(var(--t-gray-100))',
145
+
200: 'hsl(var(--t-gray-200))',
146
+
300: 'hsl(var(--t-gray-300))',
147
+
400: 'hsl(var(--t-gray-400))',
148
+
500: 'hsl(var(--t-gray-500))',
149
+
600: 'hsl(var(--t-gray-600))',
150
+
700: 'hsl(var(--t-gray-700))',
151
+
800: 'hsl(var(--t-gray-800))',
152
+
900: 'hsl(var(--t-gray-900))',
153
+
950: 'hsl(var(--t-gray-950))',
154
+
975: 'hsl(var(--t-gray-975))',
155
+
1000: 'hsl(var(--t-gray-1000))',
156
+
},
157
+
blue: {
158
+
25: 'hsl(var(--t-blue-25))',
159
+
50: 'hsl(var(--t-blue-50))',
160
+
100: 'hsl(var(--t-blue-100))',
161
+
200: 'hsl(var(--t-blue-200))',
162
+
300: 'hsl(var(--t-blue-300))',
163
+
400: 'hsl(var(--t-blue-400))',
164
+
500: 'hsl(var(--t-blue-500))',
165
+
600: 'hsl(var(--t-blue-600))',
166
+
700: 'hsl(var(--t-blue-700))',
167
+
800: 'hsl(var(--t-blue-800))',
168
+
900: 'hsl(var(--t-blue-900))',
169
+
950: 'hsl(var(--t-blue-950))',
170
+
975: 'hsl(var(--t-blue-975))',
171
+
low: 'hsl(var(--t-blue-low))',
172
+
},
173
+
green: {
174
+
25: 'hsl(var(--t-green-25))',
175
+
50: 'hsl(var(--t-green-50))',
176
+
100: 'hsl(var(--t-green-100))',
177
+
200: 'hsl(var(--t-green-200))',
178
+
300: 'hsl(var(--t-green-300))',
179
+
400: 'hsl(var(--t-green-400))',
180
+
500: 'hsl(var(--t-green-500))',
181
+
600: 'hsl(var(--t-green-600))',
182
+
700: 'hsl(var(--t-green-700))',
183
+
800: 'hsl(var(--t-green-800))',
184
+
900: 'hsl(var(--t-green-900))',
185
+
950: 'hsl(var(--t-green-950))',
186
+
975: 'hsl(var(--t-green-975))',
187
+
},
188
+
red: {
189
+
25: 'hsl(var(--t-red-25))',
190
+
50: 'hsl(var(--t-red-50))',
191
+
100: 'hsl(var(--t-red-100))',
192
+
200: 'hsl(var(--t-red-200))',
193
+
300: 'hsl(var(--t-red-300))',
194
+
400: 'hsl(var(--t-red-400))',
195
+
500: 'hsl(var(--t-red-500))',
196
+
600: 'hsl(var(--t-red-600))',
197
+
700: 'hsl(var(--t-red-700))',
198
+
800: 'hsl(var(--t-red-800))',
199
+
900: 'hsl(var(--t-red-900))',
200
+
950: 'hsl(var(--t-red-950))',
201
+
975: 'hsl(var(--t-red-975))',
202
+
},
203
+
},
204
+
},
205
+
},
206
+
corePlugins: {
207
+
outlineStyle: false,
208
+
},
209
+
plugins: [
210
+
plugin(({ addVariant, addUtilities }) => {
211
+
addVariant('modal', '&:modal');
212
+
addVariant('focus-within', '&:has(:focus-visible)');
213
+
214
+
addUtilities({
215
+
'.scrollbar-hide': {
216
+
'-ms-overflow-style': 'none',
217
+
'scrollbar-width': 'none',
218
+
219
+
'&::-webkit-scrollbar': {
220
+
display: 'none',
221
+
},
222
+
},
223
+
224
+
'.outline-none': { 'outline-style': 'none' },
225
+
'.outline': { 'outline-style': 'solid' },
226
+
'.outline-dashed': { 'outline-style': 'dashed' },
227
+
'.outline-dotted': { 'outline-style': 'dotted' },
228
+
'.outline-double': { 'outline-style': 'double' },
229
+
});
230
+
}),
231
+
],
232
+
};
+30
tsconfig.json
+30
tsconfig.json
···
1
+
{
2
+
"compilerOptions": {
3
+
"target": "ESNext",
4
+
"lib": ["DOM", "DOM.Iterable", "ESNext"],
5
+
"types": ["dom-close-watcher"],
6
+
"skipLibCheck": true,
7
+
8
+
"module": "ESNext",
9
+
"moduleResolution": "Bundler",
10
+
"allowImportingTsExtensions": true,
11
+
"resolveJsonModule": true,
12
+
"noEmit": true,
13
+
"jsx": "preserve",
14
+
"jsxImportSource": "solid-js",
15
+
16
+
"incremental": true,
17
+
"strict": true,
18
+
"verbatimModuleSyntax": true,
19
+
"noUnusedLocals": true,
20
+
"noUnusedParameters": true,
21
+
"noFallthroughCasesInSwitch": true,
22
+
23
+
"useDefineForClassFields": false,
24
+
25
+
"paths": {
26
+
"~/*": ["./src/*"],
27
+
},
28
+
},
29
+
"include": ["src"],
30
+
}
+16
vite.config.js
+16
vite.config.js
···
1
+
import * as path from 'node:path';
2
+
3
+
import { defineConfig } from 'vite';
4
+
import solid from 'vite-plugin-solid';
5
+
6
+
export default defineConfig({
7
+
plugins: [solid()],
8
+
build: {
9
+
minify: 'terser',
10
+
},
11
+
resolve: {
12
+
alias: {
13
+
'~': path.join(__dirname, './src'),
14
+
},
15
+
},
16
+
});