+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?
+131
README.md
+131
README.md
···
1
+
# Slices Teal Relay Demo
2
+
3
+
A teal.fm timeline built with React, Relay, and Tailwind CSS. Displays music
4
+
plays from the Slices GraphQL API.
5
+
6
+
## Features
7
+
8
+
- Real-time music scrobble feed
9
+
- User profiles with listening history
10
+
- Album artwork from MusicBrainz
11
+
- Infinite scroll pagination
12
+
- Dark mode UI inspired by Last.fm
13
+
- Powered by Relay for efficient GraphQL data management
14
+
15
+
## Tech Stack
16
+
17
+
- **React** - UI framework
18
+
- **Relay** - GraphQL client with automatic cache management
19
+
- **React Router** - Client-side routing
20
+
- **Tailwind CSS** - Styling
21
+
- **Vite** - Build tool
22
+
- **TypeScript** - Type safety
23
+
24
+
## Prerequisites
25
+
26
+
- Node.js 18+ or Deno
27
+
- npm or pnpm
28
+
29
+
## Getting Started
30
+
31
+
1. **Install dependencies**
32
+
33
+
```bash
34
+
npm install
35
+
```
36
+
37
+
2. **Fetch the GraphQL schema**
38
+
39
+
```bash
40
+
npm run schema
41
+
```
42
+
43
+
3. **Generate Relay types**
44
+
45
+
```bash
46
+
npx relay-compiler
47
+
```
48
+
49
+
4. **Start the development server**
50
+
51
+
```bash
52
+
npm run dev
53
+
```
54
+
55
+
The app will be available at `http://localhost:5173`
56
+
57
+
## Development
58
+
59
+
### Updating the GraphQL Schema
60
+
61
+
The project connects to the Slices API. To update the schema:
62
+
63
+
```bash
64
+
npm run schema
65
+
npx relay-compiler
66
+
```
67
+
68
+
### Project Structure
69
+
70
+
```
71
+
src/
72
+
├── App.tsx # Main feed component
73
+
├── Profile.tsx # User profile page
74
+
├── TrackItem.tsx # Individual track card component
75
+
├── LoadingFallback.tsx # Loading skeleton
76
+
├── useAlbumArt.ts # Hook to fetch album art from MusicBrainz
77
+
└── __generated__/ # Relay-generated TypeScript types
78
+
```
79
+
80
+
### Working with Relay
81
+
82
+
This project uses Relay's modern API with:
83
+
84
+
- **`useLazyLoadQuery`** - Load data on component mount
85
+
- **`usePaginationFragment`** - Infinite scroll pagination with automatic cache
86
+
updates
87
+
- **`useFragment`** - Fragment composition for data colocation
88
+
- **`@connection`** directive - Automatic list merging for pagination
89
+
90
+
After modifying any GraphQL queries or fragments, run:
91
+
92
+
```bash
93
+
npx relay-compiler
94
+
```
95
+
96
+
### Environment
97
+
98
+
The app connects to:
99
+
100
+
- **Production API**: `https://api.slices.network/graphql`
101
+
- **Slice**:
102
+
`at://did:plc:fpruhuo22xkm5o7ttr2ktxdo/network.slices.slice/3m257yljpbg2a`
103
+
104
+
## Scripts
105
+
106
+
- `npm run dev` - Start development server
107
+
- `npm run build` - Build for production
108
+
- `npm run preview` - Preview production build
109
+
- `npm run lint` - Run ESLint
110
+
- `npm run schema` - Fetch GraphQL schema from production API
111
+
112
+
## Features in Detail
113
+
114
+
### Album Artwork
115
+
116
+
The app fetches album artwork from MusicBrainz using the `releaseMbId` field.
117
+
The fetch happens asynchronously per track and displays when available.
118
+
119
+
### Infinite Scroll
120
+
121
+
Both the main feed and profile pages use Relay's `usePaginationFragment` for
122
+
efficient infinite scrolling with automatic cache management.
123
+
124
+
### Routing
125
+
126
+
- `/` - Main feed
127
+
- `/profile/:handle` - User profile page
128
+
129
+
## License
130
+
131
+
MIT
+2030
deno.lock
+2030
deno.lock
···
1
+
{
2
+
"version": "5",
3
+
"specifiers": {
4
+
"npm:@eslint/js@^9.36.0": "9.36.0",
5
+
"npm:@tailwindcss/postcss@^4.1.14": "4.1.14",
6
+
"npm:@types/node@^24.6.0": "24.6.2",
7
+
"npm:@types/react-dom@^19.1.9": "19.2.0_@types+react@19.2.0",
8
+
"npm:@types/react-relay@^18.2.1": "18.2.1",
9
+
"npm:@types/react@^19.1.16": "19.2.0",
10
+
"npm:@types/relay-runtime@^19.0.3": "19.0.3",
11
+
"npm:@vitejs/plugin-react@^5.0.4": "5.0.4_vite@7.1.8__@types+node@24.6.2__picomatch@4.0.3_@babel+core@7.28.4_@types+node@24.6.2",
12
+
"npm:autoprefixer@^10.4.21": "10.4.21_postcss@8.5.6",
13
+
"npm:babel-plugin-relay@^20.1.1": "20.1.1",
14
+
"npm:eslint-plugin-react-hooks@^5.2.0": "5.2.0_eslint@9.36.0",
15
+
"npm:eslint-plugin-react-refresh@~0.4.22": "0.4.23_eslint@9.36.0",
16
+
"npm:eslint@^9.36.0": "9.36.0",
17
+
"npm:globals@^16.4.0": "16.4.0",
18
+
"npm:graphql@^16.11.0": "16.11.0",
19
+
"npm:postcss@^8.5.6": "8.5.6",
20
+
"npm:react-dom@^19.1.1": "19.2.0_react@19.2.0",
21
+
"npm:react-relay@^20.1.1": "20.1.1_react@19.2.0",
22
+
"npm:react-router-dom@^7.9.3": "7.9.3_react@19.2.0_react-dom@19.2.0__react@19.2.0",
23
+
"npm:react@^19.1.1": "19.2.0",
24
+
"npm:relay-compiler@^20.1.1": "20.1.1",
25
+
"npm:relay-runtime@^20.1.1": "20.1.1",
26
+
"npm:tailwindcss@^4.1.14": "4.1.14",
27
+
"npm:typescript-eslint@^8.45.0": "8.45.0_eslint@9.36.0_typescript@5.9.3_@typescript-eslint+parser@8.45.0__eslint@9.36.0__typescript@5.9.3",
28
+
"npm:typescript@~5.9.3": "5.9.3",
29
+
"npm:vite@^7.1.7": "7.1.8_@types+node@24.6.2_picomatch@4.0.3"
30
+
},
31
+
"npm": {
32
+
"@alloc/quick-lru@5.2.0": {
33
+
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="
34
+
},
35
+
"@babel/code-frame@7.27.1": {
36
+
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
37
+
"dependencies": [
38
+
"@babel/helper-validator-identifier",
39
+
"js-tokens",
40
+
"picocolors"
41
+
]
42
+
},
43
+
"@babel/compat-data@7.28.4": {
44
+
"integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw=="
45
+
},
46
+
"@babel/core@7.28.4": {
47
+
"integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
48
+
"dependencies": [
49
+
"@babel/code-frame",
50
+
"@babel/generator",
51
+
"@babel/helper-compilation-targets",
52
+
"@babel/helper-module-transforms",
53
+
"@babel/helpers",
54
+
"@babel/parser",
55
+
"@babel/template",
56
+
"@babel/traverse",
57
+
"@babel/types",
58
+
"@jridgewell/remapping",
59
+
"convert-source-map",
60
+
"debug",
61
+
"gensync",
62
+
"json5",
63
+
"semver@6.3.1"
64
+
]
65
+
},
66
+
"@babel/generator@7.28.3": {
67
+
"integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
68
+
"dependencies": [
69
+
"@babel/parser",
70
+
"@babel/types",
71
+
"@jridgewell/gen-mapping",
72
+
"@jridgewell/trace-mapping",
73
+
"jsesc"
74
+
]
75
+
},
76
+
"@babel/helper-compilation-targets@7.27.2": {
77
+
"integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
78
+
"dependencies": [
79
+
"@babel/compat-data",
80
+
"@babel/helper-validator-option",
81
+
"browserslist",
82
+
"lru-cache",
83
+
"semver@6.3.1"
84
+
]
85
+
},
86
+
"@babel/helper-globals@7.28.0": {
87
+
"integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="
88
+
},
89
+
"@babel/helper-module-imports@7.27.1": {
90
+
"integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
91
+
"dependencies": [
92
+
"@babel/traverse",
93
+
"@babel/types"
94
+
]
95
+
},
96
+
"@babel/helper-module-transforms@7.28.3_@babel+core@7.28.4": {
97
+
"integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
98
+
"dependencies": [
99
+
"@babel/core",
100
+
"@babel/helper-module-imports",
101
+
"@babel/helper-validator-identifier",
102
+
"@babel/traverse"
103
+
]
104
+
},
105
+
"@babel/helper-plugin-utils@7.27.1": {
106
+
"integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="
107
+
},
108
+
"@babel/helper-string-parser@7.27.1": {
109
+
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="
110
+
},
111
+
"@babel/helper-validator-identifier@7.27.1": {
112
+
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="
113
+
},
114
+
"@babel/helper-validator-option@7.27.1": {
115
+
"integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="
116
+
},
117
+
"@babel/helpers@7.28.4": {
118
+
"integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
119
+
"dependencies": [
120
+
"@babel/template",
121
+
"@babel/types"
122
+
]
123
+
},
124
+
"@babel/parser@7.28.4": {
125
+
"integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==",
126
+
"dependencies": [
127
+
"@babel/types"
128
+
],
129
+
"bin": true
130
+
},
131
+
"@babel/plugin-transform-react-jsx-self@7.27.1_@babel+core@7.28.4": {
132
+
"integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
133
+
"dependencies": [
134
+
"@babel/core",
135
+
"@babel/helper-plugin-utils"
136
+
]
137
+
},
138
+
"@babel/plugin-transform-react-jsx-source@7.27.1_@babel+core@7.28.4": {
139
+
"integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
140
+
"dependencies": [
141
+
"@babel/core",
142
+
"@babel/helper-plugin-utils"
143
+
]
144
+
},
145
+
"@babel/runtime@7.28.4": {
146
+
"integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="
147
+
},
148
+
"@babel/template@7.27.2": {
149
+
"integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
150
+
"dependencies": [
151
+
"@babel/code-frame",
152
+
"@babel/parser",
153
+
"@babel/types"
154
+
]
155
+
},
156
+
"@babel/traverse@7.28.4": {
157
+
"integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==",
158
+
"dependencies": [
159
+
"@babel/code-frame",
160
+
"@babel/generator",
161
+
"@babel/helper-globals",
162
+
"@babel/parser",
163
+
"@babel/template",
164
+
"@babel/types",
165
+
"debug"
166
+
]
167
+
},
168
+
"@babel/types@7.28.4": {
169
+
"integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==",
170
+
"dependencies": [
171
+
"@babel/helper-string-parser",
172
+
"@babel/helper-validator-identifier"
173
+
]
174
+
},
175
+
"@emnapi/core@1.5.0": {
176
+
"integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==",
177
+
"dependencies": [
178
+
"@emnapi/wasi-threads",
179
+
"tslib"
180
+
]
181
+
},
182
+
"@emnapi/runtime@1.5.0": {
183
+
"integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==",
184
+
"dependencies": [
185
+
"tslib"
186
+
]
187
+
},
188
+
"@emnapi/wasi-threads@1.1.0": {
189
+
"integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==",
190
+
"dependencies": [
191
+
"tslib"
192
+
]
193
+
},
194
+
"@esbuild/aix-ppc64@0.25.10": {
195
+
"integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==",
196
+
"os": ["aix"],
197
+
"cpu": ["ppc64"]
198
+
},
199
+
"@esbuild/android-arm64@0.25.10": {
200
+
"integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==",
201
+
"os": ["android"],
202
+
"cpu": ["arm64"]
203
+
},
204
+
"@esbuild/android-arm@0.25.10": {
205
+
"integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==",
206
+
"os": ["android"],
207
+
"cpu": ["arm"]
208
+
},
209
+
"@esbuild/android-x64@0.25.10": {
210
+
"integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==",
211
+
"os": ["android"],
212
+
"cpu": ["x64"]
213
+
},
214
+
"@esbuild/darwin-arm64@0.25.10": {
215
+
"integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==",
216
+
"os": ["darwin"],
217
+
"cpu": ["arm64"]
218
+
},
219
+
"@esbuild/darwin-x64@0.25.10": {
220
+
"integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==",
221
+
"os": ["darwin"],
222
+
"cpu": ["x64"]
223
+
},
224
+
"@esbuild/freebsd-arm64@0.25.10": {
225
+
"integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==",
226
+
"os": ["freebsd"],
227
+
"cpu": ["arm64"]
228
+
},
229
+
"@esbuild/freebsd-x64@0.25.10": {
230
+
"integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==",
231
+
"os": ["freebsd"],
232
+
"cpu": ["x64"]
233
+
},
234
+
"@esbuild/linux-arm64@0.25.10": {
235
+
"integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==",
236
+
"os": ["linux"],
237
+
"cpu": ["arm64"]
238
+
},
239
+
"@esbuild/linux-arm@0.25.10": {
240
+
"integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==",
241
+
"os": ["linux"],
242
+
"cpu": ["arm"]
243
+
},
244
+
"@esbuild/linux-ia32@0.25.10": {
245
+
"integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==",
246
+
"os": ["linux"],
247
+
"cpu": ["ia32"]
248
+
},
249
+
"@esbuild/linux-loong64@0.25.10": {
250
+
"integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==",
251
+
"os": ["linux"],
252
+
"cpu": ["loong64"]
253
+
},
254
+
"@esbuild/linux-mips64el@0.25.10": {
255
+
"integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==",
256
+
"os": ["linux"],
257
+
"cpu": ["mips64el"]
258
+
},
259
+
"@esbuild/linux-ppc64@0.25.10": {
260
+
"integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==",
261
+
"os": ["linux"],
262
+
"cpu": ["ppc64"]
263
+
},
264
+
"@esbuild/linux-riscv64@0.25.10": {
265
+
"integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==",
266
+
"os": ["linux"],
267
+
"cpu": ["riscv64"]
268
+
},
269
+
"@esbuild/linux-s390x@0.25.10": {
270
+
"integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==",
271
+
"os": ["linux"],
272
+
"cpu": ["s390x"]
273
+
},
274
+
"@esbuild/linux-x64@0.25.10": {
275
+
"integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==",
276
+
"os": ["linux"],
277
+
"cpu": ["x64"]
278
+
},
279
+
"@esbuild/netbsd-arm64@0.25.10": {
280
+
"integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==",
281
+
"os": ["netbsd"],
282
+
"cpu": ["arm64"]
283
+
},
284
+
"@esbuild/netbsd-x64@0.25.10": {
285
+
"integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==",
286
+
"os": ["netbsd"],
287
+
"cpu": ["x64"]
288
+
},
289
+
"@esbuild/openbsd-arm64@0.25.10": {
290
+
"integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==",
291
+
"os": ["openbsd"],
292
+
"cpu": ["arm64"]
293
+
},
294
+
"@esbuild/openbsd-x64@0.25.10": {
295
+
"integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==",
296
+
"os": ["openbsd"],
297
+
"cpu": ["x64"]
298
+
},
299
+
"@esbuild/openharmony-arm64@0.25.10": {
300
+
"integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==",
301
+
"os": ["openharmony"],
302
+
"cpu": ["arm64"]
303
+
},
304
+
"@esbuild/sunos-x64@0.25.10": {
305
+
"integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==",
306
+
"os": ["sunos"],
307
+
"cpu": ["x64"]
308
+
},
309
+
"@esbuild/win32-arm64@0.25.10": {
310
+
"integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==",
311
+
"os": ["win32"],
312
+
"cpu": ["arm64"]
313
+
},
314
+
"@esbuild/win32-ia32@0.25.10": {
315
+
"integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==",
316
+
"os": ["win32"],
317
+
"cpu": ["ia32"]
318
+
},
319
+
"@esbuild/win32-x64@0.25.10": {
320
+
"integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==",
321
+
"os": ["win32"],
322
+
"cpu": ["x64"]
323
+
},
324
+
"@eslint-community/eslint-utils@4.9.0_eslint@9.36.0": {
325
+
"integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
326
+
"dependencies": [
327
+
"eslint",
328
+
"eslint-visitor-keys@3.4.3"
329
+
]
330
+
},
331
+
"@eslint-community/regexpp@4.12.1": {
332
+
"integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="
333
+
},
334
+
"@eslint/config-array@0.21.0": {
335
+
"integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
336
+
"dependencies": [
337
+
"@eslint/object-schema",
338
+
"debug",
339
+
"minimatch@3.1.2"
340
+
]
341
+
},
342
+
"@eslint/config-helpers@0.3.1": {
343
+
"integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA=="
344
+
},
345
+
"@eslint/core@0.15.2": {
346
+
"integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==",
347
+
"dependencies": [
348
+
"@types/json-schema"
349
+
]
350
+
},
351
+
"@eslint/eslintrc@3.3.1": {
352
+
"integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
353
+
"dependencies": [
354
+
"ajv",
355
+
"debug",
356
+
"espree",
357
+
"globals@14.0.0",
358
+
"ignore@5.3.2",
359
+
"import-fresh@3.3.1",
360
+
"js-yaml@4.1.0",
361
+
"minimatch@3.1.2",
362
+
"strip-json-comments"
363
+
]
364
+
},
365
+
"@eslint/js@9.36.0": {
366
+
"integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw=="
367
+
},
368
+
"@eslint/object-schema@2.1.6": {
369
+
"integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="
370
+
},
371
+
"@eslint/plugin-kit@0.3.5": {
372
+
"integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==",
373
+
"dependencies": [
374
+
"@eslint/core",
375
+
"levn"
376
+
]
377
+
},
378
+
"@humanfs/core@0.19.1": {
379
+
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="
380
+
},
381
+
"@humanfs/node@0.16.7": {
382
+
"integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
383
+
"dependencies": [
384
+
"@humanfs/core",
385
+
"@humanwhocodes/retry"
386
+
]
387
+
},
388
+
"@humanwhocodes/module-importer@1.0.1": {
389
+
"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="
390
+
},
391
+
"@humanwhocodes/retry@0.4.3": {
392
+
"integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="
393
+
},
394
+
"@isaacs/fs-minipass@4.0.1": {
395
+
"integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
396
+
"dependencies": [
397
+
"minipass"
398
+
]
399
+
},
400
+
"@jridgewell/gen-mapping@0.3.13": {
401
+
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
402
+
"dependencies": [
403
+
"@jridgewell/sourcemap-codec",
404
+
"@jridgewell/trace-mapping"
405
+
]
406
+
},
407
+
"@jridgewell/remapping@2.3.5": {
408
+
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
409
+
"dependencies": [
410
+
"@jridgewell/gen-mapping",
411
+
"@jridgewell/trace-mapping"
412
+
]
413
+
},
414
+
"@jridgewell/resolve-uri@3.1.2": {
415
+
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="
416
+
},
417
+
"@jridgewell/sourcemap-codec@1.5.5": {
418
+
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="
419
+
},
420
+
"@jridgewell/trace-mapping@0.3.31": {
421
+
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
422
+
"dependencies": [
423
+
"@jridgewell/resolve-uri",
424
+
"@jridgewell/sourcemap-codec"
425
+
]
426
+
},
427
+
"@napi-rs/wasm-runtime@1.0.5": {
428
+
"integrity": "sha512-TBr9Cf9onSAS2LQ2+QHx6XcC6h9+RIzJgbqG3++9TUZSH204AwEy5jg3BTQ0VATsyoGj4ee49tN/y6rvaOOtcg==",
429
+
"dependencies": [
430
+
"@emnapi/core",
431
+
"@emnapi/runtime",
432
+
"@tybys/wasm-util"
433
+
]
434
+
},
435
+
"@nodelib/fs.scandir@2.1.5": {
436
+
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
437
+
"dependencies": [
438
+
"@nodelib/fs.stat",
439
+
"run-parallel"
440
+
]
441
+
},
442
+
"@nodelib/fs.stat@2.0.5": {
443
+
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="
444
+
},
445
+
"@nodelib/fs.walk@1.2.8": {
446
+
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
447
+
"dependencies": [
448
+
"@nodelib/fs.scandir",
449
+
"fastq"
450
+
]
451
+
},
452
+
"@rolldown/pluginutils@1.0.0-beta.38": {
453
+
"integrity": "sha512-N/ICGKleNhA5nc9XXQG/kkKHJ7S55u0x0XUJbbkmdCnFuoRkM1Il12q9q0eX19+M7KKUEPw/daUPIRnxhcxAIw=="
454
+
},
455
+
"@rollup/rollup-android-arm-eabi@4.52.3": {
456
+
"integrity": "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==",
457
+
"os": ["android"],
458
+
"cpu": ["arm"]
459
+
},
460
+
"@rollup/rollup-android-arm64@4.52.3": {
461
+
"integrity": "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==",
462
+
"os": ["android"],
463
+
"cpu": ["arm64"]
464
+
},
465
+
"@rollup/rollup-darwin-arm64@4.52.3": {
466
+
"integrity": "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==",
467
+
"os": ["darwin"],
468
+
"cpu": ["arm64"]
469
+
},
470
+
"@rollup/rollup-darwin-x64@4.52.3": {
471
+
"integrity": "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==",
472
+
"os": ["darwin"],
473
+
"cpu": ["x64"]
474
+
},
475
+
"@rollup/rollup-freebsd-arm64@4.52.3": {
476
+
"integrity": "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==",
477
+
"os": ["freebsd"],
478
+
"cpu": ["arm64"]
479
+
},
480
+
"@rollup/rollup-freebsd-x64@4.52.3": {
481
+
"integrity": "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==",
482
+
"os": ["freebsd"],
483
+
"cpu": ["x64"]
484
+
},
485
+
"@rollup/rollup-linux-arm-gnueabihf@4.52.3": {
486
+
"integrity": "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==",
487
+
"os": ["linux"],
488
+
"cpu": ["arm"]
489
+
},
490
+
"@rollup/rollup-linux-arm-musleabihf@4.52.3": {
491
+
"integrity": "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==",
492
+
"os": ["linux"],
493
+
"cpu": ["arm"]
494
+
},
495
+
"@rollup/rollup-linux-arm64-gnu@4.52.3": {
496
+
"integrity": "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==",
497
+
"os": ["linux"],
498
+
"cpu": ["arm64"]
499
+
},
500
+
"@rollup/rollup-linux-arm64-musl@4.52.3": {
501
+
"integrity": "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==",
502
+
"os": ["linux"],
503
+
"cpu": ["arm64"]
504
+
},
505
+
"@rollup/rollup-linux-loong64-gnu@4.52.3": {
506
+
"integrity": "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==",
507
+
"os": ["linux"],
508
+
"cpu": ["loong64"]
509
+
},
510
+
"@rollup/rollup-linux-ppc64-gnu@4.52.3": {
511
+
"integrity": "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==",
512
+
"os": ["linux"],
513
+
"cpu": ["ppc64"]
514
+
},
515
+
"@rollup/rollup-linux-riscv64-gnu@4.52.3": {
516
+
"integrity": "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==",
517
+
"os": ["linux"],
518
+
"cpu": ["riscv64"]
519
+
},
520
+
"@rollup/rollup-linux-riscv64-musl@4.52.3": {
521
+
"integrity": "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==",
522
+
"os": ["linux"],
523
+
"cpu": ["riscv64"]
524
+
},
525
+
"@rollup/rollup-linux-s390x-gnu@4.52.3": {
526
+
"integrity": "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==",
527
+
"os": ["linux"],
528
+
"cpu": ["s390x"]
529
+
},
530
+
"@rollup/rollup-linux-x64-gnu@4.52.3": {
531
+
"integrity": "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==",
532
+
"os": ["linux"],
533
+
"cpu": ["x64"]
534
+
},
535
+
"@rollup/rollup-linux-x64-musl@4.52.3": {
536
+
"integrity": "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==",
537
+
"os": ["linux"],
538
+
"cpu": ["x64"]
539
+
},
540
+
"@rollup/rollup-openharmony-arm64@4.52.3": {
541
+
"integrity": "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==",
542
+
"os": ["openharmony"],
543
+
"cpu": ["arm64"]
544
+
},
545
+
"@rollup/rollup-win32-arm64-msvc@4.52.3": {
546
+
"integrity": "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==",
547
+
"os": ["win32"],
548
+
"cpu": ["arm64"]
549
+
},
550
+
"@rollup/rollup-win32-ia32-msvc@4.52.3": {
551
+
"integrity": "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==",
552
+
"os": ["win32"],
553
+
"cpu": ["ia32"]
554
+
},
555
+
"@rollup/rollup-win32-x64-gnu@4.52.3": {
556
+
"integrity": "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==",
557
+
"os": ["win32"],
558
+
"cpu": ["x64"]
559
+
},
560
+
"@rollup/rollup-win32-x64-msvc@4.52.3": {
561
+
"integrity": "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==",
562
+
"os": ["win32"],
563
+
"cpu": ["x64"]
564
+
},
565
+
"@tailwindcss/node@4.1.14": {
566
+
"integrity": "sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw==",
567
+
"dependencies": [
568
+
"@jridgewell/remapping",
569
+
"enhanced-resolve",
570
+
"jiti",
571
+
"lightningcss",
572
+
"magic-string",
573
+
"source-map-js",
574
+
"tailwindcss"
575
+
]
576
+
},
577
+
"@tailwindcss/oxide-android-arm64@4.1.14": {
578
+
"integrity": "sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ==",
579
+
"os": ["android"],
580
+
"cpu": ["arm64"]
581
+
},
582
+
"@tailwindcss/oxide-darwin-arm64@4.1.14": {
583
+
"integrity": "sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA==",
584
+
"os": ["darwin"],
585
+
"cpu": ["arm64"]
586
+
},
587
+
"@tailwindcss/oxide-darwin-x64@4.1.14": {
588
+
"integrity": "sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw==",
589
+
"os": ["darwin"],
590
+
"cpu": ["x64"]
591
+
},
592
+
"@tailwindcss/oxide-freebsd-x64@4.1.14": {
593
+
"integrity": "sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw==",
594
+
"os": ["freebsd"],
595
+
"cpu": ["x64"]
596
+
},
597
+
"@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14": {
598
+
"integrity": "sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw==",
599
+
"os": ["linux"],
600
+
"cpu": ["arm"]
601
+
},
602
+
"@tailwindcss/oxide-linux-arm64-gnu@4.1.14": {
603
+
"integrity": "sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w==",
604
+
"os": ["linux"],
605
+
"cpu": ["arm64"]
606
+
},
607
+
"@tailwindcss/oxide-linux-arm64-musl@4.1.14": {
608
+
"integrity": "sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ==",
609
+
"os": ["linux"],
610
+
"cpu": ["arm64"]
611
+
},
612
+
"@tailwindcss/oxide-linux-x64-gnu@4.1.14": {
613
+
"integrity": "sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg==",
614
+
"os": ["linux"],
615
+
"cpu": ["x64"]
616
+
},
617
+
"@tailwindcss/oxide-linux-x64-musl@4.1.14": {
618
+
"integrity": "sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q==",
619
+
"os": ["linux"],
620
+
"cpu": ["x64"]
621
+
},
622
+
"@tailwindcss/oxide-wasm32-wasi@4.1.14": {
623
+
"integrity": "sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ==",
624
+
"dependencies": [
625
+
"@emnapi/core",
626
+
"@emnapi/runtime",
627
+
"@emnapi/wasi-threads",
628
+
"@napi-rs/wasm-runtime",
629
+
"@tybys/wasm-util",
630
+
"tslib"
631
+
],
632
+
"cpu": ["wasm32"]
633
+
},
634
+
"@tailwindcss/oxide-win32-arm64-msvc@4.1.14": {
635
+
"integrity": "sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA==",
636
+
"os": ["win32"],
637
+
"cpu": ["arm64"]
638
+
},
639
+
"@tailwindcss/oxide-win32-x64-msvc@4.1.14": {
640
+
"integrity": "sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA==",
641
+
"os": ["win32"],
642
+
"cpu": ["x64"]
643
+
},
644
+
"@tailwindcss/oxide@4.1.14": {
645
+
"integrity": "sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw==",
646
+
"dependencies": [
647
+
"detect-libc",
648
+
"tar"
649
+
],
650
+
"optionalDependencies": [
651
+
"@tailwindcss/oxide-android-arm64",
652
+
"@tailwindcss/oxide-darwin-arm64",
653
+
"@tailwindcss/oxide-darwin-x64",
654
+
"@tailwindcss/oxide-freebsd-x64",
655
+
"@tailwindcss/oxide-linux-arm-gnueabihf",
656
+
"@tailwindcss/oxide-linux-arm64-gnu",
657
+
"@tailwindcss/oxide-linux-arm64-musl",
658
+
"@tailwindcss/oxide-linux-x64-gnu",
659
+
"@tailwindcss/oxide-linux-x64-musl",
660
+
"@tailwindcss/oxide-wasm32-wasi",
661
+
"@tailwindcss/oxide-win32-arm64-msvc",
662
+
"@tailwindcss/oxide-win32-x64-msvc"
663
+
],
664
+
"scripts": true
665
+
},
666
+
"@tailwindcss/postcss@4.1.14": {
667
+
"integrity": "sha512-BdMjIxy7HUNThK87C7BC8I1rE8BVUsfNQSI5siQ4JK3iIa3w0XyVvVL9SXLWO//CtYTcp1v7zci0fYwJOjB+Zg==",
668
+
"dependencies": [
669
+
"@alloc/quick-lru",
670
+
"@tailwindcss/node",
671
+
"@tailwindcss/oxide",
672
+
"postcss",
673
+
"tailwindcss"
674
+
]
675
+
},
676
+
"@tybys/wasm-util@0.10.1": {
677
+
"integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
678
+
"dependencies": [
679
+
"tslib"
680
+
]
681
+
},
682
+
"@types/babel__core@7.20.5": {
683
+
"integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
684
+
"dependencies": [
685
+
"@babel/parser",
686
+
"@babel/types",
687
+
"@types/babel__generator",
688
+
"@types/babel__template",
689
+
"@types/babel__traverse"
690
+
]
691
+
},
692
+
"@types/babel__generator@7.27.0": {
693
+
"integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
694
+
"dependencies": [
695
+
"@babel/types"
696
+
]
697
+
},
698
+
"@types/babel__template@7.4.4": {
699
+
"integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
700
+
"dependencies": [
701
+
"@babel/parser",
702
+
"@babel/types"
703
+
]
704
+
},
705
+
"@types/babel__traverse@7.28.0": {
706
+
"integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
707
+
"dependencies": [
708
+
"@babel/types"
709
+
]
710
+
},
711
+
"@types/estree@1.0.8": {
712
+
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="
713
+
},
714
+
"@types/json-schema@7.0.15": {
715
+
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
716
+
},
717
+
"@types/node@24.6.2": {
718
+
"integrity": "sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang==",
719
+
"dependencies": [
720
+
"undici-types"
721
+
]
722
+
},
723
+
"@types/parse-json@4.0.2": {
724
+
"integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="
725
+
},
726
+
"@types/react-dom@19.2.0_@types+react@19.2.0": {
727
+
"integrity": "sha512-brtBs0MnE9SMx7px208g39lRmC5uHZs96caOJfTjFcYSLHNamvaSMfJNagChVNkup2SdtOxKX1FDBkRSJe1ZAg==",
728
+
"dependencies": [
729
+
"@types/react"
730
+
]
731
+
},
732
+
"@types/react-relay@18.2.1": {
733
+
"integrity": "sha512-KgmFapsxAylhxcFfaAv5GZZJhTHnDvV8IDZVsUm5afpJUvgZC1Y68ssfOGsFfiFY/2EhxHM/YPfpdKbfmF3Ecg==",
734
+
"dependencies": [
735
+
"@types/react",
736
+
"@types/relay-runtime"
737
+
]
738
+
},
739
+
"@types/react@19.2.0": {
740
+
"integrity": "sha512-1LOH8xovvsKsCBq1wnT4ntDUdCJKmnEakhsuoUSy6ExlHCkGP2hqnatagYTgFk6oeL0VU31u7SNjunPN+GchtA==",
741
+
"dependencies": [
742
+
"csstype"
743
+
]
744
+
},
745
+
"@types/relay-runtime@19.0.3": {
746
+
"integrity": "sha512-pvpWWQq5e9KeESF8klQaP2igLLhr2bRd3XxVCxNpGElsPQiP6Mejr59RT9/OGY3O3i8jAGGQsshVe0QCQDbxNg=="
747
+
},
748
+
"@typescript-eslint/eslint-plugin@8.45.0_@typescript-eslint+parser@8.45.0__eslint@9.36.0__typescript@5.9.3_eslint@9.36.0_typescript@5.9.3": {
749
+
"integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==",
750
+
"dependencies": [
751
+
"@eslint-community/regexpp",
752
+
"@typescript-eslint/parser",
753
+
"@typescript-eslint/scope-manager",
754
+
"@typescript-eslint/type-utils",
755
+
"@typescript-eslint/utils",
756
+
"@typescript-eslint/visitor-keys",
757
+
"eslint",
758
+
"graphemer",
759
+
"ignore@7.0.5",
760
+
"natural-compare",
761
+
"ts-api-utils",
762
+
"typescript"
763
+
]
764
+
},
765
+
"@typescript-eslint/parser@8.45.0_eslint@9.36.0_typescript@5.9.3": {
766
+
"integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==",
767
+
"dependencies": [
768
+
"@typescript-eslint/scope-manager",
769
+
"@typescript-eslint/types",
770
+
"@typescript-eslint/typescript-estree",
771
+
"@typescript-eslint/visitor-keys",
772
+
"debug",
773
+
"eslint",
774
+
"typescript"
775
+
]
776
+
},
777
+
"@typescript-eslint/project-service@8.45.0_typescript@5.9.3": {
778
+
"integrity": "sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==",
779
+
"dependencies": [
780
+
"@typescript-eslint/tsconfig-utils",
781
+
"@typescript-eslint/types",
782
+
"debug",
783
+
"typescript"
784
+
]
785
+
},
786
+
"@typescript-eslint/scope-manager@8.45.0": {
787
+
"integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==",
788
+
"dependencies": [
789
+
"@typescript-eslint/types",
790
+
"@typescript-eslint/visitor-keys"
791
+
]
792
+
},
793
+
"@typescript-eslint/tsconfig-utils@8.45.0_typescript@5.9.3": {
794
+
"integrity": "sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==",
795
+
"dependencies": [
796
+
"typescript"
797
+
]
798
+
},
799
+
"@typescript-eslint/type-utils@8.45.0_eslint@9.36.0_typescript@5.9.3": {
800
+
"integrity": "sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==",
801
+
"dependencies": [
802
+
"@typescript-eslint/types",
803
+
"@typescript-eslint/typescript-estree",
804
+
"@typescript-eslint/utils",
805
+
"debug",
806
+
"eslint",
807
+
"ts-api-utils",
808
+
"typescript"
809
+
]
810
+
},
811
+
"@typescript-eslint/types@8.45.0": {
812
+
"integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA=="
813
+
},
814
+
"@typescript-eslint/typescript-estree@8.45.0_typescript@5.9.3": {
815
+
"integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==",
816
+
"dependencies": [
817
+
"@typescript-eslint/project-service",
818
+
"@typescript-eslint/tsconfig-utils",
819
+
"@typescript-eslint/types",
820
+
"@typescript-eslint/visitor-keys",
821
+
"debug",
822
+
"fast-glob",
823
+
"is-glob",
824
+
"minimatch@9.0.5",
825
+
"semver@7.7.2",
826
+
"ts-api-utils",
827
+
"typescript"
828
+
]
829
+
},
830
+
"@typescript-eslint/utils@8.45.0_eslint@9.36.0_typescript@5.9.3": {
831
+
"integrity": "sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==",
832
+
"dependencies": [
833
+
"@eslint-community/eslint-utils",
834
+
"@typescript-eslint/scope-manager",
835
+
"@typescript-eslint/types",
836
+
"@typescript-eslint/typescript-estree",
837
+
"eslint",
838
+
"typescript"
839
+
]
840
+
},
841
+
"@typescript-eslint/visitor-keys@8.45.0": {
842
+
"integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==",
843
+
"dependencies": [
844
+
"@typescript-eslint/types",
845
+
"eslint-visitor-keys@4.2.1"
846
+
]
847
+
},
848
+
"@vitejs/plugin-react@5.0.4_vite@7.1.8__@types+node@24.6.2__picomatch@4.0.3_@babel+core@7.28.4_@types+node@24.6.2": {
849
+
"integrity": "sha512-La0KD0vGkVkSk6K+piWDKRUyg8Rl5iAIKRMH0vMJI0Eg47bq1eOxmoObAaQG37WMW9MSyk7Cs8EIWwJC1PtzKA==",
850
+
"dependencies": [
851
+
"@babel/core",
852
+
"@babel/plugin-transform-react-jsx-self",
853
+
"@babel/plugin-transform-react-jsx-source",
854
+
"@rolldown/pluginutils",
855
+
"@types/babel__core",
856
+
"react-refresh",
857
+
"vite"
858
+
]
859
+
},
860
+
"acorn-jsx@5.3.2_acorn@8.15.0": {
861
+
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
862
+
"dependencies": [
863
+
"acorn"
864
+
]
865
+
},
866
+
"acorn@8.15.0": {
867
+
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
868
+
"bin": true
869
+
},
870
+
"ajv@6.12.6": {
871
+
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
872
+
"dependencies": [
873
+
"fast-deep-equal",
874
+
"fast-json-stable-stringify",
875
+
"json-schema-traverse",
876
+
"uri-js"
877
+
]
878
+
},
879
+
"ansi-styles@4.3.0": {
880
+
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
881
+
"dependencies": [
882
+
"color-convert"
883
+
]
884
+
},
885
+
"argparse@1.0.10": {
886
+
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
887
+
"dependencies": [
888
+
"sprintf-js"
889
+
]
890
+
},
891
+
"argparse@2.0.1": {
892
+
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
893
+
},
894
+
"asap@2.0.6": {
895
+
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="
896
+
},
897
+
"autoprefixer@10.4.21_postcss@8.5.6": {
898
+
"integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==",
899
+
"dependencies": [
900
+
"browserslist",
901
+
"caniuse-lite",
902
+
"fraction.js",
903
+
"normalize-range",
904
+
"picocolors",
905
+
"postcss",
906
+
"postcss-value-parser"
907
+
],
908
+
"bin": true
909
+
},
910
+
"babel-plugin-macros@2.8.0": {
911
+
"integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==",
912
+
"dependencies": [
913
+
"@babel/runtime",
914
+
"cosmiconfig@6.0.0",
915
+
"resolve"
916
+
]
917
+
},
918
+
"babel-plugin-relay@20.1.1": {
919
+
"integrity": "sha512-BWlqLPiHbxZTxlyng2rVgsZCwztHNje7H8FR4c+UKy3ErQJBG6BKLr9vUdeR7mAZCH2v0sOAxNhG6zR1FrWjAg==",
920
+
"dependencies": [
921
+
"babel-plugin-macros",
922
+
"cosmiconfig@5.2.1",
923
+
"graphql@15.3.0"
924
+
]
925
+
},
926
+
"balanced-match@1.0.2": {
927
+
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
928
+
},
929
+
"baseline-browser-mapping@2.8.10": {
930
+
"integrity": "sha512-uLfgBi+7IBNay8ECBO2mVMGZAc1VgZWEChxm4lv+TobGdG82LnXMjuNGo/BSSZZL4UmkWhxEHP2f5ziLNwGWMA==",
931
+
"bin": true
932
+
},
933
+
"brace-expansion@1.1.12": {
934
+
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
935
+
"dependencies": [
936
+
"balanced-match",
937
+
"concat-map"
938
+
]
939
+
},
940
+
"brace-expansion@2.0.2": {
941
+
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
942
+
"dependencies": [
943
+
"balanced-match"
944
+
]
945
+
},
946
+
"braces@3.0.3": {
947
+
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
948
+
"dependencies": [
949
+
"fill-range"
950
+
]
951
+
},
952
+
"browserslist@4.26.3": {
953
+
"integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==",
954
+
"dependencies": [
955
+
"baseline-browser-mapping",
956
+
"caniuse-lite",
957
+
"electron-to-chromium",
958
+
"node-releases",
959
+
"update-browserslist-db"
960
+
],
961
+
"bin": true
962
+
},
963
+
"caller-callsite@2.0.0": {
964
+
"integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==",
965
+
"dependencies": [
966
+
"callsites@2.0.0"
967
+
]
968
+
},
969
+
"caller-path@2.0.0": {
970
+
"integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==",
971
+
"dependencies": [
972
+
"caller-callsite"
973
+
]
974
+
},
975
+
"callsites@2.0.0": {
976
+
"integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ=="
977
+
},
978
+
"callsites@3.1.0": {
979
+
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
980
+
},
981
+
"caniuse-lite@1.0.30001746": {
982
+
"integrity": "sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA=="
983
+
},
984
+
"chalk@4.1.2": {
985
+
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
986
+
"dependencies": [
987
+
"ansi-styles",
988
+
"supports-color"
989
+
]
990
+
},
991
+
"chownr@3.0.0": {
992
+
"integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="
993
+
},
994
+
"color-convert@2.0.1": {
995
+
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
996
+
"dependencies": [
997
+
"color-name"
998
+
]
999
+
},
1000
+
"color-name@1.1.4": {
1001
+
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
1002
+
},
1003
+
"concat-map@0.0.1": {
1004
+
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
1005
+
},
1006
+
"convert-source-map@2.0.0": {
1007
+
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="
1008
+
},
1009
+
"cookie@1.0.2": {
1010
+
"integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="
1011
+
},
1012
+
"cosmiconfig@5.2.1": {
1013
+
"integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
1014
+
"dependencies": [
1015
+
"import-fresh@2.0.0",
1016
+
"is-directory",
1017
+
"js-yaml@3.14.1",
1018
+
"parse-json@4.0.0"
1019
+
]
1020
+
},
1021
+
"cosmiconfig@6.0.0": {
1022
+
"integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
1023
+
"dependencies": [
1024
+
"@types/parse-json",
1025
+
"import-fresh@3.3.1",
1026
+
"parse-json@5.2.0",
1027
+
"path-type",
1028
+
"yaml"
1029
+
]
1030
+
},
1031
+
"cross-fetch@3.2.0": {
1032
+
"integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==",
1033
+
"dependencies": [
1034
+
"node-fetch"
1035
+
]
1036
+
},
1037
+
"cross-spawn@7.0.6": {
1038
+
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
1039
+
"dependencies": [
1040
+
"path-key",
1041
+
"shebang-command",
1042
+
"which"
1043
+
]
1044
+
},
1045
+
"csstype@3.1.3": {
1046
+
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
1047
+
},
1048
+
"debug@4.4.3": {
1049
+
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
1050
+
"dependencies": [
1051
+
"ms"
1052
+
]
1053
+
},
1054
+
"deep-is@0.1.4": {
1055
+
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
1056
+
},
1057
+
"detect-libc@2.1.1": {
1058
+
"integrity": "sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw=="
1059
+
},
1060
+
"electron-to-chromium@1.5.229": {
1061
+
"integrity": "sha512-cwhDcZKGcT/rEthLRJ9eBlMDkh1sorgsuk+6dpsehV0g9CABsIqBxU4rLRjG+d/U6pYU1s37A4lSKrVc5lSQYg=="
1062
+
},
1063
+
"enhanced-resolve@5.18.3": {
1064
+
"integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
1065
+
"dependencies": [
1066
+
"graceful-fs",
1067
+
"tapable"
1068
+
]
1069
+
},
1070
+
"error-ex@1.3.2": {
1071
+
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
1072
+
"dependencies": [
1073
+
"is-arrayish"
1074
+
]
1075
+
},
1076
+
"esbuild@0.25.10": {
1077
+
"integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==",
1078
+
"optionalDependencies": [
1079
+
"@esbuild/aix-ppc64",
1080
+
"@esbuild/android-arm",
1081
+
"@esbuild/android-arm64",
1082
+
"@esbuild/android-x64",
1083
+
"@esbuild/darwin-arm64",
1084
+
"@esbuild/darwin-x64",
1085
+
"@esbuild/freebsd-arm64",
1086
+
"@esbuild/freebsd-x64",
1087
+
"@esbuild/linux-arm",
1088
+
"@esbuild/linux-arm64",
1089
+
"@esbuild/linux-ia32",
1090
+
"@esbuild/linux-loong64",
1091
+
"@esbuild/linux-mips64el",
1092
+
"@esbuild/linux-ppc64",
1093
+
"@esbuild/linux-riscv64",
1094
+
"@esbuild/linux-s390x",
1095
+
"@esbuild/linux-x64",
1096
+
"@esbuild/netbsd-arm64",
1097
+
"@esbuild/netbsd-x64",
1098
+
"@esbuild/openbsd-arm64",
1099
+
"@esbuild/openbsd-x64",
1100
+
"@esbuild/openharmony-arm64",
1101
+
"@esbuild/sunos-x64",
1102
+
"@esbuild/win32-arm64",
1103
+
"@esbuild/win32-ia32",
1104
+
"@esbuild/win32-x64"
1105
+
],
1106
+
"scripts": true,
1107
+
"bin": true
1108
+
},
1109
+
"escalade@3.2.0": {
1110
+
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="
1111
+
},
1112
+
"escape-string-regexp@4.0.0": {
1113
+
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
1114
+
},
1115
+
"eslint-plugin-react-hooks@5.2.0_eslint@9.36.0": {
1116
+
"integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
1117
+
"dependencies": [
1118
+
"eslint"
1119
+
]
1120
+
},
1121
+
"eslint-plugin-react-refresh@0.4.23_eslint@9.36.0": {
1122
+
"integrity": "sha512-G4j+rv0NmbIR45kni5xJOrYvCtyD3/7LjpVH8MPPcudXDcNu8gv+4ATTDXTtbRR8rTCM5HxECvCSsRmxKnWDsA==",
1123
+
"dependencies": [
1124
+
"eslint"
1125
+
]
1126
+
},
1127
+
"eslint-scope@8.4.0": {
1128
+
"integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
1129
+
"dependencies": [
1130
+
"esrecurse",
1131
+
"estraverse"
1132
+
]
1133
+
},
1134
+
"eslint-visitor-keys@3.4.3": {
1135
+
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="
1136
+
},
1137
+
"eslint-visitor-keys@4.2.1": {
1138
+
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="
1139
+
},
1140
+
"eslint@9.36.0": {
1141
+
"integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==",
1142
+
"dependencies": [
1143
+
"@eslint-community/eslint-utils",
1144
+
"@eslint-community/regexpp",
1145
+
"@eslint/config-array",
1146
+
"@eslint/config-helpers",
1147
+
"@eslint/core",
1148
+
"@eslint/eslintrc",
1149
+
"@eslint/js",
1150
+
"@eslint/plugin-kit",
1151
+
"@humanfs/node",
1152
+
"@humanwhocodes/module-importer",
1153
+
"@humanwhocodes/retry",
1154
+
"@types/estree",
1155
+
"@types/json-schema",
1156
+
"ajv",
1157
+
"chalk",
1158
+
"cross-spawn",
1159
+
"debug",
1160
+
"escape-string-regexp",
1161
+
"eslint-scope",
1162
+
"eslint-visitor-keys@4.2.1",
1163
+
"espree",
1164
+
"esquery",
1165
+
"esutils",
1166
+
"fast-deep-equal",
1167
+
"file-entry-cache",
1168
+
"find-up",
1169
+
"glob-parent@6.0.2",
1170
+
"ignore@5.3.2",
1171
+
"imurmurhash",
1172
+
"is-glob",
1173
+
"json-stable-stringify-without-jsonify",
1174
+
"lodash.merge",
1175
+
"minimatch@3.1.2",
1176
+
"natural-compare",
1177
+
"optionator"
1178
+
],
1179
+
"bin": true
1180
+
},
1181
+
"espree@10.4.0_acorn@8.15.0": {
1182
+
"integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
1183
+
"dependencies": [
1184
+
"acorn",
1185
+
"acorn-jsx",
1186
+
"eslint-visitor-keys@4.2.1"
1187
+
]
1188
+
},
1189
+
"esprima@4.0.1": {
1190
+
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
1191
+
"bin": true
1192
+
},
1193
+
"esquery@1.6.0": {
1194
+
"integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
1195
+
"dependencies": [
1196
+
"estraverse"
1197
+
]
1198
+
},
1199
+
"esrecurse@4.3.0": {
1200
+
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
1201
+
"dependencies": [
1202
+
"estraverse"
1203
+
]
1204
+
},
1205
+
"estraverse@5.3.0": {
1206
+
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="
1207
+
},
1208
+
"esutils@2.0.3": {
1209
+
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
1210
+
},
1211
+
"fast-deep-equal@3.1.3": {
1212
+
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
1213
+
},
1214
+
"fast-glob@3.3.3": {
1215
+
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
1216
+
"dependencies": [
1217
+
"@nodelib/fs.stat",
1218
+
"@nodelib/fs.walk",
1219
+
"glob-parent@5.1.2",
1220
+
"merge2",
1221
+
"micromatch"
1222
+
]
1223
+
},
1224
+
"fast-json-stable-stringify@2.1.0": {
1225
+
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
1226
+
},
1227
+
"fast-levenshtein@2.0.6": {
1228
+
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
1229
+
},
1230
+
"fastq@1.19.1": {
1231
+
"integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
1232
+
"dependencies": [
1233
+
"reusify"
1234
+
]
1235
+
},
1236
+
"fbjs-css-vars@1.0.2": {
1237
+
"integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ=="
1238
+
},
1239
+
"fbjs@3.0.5": {
1240
+
"integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==",
1241
+
"dependencies": [
1242
+
"cross-fetch",
1243
+
"fbjs-css-vars",
1244
+
"loose-envify",
1245
+
"object-assign",
1246
+
"promise",
1247
+
"setimmediate",
1248
+
"ua-parser-js"
1249
+
]
1250
+
},
1251
+
"fdir@6.5.0_picomatch@4.0.3": {
1252
+
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
1253
+
"dependencies": [
1254
+
"picomatch@4.0.3"
1255
+
],
1256
+
"optionalPeers": [
1257
+
"picomatch@4.0.3"
1258
+
]
1259
+
},
1260
+
"file-entry-cache@8.0.0": {
1261
+
"integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
1262
+
"dependencies": [
1263
+
"flat-cache"
1264
+
]
1265
+
},
1266
+
"fill-range@7.1.1": {
1267
+
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
1268
+
"dependencies": [
1269
+
"to-regex-range"
1270
+
]
1271
+
},
1272
+
"find-up@5.0.0": {
1273
+
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
1274
+
"dependencies": [
1275
+
"locate-path",
1276
+
"path-exists"
1277
+
]
1278
+
},
1279
+
"flat-cache@4.0.1": {
1280
+
"integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
1281
+
"dependencies": [
1282
+
"flatted",
1283
+
"keyv"
1284
+
]
1285
+
},
1286
+
"flatted@3.3.3": {
1287
+
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="
1288
+
},
1289
+
"fraction.js@4.3.7": {
1290
+
"integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="
1291
+
},
1292
+
"fsevents@2.3.3": {
1293
+
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
1294
+
"os": ["darwin"],
1295
+
"scripts": true
1296
+
},
1297
+
"function-bind@1.1.2": {
1298
+
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
1299
+
},
1300
+
"gensync@1.0.0-beta.2": {
1301
+
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="
1302
+
},
1303
+
"glob-parent@5.1.2": {
1304
+
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
1305
+
"dependencies": [
1306
+
"is-glob"
1307
+
]
1308
+
},
1309
+
"glob-parent@6.0.2": {
1310
+
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
1311
+
"dependencies": [
1312
+
"is-glob"
1313
+
]
1314
+
},
1315
+
"globals@14.0.0": {
1316
+
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="
1317
+
},
1318
+
"globals@16.4.0": {
1319
+
"integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="
1320
+
},
1321
+
"graceful-fs@4.2.11": {
1322
+
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
1323
+
},
1324
+
"graphemer@1.4.0": {
1325
+
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="
1326
+
},
1327
+
"graphql@15.3.0": {
1328
+
"integrity": "sha512-GTCJtzJmkFLWRfFJuoo9RWWa/FfamUHgiFosxi/X1Ani4AVWbeyBenZTNX6dM+7WSbbFfTo/25eh0LLkwHMw2w=="
1329
+
},
1330
+
"graphql@16.11.0": {
1331
+
"integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw=="
1332
+
},
1333
+
"has-flag@4.0.0": {
1334
+
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
1335
+
},
1336
+
"hasown@2.0.2": {
1337
+
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
1338
+
"dependencies": [
1339
+
"function-bind"
1340
+
]
1341
+
},
1342
+
"ignore@5.3.2": {
1343
+
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="
1344
+
},
1345
+
"ignore@7.0.5": {
1346
+
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="
1347
+
},
1348
+
"import-fresh@2.0.0": {
1349
+
"integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==",
1350
+
"dependencies": [
1351
+
"caller-path",
1352
+
"resolve-from@3.0.0"
1353
+
]
1354
+
},
1355
+
"import-fresh@3.3.1": {
1356
+
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
1357
+
"dependencies": [
1358
+
"parent-module",
1359
+
"resolve-from@4.0.0"
1360
+
]
1361
+
},
1362
+
"imurmurhash@0.1.4": {
1363
+
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="
1364
+
},
1365
+
"invariant@2.2.4": {
1366
+
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
1367
+
"dependencies": [
1368
+
"loose-envify"
1369
+
]
1370
+
},
1371
+
"is-arrayish@0.2.1": {
1372
+
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
1373
+
},
1374
+
"is-core-module@2.16.1": {
1375
+
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
1376
+
"dependencies": [
1377
+
"hasown"
1378
+
]
1379
+
},
1380
+
"is-directory@0.3.1": {
1381
+
"integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw=="
1382
+
},
1383
+
"is-extglob@2.1.1": {
1384
+
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
1385
+
},
1386
+
"is-glob@4.0.3": {
1387
+
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
1388
+
"dependencies": [
1389
+
"is-extglob"
1390
+
]
1391
+
},
1392
+
"is-number@7.0.0": {
1393
+
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
1394
+
},
1395
+
"isexe@2.0.0": {
1396
+
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
1397
+
},
1398
+
"jiti@2.6.1": {
1399
+
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
1400
+
"bin": true
1401
+
},
1402
+
"js-tokens@4.0.0": {
1403
+
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
1404
+
},
1405
+
"js-yaml@3.14.1": {
1406
+
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
1407
+
"dependencies": [
1408
+
"argparse@1.0.10",
1409
+
"esprima"
1410
+
],
1411
+
"bin": true
1412
+
},
1413
+
"js-yaml@4.1.0": {
1414
+
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
1415
+
"dependencies": [
1416
+
"argparse@2.0.1"
1417
+
],
1418
+
"bin": true
1419
+
},
1420
+
"jsesc@3.1.0": {
1421
+
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
1422
+
"bin": true
1423
+
},
1424
+
"json-buffer@3.0.1": {
1425
+
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
1426
+
},
1427
+
"json-parse-better-errors@1.0.2": {
1428
+
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
1429
+
},
1430
+
"json-parse-even-better-errors@2.3.1": {
1431
+
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
1432
+
},
1433
+
"json-schema-traverse@0.4.1": {
1434
+
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
1435
+
},
1436
+
"json-stable-stringify-without-jsonify@1.0.1": {
1437
+
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="
1438
+
},
1439
+
"json5@2.2.3": {
1440
+
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
1441
+
"bin": true
1442
+
},
1443
+
"keyv@4.5.4": {
1444
+
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
1445
+
"dependencies": [
1446
+
"json-buffer"
1447
+
]
1448
+
},
1449
+
"levn@0.4.1": {
1450
+
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
1451
+
"dependencies": [
1452
+
"prelude-ls",
1453
+
"type-check"
1454
+
]
1455
+
},
1456
+
"lightningcss-darwin-arm64@1.30.1": {
1457
+
"integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==",
1458
+
"os": ["darwin"],
1459
+
"cpu": ["arm64"]
1460
+
},
1461
+
"lightningcss-darwin-x64@1.30.1": {
1462
+
"integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==",
1463
+
"os": ["darwin"],
1464
+
"cpu": ["x64"]
1465
+
},
1466
+
"lightningcss-freebsd-x64@1.30.1": {
1467
+
"integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==",
1468
+
"os": ["freebsd"],
1469
+
"cpu": ["x64"]
1470
+
},
1471
+
"lightningcss-linux-arm-gnueabihf@1.30.1": {
1472
+
"integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==",
1473
+
"os": ["linux"],
1474
+
"cpu": ["arm"]
1475
+
},
1476
+
"lightningcss-linux-arm64-gnu@1.30.1": {
1477
+
"integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==",
1478
+
"os": ["linux"],
1479
+
"cpu": ["arm64"]
1480
+
},
1481
+
"lightningcss-linux-arm64-musl@1.30.1": {
1482
+
"integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==",
1483
+
"os": ["linux"],
1484
+
"cpu": ["arm64"]
1485
+
},
1486
+
"lightningcss-linux-x64-gnu@1.30.1": {
1487
+
"integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==",
1488
+
"os": ["linux"],
1489
+
"cpu": ["x64"]
1490
+
},
1491
+
"lightningcss-linux-x64-musl@1.30.1": {
1492
+
"integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==",
1493
+
"os": ["linux"],
1494
+
"cpu": ["x64"]
1495
+
},
1496
+
"lightningcss-win32-arm64-msvc@1.30.1": {
1497
+
"integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==",
1498
+
"os": ["win32"],
1499
+
"cpu": ["arm64"]
1500
+
},
1501
+
"lightningcss-win32-x64-msvc@1.30.1": {
1502
+
"integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==",
1503
+
"os": ["win32"],
1504
+
"cpu": ["x64"]
1505
+
},
1506
+
"lightningcss@1.30.1": {
1507
+
"integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==",
1508
+
"dependencies": [
1509
+
"detect-libc"
1510
+
],
1511
+
"optionalDependencies": [
1512
+
"lightningcss-darwin-arm64",
1513
+
"lightningcss-darwin-x64",
1514
+
"lightningcss-freebsd-x64",
1515
+
"lightningcss-linux-arm-gnueabihf",
1516
+
"lightningcss-linux-arm64-gnu",
1517
+
"lightningcss-linux-arm64-musl",
1518
+
"lightningcss-linux-x64-gnu",
1519
+
"lightningcss-linux-x64-musl",
1520
+
"lightningcss-win32-arm64-msvc",
1521
+
"lightningcss-win32-x64-msvc"
1522
+
]
1523
+
},
1524
+
"lines-and-columns@1.2.4": {
1525
+
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
1526
+
},
1527
+
"locate-path@6.0.0": {
1528
+
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
1529
+
"dependencies": [
1530
+
"p-locate"
1531
+
]
1532
+
},
1533
+
"lodash.merge@4.6.2": {
1534
+
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
1535
+
},
1536
+
"loose-envify@1.4.0": {
1537
+
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
1538
+
"dependencies": [
1539
+
"js-tokens"
1540
+
],
1541
+
"bin": true
1542
+
},
1543
+
"lru-cache@5.1.1": {
1544
+
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
1545
+
"dependencies": [
1546
+
"yallist@3.1.1"
1547
+
]
1548
+
},
1549
+
"magic-string@0.30.19": {
1550
+
"integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==",
1551
+
"dependencies": [
1552
+
"@jridgewell/sourcemap-codec"
1553
+
]
1554
+
},
1555
+
"merge2@1.4.1": {
1556
+
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="
1557
+
},
1558
+
"micromatch@4.0.8": {
1559
+
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
1560
+
"dependencies": [
1561
+
"braces",
1562
+
"picomatch@2.3.1"
1563
+
]
1564
+
},
1565
+
"minimatch@3.1.2": {
1566
+
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
1567
+
"dependencies": [
1568
+
"brace-expansion@1.1.12"
1569
+
]
1570
+
},
1571
+
"minimatch@9.0.5": {
1572
+
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
1573
+
"dependencies": [
1574
+
"brace-expansion@2.0.2"
1575
+
]
1576
+
},
1577
+
"minipass@7.1.2": {
1578
+
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="
1579
+
},
1580
+
"minizlib@3.1.0": {
1581
+
"integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
1582
+
"dependencies": [
1583
+
"minipass"
1584
+
]
1585
+
},
1586
+
"ms@2.1.3": {
1587
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
1588
+
},
1589
+
"nanoid@3.3.11": {
1590
+
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
1591
+
"bin": true
1592
+
},
1593
+
"natural-compare@1.4.0": {
1594
+
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
1595
+
},
1596
+
"node-fetch@2.7.0": {
1597
+
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
1598
+
"dependencies": [
1599
+
"whatwg-url"
1600
+
]
1601
+
},
1602
+
"node-releases@2.0.21": {
1603
+
"integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw=="
1604
+
},
1605
+
"normalize-range@0.1.2": {
1606
+
"integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA=="
1607
+
},
1608
+
"nullthrows@1.1.1": {
1609
+
"integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw=="
1610
+
},
1611
+
"object-assign@4.1.1": {
1612
+
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
1613
+
},
1614
+
"optionator@0.9.4": {
1615
+
"integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
1616
+
"dependencies": [
1617
+
"deep-is",
1618
+
"fast-levenshtein",
1619
+
"levn",
1620
+
"prelude-ls",
1621
+
"type-check",
1622
+
"word-wrap"
1623
+
]
1624
+
},
1625
+
"p-limit@3.1.0": {
1626
+
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
1627
+
"dependencies": [
1628
+
"yocto-queue"
1629
+
]
1630
+
},
1631
+
"p-locate@5.0.0": {
1632
+
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
1633
+
"dependencies": [
1634
+
"p-limit"
1635
+
]
1636
+
},
1637
+
"parent-module@1.0.1": {
1638
+
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
1639
+
"dependencies": [
1640
+
"callsites@3.1.0"
1641
+
]
1642
+
},
1643
+
"parse-json@4.0.0": {
1644
+
"integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==",
1645
+
"dependencies": [
1646
+
"error-ex",
1647
+
"json-parse-better-errors"
1648
+
]
1649
+
},
1650
+
"parse-json@5.2.0": {
1651
+
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
1652
+
"dependencies": [
1653
+
"@babel/code-frame",
1654
+
"error-ex",
1655
+
"json-parse-even-better-errors",
1656
+
"lines-and-columns"
1657
+
]
1658
+
},
1659
+
"path-exists@4.0.0": {
1660
+
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
1661
+
},
1662
+
"path-key@3.1.1": {
1663
+
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
1664
+
},
1665
+
"path-parse@1.0.7": {
1666
+
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
1667
+
},
1668
+
"path-type@4.0.0": {
1669
+
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
1670
+
},
1671
+
"picocolors@1.1.1": {
1672
+
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
1673
+
},
1674
+
"picomatch@2.3.1": {
1675
+
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
1676
+
},
1677
+
"picomatch@4.0.3": {
1678
+
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="
1679
+
},
1680
+
"postcss-value-parser@4.2.0": {
1681
+
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
1682
+
},
1683
+
"postcss@8.5.6": {
1684
+
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
1685
+
"dependencies": [
1686
+
"nanoid",
1687
+
"picocolors",
1688
+
"source-map-js"
1689
+
]
1690
+
},
1691
+
"prelude-ls@1.2.1": {
1692
+
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="
1693
+
},
1694
+
"promise@7.3.1": {
1695
+
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
1696
+
"dependencies": [
1697
+
"asap"
1698
+
]
1699
+
},
1700
+
"punycode@2.3.1": {
1701
+
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="
1702
+
},
1703
+
"queue-microtask@1.2.3": {
1704
+
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
1705
+
},
1706
+
"react-dom@19.2.0_react@19.2.0": {
1707
+
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
1708
+
"dependencies": [
1709
+
"react",
1710
+
"scheduler"
1711
+
]
1712
+
},
1713
+
"react-refresh@0.17.0": {
1714
+
"integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="
1715
+
},
1716
+
"react-relay@20.1.1_react@19.2.0": {
1717
+
"integrity": "sha512-pwl7wHHXCOx32dOg4TVNkhVojgGVvOBMo9pcMGeM1BIFYvmwGauKTtBB+qr5buXHGooCL8TXjMVg+ZLizIsbeQ==",
1718
+
"dependencies": [
1719
+
"@babel/runtime",
1720
+
"fbjs",
1721
+
"invariant",
1722
+
"nullthrows",
1723
+
"react",
1724
+
"relay-runtime"
1725
+
]
1726
+
},
1727
+
"react-router-dom@7.9.3_react@19.2.0_react-dom@19.2.0__react@19.2.0": {
1728
+
"integrity": "sha512-1QSbA0TGGFKTAc/aWjpfW/zoEukYfU4dc1dLkT/vvf54JoGMkW+fNA+3oyo2gWVW1GM7BxjJVHz5GnPJv40rvg==",
1729
+
"dependencies": [
1730
+
"react",
1731
+
"react-dom",
1732
+
"react-router"
1733
+
]
1734
+
},
1735
+
"react-router@7.9.3_react@19.2.0_react-dom@19.2.0__react@19.2.0": {
1736
+
"integrity": "sha512-4o2iWCFIwhI/eYAIL43+cjORXYn/aRQPgtFRRZb3VzoyQ5Uej0Bmqj7437L97N9NJW4wnicSwLOLS+yCXfAPgg==",
1737
+
"dependencies": [
1738
+
"cookie",
1739
+
"react",
1740
+
"react-dom",
1741
+
"set-cookie-parser"
1742
+
],
1743
+
"optionalPeers": [
1744
+
"react-dom"
1745
+
]
1746
+
},
1747
+
"react@19.2.0": {
1748
+
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="
1749
+
},
1750
+
"relay-compiler@20.1.1": {
1751
+
"integrity": "sha512-J/FFFLS/3vnbDkyQMw8l3Ev7dNHXMgC1RAs0fITz4Q63TFPOw142knKxY1Mm5ZZBABkAs9g2JghXaqM+phwTxw==",
1752
+
"bin": true
1753
+
},
1754
+
"relay-runtime@20.1.1": {
1755
+
"integrity": "sha512-N98ZkkyuIHdXmHaPuljihM1QbFYXATF0gxA0CESFphszsQzXs9A/zZloVfzdOI/xg3B3CfM/5nozvIoeTDIcfw==",
1756
+
"dependencies": [
1757
+
"@babel/runtime",
1758
+
"fbjs",
1759
+
"invariant"
1760
+
]
1761
+
},
1762
+
"resolve-from@3.0.0": {
1763
+
"integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw=="
1764
+
},
1765
+
"resolve-from@4.0.0": {
1766
+
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
1767
+
},
1768
+
"resolve@1.22.10": {
1769
+
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
1770
+
"dependencies": [
1771
+
"is-core-module",
1772
+
"path-parse",
1773
+
"supports-preserve-symlinks-flag"
1774
+
],
1775
+
"bin": true
1776
+
},
1777
+
"reusify@1.1.0": {
1778
+
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="
1779
+
},
1780
+
"rollup@4.52.3": {
1781
+
"integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==",
1782
+
"dependencies": [
1783
+
"@types/estree"
1784
+
],
1785
+
"optionalDependencies": [
1786
+
"@rollup/rollup-android-arm-eabi",
1787
+
"@rollup/rollup-android-arm64",
1788
+
"@rollup/rollup-darwin-arm64",
1789
+
"@rollup/rollup-darwin-x64",
1790
+
"@rollup/rollup-freebsd-arm64",
1791
+
"@rollup/rollup-freebsd-x64",
1792
+
"@rollup/rollup-linux-arm-gnueabihf",
1793
+
"@rollup/rollup-linux-arm-musleabihf",
1794
+
"@rollup/rollup-linux-arm64-gnu",
1795
+
"@rollup/rollup-linux-arm64-musl",
1796
+
"@rollup/rollup-linux-loong64-gnu",
1797
+
"@rollup/rollup-linux-ppc64-gnu",
1798
+
"@rollup/rollup-linux-riscv64-gnu",
1799
+
"@rollup/rollup-linux-riscv64-musl",
1800
+
"@rollup/rollup-linux-s390x-gnu",
1801
+
"@rollup/rollup-linux-x64-gnu",
1802
+
"@rollup/rollup-linux-x64-musl",
1803
+
"@rollup/rollup-openharmony-arm64",
1804
+
"@rollup/rollup-win32-arm64-msvc",
1805
+
"@rollup/rollup-win32-ia32-msvc",
1806
+
"@rollup/rollup-win32-x64-gnu",
1807
+
"@rollup/rollup-win32-x64-msvc",
1808
+
"fsevents"
1809
+
],
1810
+
"bin": true
1811
+
},
1812
+
"run-parallel@1.2.0": {
1813
+
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
1814
+
"dependencies": [
1815
+
"queue-microtask"
1816
+
]
1817
+
},
1818
+
"scheduler@0.27.0": {
1819
+
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="
1820
+
},
1821
+
"semver@6.3.1": {
1822
+
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
1823
+
"bin": true
1824
+
},
1825
+
"semver@7.7.2": {
1826
+
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
1827
+
"bin": true
1828
+
},
1829
+
"set-cookie-parser@2.7.1": {
1830
+
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="
1831
+
},
1832
+
"setimmediate@1.0.5": {
1833
+
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
1834
+
},
1835
+
"shebang-command@2.0.0": {
1836
+
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
1837
+
"dependencies": [
1838
+
"shebang-regex"
1839
+
]
1840
+
},
1841
+
"shebang-regex@3.0.0": {
1842
+
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
1843
+
},
1844
+
"source-map-js@1.2.1": {
1845
+
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
1846
+
},
1847
+
"sprintf-js@1.0.3": {
1848
+
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
1849
+
},
1850
+
"strip-json-comments@3.1.1": {
1851
+
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="
1852
+
},
1853
+
"supports-color@7.2.0": {
1854
+
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
1855
+
"dependencies": [
1856
+
"has-flag"
1857
+
]
1858
+
},
1859
+
"supports-preserve-symlinks-flag@1.0.0": {
1860
+
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
1861
+
},
1862
+
"tailwindcss@4.1.14": {
1863
+
"integrity": "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA=="
1864
+
},
1865
+
"tapable@2.2.3": {
1866
+
"integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg=="
1867
+
},
1868
+
"tar@7.5.1": {
1869
+
"integrity": "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==",
1870
+
"dependencies": [
1871
+
"@isaacs/fs-minipass",
1872
+
"chownr",
1873
+
"minipass",
1874
+
"minizlib",
1875
+
"yallist@5.0.0"
1876
+
]
1877
+
},
1878
+
"tinyglobby@0.2.15_picomatch@4.0.3": {
1879
+
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
1880
+
"dependencies": [
1881
+
"fdir",
1882
+
"picomatch@4.0.3"
1883
+
]
1884
+
},
1885
+
"to-regex-range@5.0.1": {
1886
+
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
1887
+
"dependencies": [
1888
+
"is-number"
1889
+
]
1890
+
},
1891
+
"tr46@0.0.3": {
1892
+
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
1893
+
},
1894
+
"ts-api-utils@2.1.0_typescript@5.9.3": {
1895
+
"integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
1896
+
"dependencies": [
1897
+
"typescript"
1898
+
]
1899
+
},
1900
+
"tslib@2.8.1": {
1901
+
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
1902
+
},
1903
+
"type-check@0.4.0": {
1904
+
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
1905
+
"dependencies": [
1906
+
"prelude-ls"
1907
+
]
1908
+
},
1909
+
"typescript-eslint@8.45.0_eslint@9.36.0_typescript@5.9.3_@typescript-eslint+parser@8.45.0__eslint@9.36.0__typescript@5.9.3": {
1910
+
"integrity": "sha512-qzDmZw/Z5beNLUrXfd0HIW6MzIaAV5WNDxmMs9/3ojGOpYavofgNAAD/nC6tGV2PczIi0iw8vot2eAe/sBn7zg==",
1911
+
"dependencies": [
1912
+
"@typescript-eslint/eslint-plugin",
1913
+
"@typescript-eslint/parser",
1914
+
"@typescript-eslint/typescript-estree",
1915
+
"@typescript-eslint/utils",
1916
+
"eslint",
1917
+
"typescript"
1918
+
]
1919
+
},
1920
+
"typescript@5.9.3": {
1921
+
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
1922
+
"bin": true
1923
+
},
1924
+
"ua-parser-js@1.0.41": {
1925
+
"integrity": "sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==",
1926
+
"bin": true
1927
+
},
1928
+
"undici-types@7.13.0": {
1929
+
"integrity": "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ=="
1930
+
},
1931
+
"update-browserslist-db@1.1.3_browserslist@4.26.3": {
1932
+
"integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
1933
+
"dependencies": [
1934
+
"browserslist",
1935
+
"escalade",
1936
+
"picocolors"
1937
+
],
1938
+
"bin": true
1939
+
},
1940
+
"uri-js@4.4.1": {
1941
+
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
1942
+
"dependencies": [
1943
+
"punycode"
1944
+
]
1945
+
},
1946
+
"vite@7.1.8_@types+node@24.6.2_picomatch@4.0.3": {
1947
+
"integrity": "sha512-oBXvfSHEOL8jF+R9Am7h59Up07kVVGH1NrFGFoEG6bPDZP3tGpQhvkBpy5x7U6+E6wZCu9OihsWgJqDbQIm8LQ==",
1948
+
"dependencies": [
1949
+
"@types/node",
1950
+
"esbuild",
1951
+
"fdir",
1952
+
"picomatch@4.0.3",
1953
+
"postcss",
1954
+
"rollup",
1955
+
"tinyglobby"
1956
+
],
1957
+
"optionalDependencies": [
1958
+
"fsevents"
1959
+
],
1960
+
"optionalPeers": [
1961
+
"@types/node"
1962
+
],
1963
+
"bin": true
1964
+
},
1965
+
"webidl-conversions@3.0.1": {
1966
+
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
1967
+
},
1968
+
"whatwg-url@5.0.0": {
1969
+
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
1970
+
"dependencies": [
1971
+
"tr46",
1972
+
"webidl-conversions"
1973
+
]
1974
+
},
1975
+
"which@2.0.2": {
1976
+
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
1977
+
"dependencies": [
1978
+
"isexe"
1979
+
],
1980
+
"bin": true
1981
+
},
1982
+
"word-wrap@1.2.5": {
1983
+
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="
1984
+
},
1985
+
"yallist@3.1.1": {
1986
+
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
1987
+
},
1988
+
"yallist@5.0.0": {
1989
+
"integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="
1990
+
},
1991
+
"yaml@1.10.2": {
1992
+
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="
1993
+
},
1994
+
"yocto-queue@0.1.0": {
1995
+
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
1996
+
}
1997
+
},
1998
+
"workspace": {
1999
+
"packageJson": {
2000
+
"dependencies": [
2001
+
"npm:@eslint/js@^9.36.0",
2002
+
"npm:@tailwindcss/postcss@^4.1.14",
2003
+
"npm:@types/node@^24.6.0",
2004
+
"npm:@types/react-dom@^19.1.9",
2005
+
"npm:@types/react-relay@^18.2.1",
2006
+
"npm:@types/react@^19.1.16",
2007
+
"npm:@types/relay-runtime@^19.0.3",
2008
+
"npm:@vitejs/plugin-react@^5.0.4",
2009
+
"npm:autoprefixer@^10.4.21",
2010
+
"npm:babel-plugin-relay@^20.1.1",
2011
+
"npm:eslint-plugin-react-hooks@^5.2.0",
2012
+
"npm:eslint-plugin-react-refresh@~0.4.22",
2013
+
"npm:eslint@^9.36.0",
2014
+
"npm:globals@^16.4.0",
2015
+
"npm:graphql@^16.11.0",
2016
+
"npm:postcss@^8.5.6",
2017
+
"npm:react-dom@^19.1.1",
2018
+
"npm:react-relay@^20.1.1",
2019
+
"npm:react-router-dom@^7.9.3",
2020
+
"npm:react@^19.1.1",
2021
+
"npm:relay-compiler@^20.1.1",
2022
+
"npm:relay-runtime@^20.1.1",
2023
+
"npm:tailwindcss@^4.1.14",
2024
+
"npm:typescript-eslint@^8.45.0",
2025
+
"npm:typescript@~5.9.3",
2026
+
"npm:vite@^7.1.7"
2027
+
]
2028
+
}
2029
+
}
2030
+
}
+23
eslint.config.js
+23
eslint.config.js
···
1
+
import js from '@eslint/js'
2
+
import globals from 'globals'
3
+
import reactHooks from 'eslint-plugin-react-hooks'
4
+
import reactRefresh from 'eslint-plugin-react-refresh'
5
+
import tseslint from 'typescript-eslint'
6
+
import { defineConfig, globalIgnores } from 'eslint/config'
7
+
8
+
export default defineConfig([
9
+
globalIgnores(['dist']),
10
+
{
11
+
files: ['**/*.{ts,tsx}'],
12
+
extends: [
13
+
js.configs.recommended,
14
+
tseslint.configs.recommended,
15
+
reactHooks.configs['recommended-latest'],
16
+
reactRefresh.configs.vite,
17
+
],
18
+
languageOptions: {
19
+
ecmaVersion: 2020,
20
+
globals: globals.browser,
21
+
},
22
+
},
23
+
])
+19
index.html
+19
index.html
···
1
+
<!doctype html>
2
+
<html lang="en">
3
+
<head>
4
+
<meta charset="UTF-8" />
5
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+
<title>slices-relay</title>
8
+
<style>
9
+
body {
10
+
background-color: #09090b;
11
+
margin: 0;
12
+
}
13
+
</style>
14
+
</head>
15
+
<body>
16
+
<div id="root"></div>
17
+
<script type="module" src="/src/main.tsx"></script>
18
+
</body>
19
+
</html>
+47
package.json
+47
package.json
···
1
+
{
2
+
"name": "slices-relay",
3
+
"private": true,
4
+
"version": "0.0.0",
5
+
"type": "module",
6
+
"scripts": {
7
+
"dev": "vite",
8
+
"build": "tsc -b && vite build",
9
+
"lint": "eslint .",
10
+
"preview": "vite preview",
11
+
"schema:dev": "npx get-graphql-schema 'http://localhost:3000/graphql?slice=at://did:plc:fpruhuo22xkm5o7ttr2ktxdo/network.slices.slice/3m257yljpbg2a' > schema.graphql",
12
+
"schema:prod": "npx get-graphql-schema 'https://api.slices.network/graphql?slice=at://did:plc:fpruhuo22xkm5o7ttr2ktxdo/network.slices.slice/3m257yljpbg2a' > schema.graphql"
13
+
},
14
+
"dependencies": {
15
+
"react": "^19.1.1",
16
+
"react-dom": "^19.1.1",
17
+
"react-relay": "^20.1.1",
18
+
"react-router-dom": "^7.9.3",
19
+
"relay-runtime": "^20.1.1"
20
+
},
21
+
"devDependencies": {
22
+
"@eslint/js": "^9.36.0",
23
+
"@tailwindcss/postcss": "^4.1.14",
24
+
"@types/node": "^24.6.0",
25
+
"@types/react": "^19.1.16",
26
+
"@types/react-dom": "^19.1.9",
27
+
"@types/react-relay": "^18.2.1",
28
+
"@types/relay-runtime": "^19.0.3",
29
+
"@vitejs/plugin-react": "^5.0.4",
30
+
"autoprefixer": "^10.4.21",
31
+
"babel-plugin-relay": "^20.1.1",
32
+
"eslint": "^9.36.0",
33
+
"eslint-plugin-react-hooks": "^5.2.0",
34
+
"eslint-plugin-react-refresh": "^0.4.22",
35
+
"globals": "^16.4.0",
36
+
"graphql": "^16.11.0",
37
+
"postcss": "^8.5.6",
38
+
"relay-compiler": "^20.1.1",
39
+
"tailwindcss": "^4.1.14",
40
+
"typescript": "~5.9.3",
41
+
"typescript-eslint": "^8.45.0",
42
+
"vite": "^7.1.7"
43
+
},
44
+
"overrides": {
45
+
"graphql": "^16.11.0"
46
+
}
47
+
}
+6
postcss.config.js
+6
postcss.config.js
+5
relay.config.json
+5
relay.config.json
+586
schema.graphql
+586
schema.graphql
···
1
+
"""
2
+
Indicates that an Input Object is a OneOf Input Object (and thus requires exactly one of its field be provided)
3
+
"""
4
+
directive @oneOf on INPUT_OBJECT
5
+
6
+
"""
7
+
Provides a scalar specification URL for specifying the behavior of custom scalar types.
8
+
"""
9
+
directive @specifiedBy(
10
+
"""URL that specifies the behavior of this scalar."""
11
+
url: String!
12
+
) on SCALAR
13
+
14
+
input AggregationOrderBy {
15
+
count: SortDirection
16
+
}
17
+
18
+
type AppBskyActorProfile {
19
+
uri: String!
20
+
cid: String!
21
+
did: String!
22
+
indexedAt: String!
23
+
actorHandle: String
24
+
avatar: Blob
25
+
banner: Blob
26
+
createdAt: String
27
+
description: String
28
+
displayName: String
29
+
joinedViaStarterPack: JSON
30
+
labels: JSON
31
+
pinnedPost: JSON
32
+
appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]!
33
+
appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]!
34
+
appBskyActorProfile: AppBskyActorProfile
35
+
fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]!
36
+
appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]!
37
+
appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]!
38
+
appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]!
39
+
fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]!
40
+
}
41
+
42
+
type AppBskyActorProfileAggregated {
43
+
avatar: String
44
+
banner: String
45
+
createdAt: String
46
+
description: String
47
+
displayName: String
48
+
joinedViaStarterPack: String
49
+
labels: String
50
+
pinnedPost: String
51
+
count: Int!
52
+
}
53
+
54
+
type AppBskyActorProfileConnection {
55
+
totalCount: Int!
56
+
pageInfo: PageInfo!
57
+
edges: [AppBskyActorProfileEdge!]!
58
+
nodes: [AppBskyActorProfile!]!
59
+
}
60
+
61
+
type AppBskyActorProfileEdge {
62
+
node: AppBskyActorProfile!
63
+
cursor: String!
64
+
}
65
+
66
+
type AppBskyEmbedExternal {
67
+
uri: String!
68
+
cid: String!
69
+
did: String!
70
+
indexedAt: String!
71
+
actorHandle: String
72
+
external: JSON!
73
+
appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]!
74
+
appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]!
75
+
appBskyActorProfile: AppBskyActorProfile
76
+
fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]!
77
+
appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]!
78
+
appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]!
79
+
appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]!
80
+
fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]!
81
+
}
82
+
83
+
type AppBskyEmbedExternalAggregated {
84
+
external: String
85
+
count: Int!
86
+
}
87
+
88
+
type AppBskyEmbedExternalConnection {
89
+
totalCount: Int!
90
+
pageInfo: PageInfo!
91
+
edges: [AppBskyEmbedExternalEdge!]!
92
+
nodes: [AppBskyEmbedExternal!]!
93
+
}
94
+
95
+
type AppBskyEmbedExternalEdge {
96
+
node: AppBskyEmbedExternal!
97
+
cursor: String!
98
+
}
99
+
100
+
type AppBskyEmbedImages {
101
+
uri: String!
102
+
cid: String!
103
+
did: String!
104
+
indexedAt: String!
105
+
actorHandle: String
106
+
images: JSON!
107
+
appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]!
108
+
appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]!
109
+
appBskyActorProfile: AppBskyActorProfile
110
+
fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]!
111
+
appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]!
112
+
appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]!
113
+
appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]!
114
+
fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]!
115
+
}
116
+
117
+
type AppBskyEmbedImagesAggregated {
118
+
images: String
119
+
count: Int!
120
+
}
121
+
122
+
type AppBskyEmbedImagesConnection {
123
+
totalCount: Int!
124
+
pageInfo: PageInfo!
125
+
edges: [AppBskyEmbedImagesEdge!]!
126
+
nodes: [AppBskyEmbedImages!]!
127
+
}
128
+
129
+
type AppBskyEmbedImagesEdge {
130
+
node: AppBskyEmbedImages!
131
+
cursor: String!
132
+
}
133
+
134
+
type AppBskyEmbedRecord {
135
+
uri: String!
136
+
cid: String!
137
+
did: String!
138
+
indexedAt: String!
139
+
actorHandle: String
140
+
record: JSON!
141
+
appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]!
142
+
appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]!
143
+
appBskyActorProfile: AppBskyActorProfile
144
+
fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]!
145
+
appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]!
146
+
appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]!
147
+
appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]!
148
+
fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]!
149
+
}
150
+
151
+
type AppBskyEmbedRecordAggregated {
152
+
record: String
153
+
count: Int!
154
+
}
155
+
156
+
type AppBskyEmbedRecordConnection {
157
+
totalCount: Int!
158
+
pageInfo: PageInfo!
159
+
edges: [AppBskyEmbedRecordEdge!]!
160
+
nodes: [AppBskyEmbedRecord!]!
161
+
}
162
+
163
+
type AppBskyEmbedRecordEdge {
164
+
node: AppBskyEmbedRecord!
165
+
cursor: String!
166
+
}
167
+
168
+
type AppBskyEmbedRecordWithMedia {
169
+
uri: String!
170
+
cid: String!
171
+
did: String!
172
+
indexedAt: String!
173
+
actorHandle: String
174
+
media: JSON!
175
+
record: JSON!
176
+
appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]!
177
+
appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]!
178
+
appBskyActorProfile: AppBskyActorProfile
179
+
fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]!
180
+
appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]!
181
+
appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]!
182
+
appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]!
183
+
fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]!
184
+
}
185
+
186
+
type AppBskyEmbedRecordWithMediaAggregated {
187
+
media: String
188
+
record: String
189
+
count: Int!
190
+
}
191
+
192
+
type AppBskyEmbedRecordWithMediaConnection {
193
+
totalCount: Int!
194
+
pageInfo: PageInfo!
195
+
edges: [AppBskyEmbedRecordWithMediaEdge!]!
196
+
nodes: [AppBskyEmbedRecordWithMedia!]!
197
+
}
198
+
199
+
type AppBskyEmbedRecordWithMediaEdge {
200
+
node: AppBskyEmbedRecordWithMedia!
201
+
cursor: String!
202
+
}
203
+
204
+
type AppBskyEmbedVideo {
205
+
uri: String!
206
+
cid: String!
207
+
did: String!
208
+
indexedAt: String!
209
+
actorHandle: String
210
+
alt: String
211
+
aspectRatio: JSON
212
+
captions: JSON
213
+
video: Blob!
214
+
appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]!
215
+
appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]!
216
+
appBskyActorProfile: AppBskyActorProfile
217
+
fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]!
218
+
appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]!
219
+
appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]!
220
+
appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]!
221
+
fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]!
222
+
}
223
+
224
+
type AppBskyEmbedVideoAggregated {
225
+
alt: String
226
+
aspectRatio: String
227
+
captions: String
228
+
video: String
229
+
count: Int!
230
+
}
231
+
232
+
type AppBskyEmbedVideoConnection {
233
+
totalCount: Int!
234
+
pageInfo: PageInfo!
235
+
edges: [AppBskyEmbedVideoEdge!]!
236
+
nodes: [AppBskyEmbedVideo!]!
237
+
}
238
+
239
+
type AppBskyEmbedVideoEdge {
240
+
node: AppBskyEmbedVideo!
241
+
cursor: String!
242
+
}
243
+
244
+
type AppBskyFeedPostgate {
245
+
uri: String!
246
+
cid: String!
247
+
did: String!
248
+
indexedAt: String!
249
+
actorHandle: String
250
+
createdAt: String!
251
+
detachedEmbeddingUris: [String]
252
+
embeddingRules: JSON
253
+
post: String!
254
+
appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]!
255
+
appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]!
256
+
appBskyActorProfile: AppBskyActorProfile
257
+
fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]!
258
+
appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]!
259
+
appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]!
260
+
appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]!
261
+
fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]!
262
+
}
263
+
264
+
type AppBskyFeedPostgateAggregated {
265
+
createdAt: String
266
+
detachedEmbeddingUris: String
267
+
embeddingRules: String
268
+
post: String
269
+
count: Int!
270
+
}
271
+
272
+
type AppBskyFeedPostgateConnection {
273
+
totalCount: Int!
274
+
pageInfo: PageInfo!
275
+
edges: [AppBskyFeedPostgateEdge!]!
276
+
nodes: [AppBskyFeedPostgate!]!
277
+
}
278
+
279
+
type AppBskyFeedPostgateEdge {
280
+
node: AppBskyFeedPostgate!
281
+
cursor: String!
282
+
}
283
+
284
+
type AppBskyFeedThreadgate {
285
+
uri: String!
286
+
cid: String!
287
+
did: String!
288
+
indexedAt: String!
289
+
actorHandle: String
290
+
allow: JSON
291
+
createdAt: String!
292
+
hiddenReplies: [String]
293
+
post: String!
294
+
appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]!
295
+
appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]!
296
+
appBskyActorProfile: AppBskyActorProfile
297
+
fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]!
298
+
appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]!
299
+
appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]!
300
+
appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]!
301
+
fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]!
302
+
}
303
+
304
+
type AppBskyFeedThreadgateAggregated {
305
+
allow: String
306
+
createdAt: String
307
+
hiddenReplies: String
308
+
post: String
309
+
count: Int!
310
+
}
311
+
312
+
type AppBskyFeedThreadgateConnection {
313
+
totalCount: Int!
314
+
pageInfo: PageInfo!
315
+
edges: [AppBskyFeedThreadgateEdge!]!
316
+
nodes: [AppBskyFeedThreadgate!]!
317
+
}
318
+
319
+
type AppBskyFeedThreadgateEdge {
320
+
node: AppBskyFeedThreadgate!
321
+
cursor: String!
322
+
}
323
+
324
+
type AppBskyRichtextFacet {
325
+
uri: String!
326
+
cid: String!
327
+
did: String!
328
+
indexedAt: String!
329
+
actorHandle: String
330
+
features: JSON!
331
+
index: JSON!
332
+
appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]!
333
+
appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]!
334
+
appBskyActorProfile: AppBskyActorProfile
335
+
fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]!
336
+
appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]!
337
+
appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]!
338
+
appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]!
339
+
fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]!
340
+
}
341
+
342
+
type AppBskyRichtextFacetAggregated {
343
+
features: String
344
+
index: String
345
+
count: Int!
346
+
}
347
+
348
+
type AppBskyRichtextFacetConnection {
349
+
totalCount: Int!
350
+
pageInfo: PageInfo!
351
+
edges: [AppBskyRichtextFacetEdge!]!
352
+
nodes: [AppBskyRichtextFacet!]!
353
+
}
354
+
355
+
type AppBskyRichtextFacetEdge {
356
+
node: AppBskyRichtextFacet!
357
+
cursor: String!
358
+
}
359
+
360
+
type Blob {
361
+
ref: String!
362
+
mimeType: String!
363
+
size: Int!
364
+
365
+
"""
366
+
Generate CDN URL for the blob with the specified preset (avatar, banner, feed_thumbnail, feed_fullsize)
367
+
"""
368
+
url(preset: String): String!
369
+
}
370
+
371
+
type ComAtprotoRepoStrongRef {
372
+
did: String!
373
+
indexedAt: String!
374
+
actorHandle: String
375
+
cid: String!
376
+
uri: String!
377
+
appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]!
378
+
appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]!
379
+
appBskyActorProfile: AppBskyActorProfile
380
+
fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]!
381
+
appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]!
382
+
appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]!
383
+
appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]!
384
+
fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]!
385
+
}
386
+
387
+
type ComAtprotoRepoStrongRefAggregated {
388
+
cid: String
389
+
uri: String
390
+
count: Int!
391
+
}
392
+
393
+
type ComAtprotoRepoStrongRefConnection {
394
+
totalCount: Int!
395
+
pageInfo: PageInfo!
396
+
edges: [ComAtprotoRepoStrongRefEdge!]!
397
+
nodes: [ComAtprotoRepoStrongRef!]!
398
+
}
399
+
400
+
type ComAtprotoRepoStrongRefEdge {
401
+
node: ComAtprotoRepoStrongRef!
402
+
cursor: String!
403
+
}
404
+
405
+
type FmTealAlphaFeedPlay {
406
+
uri: String!
407
+
cid: String!
408
+
did: String!
409
+
indexedAt: String!
410
+
actorHandle: String
411
+
artistMbIds: [String]
412
+
artistNames: [String]
413
+
artists: JSON
414
+
duration: Int
415
+
isrc: String
416
+
musicServiceBaseDomain: String
417
+
originUrl: String
418
+
playedTime: String
419
+
recordingMbId: String
420
+
releaseMbId: String
421
+
releaseName: String
422
+
submissionClientAgent: String
423
+
trackMbId: String
424
+
trackName: String!
425
+
appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]!
426
+
appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]!
427
+
appBskyActorProfile: AppBskyActorProfile
428
+
fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]!
429
+
appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]!
430
+
appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]!
431
+
appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]!
432
+
fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]!
433
+
}
434
+
435
+
type FmTealAlphaFeedPlayAggregated {
436
+
artistMbIds: String
437
+
artistNames: String
438
+
artists: String
439
+
duration: String
440
+
isrc: String
441
+
musicServiceBaseDomain: String
442
+
originUrl: String
443
+
playedTime: String
444
+
recordingMbId: String
445
+
releaseMbId: String
446
+
releaseName: String
447
+
submissionClientAgent: String
448
+
trackMbId: String
449
+
trackName: String
450
+
count: Int!
451
+
}
452
+
453
+
type FmTealAlphaFeedPlayConnection {
454
+
totalCount: Int!
455
+
pageInfo: PageInfo!
456
+
edges: [FmTealAlphaFeedPlayEdge!]!
457
+
nodes: [FmTealAlphaFeedPlay!]!
458
+
}
459
+
460
+
type FmTealAlphaFeedPlayEdge {
461
+
node: FmTealAlphaFeedPlay!
462
+
cursor: String!
463
+
}
464
+
465
+
scalar JSON
466
+
467
+
type Mutation {
468
+
"""Sync user collections for a given DID"""
469
+
syncUserCollections(did: String!): SyncResult!
470
+
}
471
+
472
+
type PageInfo {
473
+
hasNextPage: Boolean!
474
+
hasPreviousPage: Boolean!
475
+
startCursor: String
476
+
endCursor: String
477
+
}
478
+
479
+
type Query {
480
+
"""Query app.bsky.embed.record records"""
481
+
appBskyEmbedRecords(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): AppBskyEmbedRecordConnection!
482
+
483
+
"""
484
+
Aggregated query for app.bsky.embed.record records with GROUP BY support
485
+
"""
486
+
appBskyEmbedRecordsAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [AppBskyEmbedRecordAggregated!]!
487
+
488
+
"""Query app.bsky.embed.images records"""
489
+
appBskyEmbedImageses(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): AppBskyEmbedImagesConnection!
490
+
491
+
"""
492
+
Aggregated query for app.bsky.embed.images records with GROUP BY support
493
+
"""
494
+
appBskyEmbedImagesesAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [AppBskyEmbedImagesAggregated!]!
495
+
496
+
"""Query app.bsky.embed.video records"""
497
+
appBskyEmbedVideos(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): AppBskyEmbedVideoConnection!
498
+
499
+
"""
500
+
Aggregated query for app.bsky.embed.video records with GROUP BY support
501
+
"""
502
+
appBskyEmbedVideosAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [AppBskyEmbedVideoAggregated!]!
503
+
504
+
"""Query app.bsky.embed.recordWithMedia records"""
505
+
appBskyEmbedRecordWithMedias(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): AppBskyEmbedRecordWithMediaConnection!
506
+
507
+
"""
508
+
Aggregated query for app.bsky.embed.recordWithMedia records with GROUP BY support
509
+
"""
510
+
appBskyEmbedRecordWithMediasAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [AppBskyEmbedRecordWithMediaAggregated!]!
511
+
512
+
"""Query app.bsky.embed.external records"""
513
+
appBskyEmbedExternals(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): AppBskyEmbedExternalConnection!
514
+
515
+
"""
516
+
Aggregated query for app.bsky.embed.external records with GROUP BY support
517
+
"""
518
+
appBskyEmbedExternalsAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [AppBskyEmbedExternalAggregated!]!
519
+
520
+
"""Query app.bsky.feed.postgate records"""
521
+
appBskyFeedPostgates(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): AppBskyFeedPostgateConnection!
522
+
523
+
"""
524
+
Aggregated query for app.bsky.feed.postgate records with GROUP BY support
525
+
"""
526
+
appBskyFeedPostgatesAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [AppBskyFeedPostgateAggregated!]!
527
+
528
+
"""Query app.bsky.feed.threadgate records"""
529
+
appBskyFeedThreadgates(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): AppBskyFeedThreadgateConnection!
530
+
531
+
"""
532
+
Aggregated query for app.bsky.feed.threadgate records with GROUP BY support
533
+
"""
534
+
appBskyFeedThreadgatesAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [AppBskyFeedThreadgateAggregated!]!
535
+
536
+
"""Query app.bsky.richtext.facet records"""
537
+
appBskyRichtextFacets(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): AppBskyRichtextFacetConnection!
538
+
539
+
"""
540
+
Aggregated query for app.bsky.richtext.facet records with GROUP BY support
541
+
"""
542
+
appBskyRichtextFacetsAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [AppBskyRichtextFacetAggregated!]!
543
+
544
+
"""Query app.bsky.actor.profile records"""
545
+
appBskyActorProfiles(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): AppBskyActorProfileConnection!
546
+
547
+
"""
548
+
Aggregated query for app.bsky.actor.profile records with GROUP BY support
549
+
"""
550
+
appBskyActorProfilesAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [AppBskyActorProfileAggregated!]!
551
+
552
+
"""Query com.atproto.repo.strongRef records"""
553
+
comAtprotoRepoStrongRefs(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): ComAtprotoRepoStrongRefConnection!
554
+
555
+
"""
556
+
Aggregated query for com.atproto.repo.strongRef records with GROUP BY support
557
+
"""
558
+
comAtprotoRepoStrongRefsAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [ComAtprotoRepoStrongRefAggregated!]!
559
+
560
+
"""Query fm.teal.alpha.feed.play records"""
561
+
fmTealAlphaFeedPlays(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): FmTealAlphaFeedPlayConnection!
562
+
563
+
"""
564
+
Aggregated query for fm.teal.alpha.feed.play records with GROUP BY support
565
+
"""
566
+
fmTealAlphaFeedPlaysAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [FmTealAlphaFeedPlayAggregated!]!
567
+
}
568
+
569
+
enum SortDirection {
570
+
asc
571
+
desc
572
+
}
573
+
574
+
input SortField {
575
+
field: String!
576
+
direction: SortDirection!
577
+
}
578
+
579
+
type SyncResult {
580
+
success: Boolean!
581
+
reposProcessed: Int!
582
+
recordsSynced: Int!
583
+
timedOut: Boolean!
584
+
message: String!
585
+
}
586
+
+94
src/AlbumItem.tsx
+94
src/AlbumItem.tsx
···
1
+
import { useAlbumArt } from "./useAlbumArt";
2
+
3
+
interface Artist {
4
+
artistName: string;
5
+
}
6
+
7
+
interface AlbumItemProps {
8
+
releaseName: string;
9
+
releaseMbId: string | null | undefined;
10
+
artists: string | null | undefined;
11
+
count: number;
12
+
rank: number;
13
+
maxCount: number;
14
+
}
15
+
16
+
export default function AlbumItem({
17
+
releaseName,
18
+
releaseMbId,
19
+
artists,
20
+
count,
21
+
rank,
22
+
maxCount,
23
+
}: AlbumItemProps) {
24
+
const { albumArtUrl, isLoading } = useAlbumArt(releaseMbId);
25
+
const barWidth = maxCount > 0 ? (count / maxCount) * 100 : 0;
26
+
27
+
// Parse artists JSON
28
+
let artistNames = "Unknown Artist";
29
+
if (artists) {
30
+
try {
31
+
const parsed = typeof artists === 'string' ? JSON.parse(artists) : artists;
32
+
if (Array.isArray(parsed)) {
33
+
artistNames = parsed.map((a: Artist) => a.artistName).join(", ");
34
+
} else if (typeof parsed === 'string') {
35
+
artistNames = parsed;
36
+
}
37
+
} catch (e) {
38
+
console.log('Failed to parse artists:', artists, e);
39
+
artistNames = String(artists);
40
+
}
41
+
} else {
42
+
console.log('No artists data for:', releaseName);
43
+
}
44
+
45
+
return (
46
+
<div className="group py-3 px-4 hover:bg-zinc-900/50 transition-colors relative overflow-hidden">
47
+
<div
48
+
className="absolute inset-y-0 left-0 bg-violet-500/10 transition-all"
49
+
style={{ width: `${barWidth}%` }}
50
+
/>
51
+
<div className="flex items-center gap-4 relative">
52
+
<div className="text-xs text-zinc-600 w-8 text-right flex-shrink-0 font-medium">
53
+
{rank}
54
+
</div>
55
+
56
+
<div className="flex-shrink-0">
57
+
{isLoading ? (
58
+
<div className="w-10 h-10 bg-zinc-800 animate-pulse" />
59
+
) : albumArtUrl ? (
60
+
<img
61
+
src={albumArtUrl}
62
+
alt={`${releaseName} album art`}
63
+
className="w-10 h-10 object-cover"
64
+
loading="lazy"
65
+
/>
66
+
) : (
67
+
<div className="w-10 h-10 bg-zinc-800 flex items-center justify-center">
68
+
<svg
69
+
className="w-5 h-5 text-zinc-600"
70
+
fill="currentColor"
71
+
viewBox="0 0 20 20"
72
+
>
73
+
<path d="M18 3a1 1 0 00-1.196-.98l-10 2A1 1 0 006 5v9.114A4.369 4.369 0 005 14c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V7.82l8-1.6v5.894A4.37 4.37 0 0015 12c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V3z" />
74
+
</svg>
75
+
</div>
76
+
)}
77
+
</div>
78
+
79
+
<div className="flex-1 min-w-0">
80
+
<h3 className="text-sm font-medium text-zinc-100 truncate">
81
+
{releaseName}
82
+
</h3>
83
+
<p className="text-xs text-zinc-500 truncate">{artistNames}</p>
84
+
</div>
85
+
86
+
<div className="text-right flex-shrink-0">
87
+
<p className="text-xs text-zinc-400 font-medium">
88
+
{count.toLocaleString()}
89
+
</p>
90
+
</div>
91
+
</div>
92
+
</div>
93
+
);
94
+
}
+183
src/App.tsx
+183
src/App.tsx
···
1
+
import { graphql, useLazyLoadQuery, usePaginationFragment } from "react-relay";
2
+
import { useEffect, useRef, useState } from "react";
3
+
import type { AppQuery } from "./__generated__/AppQuery.graphql";
4
+
import type { App_plays$key } from "./__generated__/App_plays.graphql";
5
+
import TrackItem from "./TrackItem";
6
+
import TopAlbums from "./TopAlbums";
7
+
import TopTracks from "./TopTracks";
8
+
9
+
export default function App() {
10
+
const [activeTab, setActiveTab] = useState<"recent" | "tracks" | "albums">("recent");
11
+
const queryData = useLazyLoadQuery<AppQuery>(
12
+
graphql`
13
+
query AppQuery {
14
+
...App_plays
15
+
}
16
+
`,
17
+
{}
18
+
);
19
+
20
+
const { data, loadNext, hasNext, isLoadingNext } = usePaginationFragment<
21
+
AppQuery,
22
+
App_plays$key
23
+
>(
24
+
graphql`
25
+
fragment App_plays on Query
26
+
@refetchable(queryName: "AppPaginationQuery")
27
+
@argumentDefinitions(
28
+
cursor: { type: "String" }
29
+
count: { type: "Int", defaultValue: 20 }
30
+
) {
31
+
fmTealAlphaFeedPlays(
32
+
first: $count
33
+
after: $cursor
34
+
sortBy: [{ field: "playedTime", direction: desc }]
35
+
) @connection(key: "App_fmTealAlphaFeedPlays", filters: ["sortBy"]) {
36
+
totalCount
37
+
edges {
38
+
node {
39
+
playedTime
40
+
...TrackItem_play
41
+
}
42
+
}
43
+
}
44
+
}
45
+
`,
46
+
queryData
47
+
);
48
+
49
+
const loadMoreRef = useRef<HTMLDivElement>(null);
50
+
51
+
useEffect(() => {
52
+
window.scrollTo(0, 0);
53
+
}, []);
54
+
55
+
const plays =
56
+
data?.fmTealAlphaFeedPlays?.edges
57
+
?.map((edge) => edge.node)
58
+
.filter((n) => n != null) || [];
59
+
60
+
useEffect(() => {
61
+
if (!loadMoreRef.current || !hasNext || activeTab !== "recent") return;
62
+
63
+
const observer = new IntersectionObserver(
64
+
(entries) => {
65
+
if (entries[0].isIntersecting && hasNext && !isLoadingNext) {
66
+
loadNext(20);
67
+
}
68
+
},
69
+
{ threshold: 0.1 }
70
+
);
71
+
72
+
observer.observe(loadMoreRef.current);
73
+
74
+
return () => observer.disconnect();
75
+
}, [hasNext, isLoadingNext, loadNext, activeTab]);
76
+
77
+
// Group plays by date
78
+
const groupedPlays: { date: string; plays: typeof plays }[] = [];
79
+
let currentDate = "";
80
+
81
+
plays.forEach((play) => {
82
+
if (!play?.playedTime) return;
83
+
84
+
const playDate = new Date(play.playedTime).toLocaleDateString("en-US", {
85
+
weekday: "long",
86
+
day: "numeric",
87
+
month: "long",
88
+
year: "numeric",
89
+
});
90
+
91
+
if (playDate !== currentDate) {
92
+
currentDate = playDate;
93
+
groupedPlays.push({ date: playDate, plays: [play] });
94
+
} else {
95
+
groupedPlays[groupedPlays.length - 1].plays.push(play);
96
+
}
97
+
});
98
+
99
+
return (
100
+
<div className="min-h-screen bg-zinc-950 text-zinc-300 font-mono">
101
+
<div className="max-w-4xl mx-auto px-6 py-12">
102
+
<div className="mb-12 flex items-end justify-between border-b border-zinc-800 pb-6">
103
+
<div>
104
+
<h1 className="text-xs font-medium uppercase tracking-wider text-zinc-500">Listening History</h1>
105
+
<p className="text-xs text-zinc-600 mt-1">fm.teal.alpha.feed.play</p>
106
+
</div>
107
+
108
+
<div className="flex gap-4 text-xs">
109
+
<button
110
+
onClick={() => setActiveTab("recent")}
111
+
className={`px-2 py-1 transition-colors ${
112
+
activeTab === "recent"
113
+
? "text-zinc-400"
114
+
: "text-zinc-500 hover:text-zinc-300"
115
+
}`}
116
+
>
117
+
Recent
118
+
</button>
119
+
<button
120
+
onClick={() => setActiveTab("tracks")}
121
+
className={`px-2 py-1 transition-colors ${
122
+
activeTab === "tracks"
123
+
? "text-zinc-400"
124
+
: "text-zinc-500 hover:text-zinc-300"
125
+
}`}
126
+
>
127
+
Top Tracks
128
+
</button>
129
+
<button
130
+
onClick={() => setActiveTab("albums")}
131
+
className={`px-2 py-1 transition-colors ${
132
+
activeTab === "albums"
133
+
? "text-zinc-400"
134
+
: "text-zinc-500 hover:text-zinc-300"
135
+
}`}
136
+
>
137
+
Top Albums
138
+
</button>
139
+
</div>
140
+
</div>
141
+
142
+
{activeTab === "recent" ? (
143
+
<>
144
+
<div className="mb-8">
145
+
<p className="text-xs text-zinc-500 uppercase tracking-wider">
146
+
{data?.fmTealAlphaFeedPlays?.totalCount?.toLocaleString()} scrobbles
147
+
</p>
148
+
</div>
149
+
150
+
<div>
151
+
{groupedPlays.map((group, groupIndex) => (
152
+
<div key={groupIndex} className="mb-12">
153
+
<h2 className="text-xs text-zinc-600 font-medium mb-6 uppercase tracking-wider">
154
+
{group.date}
155
+
</h2>
156
+
<div className="space-y-1">
157
+
{group.plays.map((play, index) => (
158
+
<TrackItem key={index} play={play} />
159
+
))}
160
+
</div>
161
+
</div>
162
+
))}
163
+
</div>
164
+
165
+
{hasNext && (
166
+
<div ref={loadMoreRef} className="py-12 text-center">
167
+
{isLoadingNext ? (
168
+
<p className="text-xs text-zinc-600 uppercase tracking-wider">Loading...</p>
169
+
) : (
170
+
<p className="text-xs text-zinc-700 uppercase tracking-wider">·</p>
171
+
)}
172
+
</div>
173
+
)}
174
+
</>
175
+
) : activeTab === "tracks" ? (
176
+
<TopTracks />
177
+
) : (
178
+
<TopAlbums />
179
+
)}
180
+
</div>
181
+
</div>
182
+
);
183
+
}
+3
src/LoadingFallback.tsx
+3
src/LoadingFallback.tsx
+148
src/Profile.tsx
+148
src/Profile.tsx
···
1
+
import { graphql, useLazyLoadQuery, usePaginationFragment } from "react-relay";
2
+
import { useParams, Link } from "react-router-dom";
3
+
import { useEffect, useRef } from "react";
4
+
import type { ProfileQuery as ProfileQueryType } from "./__generated__/ProfileQuery.graphql";
5
+
import type { Profile_plays$key } from "./__generated__/Profile_plays.graphql";
6
+
import TrackItem from "./TrackItem";
7
+
8
+
export default function Profile() {
9
+
const { handle } = useParams<{ handle: string }>();
10
+
11
+
const queryData = useLazyLoadQuery<ProfileQueryType>(
12
+
graphql`
13
+
query ProfileQuery($where: JSON!) {
14
+
...Profile_plays @arguments(where: $where)
15
+
}
16
+
`,
17
+
{
18
+
where: { actorHandle: { eq: handle } },
19
+
}
20
+
);
21
+
22
+
const { data, loadNext, hasNext, isLoadingNext } = usePaginationFragment<
23
+
ProfileQueryType,
24
+
Profile_plays$key
25
+
>(
26
+
graphql`
27
+
fragment Profile_plays on Query
28
+
@refetchable(queryName: "ProfilePaginationQuery")
29
+
@argumentDefinitions(
30
+
cursor: { type: "String" }
31
+
count: { type: "Int", defaultValue: 20 }
32
+
where: { type: "JSON!" }
33
+
) {
34
+
fmTealAlphaFeedPlays(
35
+
first: $count
36
+
after: $cursor
37
+
sortBy: [{ field: "playedTime", direction: desc }]
38
+
where: $where
39
+
)
40
+
@connection(
41
+
key: "Profile_fmTealAlphaFeedPlays"
42
+
filters: ["where", "sortBy"]
43
+
) {
44
+
totalCount
45
+
edges {
46
+
node {
47
+
...TrackItem_play
48
+
actorHandle
49
+
appBskyActorProfile {
50
+
displayName
51
+
description
52
+
avatar {
53
+
url(preset: "avatar")
54
+
}
55
+
}
56
+
}
57
+
}
58
+
}
59
+
}
60
+
`,
61
+
queryData
62
+
);
63
+
64
+
const loadMoreRef = useRef<HTMLDivElement>(null);
65
+
66
+
const plays = data?.fmTealAlphaFeedPlays?.edges?.map((edge) => edge.node).filter((n) => n != null) || [];
67
+
const profile = plays?.[0]?.appBskyActorProfile;
68
+
69
+
useEffect(() => {
70
+
window.scrollTo(0, 0);
71
+
}, [handle]);
72
+
73
+
useEffect(() => {
74
+
if (!loadMoreRef.current || !hasNext) return;
75
+
76
+
const observer = new IntersectionObserver(
77
+
(entries) => {
78
+
if (entries[0].isIntersecting && hasNext && !isLoadingNext) {
79
+
loadNext(20);
80
+
}
81
+
},
82
+
{ threshold: 0.1 }
83
+
);
84
+
85
+
observer.observe(loadMoreRef.current);
86
+
87
+
return () => observer.disconnect();
88
+
}, [hasNext, isLoadingNext, loadNext]);
89
+
90
+
return (
91
+
<div className="min-h-screen bg-zinc-950 text-zinc-300 font-mono">
92
+
<div className="max-w-4xl mx-auto px-6 py-12">
93
+
<Link
94
+
to="/"
95
+
className="px-2 py-1 text-xs text-zinc-500 hover:text-zinc-300 transition-colors inline-block mb-8"
96
+
>
97
+
← Back
98
+
</Link>
99
+
100
+
<div className="mb-12 flex items-start gap-6 border-b border-zinc-800 pb-6">
101
+
{profile?.avatar?.url && (
102
+
<img
103
+
src={profile.avatar.url}
104
+
alt={profile.displayName ?? handle ?? "User"}
105
+
className="w-16 h-16 flex-shrink-0 object-cover"
106
+
/>
107
+
)}
108
+
<div className="flex-1">
109
+
<h1 className="text-lg font-medium mb-1 text-zinc-100">
110
+
{profile?.displayName ?? handle}
111
+
</h1>
112
+
<p className="text-xs text-zinc-500 mb-2">@{handle}</p>
113
+
{profile?.description && (
114
+
<p className="text-xs text-zinc-400">{profile.description}</p>
115
+
)}
116
+
</div>
117
+
</div>
118
+
119
+
<div className="mb-8">
120
+
<h2 className="text-sm font-medium uppercase tracking-wider text-zinc-400 mb-2">Recent Tracks</h2>
121
+
<p className="text-xs text-zinc-500 uppercase tracking-wider">
122
+
{(data?.fmTealAlphaFeedPlays?.totalCount ?? 0).toLocaleString()} scrobbles
123
+
</p>
124
+
</div>
125
+
126
+
<div className="space-y-1">
127
+
{plays && plays.length > 0 ? (
128
+
plays.map((play, index) => <TrackItem key={index} play={play} />)
129
+
) : (
130
+
<p className="text-zinc-600 text-center py-8 text-xs uppercase tracking-wider">
131
+
No tracks found for this user
132
+
</p>
133
+
)}
134
+
</div>
135
+
136
+
{hasNext && (
137
+
<div ref={loadMoreRef} className="py-12 text-center">
138
+
{isLoadingNext ? (
139
+
<p className="text-xs text-zinc-600 uppercase tracking-wider">Loading...</p>
140
+
) : (
141
+
<p className="text-xs text-zinc-700 uppercase tracking-wider">·</p>
142
+
)}
143
+
</div>
144
+
)}
145
+
</div>
146
+
</div>
147
+
);
148
+
}
+65
src/TopAlbums.tsx
+65
src/TopAlbums.tsx
···
1
+
import { graphql, useLazyLoadQuery } from "react-relay";
2
+
import type { TopAlbumsQuery } from "./__generated__/TopAlbumsQuery.graphql";
3
+
import AlbumItem from "./AlbumItem";
4
+
5
+
export default function TopAlbums() {
6
+
const data = useLazyLoadQuery<TopAlbumsQuery>(
7
+
graphql`
8
+
query TopAlbumsQuery {
9
+
fmTealAlphaFeedPlaysAggregated(
10
+
groupBy: ["releaseMbId", "releaseName", "artists"]
11
+
orderBy: { count: desc }
12
+
limit: 100
13
+
) {
14
+
releaseMbId
15
+
releaseName
16
+
artists
17
+
count
18
+
}
19
+
}
20
+
`,
21
+
{}
22
+
);
23
+
24
+
const albums = [...(data.fmTealAlphaFeedPlaysAggregated || [])];
25
+
26
+
// Deduplicate by release name, keeping the one with highest count
27
+
// Prefer entries with artist data
28
+
const seenNames = new Set<string>();
29
+
const dedupedAlbums = albums
30
+
.sort((a, b) => {
31
+
// First sort by count (already sorted from query)
32
+
if (b.count !== a.count) return b.count - a.count;
33
+
// Then prefer entries with artists data
34
+
if (a.artists && !b.artists) return -1;
35
+
if (!a.artists && b.artists) return 1;
36
+
return 0;
37
+
})
38
+
.filter((album) => {
39
+
const name = album.releaseName || "Unknown Album";
40
+
if (seenNames.has(name)) {
41
+
return false;
42
+
}
43
+
seenNames.add(name);
44
+
return true;
45
+
})
46
+
.slice(0, 50);
47
+
48
+
const maxCount = dedupedAlbums.length > 0 ? dedupedAlbums[0].count : 0;
49
+
50
+
return (
51
+
<div className="space-y-1">
52
+
{dedupedAlbums.map((album, index) => (
53
+
<AlbumItem
54
+
key={album.releaseMbId || index}
55
+
releaseName={album.releaseName || "Unknown Album"}
56
+
releaseMbId={album.releaseMbId}
57
+
artists={album.artists}
58
+
count={album.count}
59
+
rank={index + 1}
60
+
maxCount={maxCount}
61
+
/>
62
+
))}
63
+
</div>
64
+
);
65
+
}
+91
src/TopTrackItem.tsx
+91
src/TopTrackItem.tsx
···
1
+
import { useAlbumArt } from "./useAlbumArt";
2
+
3
+
interface Artist {
4
+
artistName: string;
5
+
}
6
+
7
+
interface TopTrackItemProps {
8
+
trackName: string;
9
+
releaseMbId: string | null | undefined;
10
+
artists: string | null | undefined;
11
+
count: number;
12
+
rank: number;
13
+
maxCount: number;
14
+
}
15
+
16
+
export default function TopTrackItem({
17
+
trackName,
18
+
releaseMbId,
19
+
artists,
20
+
count,
21
+
rank,
22
+
maxCount,
23
+
}: TopTrackItemProps) {
24
+
const { albumArtUrl, isLoading } = useAlbumArt(releaseMbId);
25
+
const barWidth = maxCount > 0 ? (count / maxCount) * 100 : 0;
26
+
27
+
// Parse artists JSON
28
+
let artistNames = "Unknown Artist";
29
+
if (artists) {
30
+
try {
31
+
const parsed = typeof artists === 'string' ? JSON.parse(artists) : artists;
32
+
if (Array.isArray(parsed)) {
33
+
artistNames = parsed.map((a: Artist) => a.artistName).join(", ");
34
+
} else if (typeof parsed === 'string') {
35
+
artistNames = parsed;
36
+
}
37
+
} catch {
38
+
artistNames = String(artists);
39
+
}
40
+
}
41
+
42
+
return (
43
+
<div className="group py-3 px-4 hover:bg-zinc-900/50 transition-colors relative overflow-hidden">
44
+
<div
45
+
className="absolute inset-y-0 left-0 bg-violet-500/10 transition-all"
46
+
style={{ width: `${barWidth}%` }}
47
+
/>
48
+
<div className="flex items-center gap-4 relative">
49
+
<div className="text-xs text-zinc-600 w-8 text-right flex-shrink-0 font-medium">
50
+
{rank}
51
+
</div>
52
+
53
+
<div className="flex-shrink-0">
54
+
{isLoading ? (
55
+
<div className="w-10 h-10 bg-zinc-800 animate-pulse" />
56
+
) : albumArtUrl ? (
57
+
<img
58
+
src={albumArtUrl}
59
+
alt={`${trackName} album art`}
60
+
className="w-10 h-10 object-cover"
61
+
loading="lazy"
62
+
/>
63
+
) : (
64
+
<div className="w-10 h-10 bg-zinc-800 flex items-center justify-center">
65
+
<svg
66
+
className="w-5 h-5 text-zinc-600"
67
+
fill="currentColor"
68
+
viewBox="0 0 20 20"
69
+
>
70
+
<path d="M18 3a1 1 0 00-1.196-.98l-10 2A1 1 0 006 5v9.114A4.369 4.369 0 005 14c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V7.82l8-1.6v5.894A4.37 4.37 0 0015 12c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V3z" />
71
+
</svg>
72
+
</div>
73
+
)}
74
+
</div>
75
+
76
+
<div className="flex-1 min-w-0">
77
+
<h3 className="text-sm font-medium text-zinc-100 truncate">
78
+
{trackName}
79
+
</h3>
80
+
<p className="text-xs text-zinc-500 truncate">{artistNames}</p>
81
+
</div>
82
+
83
+
<div className="text-right flex-shrink-0">
84
+
<p className="text-xs text-zinc-400 font-medium">
85
+
{count.toLocaleString()}
86
+
</p>
87
+
</div>
88
+
</div>
89
+
</div>
90
+
);
91
+
}
+42
src/TopTracks.tsx
+42
src/TopTracks.tsx
···
1
+
import { graphql, useLazyLoadQuery } from "react-relay";
2
+
import type { TopTracksQuery } from "./__generated__/TopTracksQuery.graphql";
3
+
import TopTrackItem from "./TopTrackItem";
4
+
5
+
export default function TopTracks() {
6
+
const data = useLazyLoadQuery<TopTracksQuery>(
7
+
graphql`
8
+
query TopTracksQuery {
9
+
fmTealAlphaFeedPlaysAggregated(
10
+
groupBy: ["trackName", "releaseMbId", "artists"]
11
+
orderBy: { count: desc }
12
+
limit: 50
13
+
) {
14
+
trackName
15
+
releaseMbId
16
+
artists
17
+
count
18
+
}
19
+
}
20
+
`,
21
+
{}
22
+
);
23
+
24
+
const tracks = data.fmTealAlphaFeedPlaysAggregated || [];
25
+
const maxCount = tracks.length > 0 ? tracks[0].count : 0;
26
+
27
+
return (
28
+
<div className="space-y-1">
29
+
{tracks.map((track, index) => (
30
+
<TopTrackItem
31
+
key={`${track.trackName}-${index}`}
32
+
trackName={track.trackName || "Unknown Track"}
33
+
releaseMbId={track.releaseMbId}
34
+
artists={track.artists || "Unknown Artist"}
35
+
count={track.count}
36
+
rank={index + 1}
37
+
maxCount={maxCount}
38
+
/>
39
+
))}
40
+
</div>
41
+
);
42
+
}
+88
src/TrackItem.tsx
+88
src/TrackItem.tsx
···
1
+
import { graphql, useFragment } from "react-relay";
2
+
import type { TrackItem_play$key } from "./__generated__/TrackItem_play.graphql";
3
+
import { useAlbumArt } from "./useAlbumArt";
4
+
5
+
interface TrackItemProps {
6
+
play: TrackItem_play$key;
7
+
}
8
+
9
+
export default function TrackItem({ play }: TrackItemProps) {
10
+
const data = useFragment(
11
+
graphql`
12
+
fragment TrackItem_play on FmTealAlphaFeedPlay {
13
+
trackName
14
+
playedTime
15
+
artists
16
+
releaseName
17
+
releaseMbId
18
+
actorHandle
19
+
appBskyActorProfile {
20
+
displayName
21
+
}
22
+
}
23
+
`,
24
+
play
25
+
);
26
+
27
+
const { albumArtUrl, isLoading } = useAlbumArt(data.releaseMbId);
28
+
29
+
return (
30
+
<div className="group py-3 px-4 hover:bg-zinc-900/50 transition-colors">
31
+
<div className="flex items-center gap-4">
32
+
<div className="flex-shrink-0">
33
+
{isLoading ? (
34
+
<div className="w-10 h-10 bg-zinc-800 animate-pulse" />
35
+
) : albumArtUrl ? (
36
+
<img
37
+
src={albumArtUrl}
38
+
alt={`${data.trackName} album art`}
39
+
className="w-10 h-10 object-cover"
40
+
loading="lazy"
41
+
/>
42
+
) : (
43
+
<div className="w-10 h-10 bg-zinc-800 flex items-center justify-center">
44
+
<svg className="w-5 h-5 text-zinc-600" fill="currentColor" viewBox="0 0 20 20">
45
+
<path d="M18 3a1 1 0 00-1.196-.98l-10 2A1 1 0 006 5v9.114A4.369 4.369 0 005 14c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V7.82l8-1.6v5.894A4.37 4.37 0 0015 12c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V3z" />
46
+
</svg>
47
+
</div>
48
+
)}
49
+
</div>
50
+
51
+
<div className="flex-1 min-w-0 grid grid-cols-2 gap-4">
52
+
<div className="min-w-0">
53
+
<h3 className="text-sm font-medium text-zinc-100 truncate">
54
+
{data.trackName}
55
+
</h3>
56
+
<p className="text-xs text-zinc-500 truncate">
57
+
{Array.isArray(data.artists)
58
+
? data.artists.map((a) => a.artistName).join(", ")
59
+
: data.artists}
60
+
</p>
61
+
</div>
62
+
63
+
<div className="text-right min-w-0">
64
+
<p className="text-xs text-zinc-400 truncate">
65
+
{data.releaseName}
66
+
</p>
67
+
<div className="flex items-center justify-end gap-2 mt-0.5 min-w-0 overflow-hidden">
68
+
{data.playedTime && (
69
+
<p className="text-xs text-zinc-600 flex-shrink-0">
70
+
{new Date(data.playedTime).toLocaleTimeString("en-US", {
71
+
hour: "numeric",
72
+
minute: "2-digit",
73
+
})}
74
+
</p>
75
+
)}
76
+
<a
77
+
href={`/profile/${data.actorHandle}`}
78
+
className="text-xs text-violet-500 hover:text-violet-400 transition-colors truncate block max-w-[120px]"
79
+
>
80
+
@{data.actorHandle}
81
+
</a>
82
+
</div>
83
+
</div>
84
+
</div>
85
+
</div>
86
+
</div>
87
+
);
88
+
}
+258
src/__generated__/AppPaginationQuery.graphql.ts
+258
src/__generated__/AppPaginationQuery.graphql.ts
···
1
+
/**
2
+
* @generated SignedSource<<cef2df106afea24fa8527f2def8e9991>>
3
+
* @lightSyntaxTransform
4
+
* @nogrep
5
+
*/
6
+
7
+
/* tslint:disable */
8
+
/* eslint-disable */
9
+
// @ts-nocheck
10
+
11
+
import { ConcreteRequest } from 'relay-runtime';
12
+
import { FragmentRefs } from "relay-runtime";
13
+
export type AppPaginationQuery$variables = {
14
+
count?: number | null | undefined;
15
+
cursor?: string | null | undefined;
16
+
};
17
+
export type AppPaginationQuery$data = {
18
+
readonly " $fragmentSpreads": FragmentRefs<"App_plays">;
19
+
};
20
+
export type AppPaginationQuery = {
21
+
response: AppPaginationQuery$data;
22
+
variables: AppPaginationQuery$variables;
23
+
};
24
+
25
+
const node: ConcreteRequest = (function(){
26
+
var v0 = [
27
+
{
28
+
"defaultValue": 20,
29
+
"kind": "LocalArgument",
30
+
"name": "count"
31
+
},
32
+
{
33
+
"defaultValue": null,
34
+
"kind": "LocalArgument",
35
+
"name": "cursor"
36
+
}
37
+
],
38
+
v1 = [
39
+
{
40
+
"kind": "Variable",
41
+
"name": "after",
42
+
"variableName": "cursor"
43
+
},
44
+
{
45
+
"kind": "Variable",
46
+
"name": "first",
47
+
"variableName": "count"
48
+
},
49
+
{
50
+
"kind": "Literal",
51
+
"name": "sortBy",
52
+
"value": [
53
+
{
54
+
"direction": "desc",
55
+
"field": "playedTime"
56
+
}
57
+
]
58
+
}
59
+
];
60
+
return {
61
+
"fragment": {
62
+
"argumentDefinitions": (v0/*: any*/),
63
+
"kind": "Fragment",
64
+
"metadata": null,
65
+
"name": "AppPaginationQuery",
66
+
"selections": [
67
+
{
68
+
"args": [
69
+
{
70
+
"kind": "Variable",
71
+
"name": "count",
72
+
"variableName": "count"
73
+
},
74
+
{
75
+
"kind": "Variable",
76
+
"name": "cursor",
77
+
"variableName": "cursor"
78
+
}
79
+
],
80
+
"kind": "FragmentSpread",
81
+
"name": "App_plays"
82
+
}
83
+
],
84
+
"type": "Query",
85
+
"abstractKey": null
86
+
},
87
+
"kind": "Request",
88
+
"operation": {
89
+
"argumentDefinitions": (v0/*: any*/),
90
+
"kind": "Operation",
91
+
"name": "AppPaginationQuery",
92
+
"selections": [
93
+
{
94
+
"alias": null,
95
+
"args": (v1/*: any*/),
96
+
"concreteType": "FmTealAlphaFeedPlayConnection",
97
+
"kind": "LinkedField",
98
+
"name": "fmTealAlphaFeedPlays",
99
+
"plural": false,
100
+
"selections": [
101
+
{
102
+
"alias": null,
103
+
"args": null,
104
+
"kind": "ScalarField",
105
+
"name": "totalCount",
106
+
"storageKey": null
107
+
},
108
+
{
109
+
"alias": null,
110
+
"args": null,
111
+
"concreteType": "FmTealAlphaFeedPlayEdge",
112
+
"kind": "LinkedField",
113
+
"name": "edges",
114
+
"plural": true,
115
+
"selections": [
116
+
{
117
+
"alias": null,
118
+
"args": null,
119
+
"concreteType": "FmTealAlphaFeedPlay",
120
+
"kind": "LinkedField",
121
+
"name": "node",
122
+
"plural": false,
123
+
"selections": [
124
+
{
125
+
"alias": null,
126
+
"args": null,
127
+
"kind": "ScalarField",
128
+
"name": "playedTime",
129
+
"storageKey": null
130
+
},
131
+
{
132
+
"alias": null,
133
+
"args": null,
134
+
"kind": "ScalarField",
135
+
"name": "trackName",
136
+
"storageKey": null
137
+
},
138
+
{
139
+
"alias": null,
140
+
"args": null,
141
+
"kind": "ScalarField",
142
+
"name": "artists",
143
+
"storageKey": null
144
+
},
145
+
{
146
+
"alias": null,
147
+
"args": null,
148
+
"kind": "ScalarField",
149
+
"name": "releaseName",
150
+
"storageKey": null
151
+
},
152
+
{
153
+
"alias": null,
154
+
"args": null,
155
+
"kind": "ScalarField",
156
+
"name": "releaseMbId",
157
+
"storageKey": null
158
+
},
159
+
{
160
+
"alias": null,
161
+
"args": null,
162
+
"kind": "ScalarField",
163
+
"name": "actorHandle",
164
+
"storageKey": null
165
+
},
166
+
{
167
+
"alias": null,
168
+
"args": null,
169
+
"concreteType": "AppBskyActorProfile",
170
+
"kind": "LinkedField",
171
+
"name": "appBskyActorProfile",
172
+
"plural": false,
173
+
"selections": [
174
+
{
175
+
"alias": null,
176
+
"args": null,
177
+
"kind": "ScalarField",
178
+
"name": "displayName",
179
+
"storageKey": null
180
+
}
181
+
],
182
+
"storageKey": null
183
+
},
184
+
{
185
+
"alias": null,
186
+
"args": null,
187
+
"kind": "ScalarField",
188
+
"name": "__typename",
189
+
"storageKey": null
190
+
}
191
+
],
192
+
"storageKey": null
193
+
},
194
+
{
195
+
"alias": null,
196
+
"args": null,
197
+
"kind": "ScalarField",
198
+
"name": "cursor",
199
+
"storageKey": null
200
+
}
201
+
],
202
+
"storageKey": null
203
+
},
204
+
{
205
+
"alias": null,
206
+
"args": null,
207
+
"concreteType": "PageInfo",
208
+
"kind": "LinkedField",
209
+
"name": "pageInfo",
210
+
"plural": false,
211
+
"selections": [
212
+
{
213
+
"alias": null,
214
+
"args": null,
215
+
"kind": "ScalarField",
216
+
"name": "endCursor",
217
+
"storageKey": null
218
+
},
219
+
{
220
+
"alias": null,
221
+
"args": null,
222
+
"kind": "ScalarField",
223
+
"name": "hasNextPage",
224
+
"storageKey": null
225
+
}
226
+
],
227
+
"storageKey": null
228
+
}
229
+
],
230
+
"storageKey": null
231
+
},
232
+
{
233
+
"alias": null,
234
+
"args": (v1/*: any*/),
235
+
"filters": [
236
+
"sortBy"
237
+
],
238
+
"handle": "connection",
239
+
"key": "App_fmTealAlphaFeedPlays",
240
+
"kind": "LinkedHandle",
241
+
"name": "fmTealAlphaFeedPlays"
242
+
}
243
+
]
244
+
},
245
+
"params": {
246
+
"cacheID": "e115a73de49cf6f84a35f172a7910c5c",
247
+
"id": null,
248
+
"metadata": {},
249
+
"name": "AppPaginationQuery",
250
+
"operationKind": "query",
251
+
"text": "query AppPaginationQuery(\n $count: Int = 20\n $cursor: String\n) {\n ...App_plays_1G22uz\n}\n\nfragment App_plays_1G22uz on Query {\n fmTealAlphaFeedPlays(first: $count, after: $cursor, sortBy: [{field: \"playedTime\", direction: desc}]) {\n totalCount\n edges {\n node {\n playedTime\n ...TrackItem_play\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment TrackItem_play on FmTealAlphaFeedPlay {\n trackName\n playedTime\n artists\n releaseName\n releaseMbId\n actorHandle\n appBskyActorProfile {\n displayName\n }\n}\n"
252
+
}
253
+
};
254
+
})();
255
+
256
+
(node as any).hash = "0e4acf96fedae07af90ce6e9e3bf18d6";
257
+
258
+
export default node;
+227
src/__generated__/AppQuery.graphql.ts
+227
src/__generated__/AppQuery.graphql.ts
···
1
+
/**
2
+
* @generated SignedSource<<541e6114682aef7988bd233592085337>>
3
+
* @lightSyntaxTransform
4
+
* @nogrep
5
+
*/
6
+
7
+
/* tslint:disable */
8
+
/* eslint-disable */
9
+
// @ts-nocheck
10
+
11
+
import { ConcreteRequest } from 'relay-runtime';
12
+
import { FragmentRefs } from "relay-runtime";
13
+
export type AppQuery$variables = Record<PropertyKey, never>;
14
+
export type AppQuery$data = {
15
+
readonly " $fragmentSpreads": FragmentRefs<"App_plays">;
16
+
};
17
+
export type AppQuery = {
18
+
response: AppQuery$data;
19
+
variables: AppQuery$variables;
20
+
};
21
+
22
+
const node: ConcreteRequest = (function(){
23
+
var v0 = [
24
+
{
25
+
"kind": "Literal",
26
+
"name": "first",
27
+
"value": 20
28
+
},
29
+
{
30
+
"kind": "Literal",
31
+
"name": "sortBy",
32
+
"value": [
33
+
{
34
+
"direction": "desc",
35
+
"field": "playedTime"
36
+
}
37
+
]
38
+
}
39
+
];
40
+
return {
41
+
"fragment": {
42
+
"argumentDefinitions": [],
43
+
"kind": "Fragment",
44
+
"metadata": null,
45
+
"name": "AppQuery",
46
+
"selections": [
47
+
{
48
+
"args": null,
49
+
"kind": "FragmentSpread",
50
+
"name": "App_plays"
51
+
}
52
+
],
53
+
"type": "Query",
54
+
"abstractKey": null
55
+
},
56
+
"kind": "Request",
57
+
"operation": {
58
+
"argumentDefinitions": [],
59
+
"kind": "Operation",
60
+
"name": "AppQuery",
61
+
"selections": [
62
+
{
63
+
"alias": null,
64
+
"args": (v0/*: any*/),
65
+
"concreteType": "FmTealAlphaFeedPlayConnection",
66
+
"kind": "LinkedField",
67
+
"name": "fmTealAlphaFeedPlays",
68
+
"plural": false,
69
+
"selections": [
70
+
{
71
+
"alias": null,
72
+
"args": null,
73
+
"kind": "ScalarField",
74
+
"name": "totalCount",
75
+
"storageKey": null
76
+
},
77
+
{
78
+
"alias": null,
79
+
"args": null,
80
+
"concreteType": "FmTealAlphaFeedPlayEdge",
81
+
"kind": "LinkedField",
82
+
"name": "edges",
83
+
"plural": true,
84
+
"selections": [
85
+
{
86
+
"alias": null,
87
+
"args": null,
88
+
"concreteType": "FmTealAlphaFeedPlay",
89
+
"kind": "LinkedField",
90
+
"name": "node",
91
+
"plural": false,
92
+
"selections": [
93
+
{
94
+
"alias": null,
95
+
"args": null,
96
+
"kind": "ScalarField",
97
+
"name": "playedTime",
98
+
"storageKey": null
99
+
},
100
+
{
101
+
"alias": null,
102
+
"args": null,
103
+
"kind": "ScalarField",
104
+
"name": "trackName",
105
+
"storageKey": null
106
+
},
107
+
{
108
+
"alias": null,
109
+
"args": null,
110
+
"kind": "ScalarField",
111
+
"name": "artists",
112
+
"storageKey": null
113
+
},
114
+
{
115
+
"alias": null,
116
+
"args": null,
117
+
"kind": "ScalarField",
118
+
"name": "releaseName",
119
+
"storageKey": null
120
+
},
121
+
{
122
+
"alias": null,
123
+
"args": null,
124
+
"kind": "ScalarField",
125
+
"name": "releaseMbId",
126
+
"storageKey": null
127
+
},
128
+
{
129
+
"alias": null,
130
+
"args": null,
131
+
"kind": "ScalarField",
132
+
"name": "actorHandle",
133
+
"storageKey": null
134
+
},
135
+
{
136
+
"alias": null,
137
+
"args": null,
138
+
"concreteType": "AppBskyActorProfile",
139
+
"kind": "LinkedField",
140
+
"name": "appBskyActorProfile",
141
+
"plural": false,
142
+
"selections": [
143
+
{
144
+
"alias": null,
145
+
"args": null,
146
+
"kind": "ScalarField",
147
+
"name": "displayName",
148
+
"storageKey": null
149
+
}
150
+
],
151
+
"storageKey": null
152
+
},
153
+
{
154
+
"alias": null,
155
+
"args": null,
156
+
"kind": "ScalarField",
157
+
"name": "__typename",
158
+
"storageKey": null
159
+
}
160
+
],
161
+
"storageKey": null
162
+
},
163
+
{
164
+
"alias": null,
165
+
"args": null,
166
+
"kind": "ScalarField",
167
+
"name": "cursor",
168
+
"storageKey": null
169
+
}
170
+
],
171
+
"storageKey": null
172
+
},
173
+
{
174
+
"alias": null,
175
+
"args": null,
176
+
"concreteType": "PageInfo",
177
+
"kind": "LinkedField",
178
+
"name": "pageInfo",
179
+
"plural": false,
180
+
"selections": [
181
+
{
182
+
"alias": null,
183
+
"args": null,
184
+
"kind": "ScalarField",
185
+
"name": "endCursor",
186
+
"storageKey": null
187
+
},
188
+
{
189
+
"alias": null,
190
+
"args": null,
191
+
"kind": "ScalarField",
192
+
"name": "hasNextPage",
193
+
"storageKey": null
194
+
}
195
+
],
196
+
"storageKey": null
197
+
}
198
+
],
199
+
"storageKey": "fmTealAlphaFeedPlays(first:20,sortBy:[{\"direction\":\"desc\",\"field\":\"playedTime\"}])"
200
+
},
201
+
{
202
+
"alias": null,
203
+
"args": (v0/*: any*/),
204
+
"filters": [
205
+
"sortBy"
206
+
],
207
+
"handle": "connection",
208
+
"key": "App_fmTealAlphaFeedPlays",
209
+
"kind": "LinkedHandle",
210
+
"name": "fmTealAlphaFeedPlays"
211
+
}
212
+
]
213
+
},
214
+
"params": {
215
+
"cacheID": "1cacfb0aa5545cf84688b8396079b855",
216
+
"id": null,
217
+
"metadata": {},
218
+
"name": "AppQuery",
219
+
"operationKind": "query",
220
+
"text": "query AppQuery {\n ...App_plays\n}\n\nfragment App_plays on Query {\n fmTealAlphaFeedPlays(first: 20, sortBy: [{field: \"playedTime\", direction: desc}]) {\n totalCount\n edges {\n node {\n playedTime\n ...TrackItem_play\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment TrackItem_play on FmTealAlphaFeedPlay {\n trackName\n playedTime\n artists\n releaseName\n releaseMbId\n actorHandle\n appBskyActorProfile {\n displayName\n }\n}\n"
221
+
}
222
+
};
223
+
})();
224
+
225
+
(node as any).hash = "4b1837f6cd874e31461fbead77c1b012";
226
+
227
+
export default node;
+184
src/__generated__/App_plays.graphql.ts
+184
src/__generated__/App_plays.graphql.ts
···
1
+
/**
2
+
* @generated SignedSource<<a3ae5f31f618986fb12e6c57458c9853>>
3
+
* @lightSyntaxTransform
4
+
* @nogrep
5
+
*/
6
+
7
+
/* tslint:disable */
8
+
/* eslint-disable */
9
+
// @ts-nocheck
10
+
11
+
import { ReaderFragment } from 'relay-runtime';
12
+
import { FragmentRefs } from "relay-runtime";
13
+
export type App_plays$data = {
14
+
readonly fmTealAlphaFeedPlays: {
15
+
readonly edges: ReadonlyArray<{
16
+
readonly node: {
17
+
readonly playedTime: string | null | undefined;
18
+
readonly " $fragmentSpreads": FragmentRefs<"TrackItem_play">;
19
+
};
20
+
}>;
21
+
readonly totalCount: number;
22
+
};
23
+
readonly " $fragmentType": "App_plays";
24
+
};
25
+
export type App_plays$key = {
26
+
readonly " $data"?: App_plays$data;
27
+
readonly " $fragmentSpreads": FragmentRefs<"App_plays">;
28
+
};
29
+
30
+
import AppPaginationQuery_graphql from './AppPaginationQuery.graphql';
31
+
32
+
const node: ReaderFragment = (function(){
33
+
var v0 = [
34
+
"fmTealAlphaFeedPlays"
35
+
];
36
+
return {
37
+
"argumentDefinitions": [
38
+
{
39
+
"defaultValue": 20,
40
+
"kind": "LocalArgument",
41
+
"name": "count"
42
+
},
43
+
{
44
+
"defaultValue": null,
45
+
"kind": "LocalArgument",
46
+
"name": "cursor"
47
+
}
48
+
],
49
+
"kind": "Fragment",
50
+
"metadata": {
51
+
"connection": [
52
+
{
53
+
"count": "count",
54
+
"cursor": "cursor",
55
+
"direction": "forward",
56
+
"path": (v0/*: any*/)
57
+
}
58
+
],
59
+
"refetch": {
60
+
"connection": {
61
+
"forward": {
62
+
"count": "count",
63
+
"cursor": "cursor"
64
+
},
65
+
"backward": null,
66
+
"path": (v0/*: any*/)
67
+
},
68
+
"fragmentPathInResult": [],
69
+
"operation": AppPaginationQuery_graphql
70
+
}
71
+
},
72
+
"name": "App_plays",
73
+
"selections": [
74
+
{
75
+
"alias": "fmTealAlphaFeedPlays",
76
+
"args": [
77
+
{
78
+
"kind": "Literal",
79
+
"name": "sortBy",
80
+
"value": [
81
+
{
82
+
"direction": "desc",
83
+
"field": "playedTime"
84
+
}
85
+
]
86
+
}
87
+
],
88
+
"concreteType": "FmTealAlphaFeedPlayConnection",
89
+
"kind": "LinkedField",
90
+
"name": "__App_fmTealAlphaFeedPlays_connection",
91
+
"plural": false,
92
+
"selections": [
93
+
{
94
+
"alias": null,
95
+
"args": null,
96
+
"kind": "ScalarField",
97
+
"name": "totalCount",
98
+
"storageKey": null
99
+
},
100
+
{
101
+
"alias": null,
102
+
"args": null,
103
+
"concreteType": "FmTealAlphaFeedPlayEdge",
104
+
"kind": "LinkedField",
105
+
"name": "edges",
106
+
"plural": true,
107
+
"selections": [
108
+
{
109
+
"alias": null,
110
+
"args": null,
111
+
"concreteType": "FmTealAlphaFeedPlay",
112
+
"kind": "LinkedField",
113
+
"name": "node",
114
+
"plural": false,
115
+
"selections": [
116
+
{
117
+
"alias": null,
118
+
"args": null,
119
+
"kind": "ScalarField",
120
+
"name": "playedTime",
121
+
"storageKey": null
122
+
},
123
+
{
124
+
"args": null,
125
+
"kind": "FragmentSpread",
126
+
"name": "TrackItem_play"
127
+
},
128
+
{
129
+
"alias": null,
130
+
"args": null,
131
+
"kind": "ScalarField",
132
+
"name": "__typename",
133
+
"storageKey": null
134
+
}
135
+
],
136
+
"storageKey": null
137
+
},
138
+
{
139
+
"alias": null,
140
+
"args": null,
141
+
"kind": "ScalarField",
142
+
"name": "cursor",
143
+
"storageKey": null
144
+
}
145
+
],
146
+
"storageKey": null
147
+
},
148
+
{
149
+
"alias": null,
150
+
"args": null,
151
+
"concreteType": "PageInfo",
152
+
"kind": "LinkedField",
153
+
"name": "pageInfo",
154
+
"plural": false,
155
+
"selections": [
156
+
{
157
+
"alias": null,
158
+
"args": null,
159
+
"kind": "ScalarField",
160
+
"name": "endCursor",
161
+
"storageKey": null
162
+
},
163
+
{
164
+
"alias": null,
165
+
"args": null,
166
+
"kind": "ScalarField",
167
+
"name": "hasNextPage",
168
+
"storageKey": null
169
+
}
170
+
],
171
+
"storageKey": null
172
+
}
173
+
],
174
+
"storageKey": "__App_fmTealAlphaFeedPlays_connection(sortBy:[{\"direction\":\"desc\",\"field\":\"playedTime\"}])"
175
+
}
176
+
],
177
+
"type": "Query",
178
+
"abstractKey": null
179
+
};
180
+
})();
181
+
182
+
(node as any).hash = "0e4acf96fedae07af90ce6e9e3bf18d6";
183
+
184
+
export default node;
+303
src/__generated__/ProfilePaginationQuery.graphql.ts
+303
src/__generated__/ProfilePaginationQuery.graphql.ts
···
1
+
/**
2
+
* @generated SignedSource<<97625934b32c4079cc58877234aeac04>>
3
+
* @lightSyntaxTransform
4
+
* @nogrep
5
+
*/
6
+
7
+
/* tslint:disable */
8
+
/* eslint-disable */
9
+
// @ts-nocheck
10
+
11
+
import { ConcreteRequest } from 'relay-runtime';
12
+
import { FragmentRefs } from "relay-runtime";
13
+
export type ProfilePaginationQuery$variables = {
14
+
count?: number | null | undefined;
15
+
cursor?: string | null | undefined;
16
+
where: any;
17
+
};
18
+
export type ProfilePaginationQuery$data = {
19
+
readonly " $fragmentSpreads": FragmentRefs<"Profile_plays">;
20
+
};
21
+
export type ProfilePaginationQuery = {
22
+
response: ProfilePaginationQuery$data;
23
+
variables: ProfilePaginationQuery$variables;
24
+
};
25
+
26
+
const node: ConcreteRequest = (function(){
27
+
var v0 = [
28
+
{
29
+
"defaultValue": 20,
30
+
"kind": "LocalArgument",
31
+
"name": "count"
32
+
},
33
+
{
34
+
"defaultValue": null,
35
+
"kind": "LocalArgument",
36
+
"name": "cursor"
37
+
},
38
+
{
39
+
"defaultValue": null,
40
+
"kind": "LocalArgument",
41
+
"name": "where"
42
+
}
43
+
],
44
+
v1 = {
45
+
"kind": "Variable",
46
+
"name": "where",
47
+
"variableName": "where"
48
+
},
49
+
v2 = [
50
+
{
51
+
"kind": "Variable",
52
+
"name": "after",
53
+
"variableName": "cursor"
54
+
},
55
+
{
56
+
"kind": "Variable",
57
+
"name": "first",
58
+
"variableName": "count"
59
+
},
60
+
{
61
+
"kind": "Literal",
62
+
"name": "sortBy",
63
+
"value": [
64
+
{
65
+
"direction": "desc",
66
+
"field": "playedTime"
67
+
}
68
+
]
69
+
},
70
+
(v1/*: any*/)
71
+
];
72
+
return {
73
+
"fragment": {
74
+
"argumentDefinitions": (v0/*: any*/),
75
+
"kind": "Fragment",
76
+
"metadata": null,
77
+
"name": "ProfilePaginationQuery",
78
+
"selections": [
79
+
{
80
+
"args": [
81
+
{
82
+
"kind": "Variable",
83
+
"name": "count",
84
+
"variableName": "count"
85
+
},
86
+
{
87
+
"kind": "Variable",
88
+
"name": "cursor",
89
+
"variableName": "cursor"
90
+
},
91
+
(v1/*: any*/)
92
+
],
93
+
"kind": "FragmentSpread",
94
+
"name": "Profile_plays"
95
+
}
96
+
],
97
+
"type": "Query",
98
+
"abstractKey": null
99
+
},
100
+
"kind": "Request",
101
+
"operation": {
102
+
"argumentDefinitions": (v0/*: any*/),
103
+
"kind": "Operation",
104
+
"name": "ProfilePaginationQuery",
105
+
"selections": [
106
+
{
107
+
"alias": null,
108
+
"args": (v2/*: any*/),
109
+
"concreteType": "FmTealAlphaFeedPlayConnection",
110
+
"kind": "LinkedField",
111
+
"name": "fmTealAlphaFeedPlays",
112
+
"plural": false,
113
+
"selections": [
114
+
{
115
+
"alias": null,
116
+
"args": null,
117
+
"kind": "ScalarField",
118
+
"name": "totalCount",
119
+
"storageKey": null
120
+
},
121
+
{
122
+
"alias": null,
123
+
"args": null,
124
+
"concreteType": "FmTealAlphaFeedPlayEdge",
125
+
"kind": "LinkedField",
126
+
"name": "edges",
127
+
"plural": true,
128
+
"selections": [
129
+
{
130
+
"alias": null,
131
+
"args": null,
132
+
"concreteType": "FmTealAlphaFeedPlay",
133
+
"kind": "LinkedField",
134
+
"name": "node",
135
+
"plural": false,
136
+
"selections": [
137
+
{
138
+
"alias": null,
139
+
"args": null,
140
+
"kind": "ScalarField",
141
+
"name": "trackName",
142
+
"storageKey": null
143
+
},
144
+
{
145
+
"alias": null,
146
+
"args": null,
147
+
"kind": "ScalarField",
148
+
"name": "playedTime",
149
+
"storageKey": null
150
+
},
151
+
{
152
+
"alias": null,
153
+
"args": null,
154
+
"kind": "ScalarField",
155
+
"name": "artists",
156
+
"storageKey": null
157
+
},
158
+
{
159
+
"alias": null,
160
+
"args": null,
161
+
"kind": "ScalarField",
162
+
"name": "releaseName",
163
+
"storageKey": null
164
+
},
165
+
{
166
+
"alias": null,
167
+
"args": null,
168
+
"kind": "ScalarField",
169
+
"name": "releaseMbId",
170
+
"storageKey": null
171
+
},
172
+
{
173
+
"alias": null,
174
+
"args": null,
175
+
"kind": "ScalarField",
176
+
"name": "actorHandle",
177
+
"storageKey": null
178
+
},
179
+
{
180
+
"alias": null,
181
+
"args": null,
182
+
"concreteType": "AppBskyActorProfile",
183
+
"kind": "LinkedField",
184
+
"name": "appBskyActorProfile",
185
+
"plural": false,
186
+
"selections": [
187
+
{
188
+
"alias": null,
189
+
"args": null,
190
+
"kind": "ScalarField",
191
+
"name": "displayName",
192
+
"storageKey": null
193
+
},
194
+
{
195
+
"alias": null,
196
+
"args": null,
197
+
"kind": "ScalarField",
198
+
"name": "description",
199
+
"storageKey": null
200
+
},
201
+
{
202
+
"alias": null,
203
+
"args": null,
204
+
"concreteType": "Blob",
205
+
"kind": "LinkedField",
206
+
"name": "avatar",
207
+
"plural": false,
208
+
"selections": [
209
+
{
210
+
"alias": null,
211
+
"args": [
212
+
{
213
+
"kind": "Literal",
214
+
"name": "preset",
215
+
"value": "avatar"
216
+
}
217
+
],
218
+
"kind": "ScalarField",
219
+
"name": "url",
220
+
"storageKey": "url(preset:\"avatar\")"
221
+
}
222
+
],
223
+
"storageKey": null
224
+
}
225
+
],
226
+
"storageKey": null
227
+
},
228
+
{
229
+
"alias": null,
230
+
"args": null,
231
+
"kind": "ScalarField",
232
+
"name": "__typename",
233
+
"storageKey": null
234
+
}
235
+
],
236
+
"storageKey": null
237
+
},
238
+
{
239
+
"alias": null,
240
+
"args": null,
241
+
"kind": "ScalarField",
242
+
"name": "cursor",
243
+
"storageKey": null
244
+
}
245
+
],
246
+
"storageKey": null
247
+
},
248
+
{
249
+
"alias": null,
250
+
"args": null,
251
+
"concreteType": "PageInfo",
252
+
"kind": "LinkedField",
253
+
"name": "pageInfo",
254
+
"plural": false,
255
+
"selections": [
256
+
{
257
+
"alias": null,
258
+
"args": null,
259
+
"kind": "ScalarField",
260
+
"name": "endCursor",
261
+
"storageKey": null
262
+
},
263
+
{
264
+
"alias": null,
265
+
"args": null,
266
+
"kind": "ScalarField",
267
+
"name": "hasNextPage",
268
+
"storageKey": null
269
+
}
270
+
],
271
+
"storageKey": null
272
+
}
273
+
],
274
+
"storageKey": null
275
+
},
276
+
{
277
+
"alias": null,
278
+
"args": (v2/*: any*/),
279
+
"filters": [
280
+
"where",
281
+
"sortBy"
282
+
],
283
+
"handle": "connection",
284
+
"key": "Profile_fmTealAlphaFeedPlays",
285
+
"kind": "LinkedHandle",
286
+
"name": "fmTealAlphaFeedPlays"
287
+
}
288
+
]
289
+
},
290
+
"params": {
291
+
"cacheID": "08e603fb4052c3556739bda428413453",
292
+
"id": null,
293
+
"metadata": {},
294
+
"name": "ProfilePaginationQuery",
295
+
"operationKind": "query",
296
+
"text": "query ProfilePaginationQuery(\n $count: Int = 20\n $cursor: String\n $where: JSON!\n) {\n ...Profile_plays_mjR8k\n}\n\nfragment Profile_plays_mjR8k on Query {\n fmTealAlphaFeedPlays(first: $count, after: $cursor, sortBy: [{field: \"playedTime\", direction: desc}], where: $where) {\n totalCount\n edges {\n node {\n ...TrackItem_play\n actorHandle\n appBskyActorProfile {\n displayName\n description\n avatar {\n url(preset: \"avatar\")\n }\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment TrackItem_play on FmTealAlphaFeedPlay {\n trackName\n playedTime\n artists\n releaseName\n releaseMbId\n actorHandle\n appBskyActorProfile {\n displayName\n }\n}\n"
297
+
}
298
+
};
299
+
})();
300
+
301
+
(node as any).hash = "474168bb0d13417c1b7067c09a82f7a2";
302
+
303
+
export default node;
+276
src/__generated__/ProfileQuery.graphql.ts
+276
src/__generated__/ProfileQuery.graphql.ts
···
1
+
/**
2
+
* @generated SignedSource<<c47dafb8d21963c2a9dcbcd54d7bd8d8>>
3
+
* @lightSyntaxTransform
4
+
* @nogrep
5
+
*/
6
+
7
+
/* tslint:disable */
8
+
/* eslint-disable */
9
+
// @ts-nocheck
10
+
11
+
import { ConcreteRequest } from 'relay-runtime';
12
+
import { FragmentRefs } from "relay-runtime";
13
+
export type ProfileQuery$variables = {
14
+
where: any;
15
+
};
16
+
export type ProfileQuery$data = {
17
+
readonly " $fragmentSpreads": FragmentRefs<"Profile_plays">;
18
+
};
19
+
export type ProfileQuery = {
20
+
response: ProfileQuery$data;
21
+
variables: ProfileQuery$variables;
22
+
};
23
+
24
+
const node: ConcreteRequest = (function(){
25
+
var v0 = [
26
+
{
27
+
"defaultValue": null,
28
+
"kind": "LocalArgument",
29
+
"name": "where"
30
+
}
31
+
],
32
+
v1 = {
33
+
"kind": "Variable",
34
+
"name": "where",
35
+
"variableName": "where"
36
+
},
37
+
v2 = [
38
+
{
39
+
"kind": "Literal",
40
+
"name": "first",
41
+
"value": 20
42
+
},
43
+
{
44
+
"kind": "Literal",
45
+
"name": "sortBy",
46
+
"value": [
47
+
{
48
+
"direction": "desc",
49
+
"field": "playedTime"
50
+
}
51
+
]
52
+
},
53
+
(v1/*: any*/)
54
+
];
55
+
return {
56
+
"fragment": {
57
+
"argumentDefinitions": (v0/*: any*/),
58
+
"kind": "Fragment",
59
+
"metadata": null,
60
+
"name": "ProfileQuery",
61
+
"selections": [
62
+
{
63
+
"args": [
64
+
(v1/*: any*/)
65
+
],
66
+
"kind": "FragmentSpread",
67
+
"name": "Profile_plays"
68
+
}
69
+
],
70
+
"type": "Query",
71
+
"abstractKey": null
72
+
},
73
+
"kind": "Request",
74
+
"operation": {
75
+
"argumentDefinitions": (v0/*: any*/),
76
+
"kind": "Operation",
77
+
"name": "ProfileQuery",
78
+
"selections": [
79
+
{
80
+
"alias": null,
81
+
"args": (v2/*: any*/),
82
+
"concreteType": "FmTealAlphaFeedPlayConnection",
83
+
"kind": "LinkedField",
84
+
"name": "fmTealAlphaFeedPlays",
85
+
"plural": false,
86
+
"selections": [
87
+
{
88
+
"alias": null,
89
+
"args": null,
90
+
"kind": "ScalarField",
91
+
"name": "totalCount",
92
+
"storageKey": null
93
+
},
94
+
{
95
+
"alias": null,
96
+
"args": null,
97
+
"concreteType": "FmTealAlphaFeedPlayEdge",
98
+
"kind": "LinkedField",
99
+
"name": "edges",
100
+
"plural": true,
101
+
"selections": [
102
+
{
103
+
"alias": null,
104
+
"args": null,
105
+
"concreteType": "FmTealAlphaFeedPlay",
106
+
"kind": "LinkedField",
107
+
"name": "node",
108
+
"plural": false,
109
+
"selections": [
110
+
{
111
+
"alias": null,
112
+
"args": null,
113
+
"kind": "ScalarField",
114
+
"name": "trackName",
115
+
"storageKey": null
116
+
},
117
+
{
118
+
"alias": null,
119
+
"args": null,
120
+
"kind": "ScalarField",
121
+
"name": "playedTime",
122
+
"storageKey": null
123
+
},
124
+
{
125
+
"alias": null,
126
+
"args": null,
127
+
"kind": "ScalarField",
128
+
"name": "artists",
129
+
"storageKey": null
130
+
},
131
+
{
132
+
"alias": null,
133
+
"args": null,
134
+
"kind": "ScalarField",
135
+
"name": "releaseName",
136
+
"storageKey": null
137
+
},
138
+
{
139
+
"alias": null,
140
+
"args": null,
141
+
"kind": "ScalarField",
142
+
"name": "releaseMbId",
143
+
"storageKey": null
144
+
},
145
+
{
146
+
"alias": null,
147
+
"args": null,
148
+
"kind": "ScalarField",
149
+
"name": "actorHandle",
150
+
"storageKey": null
151
+
},
152
+
{
153
+
"alias": null,
154
+
"args": null,
155
+
"concreteType": "AppBskyActorProfile",
156
+
"kind": "LinkedField",
157
+
"name": "appBskyActorProfile",
158
+
"plural": false,
159
+
"selections": [
160
+
{
161
+
"alias": null,
162
+
"args": null,
163
+
"kind": "ScalarField",
164
+
"name": "displayName",
165
+
"storageKey": null
166
+
},
167
+
{
168
+
"alias": null,
169
+
"args": null,
170
+
"kind": "ScalarField",
171
+
"name": "description",
172
+
"storageKey": null
173
+
},
174
+
{
175
+
"alias": null,
176
+
"args": null,
177
+
"concreteType": "Blob",
178
+
"kind": "LinkedField",
179
+
"name": "avatar",
180
+
"plural": false,
181
+
"selections": [
182
+
{
183
+
"alias": null,
184
+
"args": [
185
+
{
186
+
"kind": "Literal",
187
+
"name": "preset",
188
+
"value": "avatar"
189
+
}
190
+
],
191
+
"kind": "ScalarField",
192
+
"name": "url",
193
+
"storageKey": "url(preset:\"avatar\")"
194
+
}
195
+
],
196
+
"storageKey": null
197
+
}
198
+
],
199
+
"storageKey": null
200
+
},
201
+
{
202
+
"alias": null,
203
+
"args": null,
204
+
"kind": "ScalarField",
205
+
"name": "__typename",
206
+
"storageKey": null
207
+
}
208
+
],
209
+
"storageKey": null
210
+
},
211
+
{
212
+
"alias": null,
213
+
"args": null,
214
+
"kind": "ScalarField",
215
+
"name": "cursor",
216
+
"storageKey": null
217
+
}
218
+
],
219
+
"storageKey": null
220
+
},
221
+
{
222
+
"alias": null,
223
+
"args": null,
224
+
"concreteType": "PageInfo",
225
+
"kind": "LinkedField",
226
+
"name": "pageInfo",
227
+
"plural": false,
228
+
"selections": [
229
+
{
230
+
"alias": null,
231
+
"args": null,
232
+
"kind": "ScalarField",
233
+
"name": "endCursor",
234
+
"storageKey": null
235
+
},
236
+
{
237
+
"alias": null,
238
+
"args": null,
239
+
"kind": "ScalarField",
240
+
"name": "hasNextPage",
241
+
"storageKey": null
242
+
}
243
+
],
244
+
"storageKey": null
245
+
}
246
+
],
247
+
"storageKey": null
248
+
},
249
+
{
250
+
"alias": null,
251
+
"args": (v2/*: any*/),
252
+
"filters": [
253
+
"where",
254
+
"sortBy"
255
+
],
256
+
"handle": "connection",
257
+
"key": "Profile_fmTealAlphaFeedPlays",
258
+
"kind": "LinkedHandle",
259
+
"name": "fmTealAlphaFeedPlays"
260
+
}
261
+
]
262
+
},
263
+
"params": {
264
+
"cacheID": "3137e7b6ec5148299e7a7c7f4edf07b2",
265
+
"id": null,
266
+
"metadata": {},
267
+
"name": "ProfileQuery",
268
+
"operationKind": "query",
269
+
"text": "query ProfileQuery(\n $where: JSON!\n) {\n ...Profile_plays_3FC4Qo\n}\n\nfragment Profile_plays_3FC4Qo on Query {\n fmTealAlphaFeedPlays(first: 20, sortBy: [{field: \"playedTime\", direction: desc}], where: $where) {\n totalCount\n edges {\n node {\n ...TrackItem_play\n actorHandle\n appBskyActorProfile {\n displayName\n description\n avatar {\n url(preset: \"avatar\")\n }\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment TrackItem_play on FmTealAlphaFeedPlay {\n trackName\n playedTime\n artists\n releaseName\n releaseMbId\n actorHandle\n appBskyActorProfile {\n displayName\n }\n}\n"
270
+
}
271
+
};
272
+
})();
273
+
274
+
(node as any).hash = "267039e382b3b95a739ff3cdced3211e";
275
+
276
+
export default node;
+250
src/__generated__/Profile_plays.graphql.ts
+250
src/__generated__/Profile_plays.graphql.ts
···
1
+
/**
2
+
* @generated SignedSource<<bd63c9e74b05810076b60ad0e51cb230>>
3
+
* @lightSyntaxTransform
4
+
* @nogrep
5
+
*/
6
+
7
+
/* tslint:disable */
8
+
/* eslint-disable */
9
+
// @ts-nocheck
10
+
11
+
import { ReaderFragment } from 'relay-runtime';
12
+
import { FragmentRefs } from "relay-runtime";
13
+
export type Profile_plays$data = {
14
+
readonly fmTealAlphaFeedPlays: {
15
+
readonly edges: ReadonlyArray<{
16
+
readonly node: {
17
+
readonly actorHandle: string | null | undefined;
18
+
readonly appBskyActorProfile: {
19
+
readonly avatar: {
20
+
readonly url: string;
21
+
} | null | undefined;
22
+
readonly description: string | null | undefined;
23
+
readonly displayName: string | null | undefined;
24
+
} | null | undefined;
25
+
readonly " $fragmentSpreads": FragmentRefs<"TrackItem_play">;
26
+
};
27
+
}>;
28
+
readonly totalCount: number;
29
+
};
30
+
readonly " $fragmentType": "Profile_plays";
31
+
};
32
+
export type Profile_plays$key = {
33
+
readonly " $data"?: Profile_plays$data;
34
+
readonly " $fragmentSpreads": FragmentRefs<"Profile_plays">;
35
+
};
36
+
37
+
import ProfilePaginationQuery_graphql from './ProfilePaginationQuery.graphql';
38
+
39
+
const node: ReaderFragment = (function(){
40
+
var v0 = [
41
+
"fmTealAlphaFeedPlays"
42
+
];
43
+
return {
44
+
"argumentDefinitions": [
45
+
{
46
+
"defaultValue": 20,
47
+
"kind": "LocalArgument",
48
+
"name": "count"
49
+
},
50
+
{
51
+
"defaultValue": null,
52
+
"kind": "LocalArgument",
53
+
"name": "cursor"
54
+
},
55
+
{
56
+
"defaultValue": null,
57
+
"kind": "LocalArgument",
58
+
"name": "where"
59
+
}
60
+
],
61
+
"kind": "Fragment",
62
+
"metadata": {
63
+
"connection": [
64
+
{
65
+
"count": "count",
66
+
"cursor": "cursor",
67
+
"direction": "forward",
68
+
"path": (v0/*: any*/)
69
+
}
70
+
],
71
+
"refetch": {
72
+
"connection": {
73
+
"forward": {
74
+
"count": "count",
75
+
"cursor": "cursor"
76
+
},
77
+
"backward": null,
78
+
"path": (v0/*: any*/)
79
+
},
80
+
"fragmentPathInResult": [],
81
+
"operation": ProfilePaginationQuery_graphql
82
+
}
83
+
},
84
+
"name": "Profile_plays",
85
+
"selections": [
86
+
{
87
+
"alias": "fmTealAlphaFeedPlays",
88
+
"args": [
89
+
{
90
+
"kind": "Literal",
91
+
"name": "sortBy",
92
+
"value": [
93
+
{
94
+
"direction": "desc",
95
+
"field": "playedTime"
96
+
}
97
+
]
98
+
},
99
+
{
100
+
"kind": "Variable",
101
+
"name": "where",
102
+
"variableName": "where"
103
+
}
104
+
],
105
+
"concreteType": "FmTealAlphaFeedPlayConnection",
106
+
"kind": "LinkedField",
107
+
"name": "__Profile_fmTealAlphaFeedPlays_connection",
108
+
"plural": false,
109
+
"selections": [
110
+
{
111
+
"alias": null,
112
+
"args": null,
113
+
"kind": "ScalarField",
114
+
"name": "totalCount",
115
+
"storageKey": null
116
+
},
117
+
{
118
+
"alias": null,
119
+
"args": null,
120
+
"concreteType": "FmTealAlphaFeedPlayEdge",
121
+
"kind": "LinkedField",
122
+
"name": "edges",
123
+
"plural": true,
124
+
"selections": [
125
+
{
126
+
"alias": null,
127
+
"args": null,
128
+
"concreteType": "FmTealAlphaFeedPlay",
129
+
"kind": "LinkedField",
130
+
"name": "node",
131
+
"plural": false,
132
+
"selections": [
133
+
{
134
+
"args": null,
135
+
"kind": "FragmentSpread",
136
+
"name": "TrackItem_play"
137
+
},
138
+
{
139
+
"alias": null,
140
+
"args": null,
141
+
"kind": "ScalarField",
142
+
"name": "actorHandle",
143
+
"storageKey": null
144
+
},
145
+
{
146
+
"alias": null,
147
+
"args": null,
148
+
"concreteType": "AppBskyActorProfile",
149
+
"kind": "LinkedField",
150
+
"name": "appBskyActorProfile",
151
+
"plural": false,
152
+
"selections": [
153
+
{
154
+
"alias": null,
155
+
"args": null,
156
+
"kind": "ScalarField",
157
+
"name": "displayName",
158
+
"storageKey": null
159
+
},
160
+
{
161
+
"alias": null,
162
+
"args": null,
163
+
"kind": "ScalarField",
164
+
"name": "description",
165
+
"storageKey": null
166
+
},
167
+
{
168
+
"alias": null,
169
+
"args": null,
170
+
"concreteType": "Blob",
171
+
"kind": "LinkedField",
172
+
"name": "avatar",
173
+
"plural": false,
174
+
"selections": [
175
+
{
176
+
"alias": null,
177
+
"args": [
178
+
{
179
+
"kind": "Literal",
180
+
"name": "preset",
181
+
"value": "avatar"
182
+
}
183
+
],
184
+
"kind": "ScalarField",
185
+
"name": "url",
186
+
"storageKey": "url(preset:\"avatar\")"
187
+
}
188
+
],
189
+
"storageKey": null
190
+
}
191
+
],
192
+
"storageKey": null
193
+
},
194
+
{
195
+
"alias": null,
196
+
"args": null,
197
+
"kind": "ScalarField",
198
+
"name": "__typename",
199
+
"storageKey": null
200
+
}
201
+
],
202
+
"storageKey": null
203
+
},
204
+
{
205
+
"alias": null,
206
+
"args": null,
207
+
"kind": "ScalarField",
208
+
"name": "cursor",
209
+
"storageKey": null
210
+
}
211
+
],
212
+
"storageKey": null
213
+
},
214
+
{
215
+
"alias": null,
216
+
"args": null,
217
+
"concreteType": "PageInfo",
218
+
"kind": "LinkedField",
219
+
"name": "pageInfo",
220
+
"plural": false,
221
+
"selections": [
222
+
{
223
+
"alias": null,
224
+
"args": null,
225
+
"kind": "ScalarField",
226
+
"name": "endCursor",
227
+
"storageKey": null
228
+
},
229
+
{
230
+
"alias": null,
231
+
"args": null,
232
+
"kind": "ScalarField",
233
+
"name": "hasNextPage",
234
+
"storageKey": null
235
+
}
236
+
],
237
+
"storageKey": null
238
+
}
239
+
],
240
+
"storageKey": null
241
+
}
242
+
],
243
+
"type": "Query",
244
+
"abstractKey": null
245
+
};
246
+
})();
247
+
248
+
(node as any).hash = "474168bb0d13417c1b7067c09a82f7a2";
249
+
250
+
export default node;
+120
src/__generated__/TopAlbumsQuery.graphql.ts
+120
src/__generated__/TopAlbumsQuery.graphql.ts
···
1
+
/**
2
+
* @generated SignedSource<<a492b6190b60e9be64d199702b76977a>>
3
+
* @lightSyntaxTransform
4
+
* @nogrep
5
+
*/
6
+
7
+
/* tslint:disable */
8
+
/* eslint-disable */
9
+
// @ts-nocheck
10
+
11
+
import { ConcreteRequest } from 'relay-runtime';
12
+
export type TopAlbumsQuery$variables = Record<PropertyKey, never>;
13
+
export type TopAlbumsQuery$data = {
14
+
readonly fmTealAlphaFeedPlaysAggregated: ReadonlyArray<{
15
+
readonly artists: string | null | undefined;
16
+
readonly count: number;
17
+
readonly releaseMbId: string | null | undefined;
18
+
readonly releaseName: string | null | undefined;
19
+
}>;
20
+
};
21
+
export type TopAlbumsQuery = {
22
+
response: TopAlbumsQuery$data;
23
+
variables: TopAlbumsQuery$variables;
24
+
};
25
+
26
+
const node: ConcreteRequest = (function(){
27
+
var v0 = [
28
+
{
29
+
"alias": null,
30
+
"args": [
31
+
{
32
+
"kind": "Literal",
33
+
"name": "groupBy",
34
+
"value": [
35
+
"releaseMbId",
36
+
"releaseName",
37
+
"artists"
38
+
]
39
+
},
40
+
{
41
+
"kind": "Literal",
42
+
"name": "limit",
43
+
"value": 100
44
+
},
45
+
{
46
+
"kind": "Literal",
47
+
"name": "orderBy",
48
+
"value": {
49
+
"count": "desc"
50
+
}
51
+
}
52
+
],
53
+
"concreteType": "FmTealAlphaFeedPlayAggregated",
54
+
"kind": "LinkedField",
55
+
"name": "fmTealAlphaFeedPlaysAggregated",
56
+
"plural": true,
57
+
"selections": [
58
+
{
59
+
"alias": null,
60
+
"args": null,
61
+
"kind": "ScalarField",
62
+
"name": "releaseMbId",
63
+
"storageKey": null
64
+
},
65
+
{
66
+
"alias": null,
67
+
"args": null,
68
+
"kind": "ScalarField",
69
+
"name": "releaseName",
70
+
"storageKey": null
71
+
},
72
+
{
73
+
"alias": null,
74
+
"args": null,
75
+
"kind": "ScalarField",
76
+
"name": "artists",
77
+
"storageKey": null
78
+
},
79
+
{
80
+
"alias": null,
81
+
"args": null,
82
+
"kind": "ScalarField",
83
+
"name": "count",
84
+
"storageKey": null
85
+
}
86
+
],
87
+
"storageKey": "fmTealAlphaFeedPlaysAggregated(groupBy:[\"releaseMbId\",\"releaseName\",\"artists\"],limit:100,orderBy:{\"count\":\"desc\"})"
88
+
}
89
+
];
90
+
return {
91
+
"fragment": {
92
+
"argumentDefinitions": [],
93
+
"kind": "Fragment",
94
+
"metadata": null,
95
+
"name": "TopAlbumsQuery",
96
+
"selections": (v0/*: any*/),
97
+
"type": "Query",
98
+
"abstractKey": null
99
+
},
100
+
"kind": "Request",
101
+
"operation": {
102
+
"argumentDefinitions": [],
103
+
"kind": "Operation",
104
+
"name": "TopAlbumsQuery",
105
+
"selections": (v0/*: any*/)
106
+
},
107
+
"params": {
108
+
"cacheID": "65b42ff33b8a5de6eb4785e764ae70fc",
109
+
"id": null,
110
+
"metadata": {},
111
+
"name": "TopAlbumsQuery",
112
+
"operationKind": "query",
113
+
"text": "query TopAlbumsQuery {\n fmTealAlphaFeedPlaysAggregated(groupBy: [\"releaseMbId\", \"releaseName\", \"artists\"], orderBy: {count: desc}, limit: 100) {\n releaseMbId\n releaseName\n artists\n count\n }\n}\n"
114
+
}
115
+
};
116
+
})();
117
+
118
+
(node as any).hash = "b5748a3a4af3140d3cff228e7462f73d";
119
+
120
+
export default node;
+120
src/__generated__/TopTracksQuery.graphql.ts
+120
src/__generated__/TopTracksQuery.graphql.ts
···
1
+
/**
2
+
* @generated SignedSource<<ed55197dadbf24b9cf975295d63a2436>>
3
+
* @lightSyntaxTransform
4
+
* @nogrep
5
+
*/
6
+
7
+
/* tslint:disable */
8
+
/* eslint-disable */
9
+
// @ts-nocheck
10
+
11
+
import { ConcreteRequest } from 'relay-runtime';
12
+
export type TopTracksQuery$variables = Record<PropertyKey, never>;
13
+
export type TopTracksQuery$data = {
14
+
readonly fmTealAlphaFeedPlaysAggregated: ReadonlyArray<{
15
+
readonly artists: string | null | undefined;
16
+
readonly count: number;
17
+
readonly releaseMbId: string | null | undefined;
18
+
readonly trackName: string | null | undefined;
19
+
}>;
20
+
};
21
+
export type TopTracksQuery = {
22
+
response: TopTracksQuery$data;
23
+
variables: TopTracksQuery$variables;
24
+
};
25
+
26
+
const node: ConcreteRequest = (function(){
27
+
var v0 = [
28
+
{
29
+
"alias": null,
30
+
"args": [
31
+
{
32
+
"kind": "Literal",
33
+
"name": "groupBy",
34
+
"value": [
35
+
"trackName",
36
+
"releaseMbId",
37
+
"artists"
38
+
]
39
+
},
40
+
{
41
+
"kind": "Literal",
42
+
"name": "limit",
43
+
"value": 50
44
+
},
45
+
{
46
+
"kind": "Literal",
47
+
"name": "orderBy",
48
+
"value": {
49
+
"count": "desc"
50
+
}
51
+
}
52
+
],
53
+
"concreteType": "FmTealAlphaFeedPlayAggregated",
54
+
"kind": "LinkedField",
55
+
"name": "fmTealAlphaFeedPlaysAggregated",
56
+
"plural": true,
57
+
"selections": [
58
+
{
59
+
"alias": null,
60
+
"args": null,
61
+
"kind": "ScalarField",
62
+
"name": "trackName",
63
+
"storageKey": null
64
+
},
65
+
{
66
+
"alias": null,
67
+
"args": null,
68
+
"kind": "ScalarField",
69
+
"name": "releaseMbId",
70
+
"storageKey": null
71
+
},
72
+
{
73
+
"alias": null,
74
+
"args": null,
75
+
"kind": "ScalarField",
76
+
"name": "artists",
77
+
"storageKey": null
78
+
},
79
+
{
80
+
"alias": null,
81
+
"args": null,
82
+
"kind": "ScalarField",
83
+
"name": "count",
84
+
"storageKey": null
85
+
}
86
+
],
87
+
"storageKey": "fmTealAlphaFeedPlaysAggregated(groupBy:[\"trackName\",\"releaseMbId\",\"artists\"],limit:50,orderBy:{\"count\":\"desc\"})"
88
+
}
89
+
];
90
+
return {
91
+
"fragment": {
92
+
"argumentDefinitions": [],
93
+
"kind": "Fragment",
94
+
"metadata": null,
95
+
"name": "TopTracksQuery",
96
+
"selections": (v0/*: any*/),
97
+
"type": "Query",
98
+
"abstractKey": null
99
+
},
100
+
"kind": "Request",
101
+
"operation": {
102
+
"argumentDefinitions": [],
103
+
"kind": "Operation",
104
+
"name": "TopTracksQuery",
105
+
"selections": (v0/*: any*/)
106
+
},
107
+
"params": {
108
+
"cacheID": "61e9f7886dfe9eaeb599b939f2d636e5",
109
+
"id": null,
110
+
"metadata": {},
111
+
"name": "TopTracksQuery",
112
+
"operationKind": "query",
113
+
"text": "query TopTracksQuery {\n fmTealAlphaFeedPlaysAggregated(groupBy: [\"trackName\", \"releaseMbId\", \"artists\"], orderBy: {count: desc}, limit: 50) {\n trackName\n releaseMbId\n artists\n count\n }\n}\n"
114
+
}
115
+
};
116
+
})();
117
+
118
+
(node as any).hash = "536f8ddb64daa09017abff121d7ea8ce";
119
+
120
+
export default node;
+103
src/__generated__/TrackItem_play.graphql.ts
+103
src/__generated__/TrackItem_play.graphql.ts
···
1
+
/**
2
+
* @generated SignedSource<<1403d6e3403844ad955c2fe48c8a66c9>>
3
+
* @lightSyntaxTransform
4
+
* @nogrep
5
+
*/
6
+
7
+
/* tslint:disable */
8
+
/* eslint-disable */
9
+
// @ts-nocheck
10
+
11
+
import { ReaderFragment } from 'relay-runtime';
12
+
import { FragmentRefs } from "relay-runtime";
13
+
export type TrackItem_play$data = {
14
+
readonly actorHandle: string | null | undefined;
15
+
readonly appBskyActorProfile: {
16
+
readonly displayName: string | null | undefined;
17
+
} | null | undefined;
18
+
readonly artists: any | null | undefined;
19
+
readonly playedTime: string | null | undefined;
20
+
readonly releaseMbId: string | null | undefined;
21
+
readonly releaseName: string | null | undefined;
22
+
readonly trackName: string;
23
+
readonly " $fragmentType": "TrackItem_play";
24
+
};
25
+
export type TrackItem_play$key = {
26
+
readonly " $data"?: TrackItem_play$data;
27
+
readonly " $fragmentSpreads": FragmentRefs<"TrackItem_play">;
28
+
};
29
+
30
+
const node: ReaderFragment = {
31
+
"argumentDefinitions": [],
32
+
"kind": "Fragment",
33
+
"metadata": null,
34
+
"name": "TrackItem_play",
35
+
"selections": [
36
+
{
37
+
"alias": null,
38
+
"args": null,
39
+
"kind": "ScalarField",
40
+
"name": "trackName",
41
+
"storageKey": null
42
+
},
43
+
{
44
+
"alias": null,
45
+
"args": null,
46
+
"kind": "ScalarField",
47
+
"name": "playedTime",
48
+
"storageKey": null
49
+
},
50
+
{
51
+
"alias": null,
52
+
"args": null,
53
+
"kind": "ScalarField",
54
+
"name": "artists",
55
+
"storageKey": null
56
+
},
57
+
{
58
+
"alias": null,
59
+
"args": null,
60
+
"kind": "ScalarField",
61
+
"name": "releaseName",
62
+
"storageKey": null
63
+
},
64
+
{
65
+
"alias": null,
66
+
"args": null,
67
+
"kind": "ScalarField",
68
+
"name": "releaseMbId",
69
+
"storageKey": null
70
+
},
71
+
{
72
+
"alias": null,
73
+
"args": null,
74
+
"kind": "ScalarField",
75
+
"name": "actorHandle",
76
+
"storageKey": null
77
+
},
78
+
{
79
+
"alias": null,
80
+
"args": null,
81
+
"concreteType": "AppBskyActorProfile",
82
+
"kind": "LinkedField",
83
+
"name": "appBskyActorProfile",
84
+
"plural": false,
85
+
"selections": [
86
+
{
87
+
"alias": null,
88
+
"args": null,
89
+
"kind": "ScalarField",
90
+
"name": "displayName",
91
+
"storageKey": null
92
+
}
93
+
],
94
+
"storageKey": null
95
+
}
96
+
],
97
+
"type": "FmTealAlphaFeedPlay",
98
+
"abstractKey": null
99
+
};
100
+
101
+
(node as any).hash = "08e8e2c14a894471e9a3153f8918e02e";
102
+
103
+
export default node;
+1
src/albumArtCache.ts
+1
src/albumArtCache.ts
···
1
+
export const albumArtCache = new Map<string, string | null>();
+1
src/index.css
+1
src/index.css
···
1
+
@import "tailwindcss";
+43
src/main.tsx
+43
src/main.tsx
···
1
+
import { StrictMode, Suspense } from "react";
2
+
import { createRoot } from "react-dom/client";
3
+
import { BrowserRouter, Routes, Route } from "react-router-dom";
4
+
import "./index.css";
5
+
import App from "./App.tsx";
6
+
import Profile from "./Profile.tsx";
7
+
import LoadingFallback from "./LoadingFallback.tsx";
8
+
import { RelayEnvironmentProvider } from "react-relay";
9
+
import { Environment, Network, type FetchFunction } from "relay-runtime";
10
+
11
+
const HTTP_ENDPOINT =
12
+
"https://api.slices.network/graphql?slice=at://did:plc:fpruhuo22xkm5o7ttr2ktxdo/network.slices.slice/3m257yljpbg2a";
13
+
14
+
const fetchGraphQL: FetchFunction = async (request, variables) => {
15
+
const resp = await fetch(HTTP_ENDPOINT, {
16
+
method: "POST",
17
+
headers: { "Content-Type": "application/json" },
18
+
body: JSON.stringify({ query: request.text, variables }),
19
+
});
20
+
if (!resp.ok) {
21
+
throw new Error("Response failed.");
22
+
}
23
+
return await resp.json();
24
+
};
25
+
26
+
const environment = new Environment({
27
+
network: Network.create(fetchGraphQL),
28
+
});
29
+
30
+
createRoot(document.getElementById("root")!).render(
31
+
<StrictMode>
32
+
<BrowserRouter>
33
+
<RelayEnvironmentProvider environment={environment}>
34
+
<Suspense fallback={<LoadingFallback />}>
35
+
<Routes>
36
+
<Route path="/" element={<App />} />
37
+
<Route path="/profile/:handle" element={<Profile />} />
38
+
</Routes>
39
+
</Suspense>
40
+
</RelayEnvironmentProvider>
41
+
</BrowserRouter>
42
+
</StrictMode>
43
+
);
+48
src/useAlbumArt.ts
+48
src/useAlbumArt.ts
···
1
+
import { useState, useEffect } from "react";
2
+
import { albumArtCache } from "./albumArtCache";
3
+
4
+
export function useAlbumArt(releaseMbId: string | null | undefined) {
5
+
const [albumArtUrl, setAlbumArtUrl] = useState<string | null>(null);
6
+
const [isLoading, setIsLoading] = useState(false);
7
+
8
+
useEffect(() => {
9
+
if (!releaseMbId) return;
10
+
11
+
// Check cache first
12
+
if (albumArtCache.has(releaseMbId)) {
13
+
setAlbumArtUrl(albumArtCache.get(releaseMbId) || null);
14
+
setIsLoading(false);
15
+
return;
16
+
}
17
+
18
+
setIsLoading(true);
19
+
20
+
const fetchAlbumArt = async () => {
21
+
try {
22
+
// Fetch cover art from Cover Art Archive
23
+
const coverArtUrl = `https://coverartarchive.org/release/${releaseMbId}/front-250`;
24
+
25
+
// Check if cover art exists
26
+
const coverArtResponse = await fetch(coverArtUrl, { method: "HEAD" });
27
+
28
+
if (coverArtResponse.ok) {
29
+
setAlbumArtUrl(coverArtUrl);
30
+
albumArtCache.set(releaseMbId, coverArtUrl);
31
+
} else {
32
+
setAlbumArtUrl(null);
33
+
albumArtCache.set(releaseMbId, null);
34
+
}
35
+
} catch (error) {
36
+
console.error("Error fetching album art:", error);
37
+
setAlbumArtUrl(null);
38
+
albumArtCache.set(releaseMbId, null);
39
+
} finally {
40
+
setIsLoading(false);
41
+
}
42
+
};
43
+
44
+
fetchAlbumArt();
45
+
}, [releaseMbId]);
46
+
47
+
return { albumArtUrl, isLoading };
48
+
}
+28
tsconfig.app.json
+28
tsconfig.app.json
···
1
+
{
2
+
"compilerOptions": {
3
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4
+
"target": "ES2022",
5
+
"useDefineForClassFields": true,
6
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
7
+
"module": "ESNext",
8
+
"types": ["vite/client"],
9
+
"skipLibCheck": true,
10
+
11
+
/* Bundler mode */
12
+
"moduleResolution": "bundler",
13
+
"allowImportingTsExtensions": true,
14
+
"verbatimModuleSyntax": true,
15
+
"moduleDetection": "force",
16
+
"noEmit": true,
17
+
"jsx": "react-jsx",
18
+
19
+
/* Linting */
20
+
"strict": true,
21
+
"noUnusedLocals": true,
22
+
"noUnusedParameters": true,
23
+
"erasableSyntaxOnly": true,
24
+
"noFallthroughCasesInSwitch": true,
25
+
"noUncheckedSideEffectImports": true
26
+
},
27
+
"include": ["src"]
28
+
}
+7
tsconfig.json
+7
tsconfig.json
+26
tsconfig.node.json
+26
tsconfig.node.json
···
1
+
{
2
+
"compilerOptions": {
3
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4
+
"target": "ES2023",
5
+
"lib": ["ES2023"],
6
+
"module": "ESNext",
7
+
"types": ["node"],
8
+
"skipLibCheck": true,
9
+
10
+
/* Bundler mode */
11
+
"moduleResolution": "bundler",
12
+
"allowImportingTsExtensions": true,
13
+
"verbatimModuleSyntax": true,
14
+
"moduleDetection": "force",
15
+
"noEmit": true,
16
+
17
+
/* Linting */
18
+
"strict": true,
19
+
"noUnusedLocals": true,
20
+
"noUnusedParameters": true,
21
+
"erasableSyntaxOnly": true,
22
+
"noFallthroughCasesInSwitch": true,
23
+
"noUncheckedSideEffectImports": true
24
+
},
25
+
"include": ["vite.config.ts"]
26
+
}