+38
.github/ISSUE_TEMPLATE/bug_report.md
+38
.github/ISSUE_TEMPLATE/bug_report.md
···
1
+
---
2
+
name: Bug report
3
+
about: Create a report to help us improve
4
+
title: ''
5
+
labels: ''
6
+
assignees: ''
7
+
8
+
---
9
+
10
+
**Describe the bug**
11
+
A clear and concise description of what the bug is.
12
+
13
+
**To Reproduce**
14
+
Steps to reproduce the behavior:
15
+
1. Go to '...'
16
+
2. Click on '....'
17
+
3. Scroll down to '....'
18
+
4. See error
19
+
20
+
**Expected behavior**
21
+
A clear and concise description of what you expected to happen.
22
+
23
+
**Screenshots**
24
+
If applicable, add screenshots to help explain your problem.
25
+
26
+
**Desktop (please complete the following information):**
27
+
- OS: [e.g. iOS]
28
+
- Browser [e.g. chrome, safari]
29
+
- Version [e.g. 22]
30
+
31
+
**Smartphone (please complete the following information):**
32
+
- Device: [e.g. iPhone6]
33
+
- OS: [e.g. iOS8.1]
34
+
- Browser [e.g. stock browser, safari]
35
+
- Version [e.g. 22]
36
+
37
+
**Additional context**
38
+
Add any other context about the problem here.
+187
.gitignore
+187
.gitignore
···
1
+
# Created by https://www.toptal.com/developers/gitignore/api/node,macos,svelte,vercel
2
+
# Edit at https://www.toptal.com/developers/gitignore?templates=node,macos,svelte,vercel
3
+
4
+
### macOS ###
5
+
# General
6
+
.DS_Store
7
+
.AppleDouble
8
+
.LSOverride
9
+
10
+
# Icon must end with two \r
11
+
Icon
12
+
13
+
14
+
# Thumbnails
15
+
._*
16
+
17
+
# Files that might appear in the root of a volume
18
+
.DocumentRevisions-V100
19
+
.fseventsd
20
+
.Spotlight-V100
21
+
.TemporaryItems
22
+
.Trashes
23
+
.VolumeIcon.icns
24
+
.com.apple.timemachine.donotpresent
25
+
26
+
# Directories potentially created on remote AFP share
27
+
.AppleDB
28
+
.AppleDesktop
29
+
Network Trash Folder
30
+
Temporary Items
31
+
.apdisk
32
+
33
+
### macOS Patch ###
34
+
# iCloud generated files
35
+
*.icloud
36
+
37
+
### Node ###
38
+
# Logs
39
+
logs
40
+
*.log
41
+
npm-debug.log*
42
+
yarn-debug.log*
43
+
yarn-error.log*
44
+
lerna-debug.log*
45
+
.pnpm-debug.log*
46
+
47
+
# Diagnostic reports (https://nodejs.org/api/report.html)
48
+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
49
+
50
+
# Runtime data
51
+
pids
52
+
*.pid
53
+
*.seed
54
+
*.pid.lock
55
+
56
+
# Directory for instrumented libs generated by jscoverage/JSCover
57
+
lib-cov
58
+
59
+
# Coverage directory used by tools like istanbul
60
+
coverage
61
+
*.lcov
62
+
63
+
# nyc test coverage
64
+
.nyc_output
65
+
66
+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
67
+
.grunt
68
+
69
+
# Bower dependency directory (https://bower.io/)
70
+
bower_components
71
+
72
+
# node-waf configuration
73
+
.lock-wscript
74
+
75
+
# Compiled binary addons (https://nodejs.org/api/addons.html)
76
+
build/Release
77
+
78
+
# Dependency directories
79
+
node_modules/
80
+
jspm_packages/
81
+
82
+
# Snowpack dependency directory (https://snowpack.dev/)
83
+
web_modules/
84
+
85
+
# TypeScript cache
86
+
*.tsbuildinfo
87
+
88
+
# Optional npm cache directory
89
+
.npm
90
+
91
+
# Optional eslint cache
92
+
.eslintcache
93
+
94
+
# Optional stylelint cache
95
+
.stylelintcache
96
+
97
+
# Microbundle cache
98
+
.rpt2_cache/
99
+
.rts2_cache_cjs/
100
+
.rts2_cache_es/
101
+
.rts2_cache_umd/
102
+
103
+
# Optional REPL history
104
+
.node_repl_history
105
+
106
+
# Output of 'npm pack'
107
+
*.tgz
108
+
109
+
# Yarn Integrity file
110
+
.yarn-integrity
111
+
112
+
# dotenv environment variable files
113
+
.env
114
+
.env.development.local
115
+
.env.test.local
116
+
.env.production.local
117
+
.env.local
118
+
119
+
# parcel-bundler cache (https://parceljs.org/)
120
+
.cache
121
+
.parcel-cache
122
+
123
+
# Next.js build output
124
+
.next
125
+
out
126
+
127
+
# Nuxt.js build / generate output
128
+
.nuxt
129
+
dist
130
+
131
+
# Gatsby files
132
+
.cache/
133
+
# Comment in the public line in if your project uses Gatsby and not Next.js
134
+
# https://nextjs.org/blog/next-9-1#public-directory-support
135
+
# public
136
+
137
+
# vuepress build output
138
+
.vuepress/dist
139
+
140
+
# vuepress v2.x temp and cache directory
141
+
.temp
142
+
143
+
# Docusaurus cache and generated files
144
+
.docusaurus
145
+
146
+
# Serverless directories
147
+
.serverless/
148
+
149
+
# FuseBox cache
150
+
.fusebox/
151
+
152
+
# DynamoDB Local files
153
+
.dynamodb/
154
+
155
+
# TernJS port file
156
+
.tern-port
157
+
158
+
# Stores VSCode versions used for testing VSCode extensions
159
+
.vscode-test
160
+
161
+
# yarn v2
162
+
.yarn/cache
163
+
.yarn/unplugged
164
+
.yarn/build-state.yml
165
+
.yarn/install-state.gz
166
+
.pnp.*
167
+
168
+
### Node Patch ###
169
+
# Serverless Webpack directories
170
+
.webpack/
171
+
172
+
# Optional stylelint cache
173
+
174
+
# SvelteKit build / generate output
175
+
.svelte-kit
176
+
177
+
### Svelte ###
178
+
# gitignore template for the SvelteKit, frontend web component framework
179
+
# website: https://kit.svelte.dev/
180
+
181
+
.svelte-kit/
182
+
package
183
+
184
+
### Vercel ###
185
+
.vercel
186
+
187
+
# End of https://www.toptal.com/developers/gitignore/api/node,macos,svelte,vercel
+1
.npmrc
+1
.npmrc
···
1
+
engine-strict=true
+4
.prettierignore
+4
.prettierignore
+25
.vercelignore
+25
.vercelignore
···
1
+
# Local Vercel directory
2
+
.vercel
3
+
4
+
# Docker related files (not needed for Vercel deployment)
5
+
server/Dockerfile
6
+
server/compose.yml
7
+
server/Caddyfile
8
+
server/.dockerignore
9
+
10
+
# Node.js
11
+
node_modules
12
+
.npm
13
+
14
+
# Logs
15
+
logs
16
+
*.log
17
+
npm-debug.log*
18
+
yarn-debug.log*
19
+
yarn-error.log*
20
+
21
+
# Editor directories and files
22
+
.idea
23
+
.vscode
24
+
*.swp
25
+
*.swo
+7
.vscode/settings.json
+7
.vscode/settings.json
+55
.vscode/tailwind.json
+55
.vscode/tailwind.json
···
1
+
{
2
+
"version": 1.1,
3
+
"atDirectives": [
4
+
{
5
+
"name": "@tailwind",
6
+
"description": "Use the @tailwind directive to insert Tailwind's base, components, and utilities styles into your CSS.",
7
+
"references": [
8
+
{
9
+
"name": "Tailwind CSS Documentation",
10
+
"url": "https://tailwindcss.com/docs/functions-and-directives#tailwind"
11
+
}
12
+
]
13
+
},
14
+
{
15
+
"name": "@apply",
16
+
"description": "Use @apply to inline any existing utility classes into your own custom CSS.",
17
+
"references": [
18
+
{
19
+
"name": "Tailwind CSS Documentation",
20
+
"url": "https://tailwindcss.com/docs/functions-and-directives#apply"
21
+
}
22
+
]
23
+
},
24
+
{
25
+
"name": "@responsive",
26
+
"description": "You can generate responsive variants of your own classes by wrapping their definitions in the @responsive directive.",
27
+
"references": [
28
+
{
29
+
"name": "Tailwind CSS Documentation",
30
+
"url": "https://tailwindcss.com/docs/functions-and-directives#responsive"
31
+
}
32
+
]
33
+
},
34
+
{
35
+
"name": "@screen",
36
+
"description": "The @screen directive allows you to create media queries that reference your breakpoints by name instead of duplicating their values in your own CSS.",
37
+
"references": [
38
+
{
39
+
"name": "Tailwind CSS Documentation",
40
+
"url": "https://tailwindcss.com/docs/functions-and-directives#screen"
41
+
}
42
+
]
43
+
},
44
+
{
45
+
"name": "@variants",
46
+
"description": "Generate variants for your own utilities by wrapping their definitions in the @variants directive.",
47
+
"references": [
48
+
{
49
+
"name": "Tailwind CSS Documentation",
50
+
"url": "https://tailwindcss.com/docs/functions-and-directives#variants"
51
+
}
52
+
]
53
+
}
54
+
]
55
+
}
+661
LICENSE
+661
LICENSE
···
1
+
GNU AFFERO GENERAL PUBLIC LICENSE
2
+
Version 3, 19 November 2007
3
+
4
+
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
5
+
Everyone is permitted to copy and distribute verbatim copies
6
+
of this license document, but changing it is not allowed.
7
+
8
+
Preamble
9
+
10
+
The GNU Affero General Public License is a free, copyleft license for
11
+
software and other kinds of works, specifically designed to ensure
12
+
cooperation with the community in the case of network server software.
13
+
14
+
The licenses for most software and other practical works are designed
15
+
to take away your freedom to share and change the works. By contrast,
16
+
our General Public Licenses are intended to guarantee your freedom to
17
+
share and change all versions of a program--to make sure it remains free
18
+
software for all its users.
19
+
20
+
When we speak of free software, we are referring to freedom, not
21
+
price. Our General Public Licenses are designed to make sure that you
22
+
have the freedom to distribute copies of free software (and charge for
23
+
them if you wish), that you receive source code or can get it if you
24
+
want it, that you can change the software or use pieces of it in new
25
+
free programs, and that you know you can do these things.
26
+
27
+
Developers that use our General Public Licenses protect your rights
28
+
with two steps: (1) assert copyright on the software, and (2) offer
29
+
you this License which gives you legal permission to copy, distribute
30
+
and/or modify the software.
31
+
32
+
A secondary benefit of defending all users' freedom is that
33
+
improvements made in alternate versions of the program, if they
34
+
receive widespread use, become available for other developers to
35
+
incorporate. Many developers of free software are heartened and
36
+
encouraged by the resulting cooperation. However, in the case of
37
+
software used on network servers, this result may fail to come about.
38
+
The GNU General Public License permits making a modified version and
39
+
letting the public access it on a server without ever releasing its
40
+
source code to the public.
41
+
42
+
The GNU Affero General Public License is designed specifically to
43
+
ensure that, in such cases, the modified source code becomes available
44
+
to the community. It requires the operator of a network server to
45
+
provide the source code of the modified version running there to the
46
+
users of that server. Therefore, public use of a modified version, on
47
+
a publicly accessible server, gives the public access to the source
48
+
code of the modified version.
49
+
50
+
An older license, called the Affero General Public License and
51
+
published by Affero, was designed to accomplish similar goals. This is
52
+
a different license, not a version of the Affero GPL, but Affero has
53
+
released a new version of the Affero GPL which permits relicensing under
54
+
this license.
55
+
56
+
The precise terms and conditions for copying, distribution and
57
+
modification follow.
58
+
59
+
TERMS AND CONDITIONS
60
+
61
+
0. Definitions.
62
+
63
+
"This License" refers to version 3 of the GNU Affero General Public License.
64
+
65
+
"Copyright" also means copyright-like laws that apply to other kinds of
66
+
works, such as semiconductor masks.
67
+
68
+
"The Program" refers to any copyrightable work licensed under this
69
+
License. Each licensee is addressed as "you". "Licensees" and
70
+
"recipients" may be individuals or organizations.
71
+
72
+
To "modify" a work means to copy from or adapt all or part of the work
73
+
in a fashion requiring copyright permission, other than the making of an
74
+
exact copy. The resulting work is called a "modified version" of the
75
+
earlier work or a work "based on" the earlier work.
76
+
77
+
A "covered work" means either the unmodified Program or a work based
78
+
on the Program.
79
+
80
+
To "propagate" a work means to do anything with it that, without
81
+
permission, would make you directly or secondarily liable for
82
+
infringement under applicable copyright law, except executing it on a
83
+
computer or modifying a private copy. Propagation includes copying,
84
+
distribution (with or without modification), making available to the
85
+
public, and in some countries other activities as well.
86
+
87
+
To "convey" a work means any kind of propagation that enables other
88
+
parties to make or receive copies. Mere interaction with a user through
89
+
a computer network, with no transfer of a copy, is not conveying.
90
+
91
+
An interactive user interface displays "Appropriate Legal Notices"
92
+
to the extent that it includes a convenient and prominently visible
93
+
feature that (1) displays an appropriate copyright notice, and (2)
94
+
tells the user that there is no warranty for the work (except to the
95
+
extent that warranties are provided), that licensees may convey the
96
+
work under this License, and how to view a copy of this License. If
97
+
the interface presents a list of user commands or options, such as a
98
+
menu, a prominent item in the list meets this criterion.
99
+
100
+
1. Source Code.
101
+
102
+
The "source code" for a work means the preferred form of the work
103
+
for making modifications to it. "Object code" means any non-source
104
+
form of a work.
105
+
106
+
A "Standard Interface" means an interface that either is an official
107
+
standard defined by a recognized standards body, or, in the case of
108
+
interfaces specified for a particular programming language, one that
109
+
is widely used among developers working in that language.
110
+
111
+
The "System Libraries" of an executable work include anything, other
112
+
than the work as a whole, that (a) is included in the normal form of
113
+
packaging a Major Component, but which is not part of that Major
114
+
Component, and (b) serves only to enable use of the work with that
115
+
Major Component, or to implement a Standard Interface for which an
116
+
implementation is available to the public in source code form. A
117
+
"Major Component", in this context, means a major essential component
118
+
(kernel, window system, and so on) of the specific operating system
119
+
(if any) on which the executable work runs, or a compiler used to
120
+
produce the work, or an object code interpreter used to run it.
121
+
122
+
The "Corresponding Source" for a work in object code form means all
123
+
the source code needed to generate, install, and (for an executable
124
+
work) run the object code and to modify the work, including scripts to
125
+
control those activities. However, it does not include the work's
126
+
System Libraries, or general-purpose tools or generally available free
127
+
programs which are used unmodified in performing those activities but
128
+
which are not part of the work. For example, Corresponding Source
129
+
includes interface definition files associated with source files for
130
+
the work, and the source code for shared libraries and dynamically
131
+
linked subprograms that the work is specifically designed to require,
132
+
such as by intimate data communication or control flow between those
133
+
subprograms and other parts of the work.
134
+
135
+
The Corresponding Source need not include anything that users
136
+
can regenerate automatically from other parts of the Corresponding
137
+
Source.
138
+
139
+
The Corresponding Source for a work in source code form is that
140
+
same work.
141
+
142
+
2. Basic Permissions.
143
+
144
+
All rights granted under this License are granted for the term of
145
+
copyright on the Program, and are irrevocable provided the stated
146
+
conditions are met. This License explicitly affirms your unlimited
147
+
permission to run the unmodified Program. The output from running a
148
+
covered work is covered by this License only if the output, given its
149
+
content, constitutes a covered work. This License acknowledges your
150
+
rights of fair use or other equivalent, as provided by copyright law.
151
+
152
+
You may make, run and propagate covered works that you do not
153
+
convey, without conditions so long as your license otherwise remains
154
+
in force. You may convey covered works to others for the sole purpose
155
+
of having them make modifications exclusively for you, or provide you
156
+
with facilities for running those works, provided that you comply with
157
+
the terms of this License in conveying all material for which you do
158
+
not control copyright. Those thus making or running the covered works
159
+
for you must do so exclusively on your behalf, under your direction
160
+
and control, on terms that prohibit them from making any copies of
161
+
your copyrighted material outside their relationship with you.
162
+
163
+
Conveying under any other circumstances is permitted solely under
164
+
the conditions stated below. Sublicensing is not allowed; section 10
165
+
makes it unnecessary.
166
+
167
+
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
168
+
169
+
No covered work shall be deemed part of an effective technological
170
+
measure under any applicable law fulfilling obligations under article
171
+
11 of the WIPO copyright treaty adopted on 20 December 1996, or
172
+
similar laws prohibiting or restricting circumvention of such
173
+
measures.
174
+
175
+
When you convey a covered work, you waive any legal power to forbid
176
+
circumvention of technological measures to the extent such circumvention
177
+
is effected by exercising rights under this License with respect to
178
+
the covered work, and you disclaim any intention to limit operation or
179
+
modification of the work as a means of enforcing, against the work's
180
+
users, your or third parties' legal rights to forbid circumvention of
181
+
technological measures.
182
+
183
+
4. Conveying Verbatim Copies.
184
+
185
+
You may convey verbatim copies of the Program's source code as you
186
+
receive it, in any medium, provided that you conspicuously and
187
+
appropriately publish on each copy an appropriate copyright notice;
188
+
keep intact all notices stating that this License and any
189
+
non-permissive terms added in accord with section 7 apply to the code;
190
+
keep intact all notices of the absence of any warranty; and give all
191
+
recipients a copy of this License along with the Program.
192
+
193
+
You may charge any price or no price for each copy that you convey,
194
+
and you may offer support or warranty protection for a fee.
195
+
196
+
5. Conveying Modified Source Versions.
197
+
198
+
You may convey a work based on the Program, or the modifications to
199
+
produce it from the Program, in the form of source code under the
200
+
terms of section 4, provided that you also meet all of these conditions:
201
+
202
+
a) The work must carry prominent notices stating that you modified
203
+
it, and giving a relevant date.
204
+
205
+
b) The work must carry prominent notices stating that it is
206
+
released under this License and any conditions added under section
207
+
7. This requirement modifies the requirement in section 4 to
208
+
"keep intact all notices".
209
+
210
+
c) You must license the entire work, as a whole, under this
211
+
License to anyone who comes into possession of a copy. This
212
+
License will therefore apply, along with any applicable section 7
213
+
additional terms, to the whole of the work, and all its parts,
214
+
regardless of how they are packaged. This License gives no
215
+
permission to license the work in any other way, but it does not
216
+
invalidate such permission if you have separately received it.
217
+
218
+
d) If the work has interactive user interfaces, each must display
219
+
Appropriate Legal Notices; however, if the Program has interactive
220
+
interfaces that do not display Appropriate Legal Notices, your
221
+
work need not make them do so.
222
+
223
+
A compilation of a covered work with other separate and independent
224
+
works, which are not by their nature extensions of the covered work,
225
+
and which are not combined with it such as to form a larger program,
226
+
in or on a volume of a storage or distribution medium, is called an
227
+
"aggregate" if the compilation and its resulting copyright are not
228
+
used to limit the access or legal rights of the compilation's users
229
+
beyond what the individual works permit. Inclusion of a covered work
230
+
in an aggregate does not cause this License to apply to the other
231
+
parts of the aggregate.
232
+
233
+
6. Conveying Non-Source Forms.
234
+
235
+
You may convey a covered work in object code form under the terms
236
+
of sections 4 and 5, provided that you also convey the
237
+
machine-readable Corresponding Source under the terms of this License,
238
+
in one of these ways:
239
+
240
+
a) Convey the object code in, or embodied in, a physical product
241
+
(including a physical distribution medium), accompanied by the
242
+
Corresponding Source fixed on a durable physical medium
243
+
customarily used for software interchange.
244
+
245
+
b) Convey the object code in, or embodied in, a physical product
246
+
(including a physical distribution medium), accompanied by a
247
+
written offer, valid for at least three years and valid for as
248
+
long as you offer spare parts or customer support for that product
249
+
model, to give anyone who possesses the object code either (1) a
250
+
copy of the Corresponding Source for all the software in the
251
+
product that is covered by this License, on a durable physical
252
+
medium customarily used for software interchange, for a price no
253
+
more than your reasonable cost of physically performing this
254
+
conveying of source, or (2) access to copy the
255
+
Corresponding Source from a network server at no charge.
256
+
257
+
c) Convey individual copies of the object code with a copy of the
258
+
written offer to provide the Corresponding Source. This
259
+
alternative is allowed only occasionally and noncommercially, and
260
+
only if you received the object code with such an offer, in accord
261
+
with subsection 6b.
262
+
263
+
d) Convey the object code by offering access from a designated
264
+
place (gratis or for a charge), and offer equivalent access to the
265
+
Corresponding Source in the same way through the same place at no
266
+
further charge. You need not require recipients to copy the
267
+
Corresponding Source along with the object code. If the place to
268
+
copy the object code is a network server, the Corresponding Source
269
+
may be on a different server (operated by you or a third party)
270
+
that supports equivalent copying facilities, provided you maintain
271
+
clear directions next to the object code saying where to find the
272
+
Corresponding Source. Regardless of what server hosts the
273
+
Corresponding Source, you remain obligated to ensure that it is
274
+
available for as long as needed to satisfy these requirements.
275
+
276
+
e) Convey the object code using peer-to-peer transmission, provided
277
+
you inform other peers where the object code and Corresponding
278
+
Source of the work are being offered to the general public at no
279
+
charge under subsection 6d.
280
+
281
+
A separable portion of the object code, whose source code is excluded
282
+
from the Corresponding Source as a System Library, need not be
283
+
included in conveying the object code work.
284
+
285
+
A "User Product" is either (1) a "consumer product", which means any
286
+
tangible personal property which is normally used for personal, family,
287
+
or household purposes, or (2) anything designed or sold for incorporation
288
+
into a dwelling. In determining whether a product is a consumer product,
289
+
doubtful cases shall be resolved in favor of coverage. For a particular
290
+
product received by a particular user, "normally used" refers to a
291
+
typical or common use of that class of product, regardless of the status
292
+
of the particular user or of the way in which the particular user
293
+
actually uses, or expects or is expected to use, the product. A product
294
+
is a consumer product regardless of whether the product has substantial
295
+
commercial, industrial or non-consumer uses, unless such uses represent
296
+
the only significant mode of use of the product.
297
+
298
+
"Installation Information" for a User Product means any methods,
299
+
procedures, authorization keys, or other information required to install
300
+
and execute modified versions of a covered work in that User Product from
301
+
a modified version of its Corresponding Source. The information must
302
+
suffice to ensure that the continued functioning of the modified object
303
+
code is in no case prevented or interfered with solely because
304
+
modification has been made.
305
+
306
+
If you convey an object code work under this section in, or with, or
307
+
specifically for use in, a User Product, and the conveying occurs as
308
+
part of a transaction in which the right of possession and use of the
309
+
User Product is transferred to the recipient in perpetuity or for a
310
+
fixed term (regardless of how the transaction is characterized), the
311
+
Corresponding Source conveyed under this section must be accompanied
312
+
by the Installation Information. But this requirement does not apply
313
+
if neither you nor any third party retains the ability to install
314
+
modified object code on the User Product (for example, the work has
315
+
been installed in ROM).
316
+
317
+
The requirement to provide Installation Information does not include a
318
+
requirement to continue to provide support service, warranty, or updates
319
+
for a work that has been modified or installed by the recipient, or for
320
+
the User Product in which it has been modified or installed. Access to a
321
+
network may be denied when the modification itself materially and
322
+
adversely affects the operation of the network or violates the rules and
323
+
protocols for communication across the network.
324
+
325
+
Corresponding Source conveyed, and Installation Information provided,
326
+
in accord with this section must be in a format that is publicly
327
+
documented (and with an implementation available to the public in
328
+
source code form), and must require no special password or key for
329
+
unpacking, reading or copying.
330
+
331
+
7. Additional Terms.
332
+
333
+
"Additional permissions" are terms that supplement the terms of this
334
+
License by making exceptions from one or more of its conditions.
335
+
Additional permissions that are applicable to the entire Program shall
336
+
be treated as though they were included in this License, to the extent
337
+
that they are valid under applicable law. If additional permissions
338
+
apply only to part of the Program, that part may be used separately
339
+
under those permissions, but the entire Program remains governed by
340
+
this License without regard to the additional permissions.
341
+
342
+
When you convey a copy of a covered work, you may at your option
343
+
remove any additional permissions from that copy, or from any part of
344
+
it. (Additional permissions may be written to require their own
345
+
removal in certain cases when you modify the work.) You may place
346
+
additional permissions on material, added by you to a covered work,
347
+
for which you have or can give appropriate copyright permission.
348
+
349
+
Notwithstanding any other provision of this License, for material you
350
+
add to a covered work, you may (if authorized by the copyright holders of
351
+
that material) supplement the terms of this License with terms:
352
+
353
+
a) Disclaiming warranty or limiting liability differently from the
354
+
terms of sections 15 and 16 of this License; or
355
+
356
+
b) Requiring preservation of specified reasonable legal notices or
357
+
author attributions in that material or in the Appropriate Legal
358
+
Notices displayed by works containing it; or
359
+
360
+
c) Prohibiting misrepresentation of the origin of that material, or
361
+
requiring that modified versions of such material be marked in
362
+
reasonable ways as different from the original version; or
363
+
364
+
d) Limiting the use for publicity purposes of names of licensors or
365
+
authors of the material; or
366
+
367
+
e) Declining to grant rights under trademark law for use of some
368
+
trade names, trademarks, or service marks; or
369
+
370
+
f) Requiring indemnification of licensors and authors of that
371
+
material by anyone who conveys the material (or modified versions of
372
+
it) with contractual assumptions of liability to the recipient, for
373
+
any liability that these contractual assumptions directly impose on
374
+
those licensors and authors.
375
+
376
+
All other non-permissive additional terms are considered "further
377
+
restrictions" within the meaning of section 10. If the Program as you
378
+
received it, or any part of it, contains a notice stating that it is
379
+
governed by this License along with a term that is a further
380
+
restriction, you may remove that term. If a license document contains
381
+
a further restriction but permits relicensing or conveying under this
382
+
License, you may add to a covered work material governed by the terms
383
+
of that license document, provided that the further restriction does
384
+
not survive such relicensing or conveying.
385
+
386
+
If you add terms to a covered work in accord with this section, you
387
+
must place, in the relevant source files, a statement of the
388
+
additional terms that apply to those files, or a notice indicating
389
+
where to find the applicable terms.
390
+
391
+
Additional terms, permissive or non-permissive, may be stated in the
392
+
form of a separately written license, or stated as exceptions;
393
+
the above requirements apply either way.
394
+
395
+
8. Termination.
396
+
397
+
You may not propagate or modify a covered work except as expressly
398
+
provided under this License. Any attempt otherwise to propagate or
399
+
modify it is void, and will automatically terminate your rights under
400
+
this License (including any patent licenses granted under the third
401
+
paragraph of section 11).
402
+
403
+
However, if you cease all violation of this License, then your
404
+
license from a particular copyright holder is reinstated (a)
405
+
provisionally, unless and until the copyright holder explicitly and
406
+
finally terminates your license, and (b) permanently, if the copyright
407
+
holder fails to notify you of the violation by some reasonable means
408
+
prior to 60 days after the cessation.
409
+
410
+
Moreover, your license from a particular copyright holder is
411
+
reinstated permanently if the copyright holder notifies you of the
412
+
violation by some reasonable means, this is the first time you have
413
+
received notice of violation of this License (for any work) from that
414
+
copyright holder, and you cure the violation prior to 30 days after
415
+
your receipt of the notice.
416
+
417
+
Termination of your rights under this section does not terminate the
418
+
licenses of parties who have received copies or rights from you under
419
+
this License. If your rights have been terminated and not permanently
420
+
reinstated, you do not qualify to receive new licenses for the same
421
+
material under section 10.
422
+
423
+
9. Acceptance Not Required for Having Copies.
424
+
425
+
You are not required to accept this License in order to receive or
426
+
run a copy of the Program. Ancillary propagation of a covered work
427
+
occurring solely as a consequence of using peer-to-peer transmission
428
+
to receive a copy likewise does not require acceptance. However,
429
+
nothing other than this License grants you permission to propagate or
430
+
modify any covered work. These actions infringe copyright if you do
431
+
not accept this License. Therefore, by modifying or propagating a
432
+
covered work, you indicate your acceptance of this License to do so.
433
+
434
+
10. Automatic Licensing of Downstream Recipients.
435
+
436
+
Each time you convey a covered work, the recipient automatically
437
+
receives a license from the original licensors, to run, modify and
438
+
propagate that work, subject to this License. You are not responsible
439
+
for enforcing compliance by third parties with this License.
440
+
441
+
An "entity transaction" is a transaction transferring control of an
442
+
organization, or substantially all assets of one, or subdividing an
443
+
organization, or merging organizations. If propagation of a covered
444
+
work results from an entity transaction, each party to that
445
+
transaction who receives a copy of the work also receives whatever
446
+
licenses to the work the party's predecessor in interest had or could
447
+
give under the previous paragraph, plus a right to possession of the
448
+
Corresponding Source of the work from the predecessor in interest, if
449
+
the predecessor has it or can get it with reasonable efforts.
450
+
451
+
You may not impose any further restrictions on the exercise of the
452
+
rights granted or affirmed under this License. For example, you may
453
+
not impose a license fee, royalty, or other charge for exercise of
454
+
rights granted under this License, and you may not initiate litigation
455
+
(including a cross-claim or counterclaim in a lawsuit) alleging that
456
+
any patent claim is infringed by making, using, selling, offering for
457
+
sale, or importing the Program or any portion of it.
458
+
459
+
11. Patents.
460
+
461
+
A "contributor" is a copyright holder who authorizes use under this
462
+
License of the Program or a work on which the Program is based. The
463
+
work thus licensed is called the contributor's "contributor version".
464
+
465
+
A contributor's "essential patent claims" are all patent claims
466
+
owned or controlled by the contributor, whether already acquired or
467
+
hereafter acquired, that would be infringed by some manner, permitted
468
+
by this License, of making, using, or selling its contributor version,
469
+
but do not include claims that would be infringed only as a
470
+
consequence of further modification of the contributor version. For
471
+
purposes of this definition, "control" includes the right to grant
472
+
patent sublicenses in a manner consistent with the requirements of
473
+
this License.
474
+
475
+
Each contributor grants you a non-exclusive, worldwide, royalty-free
476
+
patent license under the contributor's essential patent claims, to
477
+
make, use, sell, offer for sale, import and otherwise run, modify and
478
+
propagate the contents of its contributor version.
479
+
480
+
In the following three paragraphs, a "patent license" is any express
481
+
agreement or commitment, however denominated, not to enforce a patent
482
+
(such as an express permission to practice a patent or covenant not to
483
+
sue for patent infringement). To "grant" such a patent license to a
484
+
party means to make such an agreement or commitment not to enforce a
485
+
patent against the party.
486
+
487
+
If you convey a covered work, knowingly relying on a patent license,
488
+
and the Corresponding Source of the work is not available for anyone
489
+
to copy, free of charge and under the terms of this License, through a
490
+
publicly available network server or other readily accessible means,
491
+
then you must either (1) cause the Corresponding Source to be so
492
+
available, or (2) arrange to deprive yourself of the benefit of the
493
+
patent license for this particular work, or (3) arrange, in a manner
494
+
consistent with the requirements of this License, to extend the patent
495
+
license to downstream recipients. "Knowingly relying" means you have
496
+
actual knowledge that, but for the patent license, your conveying the
497
+
covered work in a country, or your recipient's use of the covered work
498
+
in a country, would infringe one or more identifiable patents in that
499
+
country that you have reason to believe are valid.
500
+
501
+
If, pursuant to or in connection with a single transaction or
502
+
arrangement, you convey, or propagate by procuring conveyance of, a
503
+
covered work, and grant a patent license to some of the parties
504
+
receiving the covered work authorizing them to use, propagate, modify
505
+
or convey a specific copy of the covered work, then the patent license
506
+
you grant is automatically extended to all recipients of the covered
507
+
work and works based on it.
508
+
509
+
A patent license is "discriminatory" if it does not include within
510
+
the scope of its coverage, prohibits the exercise of, or is
511
+
conditioned on the non-exercise of one or more of the rights that are
512
+
specifically granted under this License. You may not convey a covered
513
+
work if you are a party to an arrangement with a third party that is
514
+
in the business of distributing software, under which you make payment
515
+
to the third party based on the extent of your activity of conveying
516
+
the work, and under which the third party grants, to any of the
517
+
parties who would receive the covered work from you, a discriminatory
518
+
patent license (a) in connection with copies of the covered work
519
+
conveyed by you (or copies made from those copies), or (b) primarily
520
+
for and in connection with specific products or compilations that
521
+
contain the covered work, unless you entered into that arrangement,
522
+
or that patent license was granted, prior to 28 March 2007.
523
+
524
+
Nothing in this License shall be construed as excluding or limiting
525
+
any implied license or other defenses to infringement that may
526
+
otherwise be available to you under applicable patent law.
527
+
528
+
12. No Surrender of Others' Freedom.
529
+
530
+
If conditions are imposed on you (whether by court order, agreement or
531
+
otherwise) that contradict the conditions of this License, they do not
532
+
excuse you from the conditions of this License. If you cannot convey a
533
+
covered work so as to satisfy simultaneously your obligations under this
534
+
License and any other pertinent obligations, then as a consequence you may
535
+
not convey it at all. For example, if you agree to terms that obligate you
536
+
to collect a royalty for further conveying from those to whom you convey
537
+
the Program, the only way you could satisfy both those terms and this
538
+
License would be to refrain entirely from conveying the Program.
539
+
540
+
13. Remote Network Interaction; Use with the GNU General Public License.
541
+
542
+
Notwithstanding any other provision of this License, if you modify the
543
+
Program, your modified version must prominently offer all users
544
+
interacting with it remotely through a computer network (if your version
545
+
supports such interaction) an opportunity to receive the Corresponding
546
+
Source of your version by providing access to the Corresponding Source
547
+
from a network server at no charge, through some standard or customary
548
+
means of facilitating copying of software. This Corresponding Source
549
+
shall include the Corresponding Source for any work covered by version 3
550
+
of the GNU General Public License that is incorporated pursuant to the
551
+
following paragraph.
552
+
553
+
Notwithstanding any other provision of this License, you have
554
+
permission to link or combine any covered work with a work licensed
555
+
under version 3 of the GNU General Public License into a single
556
+
combined work, and to convey the resulting work. The terms of this
557
+
License will continue to apply to the part which is the covered work,
558
+
but the work with which it is combined will remain governed by version
559
+
3 of the GNU General Public License.
560
+
561
+
14. Revised Versions of this License.
562
+
563
+
The Free Software Foundation may publish revised and/or new versions of
564
+
the GNU Affero General Public License from time to time. Such new versions
565
+
will be similar in spirit to the present version, but may differ in detail to
566
+
address new problems or concerns.
567
+
568
+
Each version is given a distinguishing version number. If the
569
+
Program specifies that a certain numbered version of the GNU Affero General
570
+
Public License "or any later version" applies to it, you have the
571
+
option of following the terms and conditions either of that numbered
572
+
version or of any later version published by the Free Software
573
+
Foundation. If the Program does not specify a version number of the
574
+
GNU Affero General Public License, you may choose any version ever published
575
+
by the Free Software Foundation.
576
+
577
+
If the Program specifies that a proxy can decide which future
578
+
versions of the GNU Affero General Public License can be used, that proxy's
579
+
public statement of acceptance of a version permanently authorizes you
580
+
to choose that version for the Program.
581
+
582
+
Later license versions may give you additional or different
583
+
permissions. However, no additional obligations are imposed on any
584
+
author or copyright holder as a result of your choosing to follow a
585
+
later version.
586
+
587
+
15. Disclaimer of Warranty.
588
+
589
+
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
590
+
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
591
+
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
592
+
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
593
+
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
594
+
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
595
+
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
596
+
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
597
+
598
+
16. Limitation of Liability.
599
+
600
+
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
601
+
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
602
+
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
603
+
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
604
+
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
605
+
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
606
+
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
607
+
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
608
+
SUCH DAMAGES.
609
+
610
+
17. Interpretation of Sections 15 and 16.
611
+
612
+
If the disclaimer of warranty and limitation of liability provided
613
+
above cannot be given local legal effect according to their terms,
614
+
reviewing courts shall apply local law that most closely approximates
615
+
an absolute waiver of all civil liability in connection with the
616
+
Program, unless a warranty or assumption of liability accompanies a
617
+
copy of the Program in return for a fee.
618
+
619
+
END OF TERMS AND CONDITIONS
620
+
621
+
How to Apply These Terms to Your New Programs
622
+
623
+
If you develop a new program, and you want it to be of the greatest
624
+
possible use to the public, the best way to achieve this is to make it
625
+
free software which everyone can redistribute and change under these terms.
626
+
627
+
To do so, attach the following notices to the program. It is safest
628
+
to attach them to the start of each source file to most effectively
629
+
state the exclusion of warranty; and each file should have at least
630
+
the "copyright" line and a pointer to where the full notice is found.
631
+
632
+
<one line to give the program's name and a brief idea of what it does.>
633
+
Copyright (C) <year> <name of author>
634
+
635
+
This program is free software: you can redistribute it and/or modify
636
+
it under the terms of the GNU Affero General Public License as published
637
+
by the Free Software Foundation, either version 3 of the License, or
638
+
(at your option) any later version.
639
+
640
+
This program is distributed in the hope that it will be useful,
641
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
642
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
643
+
GNU Affero General Public License for more details.
644
+
645
+
You should have received a copy of the GNU Affero General Public License
646
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
647
+
648
+
Also add information on how to contact you by electronic and paper mail.
649
+
650
+
If your software can interact with users remotely through a computer
651
+
network, you should also make sure that it provides a way for users to
652
+
get its source. For example, if your program is a web application, its
653
+
interface could display a "Source" link that leads users to an archive
654
+
of the code. There are many ways you could offer source, and different
655
+
solutions will be better for different programs; see section 13 for the
656
+
specific requirements.
657
+
658
+
You should also get your employer (if you work as a programmer) or school,
659
+
if any, to sign a "copyright disclaimer" for the program, if necessary.
660
+
For more information on this, and how to apply and follow the GNU AGPL, see
661
+
<https://www.gnu.org/licenses/>.
+87
README.md
+87
README.md
···
1
+
# Website Template
2
+
3
+
[](https://www.repostatus.org/#unmaintained)
4
+
5
+
This repository offers a template for building a frontend for [WhiteWind](https://whtwnd.com/), a Markdown blog service utilising [ATProto](https://atproto.com/). It is based on a customised version of [WhiteBreeze](https://github.com/hugeblank/whitebreeze), specifically derived from commit [ff402f3](https://github.com/ewanc26/website/commit/ff402f3460d86c40ead13294ae1ff5d8605f741c) of [my website](https://github.com/ewanc26/website).
6
+
7
+
This template is designed to facilitate the rapid setup of a WhiteWind-compatible frontend, providing a pre-configured starting point with a structure ready for customisation.
8
+
9
+
## Purpose
10
+
11
+
This project serves as a foundational template for creating WhiteWind-compatible blog frontends, leveraging the WhiteBreeze framework to provide a robust and customisable base for displaying AT Protocol-based blog posts.
12
+
13
+
## Installation
14
+
15
+
To commence using this template, ensure Node.js and npm are installed on your system.
16
+
17
+
### Prerequisites
18
+
19
+
- Node.js (LTS version recommended)
20
+
- npm (comes with Node.js)
21
+
- Docker and Docker Compose (for Dockerised deployment)
22
+
23
+
### Environment Variables
24
+
25
+
Prior to running the application, configure the following environment variables within a `.env` file located in the project root:
26
+
27
+
```ini
28
+
PUBLIC_ATPROTOCOL_USER="myhandle.bsky.social" # Your handle, or DID
29
+
```
30
+
#### Note
31
+
32
+
You should also add your DID to the `.static/.well-known/atproto-did` file if you want to use your domain as your AT Protocol handle.
33
+
34
+
#### Optional Environment Variables
35
+
36
+
- `PUBLIC_LASTFM_USERNAME`: Required for the Now Playing (Last.fm) feature in `src/lib/components/profile/Status.svelte`.
37
+
- `PUBLIC_ACTIVITYPUB_USER=@user@server.tld`: Enables ActivityPub compatibility for improved content sharing and discoverability.
38
+
39
+
## Usage
40
+
41
+
### Development
42
+
43
+
To run the project in development mode:
44
+
45
+
```sh
46
+
npm install
47
+
npm run dev
48
+
```
49
+
50
+
### Production
51
+
52
+
For optimal production deployment, the following record types are required in your [AT Protocol repository](https://atproto.com/specs/repository):
53
+
54
+
#### Required Records
55
+
56
+
- `app.bsky.actor.profile`: Your profile.
57
+
- `com.whtwnd.blog.entry`: Your blog posts.
58
+
- `blue.linkat.board`: Your links.
59
+
60
+
### Deployment
61
+
62
+
#### Standalone
63
+
64
+
To build and run the project as a standalone application:
65
+
66
+
```sh
67
+
npm install
68
+
npm run build
69
+
node index.js
70
+
```
71
+
72
+
Environment variables can be set before the last command, and the port can be configured with the `PORT` variable.
73
+
74
+
#### Dockerised
75
+
76
+
To deploy using Docker:
77
+
78
+
1. Modify `compose.yaml` to change the host port if necessary.
79
+
2. Run the following command:
80
+
81
+
```sh
82
+
docker compose up -d
83
+
```
84
+
85
+
## Licensing
86
+
87
+
This project is a template based on WhiteBreeze. For comprehensive licensing details, please consult the `LICENSE` file within this repository.
+30
eslint.config.js
+30
eslint.config.js
···
1
+
import js from '@eslint/js';
2
+
import svelte from 'eslint-plugin-svelte';
3
+
import globals from 'globals';
4
+
import ts from 'typescript-eslint';
5
+
6
+
export default ts.config(
7
+
js.configs.recommended,
8
+
...ts.configs.recommended,
9
+
...svelte.configs["flat/recommended"],
10
+
{
11
+
languageOptions: {
12
+
globals: {
13
+
...globals.browser,
14
+
...globals.node
15
+
}
16
+
}
17
+
},
18
+
{
19
+
files: ["**/*.svelte"],
20
+
21
+
languageOptions: {
22
+
parserOptions: {
23
+
parser: ts.parser
24
+
}
25
+
}
26
+
},
27
+
{
28
+
ignores: ["build/", ".svelte-kit/", "dist/"]
29
+
}
30
+
);
+5217
package-lock.json
+5217
package-lock.json
···
1
+
{
2
+
"name": "website-template",
3
+
"version": "0.0.1",
4
+
"lockfileVersion": 3,
5
+
"requires": true,
6
+
"packages": {
7
+
"": {
8
+
"name": "website-template",
9
+
"version": "0.0.1",
10
+
"dependencies": {
11
+
"@resvg/resvg-js": "^2.6.2",
12
+
"@tailwindcss/nesting": "^0.0.0-insiders.565cd3e",
13
+
"postcss-import": "^16.1.1",
14
+
"rehype-raw": "^7.0.0",
15
+
"rehype-sanitize": "^6.0.0",
16
+
"rehype-stringify": "^10.0.1",
17
+
"remark": "^15.0.1",
18
+
"remark-gfm": "^4.0.0",
19
+
"remark-rehype": "^11.1.1",
20
+
"sanitize-html": "^2.13.1",
21
+
"satori": "^0.12.2",
22
+
"unified": "^11.0.5"
23
+
},
24
+
"devDependencies": {
25
+
"@sveltejs/adapter-vercel": "^5.7.0",
26
+
"@sveltejs/kit": "^2.24.0",
27
+
"@sveltejs/vite-plugin-svelte": "^4.0.4",
28
+
"@tailwindcss/forms": "^0.5.9",
29
+
"@tailwindcss/typography": "^0.5.15",
30
+
"@types/node": "^22.10.1",
31
+
"@types/sanitize-html": "^2.13.0",
32
+
"autoprefixer": "^10.4.21",
33
+
"eslint": "^9.7.0",
34
+
"eslint-plugin-svelte": "^2.36.0",
35
+
"globals": "^15.0.0",
36
+
"postcss": "^8.5.6",
37
+
"svelte": "^5.36.2",
38
+
"svelte-check": "^4.2.2",
39
+
"tailwindcss": "^3.4.17",
40
+
"typescript": "^5.0.0",
41
+
"typescript-eslint": "^8.0.0",
42
+
"vite": "^5.0.3"
43
+
}
44
+
},
45
+
"node_modules/@alloc/quick-lru": {
46
+
"version": "5.2.0",
47
+
"dev": true,
48
+
"license": "MIT",
49
+
"engines": {
50
+
"node": ">=10"
51
+
},
52
+
"funding": {
53
+
"url": "https://github.com/sponsors/sindresorhus"
54
+
}
55
+
},
56
+
"node_modules/@ampproject/remapping": {
57
+
"version": "2.3.0",
58
+
"dev": true,
59
+
"license": "Apache-2.0",
60
+
"dependencies": {
61
+
"@jridgewell/gen-mapping": "^0.3.5",
62
+
"@jridgewell/trace-mapping": "^0.3.24"
63
+
},
64
+
"engines": {
65
+
"node": ">=6.0.0"
66
+
}
67
+
},
68
+
"node_modules/@esbuild/darwin-arm64": {
69
+
"version": "0.21.5",
70
+
"cpu": [
71
+
"arm64"
72
+
],
73
+
"dev": true,
74
+
"license": "MIT",
75
+
"optional": true,
76
+
"os": [
77
+
"darwin"
78
+
],
79
+
"engines": {
80
+
"node": ">=12"
81
+
}
82
+
},
83
+
"node_modules/@eslint-community/eslint-utils": {
84
+
"version": "4.4.1",
85
+
"dev": true,
86
+
"license": "MIT",
87
+
"dependencies": {
88
+
"eslint-visitor-keys": "^3.4.3"
89
+
},
90
+
"engines": {
91
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
92
+
},
93
+
"funding": {
94
+
"url": "https://opencollective.com/eslint"
95
+
},
96
+
"peerDependencies": {
97
+
"eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
98
+
}
99
+
},
100
+
"node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
101
+
"version": "3.4.3",
102
+
"dev": true,
103
+
"license": "Apache-2.0",
104
+
"engines": {
105
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
106
+
},
107
+
"funding": {
108
+
"url": "https://opencollective.com/eslint"
109
+
}
110
+
},
111
+
"node_modules/@eslint-community/regexpp": {
112
+
"version": "4.12.1",
113
+
"dev": true,
114
+
"license": "MIT",
115
+
"engines": {
116
+
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
117
+
}
118
+
},
119
+
"node_modules/@eslint/config-array": {
120
+
"version": "0.19.0",
121
+
"dev": true,
122
+
"license": "Apache-2.0",
123
+
"dependencies": {
124
+
"@eslint/object-schema": "^2.1.4",
125
+
"debug": "^4.3.1",
126
+
"minimatch": "^3.1.2"
127
+
},
128
+
"engines": {
129
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
130
+
}
131
+
},
132
+
"node_modules/@eslint/core": {
133
+
"version": "0.9.0",
134
+
"dev": true,
135
+
"license": "Apache-2.0",
136
+
"engines": {
137
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
138
+
}
139
+
},
140
+
"node_modules/@eslint/eslintrc": {
141
+
"version": "3.2.0",
142
+
"dev": true,
143
+
"license": "MIT",
144
+
"dependencies": {
145
+
"ajv": "^6.12.4",
146
+
"debug": "^4.3.2",
147
+
"espree": "^10.0.1",
148
+
"globals": "^14.0.0",
149
+
"ignore": "^5.2.0",
150
+
"import-fresh": "^3.2.1",
151
+
"js-yaml": "^4.1.0",
152
+
"minimatch": "^3.1.2",
153
+
"strip-json-comments": "^3.1.1"
154
+
},
155
+
"engines": {
156
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
157
+
},
158
+
"funding": {
159
+
"url": "https://opencollective.com/eslint"
160
+
}
161
+
},
162
+
"node_modules/@eslint/eslintrc/node_modules/globals": {
163
+
"version": "14.0.0",
164
+
"dev": true,
165
+
"license": "MIT",
166
+
"engines": {
167
+
"node": ">=18"
168
+
},
169
+
"funding": {
170
+
"url": "https://github.com/sponsors/sindresorhus"
171
+
}
172
+
},
173
+
"node_modules/@eslint/js": {
174
+
"version": "9.16.0",
175
+
"dev": true,
176
+
"license": "MIT",
177
+
"engines": {
178
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
179
+
}
180
+
},
181
+
"node_modules/@eslint/object-schema": {
182
+
"version": "2.1.4",
183
+
"dev": true,
184
+
"license": "Apache-2.0",
185
+
"engines": {
186
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
187
+
}
188
+
},
189
+
"node_modules/@eslint/plugin-kit": {
190
+
"version": "0.2.3",
191
+
"dev": true,
192
+
"license": "Apache-2.0",
193
+
"dependencies": {
194
+
"levn": "^0.4.1"
195
+
},
196
+
"engines": {
197
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
198
+
}
199
+
},
200
+
"node_modules/@humanfs/core": {
201
+
"version": "0.19.1",
202
+
"dev": true,
203
+
"license": "Apache-2.0",
204
+
"engines": {
205
+
"node": ">=18.18.0"
206
+
}
207
+
},
208
+
"node_modules/@humanfs/node": {
209
+
"version": "0.16.6",
210
+
"dev": true,
211
+
"license": "Apache-2.0",
212
+
"dependencies": {
213
+
"@humanfs/core": "^0.19.1",
214
+
"@humanwhocodes/retry": "^0.3.0"
215
+
},
216
+
"engines": {
217
+
"node": ">=18.18.0"
218
+
}
219
+
},
220
+
"node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
221
+
"version": "0.3.1",
222
+
"dev": true,
223
+
"license": "Apache-2.0",
224
+
"engines": {
225
+
"node": ">=18.18"
226
+
},
227
+
"funding": {
228
+
"type": "github",
229
+
"url": "https://github.com/sponsors/nzakas"
230
+
}
231
+
},
232
+
"node_modules/@humanwhocodes/module-importer": {
233
+
"version": "1.0.1",
234
+
"dev": true,
235
+
"license": "Apache-2.0",
236
+
"engines": {
237
+
"node": ">=12.22"
238
+
},
239
+
"funding": {
240
+
"type": "github",
241
+
"url": "https://github.com/sponsors/nzakas"
242
+
}
243
+
},
244
+
"node_modules/@humanwhocodes/retry": {
245
+
"version": "0.4.1",
246
+
"dev": true,
247
+
"license": "Apache-2.0",
248
+
"engines": {
249
+
"node": ">=18.18"
250
+
},
251
+
"funding": {
252
+
"type": "github",
253
+
"url": "https://github.com/sponsors/nzakas"
254
+
}
255
+
},
256
+
"node_modules/@isaacs/cliui": {
257
+
"version": "8.0.2",
258
+
"dev": true,
259
+
"license": "ISC",
260
+
"dependencies": {
261
+
"string-width": "^5.1.2",
262
+
"string-width-cjs": "npm:string-width@^4.2.0",
263
+
"strip-ansi": "^7.0.1",
264
+
"strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
265
+
"wrap-ansi": "^8.1.0",
266
+
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
267
+
},
268
+
"engines": {
269
+
"node": ">=12"
270
+
}
271
+
},
272
+
"node_modules/@isaacs/fs-minipass": {
273
+
"version": "4.0.1",
274
+
"dev": true,
275
+
"license": "ISC",
276
+
"dependencies": {
277
+
"minipass": "^7.0.4"
278
+
},
279
+
"engines": {
280
+
"node": ">=18.0.0"
281
+
}
282
+
},
283
+
"node_modules/@jridgewell/gen-mapping": {
284
+
"version": "0.3.5",
285
+
"dev": true,
286
+
"license": "MIT",
287
+
"dependencies": {
288
+
"@jridgewell/set-array": "^1.2.1",
289
+
"@jridgewell/sourcemap-codec": "^1.4.10",
290
+
"@jridgewell/trace-mapping": "^0.3.24"
291
+
},
292
+
"engines": {
293
+
"node": ">=6.0.0"
294
+
}
295
+
},
296
+
"node_modules/@jridgewell/resolve-uri": {
297
+
"version": "3.1.2",
298
+
"dev": true,
299
+
"license": "MIT",
300
+
"engines": {
301
+
"node": ">=6.0.0"
302
+
}
303
+
},
304
+
"node_modules/@jridgewell/set-array": {
305
+
"version": "1.2.1",
306
+
"dev": true,
307
+
"license": "MIT",
308
+
"engines": {
309
+
"node": ">=6.0.0"
310
+
}
311
+
},
312
+
"node_modules/@jridgewell/sourcemap-codec": {
313
+
"version": "1.5.0",
314
+
"dev": true,
315
+
"license": "MIT"
316
+
},
317
+
"node_modules/@jridgewell/trace-mapping": {
318
+
"version": "0.3.25",
319
+
"dev": true,
320
+
"license": "MIT",
321
+
"dependencies": {
322
+
"@jridgewell/resolve-uri": "^3.1.0",
323
+
"@jridgewell/sourcemap-codec": "^1.4.14"
324
+
}
325
+
},
326
+
"node_modules/@mapbox/node-pre-gyp": {
327
+
"version": "2.0.0",
328
+
"dev": true,
329
+
"license": "BSD-3-Clause",
330
+
"dependencies": {
331
+
"consola": "^3.2.3",
332
+
"detect-libc": "^2.0.0",
333
+
"https-proxy-agent": "^7.0.5",
334
+
"node-fetch": "^2.6.7",
335
+
"nopt": "^8.0.0",
336
+
"semver": "^7.5.3",
337
+
"tar": "^7.4.0"
338
+
},
339
+
"bin": {
340
+
"node-pre-gyp": "bin/node-pre-gyp"
341
+
},
342
+
"engines": {
343
+
"node": ">=18"
344
+
}
345
+
},
346
+
"node_modules/@nodelib/fs.scandir": {
347
+
"version": "2.1.5",
348
+
"dev": true,
349
+
"license": "MIT",
350
+
"dependencies": {
351
+
"@nodelib/fs.stat": "2.0.5",
352
+
"run-parallel": "^1.1.9"
353
+
},
354
+
"engines": {
355
+
"node": ">= 8"
356
+
}
357
+
},
358
+
"node_modules/@nodelib/fs.stat": {
359
+
"version": "2.0.5",
360
+
"dev": true,
361
+
"license": "MIT",
362
+
"engines": {
363
+
"node": ">= 8"
364
+
}
365
+
},
366
+
"node_modules/@nodelib/fs.walk": {
367
+
"version": "1.2.8",
368
+
"dev": true,
369
+
"license": "MIT",
370
+
"dependencies": {
371
+
"@nodelib/fs.scandir": "2.1.5",
372
+
"fastq": "^1.6.0"
373
+
},
374
+
"engines": {
375
+
"node": ">= 8"
376
+
}
377
+
},
378
+
"node_modules/@pkgjs/parseargs": {
379
+
"version": "0.11.0",
380
+
"dev": true,
381
+
"license": "MIT",
382
+
"optional": true,
383
+
"engines": {
384
+
"node": ">=14"
385
+
}
386
+
},
387
+
"node_modules/@polka/url": {
388
+
"version": "1.0.0-next.28",
389
+
"dev": true,
390
+
"license": "MIT"
391
+
},
392
+
"node_modules/@resvg/resvg-js": {
393
+
"version": "2.6.2",
394
+
"license": "MPL-2.0",
395
+
"engines": {
396
+
"node": ">= 10"
397
+
},
398
+
"optionalDependencies": {
399
+
"@resvg/resvg-js-android-arm-eabi": "2.6.2",
400
+
"@resvg/resvg-js-android-arm64": "2.6.2",
401
+
"@resvg/resvg-js-darwin-arm64": "2.6.2",
402
+
"@resvg/resvg-js-darwin-x64": "2.6.2",
403
+
"@resvg/resvg-js-linux-arm-gnueabihf": "2.6.2",
404
+
"@resvg/resvg-js-linux-arm64-gnu": "2.6.2",
405
+
"@resvg/resvg-js-linux-arm64-musl": "2.6.2",
406
+
"@resvg/resvg-js-linux-x64-gnu": "2.6.2",
407
+
"@resvg/resvg-js-linux-x64-musl": "2.6.2",
408
+
"@resvg/resvg-js-win32-arm64-msvc": "2.6.2",
409
+
"@resvg/resvg-js-win32-ia32-msvc": "2.6.2",
410
+
"@resvg/resvg-js-win32-x64-msvc": "2.6.2"
411
+
}
412
+
},
413
+
"node_modules/@resvg/resvg-js-darwin-arm64": {
414
+
"version": "2.6.2",
415
+
"cpu": [
416
+
"arm64"
417
+
],
418
+
"license": "MPL-2.0",
419
+
"optional": true,
420
+
"os": [
421
+
"darwin"
422
+
],
423
+
"engines": {
424
+
"node": ">= 10"
425
+
}
426
+
},
427
+
"node_modules/@rollup/pluginutils": {
428
+
"version": "5.1.3",
429
+
"dev": true,
430
+
"license": "MIT",
431
+
"dependencies": {
432
+
"@types/estree": "^1.0.0",
433
+
"estree-walker": "^2.0.2",
434
+
"picomatch": "^4.0.2"
435
+
},
436
+
"engines": {
437
+
"node": ">=14.0.0"
438
+
},
439
+
"peerDependencies": {
440
+
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
441
+
},
442
+
"peerDependenciesMeta": {
443
+
"rollup": {
444
+
"optional": true
445
+
}
446
+
}
447
+
},
448
+
"node_modules/@rollup/rollup-darwin-arm64": {
449
+
"version": "4.28.0",
450
+
"cpu": [
451
+
"arm64"
452
+
],
453
+
"dev": true,
454
+
"license": "MIT",
455
+
"optional": true,
456
+
"os": [
457
+
"darwin"
458
+
]
459
+
},
460
+
"node_modules/@shuding/opentype.js": {
461
+
"version": "1.4.0-beta.0",
462
+
"license": "MIT",
463
+
"dependencies": {
464
+
"fflate": "^0.7.3",
465
+
"string.prototype.codepointat": "^0.2.1"
466
+
},
467
+
"bin": {
468
+
"ot": "bin/ot"
469
+
},
470
+
"engines": {
471
+
"node": ">= 8.0.0"
472
+
}
473
+
},
474
+
"node_modules/@sveltejs/acorn-typescript": {
475
+
"version": "1.0.5",
476
+
"dev": true,
477
+
"license": "MIT",
478
+
"peerDependencies": {
479
+
"acorn": "^8.9.0"
480
+
}
481
+
},
482
+
"node_modules/@sveltejs/adapter-vercel": {
483
+
"version": "5.7.0",
484
+
"dev": true,
485
+
"license": "MIT",
486
+
"dependencies": {
487
+
"@vercel/nft": "^0.29.2",
488
+
"esbuild": "^0.24.0"
489
+
},
490
+
"peerDependencies": {
491
+
"@sveltejs/kit": "^2.4.0"
492
+
}
493
+
},
494
+
"node_modules/@sveltejs/adapter-vercel/node_modules/@esbuild/darwin-arm64": {
495
+
"version": "0.24.2",
496
+
"cpu": [
497
+
"arm64"
498
+
],
499
+
"dev": true,
500
+
"license": "MIT",
501
+
"optional": true,
502
+
"os": [
503
+
"darwin"
504
+
],
505
+
"engines": {
506
+
"node": ">=18"
507
+
}
508
+
},
509
+
"node_modules/@sveltejs/adapter-vercel/node_modules/esbuild": {
510
+
"version": "0.24.2",
511
+
"dev": true,
512
+
"hasInstallScript": true,
513
+
"license": "MIT",
514
+
"bin": {
515
+
"esbuild": "bin/esbuild"
516
+
},
517
+
"engines": {
518
+
"node": ">=18"
519
+
},
520
+
"optionalDependencies": {
521
+
"@esbuild/aix-ppc64": "0.24.2",
522
+
"@esbuild/android-arm": "0.24.2",
523
+
"@esbuild/android-arm64": "0.24.2",
524
+
"@esbuild/android-x64": "0.24.2",
525
+
"@esbuild/darwin-arm64": "0.24.2",
526
+
"@esbuild/darwin-x64": "0.24.2",
527
+
"@esbuild/freebsd-arm64": "0.24.2",
528
+
"@esbuild/freebsd-x64": "0.24.2",
529
+
"@esbuild/linux-arm": "0.24.2",
530
+
"@esbuild/linux-arm64": "0.24.2",
531
+
"@esbuild/linux-ia32": "0.24.2",
532
+
"@esbuild/linux-loong64": "0.24.2",
533
+
"@esbuild/linux-mips64el": "0.24.2",
534
+
"@esbuild/linux-ppc64": "0.24.2",
535
+
"@esbuild/linux-riscv64": "0.24.2",
536
+
"@esbuild/linux-s390x": "0.24.2",
537
+
"@esbuild/linux-x64": "0.24.2",
538
+
"@esbuild/netbsd-arm64": "0.24.2",
539
+
"@esbuild/netbsd-x64": "0.24.2",
540
+
"@esbuild/openbsd-arm64": "0.24.2",
541
+
"@esbuild/openbsd-x64": "0.24.2",
542
+
"@esbuild/sunos-x64": "0.24.2",
543
+
"@esbuild/win32-arm64": "0.24.2",
544
+
"@esbuild/win32-ia32": "0.24.2",
545
+
"@esbuild/win32-x64": "0.24.2"
546
+
}
547
+
},
548
+
"node_modules/@sveltejs/kit": {
549
+
"version": "2.24.0",
550
+
"dev": true,
551
+
"license": "MIT",
552
+
"dependencies": {
553
+
"@sveltejs/acorn-typescript": "^1.0.5",
554
+
"@types/cookie": "^0.6.0",
555
+
"acorn": "^8.14.1",
556
+
"cookie": "^0.6.0",
557
+
"devalue": "^5.1.0",
558
+
"esm-env": "^1.2.2",
559
+
"kleur": "^4.1.5",
560
+
"magic-string": "^0.30.5",
561
+
"mrmime": "^2.0.0",
562
+
"sade": "^1.8.1",
563
+
"set-cookie-parser": "^2.6.0",
564
+
"sirv": "^3.0.0"
565
+
},
566
+
"bin": {
567
+
"svelte-kit": "svelte-kit.js"
568
+
},
569
+
"engines": {
570
+
"node": ">=18.13"
571
+
},
572
+
"peerDependencies": {
573
+
"@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0",
574
+
"svelte": "^4.0.0 || ^5.0.0-next.0",
575
+
"vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0"
576
+
}
577
+
},
578
+
"node_modules/@sveltejs/vite-plugin-svelte": {
579
+
"version": "4.0.4",
580
+
"dev": true,
581
+
"license": "MIT",
582
+
"dependencies": {
583
+
"@sveltejs/vite-plugin-svelte-inspector": "^3.0.0-next.0||^3.0.0",
584
+
"debug": "^4.3.7",
585
+
"deepmerge": "^4.3.1",
586
+
"kleur": "^4.1.5",
587
+
"magic-string": "^0.30.12",
588
+
"vitefu": "^1.0.3"
589
+
},
590
+
"engines": {
591
+
"node": "^18.0.0 || ^20.0.0 || >=22"
592
+
},
593
+
"peerDependencies": {
594
+
"svelte": "^5.0.0-next.96 || ^5.0.0",
595
+
"vite": "^5.0.0"
596
+
}
597
+
},
598
+
"node_modules/@sveltejs/vite-plugin-svelte-inspector": {
599
+
"version": "3.0.1",
600
+
"dev": true,
601
+
"license": "MIT",
602
+
"dependencies": {
603
+
"debug": "^4.3.7"
604
+
},
605
+
"engines": {
606
+
"node": "^18.0.0 || ^20.0.0 || >=22"
607
+
},
608
+
"peerDependencies": {
609
+
"@sveltejs/vite-plugin-svelte": "^4.0.0-next.0||^4.0.0",
610
+
"svelte": "^5.0.0-next.96 || ^5.0.0",
611
+
"vite": "^5.0.0"
612
+
}
613
+
},
614
+
"node_modules/@tailwindcss/forms": {
615
+
"version": "0.5.9",
616
+
"dev": true,
617
+
"license": "MIT",
618
+
"dependencies": {
619
+
"mini-svg-data-uri": "^1.2.3"
620
+
},
621
+
"peerDependencies": {
622
+
"tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20"
623
+
}
624
+
},
625
+
"node_modules/@tailwindcss/nesting": {
626
+
"version": "0.0.0-insiders.565cd3e",
627
+
"license": "MIT",
628
+
"dependencies": {
629
+
"postcss-nested": "^5.0.5"
630
+
},
631
+
"peerDependencies": {
632
+
"postcss": "^8.2.15"
633
+
}
634
+
},
635
+
"node_modules/@tailwindcss/nesting/node_modules/postcss-nested": {
636
+
"version": "5.0.6",
637
+
"license": "MIT",
638
+
"dependencies": {
639
+
"postcss-selector-parser": "^6.0.6"
640
+
},
641
+
"engines": {
642
+
"node": ">=12.0"
643
+
},
644
+
"funding": {
645
+
"type": "opencollective",
646
+
"url": "https://opencollective.com/postcss/"
647
+
},
648
+
"peerDependencies": {
649
+
"postcss": "^8.2.14"
650
+
}
651
+
},
652
+
"node_modules/@tailwindcss/typography": {
653
+
"version": "0.5.15",
654
+
"dev": true,
655
+
"license": "MIT",
656
+
"dependencies": {
657
+
"lodash.castarray": "^4.4.0",
658
+
"lodash.isplainobject": "^4.0.6",
659
+
"lodash.merge": "^4.6.2",
660
+
"postcss-selector-parser": "6.0.10"
661
+
},
662
+
"peerDependencies": {
663
+
"tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20"
664
+
}
665
+
},
666
+
"node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": {
667
+
"version": "6.0.10",
668
+
"dev": true,
669
+
"license": "MIT",
670
+
"dependencies": {
671
+
"cssesc": "^3.0.0",
672
+
"util-deprecate": "^1.0.2"
673
+
},
674
+
"engines": {
675
+
"node": ">=4"
676
+
}
677
+
},
678
+
"node_modules/@types/cookie": {
679
+
"version": "0.6.0",
680
+
"dev": true,
681
+
"license": "MIT"
682
+
},
683
+
"node_modules/@types/debug": {
684
+
"version": "4.1.12",
685
+
"license": "MIT",
686
+
"dependencies": {
687
+
"@types/ms": "*"
688
+
}
689
+
},
690
+
"node_modules/@types/estree": {
691
+
"version": "1.0.6",
692
+
"dev": true,
693
+
"license": "MIT"
694
+
},
695
+
"node_modules/@types/hast": {
696
+
"version": "3.0.4",
697
+
"license": "MIT",
698
+
"dependencies": {
699
+
"@types/unist": "*"
700
+
}
701
+
},
702
+
"node_modules/@types/json-schema": {
703
+
"version": "7.0.15",
704
+
"dev": true,
705
+
"license": "MIT"
706
+
},
707
+
"node_modules/@types/mdast": {
708
+
"version": "4.0.4",
709
+
"license": "MIT",
710
+
"dependencies": {
711
+
"@types/unist": "*"
712
+
}
713
+
},
714
+
"node_modules/@types/ms": {
715
+
"version": "0.7.34",
716
+
"license": "MIT"
717
+
},
718
+
"node_modules/@types/node": {
719
+
"version": "22.10.1",
720
+
"dev": true,
721
+
"license": "MIT",
722
+
"dependencies": {
723
+
"undici-types": "~6.20.0"
724
+
}
725
+
},
726
+
"node_modules/@types/sanitize-html": {
727
+
"version": "2.13.0",
728
+
"dev": true,
729
+
"license": "MIT",
730
+
"dependencies": {
731
+
"htmlparser2": "^8.0.0"
732
+
}
733
+
},
734
+
"node_modules/@types/unist": {
735
+
"version": "3.0.3",
736
+
"license": "MIT"
737
+
},
738
+
"node_modules/@typescript-eslint/eslint-plugin": {
739
+
"version": "8.16.0",
740
+
"dev": true,
741
+
"license": "MIT",
742
+
"dependencies": {
743
+
"@eslint-community/regexpp": "^4.10.0",
744
+
"@typescript-eslint/scope-manager": "8.16.0",
745
+
"@typescript-eslint/type-utils": "8.16.0",
746
+
"@typescript-eslint/utils": "8.16.0",
747
+
"@typescript-eslint/visitor-keys": "8.16.0",
748
+
"graphemer": "^1.4.0",
749
+
"ignore": "^5.3.1",
750
+
"natural-compare": "^1.4.0",
751
+
"ts-api-utils": "^1.3.0"
752
+
},
753
+
"engines": {
754
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
755
+
},
756
+
"funding": {
757
+
"type": "opencollective",
758
+
"url": "https://opencollective.com/typescript-eslint"
759
+
},
760
+
"peerDependencies": {
761
+
"@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
762
+
"eslint": "^8.57.0 || ^9.0.0"
763
+
},
764
+
"peerDependenciesMeta": {
765
+
"typescript": {
766
+
"optional": true
767
+
}
768
+
}
769
+
},
770
+
"node_modules/@typescript-eslint/parser": {
771
+
"version": "8.16.0",
772
+
"dev": true,
773
+
"license": "BSD-2-Clause",
774
+
"dependencies": {
775
+
"@typescript-eslint/scope-manager": "8.16.0",
776
+
"@typescript-eslint/types": "8.16.0",
777
+
"@typescript-eslint/typescript-estree": "8.16.0",
778
+
"@typescript-eslint/visitor-keys": "8.16.0",
779
+
"debug": "^4.3.4"
780
+
},
781
+
"engines": {
782
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
783
+
},
784
+
"funding": {
785
+
"type": "opencollective",
786
+
"url": "https://opencollective.com/typescript-eslint"
787
+
},
788
+
"peerDependencies": {
789
+
"eslint": "^8.57.0 || ^9.0.0"
790
+
},
791
+
"peerDependenciesMeta": {
792
+
"typescript": {
793
+
"optional": true
794
+
}
795
+
}
796
+
},
797
+
"node_modules/@typescript-eslint/scope-manager": {
798
+
"version": "8.16.0",
799
+
"dev": true,
800
+
"license": "MIT",
801
+
"dependencies": {
802
+
"@typescript-eslint/types": "8.16.0",
803
+
"@typescript-eslint/visitor-keys": "8.16.0"
804
+
},
805
+
"engines": {
806
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
807
+
},
808
+
"funding": {
809
+
"type": "opencollective",
810
+
"url": "https://opencollective.com/typescript-eslint"
811
+
}
812
+
},
813
+
"node_modules/@typescript-eslint/type-utils": {
814
+
"version": "8.16.0",
815
+
"dev": true,
816
+
"license": "MIT",
817
+
"dependencies": {
818
+
"@typescript-eslint/typescript-estree": "8.16.0",
819
+
"@typescript-eslint/utils": "8.16.0",
820
+
"debug": "^4.3.4",
821
+
"ts-api-utils": "^1.3.0"
822
+
},
823
+
"engines": {
824
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
825
+
},
826
+
"funding": {
827
+
"type": "opencollective",
828
+
"url": "https://opencollective.com/typescript-eslint"
829
+
},
830
+
"peerDependencies": {
831
+
"eslint": "^8.57.0 || ^9.0.0"
832
+
},
833
+
"peerDependenciesMeta": {
834
+
"typescript": {
835
+
"optional": true
836
+
}
837
+
}
838
+
},
839
+
"node_modules/@typescript-eslint/types": {
840
+
"version": "8.16.0",
841
+
"dev": true,
842
+
"license": "MIT",
843
+
"engines": {
844
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
845
+
},
846
+
"funding": {
847
+
"type": "opencollective",
848
+
"url": "https://opencollective.com/typescript-eslint"
849
+
}
850
+
},
851
+
"node_modules/@typescript-eslint/typescript-estree": {
852
+
"version": "8.16.0",
853
+
"dev": true,
854
+
"license": "BSD-2-Clause",
855
+
"dependencies": {
856
+
"@typescript-eslint/types": "8.16.0",
857
+
"@typescript-eslint/visitor-keys": "8.16.0",
858
+
"debug": "^4.3.4",
859
+
"fast-glob": "^3.3.2",
860
+
"is-glob": "^4.0.3",
861
+
"minimatch": "^9.0.4",
862
+
"semver": "^7.6.0",
863
+
"ts-api-utils": "^1.3.0"
864
+
},
865
+
"engines": {
866
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
867
+
},
868
+
"funding": {
869
+
"type": "opencollective",
870
+
"url": "https://opencollective.com/typescript-eslint"
871
+
},
872
+
"peerDependenciesMeta": {
873
+
"typescript": {
874
+
"optional": true
875
+
}
876
+
}
877
+
},
878
+
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
879
+
"version": "2.0.1",
880
+
"dev": true,
881
+
"license": "MIT",
882
+
"dependencies": {
883
+
"balanced-match": "^1.0.0"
884
+
}
885
+
},
886
+
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
887
+
"version": "9.0.5",
888
+
"dev": true,
889
+
"license": "ISC",
890
+
"dependencies": {
891
+
"brace-expansion": "^2.0.1"
892
+
},
893
+
"engines": {
894
+
"node": ">=16 || 14 >=14.17"
895
+
},
896
+
"funding": {
897
+
"url": "https://github.com/sponsors/isaacs"
898
+
}
899
+
},
900
+
"node_modules/@typescript-eslint/utils": {
901
+
"version": "8.16.0",
902
+
"dev": true,
903
+
"license": "MIT",
904
+
"dependencies": {
905
+
"@eslint-community/eslint-utils": "^4.4.0",
906
+
"@typescript-eslint/scope-manager": "8.16.0",
907
+
"@typescript-eslint/types": "8.16.0",
908
+
"@typescript-eslint/typescript-estree": "8.16.0"
909
+
},
910
+
"engines": {
911
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
912
+
},
913
+
"funding": {
914
+
"type": "opencollective",
915
+
"url": "https://opencollective.com/typescript-eslint"
916
+
},
917
+
"peerDependencies": {
918
+
"eslint": "^8.57.0 || ^9.0.0"
919
+
},
920
+
"peerDependenciesMeta": {
921
+
"typescript": {
922
+
"optional": true
923
+
}
924
+
}
925
+
},
926
+
"node_modules/@typescript-eslint/visitor-keys": {
927
+
"version": "8.16.0",
928
+
"dev": true,
929
+
"license": "MIT",
930
+
"dependencies": {
931
+
"@typescript-eslint/types": "8.16.0",
932
+
"eslint-visitor-keys": "^4.2.0"
933
+
},
934
+
"engines": {
935
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
936
+
},
937
+
"funding": {
938
+
"type": "opencollective",
939
+
"url": "https://opencollective.com/typescript-eslint"
940
+
}
941
+
},
942
+
"node_modules/@ungap/structured-clone": {
943
+
"version": "1.2.0",
944
+
"license": "ISC"
945
+
},
946
+
"node_modules/@vercel/nft": {
947
+
"version": "0.29.2",
948
+
"dev": true,
949
+
"license": "MIT",
950
+
"dependencies": {
951
+
"@mapbox/node-pre-gyp": "^2.0.0",
952
+
"@rollup/pluginutils": "^5.1.3",
953
+
"acorn": "^8.6.0",
954
+
"acorn-import-attributes": "^1.9.5",
955
+
"async-sema": "^3.1.1",
956
+
"bindings": "^1.4.0",
957
+
"estree-walker": "2.0.2",
958
+
"glob": "^10.4.5",
959
+
"graceful-fs": "^4.2.9",
960
+
"node-gyp-build": "^4.2.2",
961
+
"picomatch": "^4.0.2",
962
+
"resolve-from": "^5.0.0"
963
+
},
964
+
"bin": {
965
+
"nft": "out/cli.js"
966
+
},
967
+
"engines": {
968
+
"node": ">=18"
969
+
}
970
+
},
971
+
"node_modules/@vercel/nft/node_modules/resolve-from": {
972
+
"version": "5.0.0",
973
+
"dev": true,
974
+
"license": "MIT",
975
+
"engines": {
976
+
"node": ">=8"
977
+
}
978
+
},
979
+
"node_modules/abbrev": {
980
+
"version": "3.0.0",
981
+
"dev": true,
982
+
"license": "ISC",
983
+
"engines": {
984
+
"node": "^18.17.0 || >=20.5.0"
985
+
}
986
+
},
987
+
"node_modules/acorn": {
988
+
"version": "8.15.0",
989
+
"dev": true,
990
+
"license": "MIT",
991
+
"bin": {
992
+
"acorn": "bin/acorn"
993
+
},
994
+
"engines": {
995
+
"node": ">=0.4.0"
996
+
}
997
+
},
998
+
"node_modules/acorn-import-attributes": {
999
+
"version": "1.9.5",
1000
+
"dev": true,
1001
+
"license": "MIT",
1002
+
"peerDependencies": {
1003
+
"acorn": "^8"
1004
+
}
1005
+
},
1006
+
"node_modules/acorn-jsx": {
1007
+
"version": "5.3.2",
1008
+
"dev": true,
1009
+
"license": "MIT",
1010
+
"peerDependencies": {
1011
+
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
1012
+
}
1013
+
},
1014
+
"node_modules/agent-base": {
1015
+
"version": "7.1.3",
1016
+
"dev": true,
1017
+
"license": "MIT",
1018
+
"engines": {
1019
+
"node": ">= 14"
1020
+
}
1021
+
},
1022
+
"node_modules/ajv": {
1023
+
"version": "6.12.6",
1024
+
"dev": true,
1025
+
"license": "MIT",
1026
+
"dependencies": {
1027
+
"fast-deep-equal": "^3.1.1",
1028
+
"fast-json-stable-stringify": "^2.0.0",
1029
+
"json-schema-traverse": "^0.4.1",
1030
+
"uri-js": "^4.2.2"
1031
+
},
1032
+
"funding": {
1033
+
"type": "github",
1034
+
"url": "https://github.com/sponsors/epoberezkin"
1035
+
}
1036
+
},
1037
+
"node_modules/ansi-regex": {
1038
+
"version": "6.1.0",
1039
+
"dev": true,
1040
+
"license": "MIT",
1041
+
"engines": {
1042
+
"node": ">=12"
1043
+
},
1044
+
"funding": {
1045
+
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
1046
+
}
1047
+
},
1048
+
"node_modules/ansi-styles": {
1049
+
"version": "4.3.0",
1050
+
"dev": true,
1051
+
"license": "MIT",
1052
+
"dependencies": {
1053
+
"color-convert": "^2.0.1"
1054
+
},
1055
+
"engines": {
1056
+
"node": ">=8"
1057
+
},
1058
+
"funding": {
1059
+
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
1060
+
}
1061
+
},
1062
+
"node_modules/any-promise": {
1063
+
"version": "1.3.0",
1064
+
"dev": true,
1065
+
"license": "MIT"
1066
+
},
1067
+
"node_modules/anymatch": {
1068
+
"version": "3.1.3",
1069
+
"dev": true,
1070
+
"license": "ISC",
1071
+
"dependencies": {
1072
+
"normalize-path": "^3.0.0",
1073
+
"picomatch": "^2.0.4"
1074
+
},
1075
+
"engines": {
1076
+
"node": ">= 8"
1077
+
}
1078
+
},
1079
+
"node_modules/anymatch/node_modules/picomatch": {
1080
+
"version": "2.3.1",
1081
+
"dev": true,
1082
+
"license": "MIT",
1083
+
"engines": {
1084
+
"node": ">=8.6"
1085
+
},
1086
+
"funding": {
1087
+
"url": "https://github.com/sponsors/jonschlinkert"
1088
+
}
1089
+
},
1090
+
"node_modules/arg": {
1091
+
"version": "5.0.2",
1092
+
"dev": true,
1093
+
"license": "MIT"
1094
+
},
1095
+
"node_modules/argparse": {
1096
+
"version": "2.0.1",
1097
+
"dev": true,
1098
+
"license": "Python-2.0"
1099
+
},
1100
+
"node_modules/aria-query": {
1101
+
"version": "5.3.2",
1102
+
"dev": true,
1103
+
"license": "Apache-2.0",
1104
+
"engines": {
1105
+
"node": ">= 0.4"
1106
+
}
1107
+
},
1108
+
"node_modules/async-sema": {
1109
+
"version": "3.1.1",
1110
+
"dev": true,
1111
+
"license": "MIT"
1112
+
},
1113
+
"node_modules/autoprefixer": {
1114
+
"version": "10.4.21",
1115
+
"dev": true,
1116
+
"funding": [
1117
+
{
1118
+
"type": "opencollective",
1119
+
"url": "https://opencollective.com/postcss/"
1120
+
},
1121
+
{
1122
+
"type": "tidelift",
1123
+
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
1124
+
},
1125
+
{
1126
+
"type": "github",
1127
+
"url": "https://github.com/sponsors/ai"
1128
+
}
1129
+
],
1130
+
"license": "MIT",
1131
+
"dependencies": {
1132
+
"browserslist": "^4.24.4",
1133
+
"caniuse-lite": "^1.0.30001702",
1134
+
"fraction.js": "^4.3.7",
1135
+
"normalize-range": "^0.1.2",
1136
+
"picocolors": "^1.1.1",
1137
+
"postcss-value-parser": "^4.2.0"
1138
+
},
1139
+
"bin": {
1140
+
"autoprefixer": "bin/autoprefixer"
1141
+
},
1142
+
"engines": {
1143
+
"node": "^10 || ^12 || >=14"
1144
+
},
1145
+
"peerDependencies": {
1146
+
"postcss": "^8.1.0"
1147
+
}
1148
+
},
1149
+
"node_modules/axobject-query": {
1150
+
"version": "4.1.0",
1151
+
"dev": true,
1152
+
"license": "Apache-2.0",
1153
+
"engines": {
1154
+
"node": ">= 0.4"
1155
+
}
1156
+
},
1157
+
"node_modules/bail": {
1158
+
"version": "2.0.2",
1159
+
"license": "MIT",
1160
+
"funding": {
1161
+
"type": "github",
1162
+
"url": "https://github.com/sponsors/wooorm"
1163
+
}
1164
+
},
1165
+
"node_modules/balanced-match": {
1166
+
"version": "1.0.2",
1167
+
"dev": true,
1168
+
"license": "MIT"
1169
+
},
1170
+
"node_modules/base64-js": {
1171
+
"version": "0.0.8",
1172
+
"license": "MIT",
1173
+
"engines": {
1174
+
"node": ">= 0.4"
1175
+
}
1176
+
},
1177
+
"node_modules/binary-extensions": {
1178
+
"version": "2.3.0",
1179
+
"dev": true,
1180
+
"license": "MIT",
1181
+
"engines": {
1182
+
"node": ">=8"
1183
+
},
1184
+
"funding": {
1185
+
"url": "https://github.com/sponsors/sindresorhus"
1186
+
}
1187
+
},
1188
+
"node_modules/bindings": {
1189
+
"version": "1.5.0",
1190
+
"dev": true,
1191
+
"license": "MIT",
1192
+
"dependencies": {
1193
+
"file-uri-to-path": "1.0.0"
1194
+
}
1195
+
},
1196
+
"node_modules/brace-expansion": {
1197
+
"version": "1.1.11",
1198
+
"dev": true,
1199
+
"license": "MIT",
1200
+
"dependencies": {
1201
+
"balanced-match": "^1.0.0",
1202
+
"concat-map": "0.0.1"
1203
+
}
1204
+
},
1205
+
"node_modules/braces": {
1206
+
"version": "3.0.3",
1207
+
"dev": true,
1208
+
"license": "MIT",
1209
+
"dependencies": {
1210
+
"fill-range": "^7.1.1"
1211
+
},
1212
+
"engines": {
1213
+
"node": ">=8"
1214
+
}
1215
+
},
1216
+
"node_modules/browserslist": {
1217
+
"version": "4.25.1",
1218
+
"dev": true,
1219
+
"funding": [
1220
+
{
1221
+
"type": "opencollective",
1222
+
"url": "https://opencollective.com/browserslist"
1223
+
},
1224
+
{
1225
+
"type": "tidelift",
1226
+
"url": "https://tidelift.com/funding/github/npm/browserslist"
1227
+
},
1228
+
{
1229
+
"type": "github",
1230
+
"url": "https://github.com/sponsors/ai"
1231
+
}
1232
+
],
1233
+
"license": "MIT",
1234
+
"dependencies": {
1235
+
"caniuse-lite": "^1.0.30001726",
1236
+
"electron-to-chromium": "^1.5.173",
1237
+
"node-releases": "^2.0.19",
1238
+
"update-browserslist-db": "^1.1.3"
1239
+
},
1240
+
"bin": {
1241
+
"browserslist": "cli.js"
1242
+
},
1243
+
"engines": {
1244
+
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
1245
+
}
1246
+
},
1247
+
"node_modules/callsites": {
1248
+
"version": "3.1.0",
1249
+
"dev": true,
1250
+
"license": "MIT",
1251
+
"engines": {
1252
+
"node": ">=6"
1253
+
}
1254
+
},
1255
+
"node_modules/camelcase-css": {
1256
+
"version": "2.0.1",
1257
+
"dev": true,
1258
+
"license": "MIT",
1259
+
"engines": {
1260
+
"node": ">= 6"
1261
+
}
1262
+
},
1263
+
"node_modules/camelize": {
1264
+
"version": "1.0.1",
1265
+
"license": "MIT",
1266
+
"funding": {
1267
+
"url": "https://github.com/sponsors/ljharb"
1268
+
}
1269
+
},
1270
+
"node_modules/caniuse-lite": {
1271
+
"version": "1.0.30001727",
1272
+
"dev": true,
1273
+
"funding": [
1274
+
{
1275
+
"type": "opencollective",
1276
+
"url": "https://opencollective.com/browserslist"
1277
+
},
1278
+
{
1279
+
"type": "tidelift",
1280
+
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
1281
+
},
1282
+
{
1283
+
"type": "github",
1284
+
"url": "https://github.com/sponsors/ai"
1285
+
}
1286
+
],
1287
+
"license": "CC-BY-4.0"
1288
+
},
1289
+
"node_modules/ccount": {
1290
+
"version": "2.0.1",
1291
+
"license": "MIT",
1292
+
"funding": {
1293
+
"type": "github",
1294
+
"url": "https://github.com/sponsors/wooorm"
1295
+
}
1296
+
},
1297
+
"node_modules/chalk": {
1298
+
"version": "4.1.2",
1299
+
"dev": true,
1300
+
"license": "MIT",
1301
+
"dependencies": {
1302
+
"ansi-styles": "^4.1.0",
1303
+
"supports-color": "^7.1.0"
1304
+
},
1305
+
"engines": {
1306
+
"node": ">=10"
1307
+
},
1308
+
"funding": {
1309
+
"url": "https://github.com/chalk/chalk?sponsor=1"
1310
+
}
1311
+
},
1312
+
"node_modules/character-entities": {
1313
+
"version": "2.0.2",
1314
+
"license": "MIT",
1315
+
"funding": {
1316
+
"type": "github",
1317
+
"url": "https://github.com/sponsors/wooorm"
1318
+
}
1319
+
},
1320
+
"node_modules/character-entities-html4": {
1321
+
"version": "2.1.0",
1322
+
"license": "MIT",
1323
+
"funding": {
1324
+
"type": "github",
1325
+
"url": "https://github.com/sponsors/wooorm"
1326
+
}
1327
+
},
1328
+
"node_modules/character-entities-legacy": {
1329
+
"version": "3.0.0",
1330
+
"license": "MIT",
1331
+
"funding": {
1332
+
"type": "github",
1333
+
"url": "https://github.com/sponsors/wooorm"
1334
+
}
1335
+
},
1336
+
"node_modules/chokidar": {
1337
+
"version": "4.0.1",
1338
+
"dev": true,
1339
+
"license": "MIT",
1340
+
"dependencies": {
1341
+
"readdirp": "^4.0.1"
1342
+
},
1343
+
"engines": {
1344
+
"node": ">= 14.16.0"
1345
+
},
1346
+
"funding": {
1347
+
"url": "https://paulmillr.com/funding/"
1348
+
}
1349
+
},
1350
+
"node_modules/chownr": {
1351
+
"version": "3.0.0",
1352
+
"dev": true,
1353
+
"license": "BlueOak-1.0.0",
1354
+
"engines": {
1355
+
"node": ">=18"
1356
+
}
1357
+
},
1358
+
"node_modules/clsx": {
1359
+
"version": "2.1.1",
1360
+
"dev": true,
1361
+
"license": "MIT",
1362
+
"engines": {
1363
+
"node": ">=6"
1364
+
}
1365
+
},
1366
+
"node_modules/color-convert": {
1367
+
"version": "2.0.1",
1368
+
"dev": true,
1369
+
"license": "MIT",
1370
+
"dependencies": {
1371
+
"color-name": "~1.1.4"
1372
+
},
1373
+
"engines": {
1374
+
"node": ">=7.0.0"
1375
+
}
1376
+
},
1377
+
"node_modules/color-name": {
1378
+
"version": "1.1.4",
1379
+
"license": "MIT"
1380
+
},
1381
+
"node_modules/comma-separated-tokens": {
1382
+
"version": "2.0.3",
1383
+
"license": "MIT",
1384
+
"funding": {
1385
+
"type": "github",
1386
+
"url": "https://github.com/sponsors/wooorm"
1387
+
}
1388
+
},
1389
+
"node_modules/commander": {
1390
+
"version": "4.1.1",
1391
+
"dev": true,
1392
+
"license": "MIT",
1393
+
"engines": {
1394
+
"node": ">= 6"
1395
+
}
1396
+
},
1397
+
"node_modules/concat-map": {
1398
+
"version": "0.0.1",
1399
+
"dev": true,
1400
+
"license": "MIT"
1401
+
},
1402
+
"node_modules/consola": {
1403
+
"version": "3.4.2",
1404
+
"dev": true,
1405
+
"license": "MIT",
1406
+
"engines": {
1407
+
"node": "^14.18.0 || >=16.10.0"
1408
+
}
1409
+
},
1410
+
"node_modules/cookie": {
1411
+
"version": "0.6.0",
1412
+
"dev": true,
1413
+
"license": "MIT",
1414
+
"engines": {
1415
+
"node": ">= 0.6"
1416
+
}
1417
+
},
1418
+
"node_modules/cross-spawn": {
1419
+
"version": "7.0.6",
1420
+
"dev": true,
1421
+
"license": "MIT",
1422
+
"dependencies": {
1423
+
"path-key": "^3.1.0",
1424
+
"shebang-command": "^2.0.0",
1425
+
"which": "^2.0.1"
1426
+
},
1427
+
"engines": {
1428
+
"node": ">= 8"
1429
+
}
1430
+
},
1431
+
"node_modules/css-background-parser": {
1432
+
"version": "0.1.0",
1433
+
"license": "MIT"
1434
+
},
1435
+
"node_modules/css-box-shadow": {
1436
+
"version": "1.0.0-3",
1437
+
"license": "MIT"
1438
+
},
1439
+
"node_modules/css-color-keywords": {
1440
+
"version": "1.0.0",
1441
+
"license": "ISC",
1442
+
"engines": {
1443
+
"node": ">=4"
1444
+
}
1445
+
},
1446
+
"node_modules/css-gradient-parser": {
1447
+
"version": "0.0.16",
1448
+
"license": "MIT",
1449
+
"engines": {
1450
+
"node": ">=16"
1451
+
}
1452
+
},
1453
+
"node_modules/css-to-react-native": {
1454
+
"version": "3.2.0",
1455
+
"license": "MIT",
1456
+
"dependencies": {
1457
+
"camelize": "^1.0.0",
1458
+
"css-color-keywords": "^1.0.0",
1459
+
"postcss-value-parser": "^4.0.2"
1460
+
}
1461
+
},
1462
+
"node_modules/cssesc": {
1463
+
"version": "3.0.0",
1464
+
"license": "MIT",
1465
+
"bin": {
1466
+
"cssesc": "bin/cssesc"
1467
+
},
1468
+
"engines": {
1469
+
"node": ">=4"
1470
+
}
1471
+
},
1472
+
"node_modules/debug": {
1473
+
"version": "4.3.7",
1474
+
"license": "MIT",
1475
+
"dependencies": {
1476
+
"ms": "^2.1.3"
1477
+
},
1478
+
"engines": {
1479
+
"node": ">=6.0"
1480
+
},
1481
+
"peerDependenciesMeta": {
1482
+
"supports-color": {
1483
+
"optional": true
1484
+
}
1485
+
}
1486
+
},
1487
+
"node_modules/decode-named-character-reference": {
1488
+
"version": "1.0.2",
1489
+
"license": "MIT",
1490
+
"dependencies": {
1491
+
"character-entities": "^2.0.0"
1492
+
},
1493
+
"funding": {
1494
+
"type": "github",
1495
+
"url": "https://github.com/sponsors/wooorm"
1496
+
}
1497
+
},
1498
+
"node_modules/deep-is": {
1499
+
"version": "0.1.4",
1500
+
"dev": true,
1501
+
"license": "MIT"
1502
+
},
1503
+
"node_modules/deepmerge": {
1504
+
"version": "4.3.1",
1505
+
"license": "MIT",
1506
+
"engines": {
1507
+
"node": ">=0.10.0"
1508
+
}
1509
+
},
1510
+
"node_modules/dequal": {
1511
+
"version": "2.0.3",
1512
+
"license": "MIT",
1513
+
"engines": {
1514
+
"node": ">=6"
1515
+
}
1516
+
},
1517
+
"node_modules/detect-libc": {
1518
+
"version": "2.0.3",
1519
+
"dev": true,
1520
+
"license": "Apache-2.0",
1521
+
"engines": {
1522
+
"node": ">=8"
1523
+
}
1524
+
},
1525
+
"node_modules/devalue": {
1526
+
"version": "5.1.1",
1527
+
"dev": true,
1528
+
"license": "MIT"
1529
+
},
1530
+
"node_modules/devlop": {
1531
+
"version": "1.1.0",
1532
+
"license": "MIT",
1533
+
"dependencies": {
1534
+
"dequal": "^2.0.0"
1535
+
},
1536
+
"funding": {
1537
+
"type": "github",
1538
+
"url": "https://github.com/sponsors/wooorm"
1539
+
}
1540
+
},
1541
+
"node_modules/didyoumean": {
1542
+
"version": "1.2.2",
1543
+
"dev": true,
1544
+
"license": "Apache-2.0"
1545
+
},
1546
+
"node_modules/dlv": {
1547
+
"version": "1.1.3",
1548
+
"dev": true,
1549
+
"license": "MIT"
1550
+
},
1551
+
"node_modules/dom-serializer": {
1552
+
"version": "2.0.0",
1553
+
"license": "MIT",
1554
+
"dependencies": {
1555
+
"domelementtype": "^2.3.0",
1556
+
"domhandler": "^5.0.2",
1557
+
"entities": "^4.2.0"
1558
+
},
1559
+
"funding": {
1560
+
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
1561
+
}
1562
+
},
1563
+
"node_modules/domelementtype": {
1564
+
"version": "2.3.0",
1565
+
"funding": [
1566
+
{
1567
+
"type": "github",
1568
+
"url": "https://github.com/sponsors/fb55"
1569
+
}
1570
+
],
1571
+
"license": "BSD-2-Clause"
1572
+
},
1573
+
"node_modules/domhandler": {
1574
+
"version": "5.0.3",
1575
+
"license": "BSD-2-Clause",
1576
+
"dependencies": {
1577
+
"domelementtype": "^2.3.0"
1578
+
},
1579
+
"engines": {
1580
+
"node": ">= 4"
1581
+
},
1582
+
"funding": {
1583
+
"url": "https://github.com/fb55/domhandler?sponsor=1"
1584
+
}
1585
+
},
1586
+
"node_modules/domutils": {
1587
+
"version": "3.1.0",
1588
+
"license": "BSD-2-Clause",
1589
+
"dependencies": {
1590
+
"dom-serializer": "^2.0.0",
1591
+
"domelementtype": "^2.3.0",
1592
+
"domhandler": "^5.0.3"
1593
+
},
1594
+
"funding": {
1595
+
"url": "https://github.com/fb55/domutils?sponsor=1"
1596
+
}
1597
+
},
1598
+
"node_modules/eastasianwidth": {
1599
+
"version": "0.2.0",
1600
+
"dev": true,
1601
+
"license": "MIT"
1602
+
},
1603
+
"node_modules/electron-to-chromium": {
1604
+
"version": "1.5.185",
1605
+
"dev": true,
1606
+
"license": "ISC"
1607
+
},
1608
+
"node_modules/emoji-regex": {
1609
+
"version": "9.2.2",
1610
+
"dev": true,
1611
+
"license": "MIT"
1612
+
},
1613
+
"node_modules/entities": {
1614
+
"version": "4.5.0",
1615
+
"license": "BSD-2-Clause",
1616
+
"engines": {
1617
+
"node": ">=0.12"
1618
+
},
1619
+
"funding": {
1620
+
"url": "https://github.com/fb55/entities?sponsor=1"
1621
+
}
1622
+
},
1623
+
"node_modules/esbuild": {
1624
+
"version": "0.21.5",
1625
+
"dev": true,
1626
+
"hasInstallScript": true,
1627
+
"license": "MIT",
1628
+
"bin": {
1629
+
"esbuild": "bin/esbuild"
1630
+
},
1631
+
"engines": {
1632
+
"node": ">=12"
1633
+
},
1634
+
"optionalDependencies": {
1635
+
"@esbuild/aix-ppc64": "0.21.5",
1636
+
"@esbuild/android-arm": "0.21.5",
1637
+
"@esbuild/android-arm64": "0.21.5",
1638
+
"@esbuild/android-x64": "0.21.5",
1639
+
"@esbuild/darwin-arm64": "0.21.5",
1640
+
"@esbuild/darwin-x64": "0.21.5",
1641
+
"@esbuild/freebsd-arm64": "0.21.5",
1642
+
"@esbuild/freebsd-x64": "0.21.5",
1643
+
"@esbuild/linux-arm": "0.21.5",
1644
+
"@esbuild/linux-arm64": "0.21.5",
1645
+
"@esbuild/linux-ia32": "0.21.5",
1646
+
"@esbuild/linux-loong64": "0.21.5",
1647
+
"@esbuild/linux-mips64el": "0.21.5",
1648
+
"@esbuild/linux-ppc64": "0.21.5",
1649
+
"@esbuild/linux-riscv64": "0.21.5",
1650
+
"@esbuild/linux-s390x": "0.21.5",
1651
+
"@esbuild/linux-x64": "0.21.5",
1652
+
"@esbuild/netbsd-x64": "0.21.5",
1653
+
"@esbuild/openbsd-x64": "0.21.5",
1654
+
"@esbuild/sunos-x64": "0.21.5",
1655
+
"@esbuild/win32-arm64": "0.21.5",
1656
+
"@esbuild/win32-ia32": "0.21.5",
1657
+
"@esbuild/win32-x64": "0.21.5"
1658
+
}
1659
+
},
1660
+
"node_modules/escalade": {
1661
+
"version": "3.2.0",
1662
+
"dev": true,
1663
+
"license": "MIT",
1664
+
"engines": {
1665
+
"node": ">=6"
1666
+
}
1667
+
},
1668
+
"node_modules/escape-html": {
1669
+
"version": "1.0.3",
1670
+
"license": "MIT"
1671
+
},
1672
+
"node_modules/escape-string-regexp": {
1673
+
"version": "4.0.0",
1674
+
"license": "MIT",
1675
+
"engines": {
1676
+
"node": ">=10"
1677
+
},
1678
+
"funding": {
1679
+
"url": "https://github.com/sponsors/sindresorhus"
1680
+
}
1681
+
},
1682
+
"node_modules/eslint": {
1683
+
"version": "9.16.0",
1684
+
"dev": true,
1685
+
"license": "MIT",
1686
+
"dependencies": {
1687
+
"@eslint-community/eslint-utils": "^4.2.0",
1688
+
"@eslint-community/regexpp": "^4.12.1",
1689
+
"@eslint/config-array": "^0.19.0",
1690
+
"@eslint/core": "^0.9.0",
1691
+
"@eslint/eslintrc": "^3.2.0",
1692
+
"@eslint/js": "9.16.0",
1693
+
"@eslint/plugin-kit": "^0.2.3",
1694
+
"@humanfs/node": "^0.16.6",
1695
+
"@humanwhocodes/module-importer": "^1.0.1",
1696
+
"@humanwhocodes/retry": "^0.4.1",
1697
+
"@types/estree": "^1.0.6",
1698
+
"@types/json-schema": "^7.0.15",
1699
+
"ajv": "^6.12.4",
1700
+
"chalk": "^4.0.0",
1701
+
"cross-spawn": "^7.0.5",
1702
+
"debug": "^4.3.2",
1703
+
"escape-string-regexp": "^4.0.0",
1704
+
"eslint-scope": "^8.2.0",
1705
+
"eslint-visitor-keys": "^4.2.0",
1706
+
"espree": "^10.3.0",
1707
+
"esquery": "^1.5.0",
1708
+
"esutils": "^2.0.2",
1709
+
"fast-deep-equal": "^3.1.3",
1710
+
"file-entry-cache": "^8.0.0",
1711
+
"find-up": "^5.0.0",
1712
+
"glob-parent": "^6.0.2",
1713
+
"ignore": "^5.2.0",
1714
+
"imurmurhash": "^0.1.4",
1715
+
"is-glob": "^4.0.0",
1716
+
"json-stable-stringify-without-jsonify": "^1.0.1",
1717
+
"lodash.merge": "^4.6.2",
1718
+
"minimatch": "^3.1.2",
1719
+
"natural-compare": "^1.4.0",
1720
+
"optionator": "^0.9.3"
1721
+
},
1722
+
"bin": {
1723
+
"eslint": "bin/eslint.js"
1724
+
},
1725
+
"engines": {
1726
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1727
+
},
1728
+
"funding": {
1729
+
"url": "https://eslint.org/donate"
1730
+
},
1731
+
"peerDependencies": {
1732
+
"jiti": "*"
1733
+
},
1734
+
"peerDependenciesMeta": {
1735
+
"jiti": {
1736
+
"optional": true
1737
+
}
1738
+
}
1739
+
},
1740
+
"node_modules/eslint-compat-utils": {
1741
+
"version": "0.5.1",
1742
+
"dev": true,
1743
+
"license": "MIT",
1744
+
"dependencies": {
1745
+
"semver": "^7.5.4"
1746
+
},
1747
+
"engines": {
1748
+
"node": ">=12"
1749
+
},
1750
+
"peerDependencies": {
1751
+
"eslint": ">=6.0.0"
1752
+
}
1753
+
},
1754
+
"node_modules/eslint-plugin-svelte": {
1755
+
"version": "2.46.1",
1756
+
"dev": true,
1757
+
"license": "MIT",
1758
+
"dependencies": {
1759
+
"@eslint-community/eslint-utils": "^4.4.0",
1760
+
"@jridgewell/sourcemap-codec": "^1.4.15",
1761
+
"eslint-compat-utils": "^0.5.1",
1762
+
"esutils": "^2.0.3",
1763
+
"known-css-properties": "^0.35.0",
1764
+
"postcss": "^8.4.38",
1765
+
"postcss-load-config": "^3.1.4",
1766
+
"postcss-safe-parser": "^6.0.0",
1767
+
"postcss-selector-parser": "^6.1.0",
1768
+
"semver": "^7.6.2",
1769
+
"svelte-eslint-parser": "^0.43.0"
1770
+
},
1771
+
"engines": {
1772
+
"node": "^14.17.0 || >=16.0.0"
1773
+
},
1774
+
"funding": {
1775
+
"url": "https://github.com/sponsors/ota-meshi"
1776
+
},
1777
+
"peerDependencies": {
1778
+
"eslint": "^7.0.0 || ^8.0.0-0 || ^9.0.0-0",
1779
+
"svelte": "^3.37.0 || ^4.0.0 || ^5.0.0"
1780
+
},
1781
+
"peerDependenciesMeta": {
1782
+
"svelte": {
1783
+
"optional": true
1784
+
}
1785
+
}
1786
+
},
1787
+
"node_modules/eslint-scope": {
1788
+
"version": "8.2.0",
1789
+
"dev": true,
1790
+
"license": "BSD-2-Clause",
1791
+
"dependencies": {
1792
+
"esrecurse": "^4.3.0",
1793
+
"estraverse": "^5.2.0"
1794
+
},
1795
+
"engines": {
1796
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1797
+
},
1798
+
"funding": {
1799
+
"url": "https://opencollective.com/eslint"
1800
+
}
1801
+
},
1802
+
"node_modules/eslint-visitor-keys": {
1803
+
"version": "4.2.0",
1804
+
"dev": true,
1805
+
"license": "Apache-2.0",
1806
+
"engines": {
1807
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1808
+
},
1809
+
"funding": {
1810
+
"url": "https://opencollective.com/eslint"
1811
+
}
1812
+
},
1813
+
"node_modules/esm-env": {
1814
+
"version": "1.2.2",
1815
+
"dev": true,
1816
+
"license": "MIT"
1817
+
},
1818
+
"node_modules/espree": {
1819
+
"version": "10.3.0",
1820
+
"dev": true,
1821
+
"license": "BSD-2-Clause",
1822
+
"dependencies": {
1823
+
"acorn": "^8.14.0",
1824
+
"acorn-jsx": "^5.3.2",
1825
+
"eslint-visitor-keys": "^4.2.0"
1826
+
},
1827
+
"engines": {
1828
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1829
+
},
1830
+
"funding": {
1831
+
"url": "https://opencollective.com/eslint"
1832
+
}
1833
+
},
1834
+
"node_modules/esquery": {
1835
+
"version": "1.6.0",
1836
+
"dev": true,
1837
+
"license": "BSD-3-Clause",
1838
+
"dependencies": {
1839
+
"estraverse": "^5.1.0"
1840
+
},
1841
+
"engines": {
1842
+
"node": ">=0.10"
1843
+
}
1844
+
},
1845
+
"node_modules/esrap": {
1846
+
"version": "2.1.0",
1847
+
"dev": true,
1848
+
"license": "MIT",
1849
+
"dependencies": {
1850
+
"@jridgewell/sourcemap-codec": "^1.4.15"
1851
+
}
1852
+
},
1853
+
"node_modules/esrecurse": {
1854
+
"version": "4.3.0",
1855
+
"dev": true,
1856
+
"license": "BSD-2-Clause",
1857
+
"dependencies": {
1858
+
"estraverse": "^5.2.0"
1859
+
},
1860
+
"engines": {
1861
+
"node": ">=4.0"
1862
+
}
1863
+
},
1864
+
"node_modules/estraverse": {
1865
+
"version": "5.3.0",
1866
+
"dev": true,
1867
+
"license": "BSD-2-Clause",
1868
+
"engines": {
1869
+
"node": ">=4.0"
1870
+
}
1871
+
},
1872
+
"node_modules/estree-walker": {
1873
+
"version": "2.0.2",
1874
+
"dev": true,
1875
+
"license": "MIT"
1876
+
},
1877
+
"node_modules/esutils": {
1878
+
"version": "2.0.3",
1879
+
"dev": true,
1880
+
"license": "BSD-2-Clause",
1881
+
"engines": {
1882
+
"node": ">=0.10.0"
1883
+
}
1884
+
},
1885
+
"node_modules/extend": {
1886
+
"version": "3.0.2",
1887
+
"license": "MIT"
1888
+
},
1889
+
"node_modules/fast-deep-equal": {
1890
+
"version": "3.1.3",
1891
+
"dev": true,
1892
+
"license": "MIT"
1893
+
},
1894
+
"node_modules/fast-glob": {
1895
+
"version": "3.3.2",
1896
+
"dev": true,
1897
+
"license": "MIT",
1898
+
"dependencies": {
1899
+
"@nodelib/fs.stat": "^2.0.2",
1900
+
"@nodelib/fs.walk": "^1.2.3",
1901
+
"glob-parent": "^5.1.2",
1902
+
"merge2": "^1.3.0",
1903
+
"micromatch": "^4.0.4"
1904
+
},
1905
+
"engines": {
1906
+
"node": ">=8.6.0"
1907
+
}
1908
+
},
1909
+
"node_modules/fast-glob/node_modules/glob-parent": {
1910
+
"version": "5.1.2",
1911
+
"dev": true,
1912
+
"license": "ISC",
1913
+
"dependencies": {
1914
+
"is-glob": "^4.0.1"
1915
+
},
1916
+
"engines": {
1917
+
"node": ">= 6"
1918
+
}
1919
+
},
1920
+
"node_modules/fast-json-stable-stringify": {
1921
+
"version": "2.1.0",
1922
+
"dev": true,
1923
+
"license": "MIT"
1924
+
},
1925
+
"node_modules/fast-levenshtein": {
1926
+
"version": "2.0.6",
1927
+
"dev": true,
1928
+
"license": "MIT"
1929
+
},
1930
+
"node_modules/fastq": {
1931
+
"version": "1.17.1",
1932
+
"dev": true,
1933
+
"license": "ISC",
1934
+
"dependencies": {
1935
+
"reusify": "^1.0.4"
1936
+
}
1937
+
},
1938
+
"node_modules/fdir": {
1939
+
"version": "6.4.2",
1940
+
"dev": true,
1941
+
"license": "MIT",
1942
+
"peerDependencies": {
1943
+
"picomatch": "^3 || ^4"
1944
+
},
1945
+
"peerDependenciesMeta": {
1946
+
"picomatch": {
1947
+
"optional": true
1948
+
}
1949
+
}
1950
+
},
1951
+
"node_modules/fflate": {
1952
+
"version": "0.7.4",
1953
+
"license": "MIT"
1954
+
},
1955
+
"node_modules/file-entry-cache": {
1956
+
"version": "8.0.0",
1957
+
"dev": true,
1958
+
"license": "MIT",
1959
+
"dependencies": {
1960
+
"flat-cache": "^4.0.0"
1961
+
},
1962
+
"engines": {
1963
+
"node": ">=16.0.0"
1964
+
}
1965
+
},
1966
+
"node_modules/file-uri-to-path": {
1967
+
"version": "1.0.0",
1968
+
"dev": true,
1969
+
"license": "MIT"
1970
+
},
1971
+
"node_modules/fill-range": {
1972
+
"version": "7.1.1",
1973
+
"dev": true,
1974
+
"license": "MIT",
1975
+
"dependencies": {
1976
+
"to-regex-range": "^5.0.1"
1977
+
},
1978
+
"engines": {
1979
+
"node": ">=8"
1980
+
}
1981
+
},
1982
+
"node_modules/find-up": {
1983
+
"version": "5.0.0",
1984
+
"dev": true,
1985
+
"license": "MIT",
1986
+
"dependencies": {
1987
+
"locate-path": "^6.0.0",
1988
+
"path-exists": "^4.0.0"
1989
+
},
1990
+
"engines": {
1991
+
"node": ">=10"
1992
+
},
1993
+
"funding": {
1994
+
"url": "https://github.com/sponsors/sindresorhus"
1995
+
}
1996
+
},
1997
+
"node_modules/flat-cache": {
1998
+
"version": "4.0.1",
1999
+
"dev": true,
2000
+
"license": "MIT",
2001
+
"dependencies": {
2002
+
"flatted": "^3.2.9",
2003
+
"keyv": "^4.5.4"
2004
+
},
2005
+
"engines": {
2006
+
"node": ">=16"
2007
+
}
2008
+
},
2009
+
"node_modules/flatted": {
2010
+
"version": "3.3.2",
2011
+
"dev": true,
2012
+
"license": "ISC"
2013
+
},
2014
+
"node_modules/foreground-child": {
2015
+
"version": "3.3.0",
2016
+
"dev": true,
2017
+
"license": "ISC",
2018
+
"dependencies": {
2019
+
"cross-spawn": "^7.0.0",
2020
+
"signal-exit": "^4.0.1"
2021
+
},
2022
+
"engines": {
2023
+
"node": ">=14"
2024
+
},
2025
+
"funding": {
2026
+
"url": "https://github.com/sponsors/isaacs"
2027
+
}
2028
+
},
2029
+
"node_modules/fraction.js": {
2030
+
"version": "4.3.7",
2031
+
"dev": true,
2032
+
"license": "MIT",
2033
+
"engines": {
2034
+
"node": "*"
2035
+
},
2036
+
"funding": {
2037
+
"type": "patreon",
2038
+
"url": "https://github.com/sponsors/rawify"
2039
+
}
2040
+
},
2041
+
"node_modules/fsevents": {
2042
+
"version": "2.3.3",
2043
+
"dev": true,
2044
+
"license": "MIT",
2045
+
"optional": true,
2046
+
"os": [
2047
+
"darwin"
2048
+
],
2049
+
"engines": {
2050
+
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
2051
+
}
2052
+
},
2053
+
"node_modules/function-bind": {
2054
+
"version": "1.1.2",
2055
+
"license": "MIT",
2056
+
"funding": {
2057
+
"url": "https://github.com/sponsors/ljharb"
2058
+
}
2059
+
},
2060
+
"node_modules/glob": {
2061
+
"version": "10.4.5",
2062
+
"dev": true,
2063
+
"license": "ISC",
2064
+
"dependencies": {
2065
+
"foreground-child": "^3.1.0",
2066
+
"jackspeak": "^3.1.2",
2067
+
"minimatch": "^9.0.4",
2068
+
"minipass": "^7.1.2",
2069
+
"package-json-from-dist": "^1.0.0",
2070
+
"path-scurry": "^1.11.1"
2071
+
},
2072
+
"bin": {
2073
+
"glob": "dist/esm/bin.mjs"
2074
+
},
2075
+
"funding": {
2076
+
"url": "https://github.com/sponsors/isaacs"
2077
+
}
2078
+
},
2079
+
"node_modules/glob-parent": {
2080
+
"version": "6.0.2",
2081
+
"dev": true,
2082
+
"license": "ISC",
2083
+
"dependencies": {
2084
+
"is-glob": "^4.0.3"
2085
+
},
2086
+
"engines": {
2087
+
"node": ">=10.13.0"
2088
+
}
2089
+
},
2090
+
"node_modules/glob/node_modules/brace-expansion": {
2091
+
"version": "2.0.1",
2092
+
"dev": true,
2093
+
"license": "MIT",
2094
+
"dependencies": {
2095
+
"balanced-match": "^1.0.0"
2096
+
}
2097
+
},
2098
+
"node_modules/glob/node_modules/minimatch": {
2099
+
"version": "9.0.5",
2100
+
"dev": true,
2101
+
"license": "ISC",
2102
+
"dependencies": {
2103
+
"brace-expansion": "^2.0.1"
2104
+
},
2105
+
"engines": {
2106
+
"node": ">=16 || 14 >=14.17"
2107
+
},
2108
+
"funding": {
2109
+
"url": "https://github.com/sponsors/isaacs"
2110
+
}
2111
+
},
2112
+
"node_modules/globals": {
2113
+
"version": "15.13.0",
2114
+
"dev": true,
2115
+
"license": "MIT",
2116
+
"engines": {
2117
+
"node": ">=18"
2118
+
},
2119
+
"funding": {
2120
+
"url": "https://github.com/sponsors/sindresorhus"
2121
+
}
2122
+
},
2123
+
"node_modules/graceful-fs": {
2124
+
"version": "4.2.11",
2125
+
"dev": true,
2126
+
"license": "ISC"
2127
+
},
2128
+
"node_modules/graphemer": {
2129
+
"version": "1.4.0",
2130
+
"dev": true,
2131
+
"license": "MIT"
2132
+
},
2133
+
"node_modules/has-flag": {
2134
+
"version": "4.0.0",
2135
+
"dev": true,
2136
+
"license": "MIT",
2137
+
"engines": {
2138
+
"node": ">=8"
2139
+
}
2140
+
},
2141
+
"node_modules/hasown": {
2142
+
"version": "2.0.2",
2143
+
"license": "MIT",
2144
+
"dependencies": {
2145
+
"function-bind": "^1.1.2"
2146
+
},
2147
+
"engines": {
2148
+
"node": ">= 0.4"
2149
+
}
2150
+
},
2151
+
"node_modules/hast-util-from-parse5": {
2152
+
"version": "8.0.2",
2153
+
"license": "MIT",
2154
+
"dependencies": {
2155
+
"@types/hast": "^3.0.0",
2156
+
"@types/unist": "^3.0.0",
2157
+
"devlop": "^1.0.0",
2158
+
"hastscript": "^9.0.0",
2159
+
"property-information": "^6.0.0",
2160
+
"vfile": "^6.0.0",
2161
+
"vfile-location": "^5.0.0",
2162
+
"web-namespaces": "^2.0.0"
2163
+
},
2164
+
"funding": {
2165
+
"type": "opencollective",
2166
+
"url": "https://opencollective.com/unified"
2167
+
}
2168
+
},
2169
+
"node_modules/hast-util-parse-selector": {
2170
+
"version": "4.0.0",
2171
+
"license": "MIT",
2172
+
"dependencies": {
2173
+
"@types/hast": "^3.0.0"
2174
+
},
2175
+
"funding": {
2176
+
"type": "opencollective",
2177
+
"url": "https://opencollective.com/unified"
2178
+
}
2179
+
},
2180
+
"node_modules/hast-util-raw": {
2181
+
"version": "9.1.0",
2182
+
"license": "MIT",
2183
+
"dependencies": {
2184
+
"@types/hast": "^3.0.0",
2185
+
"@types/unist": "^3.0.0",
2186
+
"@ungap/structured-clone": "^1.0.0",
2187
+
"hast-util-from-parse5": "^8.0.0",
2188
+
"hast-util-to-parse5": "^8.0.0",
2189
+
"html-void-elements": "^3.0.0",
2190
+
"mdast-util-to-hast": "^13.0.0",
2191
+
"parse5": "^7.0.0",
2192
+
"unist-util-position": "^5.0.0",
2193
+
"unist-util-visit": "^5.0.0",
2194
+
"vfile": "^6.0.0",
2195
+
"web-namespaces": "^2.0.0",
2196
+
"zwitch": "^2.0.0"
2197
+
},
2198
+
"funding": {
2199
+
"type": "opencollective",
2200
+
"url": "https://opencollective.com/unified"
2201
+
}
2202
+
},
2203
+
"node_modules/hast-util-sanitize": {
2204
+
"version": "5.0.2",
2205
+
"license": "MIT",
2206
+
"dependencies": {
2207
+
"@types/hast": "^3.0.0",
2208
+
"@ungap/structured-clone": "^1.0.0",
2209
+
"unist-util-position": "^5.0.0"
2210
+
},
2211
+
"funding": {
2212
+
"type": "opencollective",
2213
+
"url": "https://opencollective.com/unified"
2214
+
}
2215
+
},
2216
+
"node_modules/hast-util-to-html": {
2217
+
"version": "9.0.3",
2218
+
"license": "MIT",
2219
+
"dependencies": {
2220
+
"@types/hast": "^3.0.0",
2221
+
"@types/unist": "^3.0.0",
2222
+
"ccount": "^2.0.0",
2223
+
"comma-separated-tokens": "^2.0.0",
2224
+
"hast-util-whitespace": "^3.0.0",
2225
+
"html-void-elements": "^3.0.0",
2226
+
"mdast-util-to-hast": "^13.0.0",
2227
+
"property-information": "^6.0.0",
2228
+
"space-separated-tokens": "^2.0.0",
2229
+
"stringify-entities": "^4.0.0",
2230
+
"zwitch": "^2.0.4"
2231
+
},
2232
+
"funding": {
2233
+
"type": "opencollective",
2234
+
"url": "https://opencollective.com/unified"
2235
+
}
2236
+
},
2237
+
"node_modules/hast-util-to-parse5": {
2238
+
"version": "8.0.0",
2239
+
"license": "MIT",
2240
+
"dependencies": {
2241
+
"@types/hast": "^3.0.0",
2242
+
"comma-separated-tokens": "^2.0.0",
2243
+
"devlop": "^1.0.0",
2244
+
"property-information": "^6.0.0",
2245
+
"space-separated-tokens": "^2.0.0",
2246
+
"web-namespaces": "^2.0.0",
2247
+
"zwitch": "^2.0.0"
2248
+
},
2249
+
"funding": {
2250
+
"type": "opencollective",
2251
+
"url": "https://opencollective.com/unified"
2252
+
}
2253
+
},
2254
+
"node_modules/hast-util-whitespace": {
2255
+
"version": "3.0.0",
2256
+
"license": "MIT",
2257
+
"dependencies": {
2258
+
"@types/hast": "^3.0.0"
2259
+
},
2260
+
"funding": {
2261
+
"type": "opencollective",
2262
+
"url": "https://opencollective.com/unified"
2263
+
}
2264
+
},
2265
+
"node_modules/hastscript": {
2266
+
"version": "9.0.0",
2267
+
"license": "MIT",
2268
+
"dependencies": {
2269
+
"@types/hast": "^3.0.0",
2270
+
"comma-separated-tokens": "^2.0.0",
2271
+
"hast-util-parse-selector": "^4.0.0",
2272
+
"property-information": "^6.0.0",
2273
+
"space-separated-tokens": "^2.0.0"
2274
+
},
2275
+
"funding": {
2276
+
"type": "opencollective",
2277
+
"url": "https://opencollective.com/unified"
2278
+
}
2279
+
},
2280
+
"node_modules/hex-rgb": {
2281
+
"version": "4.3.0",
2282
+
"license": "MIT",
2283
+
"engines": {
2284
+
"node": ">=6"
2285
+
},
2286
+
"funding": {
2287
+
"url": "https://github.com/sponsors/sindresorhus"
2288
+
}
2289
+
},
2290
+
"node_modules/html-void-elements": {
2291
+
"version": "3.0.0",
2292
+
"license": "MIT",
2293
+
"funding": {
2294
+
"type": "github",
2295
+
"url": "https://github.com/sponsors/wooorm"
2296
+
}
2297
+
},
2298
+
"node_modules/htmlparser2": {
2299
+
"version": "8.0.2",
2300
+
"funding": [
2301
+
"https://github.com/fb55/htmlparser2?sponsor=1",
2302
+
{
2303
+
"type": "github",
2304
+
"url": "https://github.com/sponsors/fb55"
2305
+
}
2306
+
],
2307
+
"license": "MIT",
2308
+
"dependencies": {
2309
+
"domelementtype": "^2.3.0",
2310
+
"domhandler": "^5.0.3",
2311
+
"domutils": "^3.0.1",
2312
+
"entities": "^4.4.0"
2313
+
}
2314
+
},
2315
+
"node_modules/https-proxy-agent": {
2316
+
"version": "7.0.6",
2317
+
"dev": true,
2318
+
"license": "MIT",
2319
+
"dependencies": {
2320
+
"agent-base": "^7.1.2",
2321
+
"debug": "4"
2322
+
},
2323
+
"engines": {
2324
+
"node": ">= 14"
2325
+
}
2326
+
},
2327
+
"node_modules/ignore": {
2328
+
"version": "5.3.2",
2329
+
"dev": true,
2330
+
"license": "MIT",
2331
+
"engines": {
2332
+
"node": ">= 4"
2333
+
}
2334
+
},
2335
+
"node_modules/import-fresh": {
2336
+
"version": "3.3.0",
2337
+
"dev": true,
2338
+
"license": "MIT",
2339
+
"dependencies": {
2340
+
"parent-module": "^1.0.0",
2341
+
"resolve-from": "^4.0.0"
2342
+
},
2343
+
"engines": {
2344
+
"node": ">=6"
2345
+
},
2346
+
"funding": {
2347
+
"url": "https://github.com/sponsors/sindresorhus"
2348
+
}
2349
+
},
2350
+
"node_modules/imurmurhash": {
2351
+
"version": "0.1.4",
2352
+
"dev": true,
2353
+
"license": "MIT",
2354
+
"engines": {
2355
+
"node": ">=0.8.19"
2356
+
}
2357
+
},
2358
+
"node_modules/is-binary-path": {
2359
+
"version": "2.1.0",
2360
+
"dev": true,
2361
+
"license": "MIT",
2362
+
"dependencies": {
2363
+
"binary-extensions": "^2.0.0"
2364
+
},
2365
+
"engines": {
2366
+
"node": ">=8"
2367
+
}
2368
+
},
2369
+
"node_modules/is-core-module": {
2370
+
"version": "2.15.1",
2371
+
"license": "MIT",
2372
+
"dependencies": {
2373
+
"hasown": "^2.0.2"
2374
+
},
2375
+
"engines": {
2376
+
"node": ">= 0.4"
2377
+
},
2378
+
"funding": {
2379
+
"url": "https://github.com/sponsors/ljharb"
2380
+
}
2381
+
},
2382
+
"node_modules/is-extglob": {
2383
+
"version": "2.1.1",
2384
+
"dev": true,
2385
+
"license": "MIT",
2386
+
"engines": {
2387
+
"node": ">=0.10.0"
2388
+
}
2389
+
},
2390
+
"node_modules/is-fullwidth-code-point": {
2391
+
"version": "3.0.0",
2392
+
"dev": true,
2393
+
"license": "MIT",
2394
+
"engines": {
2395
+
"node": ">=8"
2396
+
}
2397
+
},
2398
+
"node_modules/is-glob": {
2399
+
"version": "4.0.3",
2400
+
"dev": true,
2401
+
"license": "MIT",
2402
+
"dependencies": {
2403
+
"is-extglob": "^2.1.1"
2404
+
},
2405
+
"engines": {
2406
+
"node": ">=0.10.0"
2407
+
}
2408
+
},
2409
+
"node_modules/is-number": {
2410
+
"version": "7.0.0",
2411
+
"dev": true,
2412
+
"license": "MIT",
2413
+
"engines": {
2414
+
"node": ">=0.12.0"
2415
+
}
2416
+
},
2417
+
"node_modules/is-plain-obj": {
2418
+
"version": "4.1.0",
2419
+
"license": "MIT",
2420
+
"engines": {
2421
+
"node": ">=12"
2422
+
},
2423
+
"funding": {
2424
+
"url": "https://github.com/sponsors/sindresorhus"
2425
+
}
2426
+
},
2427
+
"node_modules/is-plain-object": {
2428
+
"version": "5.0.0",
2429
+
"license": "MIT",
2430
+
"engines": {
2431
+
"node": ">=0.10.0"
2432
+
}
2433
+
},
2434
+
"node_modules/is-reference": {
2435
+
"version": "3.0.3",
2436
+
"dev": true,
2437
+
"license": "MIT",
2438
+
"dependencies": {
2439
+
"@types/estree": "^1.0.6"
2440
+
}
2441
+
},
2442
+
"node_modules/isexe": {
2443
+
"version": "2.0.0",
2444
+
"dev": true,
2445
+
"license": "ISC"
2446
+
},
2447
+
"node_modules/jackspeak": {
2448
+
"version": "3.4.3",
2449
+
"dev": true,
2450
+
"license": "BlueOak-1.0.0",
2451
+
"dependencies": {
2452
+
"@isaacs/cliui": "^8.0.2"
2453
+
},
2454
+
"funding": {
2455
+
"url": "https://github.com/sponsors/isaacs"
2456
+
},
2457
+
"optionalDependencies": {
2458
+
"@pkgjs/parseargs": "^0.11.0"
2459
+
}
2460
+
},
2461
+
"node_modules/jiti": {
2462
+
"version": "1.21.6",
2463
+
"dev": true,
2464
+
"license": "MIT",
2465
+
"bin": {
2466
+
"jiti": "bin/jiti.js"
2467
+
}
2468
+
},
2469
+
"node_modules/js-yaml": {
2470
+
"version": "4.1.0",
2471
+
"dev": true,
2472
+
"license": "MIT",
2473
+
"dependencies": {
2474
+
"argparse": "^2.0.1"
2475
+
},
2476
+
"bin": {
2477
+
"js-yaml": "bin/js-yaml.js"
2478
+
}
2479
+
},
2480
+
"node_modules/json-buffer": {
2481
+
"version": "3.0.1",
2482
+
"dev": true,
2483
+
"license": "MIT"
2484
+
},
2485
+
"node_modules/json-schema-traverse": {
2486
+
"version": "0.4.1",
2487
+
"dev": true,
2488
+
"license": "MIT"
2489
+
},
2490
+
"node_modules/json-stable-stringify-without-jsonify": {
2491
+
"version": "1.0.1",
2492
+
"dev": true,
2493
+
"license": "MIT"
2494
+
},
2495
+
"node_modules/keyv": {
2496
+
"version": "4.5.4",
2497
+
"dev": true,
2498
+
"license": "MIT",
2499
+
"dependencies": {
2500
+
"json-buffer": "3.0.1"
2501
+
}
2502
+
},
2503
+
"node_modules/kleur": {
2504
+
"version": "4.1.5",
2505
+
"dev": true,
2506
+
"license": "MIT",
2507
+
"engines": {
2508
+
"node": ">=6"
2509
+
}
2510
+
},
2511
+
"node_modules/known-css-properties": {
2512
+
"version": "0.35.0",
2513
+
"dev": true,
2514
+
"license": "MIT"
2515
+
},
2516
+
"node_modules/levn": {
2517
+
"version": "0.4.1",
2518
+
"dev": true,
2519
+
"license": "MIT",
2520
+
"dependencies": {
2521
+
"prelude-ls": "^1.2.1",
2522
+
"type-check": "~0.4.0"
2523
+
},
2524
+
"engines": {
2525
+
"node": ">= 0.8.0"
2526
+
}
2527
+
},
2528
+
"node_modules/lilconfig": {
2529
+
"version": "2.1.0",
2530
+
"dev": true,
2531
+
"license": "MIT",
2532
+
"engines": {
2533
+
"node": ">=10"
2534
+
}
2535
+
},
2536
+
"node_modules/linebreak": {
2537
+
"version": "1.1.0",
2538
+
"license": "MIT",
2539
+
"dependencies": {
2540
+
"base64-js": "0.0.8",
2541
+
"unicode-trie": "^2.0.0"
2542
+
}
2543
+
},
2544
+
"node_modules/lines-and-columns": {
2545
+
"version": "1.2.4",
2546
+
"dev": true,
2547
+
"license": "MIT"
2548
+
},
2549
+
"node_modules/locate-character": {
2550
+
"version": "3.0.0",
2551
+
"dev": true,
2552
+
"license": "MIT"
2553
+
},
2554
+
"node_modules/locate-path": {
2555
+
"version": "6.0.0",
2556
+
"dev": true,
2557
+
"license": "MIT",
2558
+
"dependencies": {
2559
+
"p-locate": "^5.0.0"
2560
+
},
2561
+
"engines": {
2562
+
"node": ">=10"
2563
+
},
2564
+
"funding": {
2565
+
"url": "https://github.com/sponsors/sindresorhus"
2566
+
}
2567
+
},
2568
+
"node_modules/lodash.castarray": {
2569
+
"version": "4.4.0",
2570
+
"dev": true,
2571
+
"license": "MIT"
2572
+
},
2573
+
"node_modules/lodash.isplainobject": {
2574
+
"version": "4.0.6",
2575
+
"dev": true,
2576
+
"license": "MIT"
2577
+
},
2578
+
"node_modules/lodash.merge": {
2579
+
"version": "4.6.2",
2580
+
"dev": true,
2581
+
"license": "MIT"
2582
+
},
2583
+
"node_modules/longest-streak": {
2584
+
"version": "3.1.0",
2585
+
"license": "MIT",
2586
+
"funding": {
2587
+
"type": "github",
2588
+
"url": "https://github.com/sponsors/wooorm"
2589
+
}
2590
+
},
2591
+
"node_modules/lru-cache": {
2592
+
"version": "10.4.3",
2593
+
"dev": true,
2594
+
"license": "ISC"
2595
+
},
2596
+
"node_modules/magic-string": {
2597
+
"version": "0.30.14",
2598
+
"dev": true,
2599
+
"license": "MIT",
2600
+
"dependencies": {
2601
+
"@jridgewell/sourcemap-codec": "^1.5.0"
2602
+
}
2603
+
},
2604
+
"node_modules/markdown-table": {
2605
+
"version": "3.0.4",
2606
+
"license": "MIT",
2607
+
"funding": {
2608
+
"type": "github",
2609
+
"url": "https://github.com/sponsors/wooorm"
2610
+
}
2611
+
},
2612
+
"node_modules/mdast-util-find-and-replace": {
2613
+
"version": "3.0.1",
2614
+
"license": "MIT",
2615
+
"dependencies": {
2616
+
"@types/mdast": "^4.0.0",
2617
+
"escape-string-regexp": "^5.0.0",
2618
+
"unist-util-is": "^6.0.0",
2619
+
"unist-util-visit-parents": "^6.0.0"
2620
+
},
2621
+
"funding": {
2622
+
"type": "opencollective",
2623
+
"url": "https://opencollective.com/unified"
2624
+
}
2625
+
},
2626
+
"node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": {
2627
+
"version": "5.0.0",
2628
+
"license": "MIT",
2629
+
"engines": {
2630
+
"node": ">=12"
2631
+
},
2632
+
"funding": {
2633
+
"url": "https://github.com/sponsors/sindresorhus"
2634
+
}
2635
+
},
2636
+
"node_modules/mdast-util-from-markdown": {
2637
+
"version": "2.0.2",
2638
+
"license": "MIT",
2639
+
"dependencies": {
2640
+
"@types/mdast": "^4.0.0",
2641
+
"@types/unist": "^3.0.0",
2642
+
"decode-named-character-reference": "^1.0.0",
2643
+
"devlop": "^1.0.0",
2644
+
"mdast-util-to-string": "^4.0.0",
2645
+
"micromark": "^4.0.0",
2646
+
"micromark-util-decode-numeric-character-reference": "^2.0.0",
2647
+
"micromark-util-decode-string": "^2.0.0",
2648
+
"micromark-util-normalize-identifier": "^2.0.0",
2649
+
"micromark-util-symbol": "^2.0.0",
2650
+
"micromark-util-types": "^2.0.0",
2651
+
"unist-util-stringify-position": "^4.0.0"
2652
+
},
2653
+
"funding": {
2654
+
"type": "opencollective",
2655
+
"url": "https://opencollective.com/unified"
2656
+
}
2657
+
},
2658
+
"node_modules/mdast-util-gfm": {
2659
+
"version": "3.0.0",
2660
+
"license": "MIT",
2661
+
"dependencies": {
2662
+
"mdast-util-from-markdown": "^2.0.0",
2663
+
"mdast-util-gfm-autolink-literal": "^2.0.0",
2664
+
"mdast-util-gfm-footnote": "^2.0.0",
2665
+
"mdast-util-gfm-strikethrough": "^2.0.0",
2666
+
"mdast-util-gfm-table": "^2.0.0",
2667
+
"mdast-util-gfm-task-list-item": "^2.0.0",
2668
+
"mdast-util-to-markdown": "^2.0.0"
2669
+
},
2670
+
"funding": {
2671
+
"type": "opencollective",
2672
+
"url": "https://opencollective.com/unified"
2673
+
}
2674
+
},
2675
+
"node_modules/mdast-util-gfm-autolink-literal": {
2676
+
"version": "2.0.1",
2677
+
"license": "MIT",
2678
+
"dependencies": {
2679
+
"@types/mdast": "^4.0.0",
2680
+
"ccount": "^2.0.0",
2681
+
"devlop": "^1.0.0",
2682
+
"mdast-util-find-and-replace": "^3.0.0",
2683
+
"micromark-util-character": "^2.0.0"
2684
+
},
2685
+
"funding": {
2686
+
"type": "opencollective",
2687
+
"url": "https://opencollective.com/unified"
2688
+
}
2689
+
},
2690
+
"node_modules/mdast-util-gfm-footnote": {
2691
+
"version": "2.0.0",
2692
+
"license": "MIT",
2693
+
"dependencies": {
2694
+
"@types/mdast": "^4.0.0",
2695
+
"devlop": "^1.1.0",
2696
+
"mdast-util-from-markdown": "^2.0.0",
2697
+
"mdast-util-to-markdown": "^2.0.0",
2698
+
"micromark-util-normalize-identifier": "^2.0.0"
2699
+
},
2700
+
"funding": {
2701
+
"type": "opencollective",
2702
+
"url": "https://opencollective.com/unified"
2703
+
}
2704
+
},
2705
+
"node_modules/mdast-util-gfm-strikethrough": {
2706
+
"version": "2.0.0",
2707
+
"license": "MIT",
2708
+
"dependencies": {
2709
+
"@types/mdast": "^4.0.0",
2710
+
"mdast-util-from-markdown": "^2.0.0",
2711
+
"mdast-util-to-markdown": "^2.0.0"
2712
+
},
2713
+
"funding": {
2714
+
"type": "opencollective",
2715
+
"url": "https://opencollective.com/unified"
2716
+
}
2717
+
},
2718
+
"node_modules/mdast-util-gfm-table": {
2719
+
"version": "2.0.0",
2720
+
"license": "MIT",
2721
+
"dependencies": {
2722
+
"@types/mdast": "^4.0.0",
2723
+
"devlop": "^1.0.0",
2724
+
"markdown-table": "^3.0.0",
2725
+
"mdast-util-from-markdown": "^2.0.0",
2726
+
"mdast-util-to-markdown": "^2.0.0"
2727
+
},
2728
+
"funding": {
2729
+
"type": "opencollective",
2730
+
"url": "https://opencollective.com/unified"
2731
+
}
2732
+
},
2733
+
"node_modules/mdast-util-gfm-task-list-item": {
2734
+
"version": "2.0.0",
2735
+
"license": "MIT",
2736
+
"dependencies": {
2737
+
"@types/mdast": "^4.0.0",
2738
+
"devlop": "^1.0.0",
2739
+
"mdast-util-from-markdown": "^2.0.0",
2740
+
"mdast-util-to-markdown": "^2.0.0"
2741
+
},
2742
+
"funding": {
2743
+
"type": "opencollective",
2744
+
"url": "https://opencollective.com/unified"
2745
+
}
2746
+
},
2747
+
"node_modules/mdast-util-phrasing": {
2748
+
"version": "4.1.0",
2749
+
"license": "MIT",
2750
+
"dependencies": {
2751
+
"@types/mdast": "^4.0.0",
2752
+
"unist-util-is": "^6.0.0"
2753
+
},
2754
+
"funding": {
2755
+
"type": "opencollective",
2756
+
"url": "https://opencollective.com/unified"
2757
+
}
2758
+
},
2759
+
"node_modules/mdast-util-to-hast": {
2760
+
"version": "13.2.0",
2761
+
"license": "MIT",
2762
+
"dependencies": {
2763
+
"@types/hast": "^3.0.0",
2764
+
"@types/mdast": "^4.0.0",
2765
+
"@ungap/structured-clone": "^1.0.0",
2766
+
"devlop": "^1.0.0",
2767
+
"micromark-util-sanitize-uri": "^2.0.0",
2768
+
"trim-lines": "^3.0.0",
2769
+
"unist-util-position": "^5.0.0",
2770
+
"unist-util-visit": "^5.0.0",
2771
+
"vfile": "^6.0.0"
2772
+
},
2773
+
"funding": {
2774
+
"type": "opencollective",
2775
+
"url": "https://opencollective.com/unified"
2776
+
}
2777
+
},
2778
+
"node_modules/mdast-util-to-markdown": {
2779
+
"version": "2.1.2",
2780
+
"license": "MIT",
2781
+
"dependencies": {
2782
+
"@types/mdast": "^4.0.0",
2783
+
"@types/unist": "^3.0.0",
2784
+
"longest-streak": "^3.0.0",
2785
+
"mdast-util-phrasing": "^4.0.0",
2786
+
"mdast-util-to-string": "^4.0.0",
2787
+
"micromark-util-classify-character": "^2.0.0",
2788
+
"micromark-util-decode-string": "^2.0.0",
2789
+
"unist-util-visit": "^5.0.0",
2790
+
"zwitch": "^2.0.0"
2791
+
},
2792
+
"funding": {
2793
+
"type": "opencollective",
2794
+
"url": "https://opencollective.com/unified"
2795
+
}
2796
+
},
2797
+
"node_modules/mdast-util-to-string": {
2798
+
"version": "4.0.0",
2799
+
"license": "MIT",
2800
+
"dependencies": {
2801
+
"@types/mdast": "^4.0.0"
2802
+
},
2803
+
"funding": {
2804
+
"type": "opencollective",
2805
+
"url": "https://opencollective.com/unified"
2806
+
}
2807
+
},
2808
+
"node_modules/merge2": {
2809
+
"version": "1.4.1",
2810
+
"dev": true,
2811
+
"license": "MIT",
2812
+
"engines": {
2813
+
"node": ">= 8"
2814
+
}
2815
+
},
2816
+
"node_modules/micromark": {
2817
+
"version": "4.0.1",
2818
+
"funding": [
2819
+
{
2820
+
"type": "GitHub Sponsors",
2821
+
"url": "https://github.com/sponsors/unifiedjs"
2822
+
},
2823
+
{
2824
+
"type": "OpenCollective",
2825
+
"url": "https://opencollective.com/unified"
2826
+
}
2827
+
],
2828
+
"license": "MIT",
2829
+
"dependencies": {
2830
+
"@types/debug": "^4.0.0",
2831
+
"debug": "^4.0.0",
2832
+
"decode-named-character-reference": "^1.0.0",
2833
+
"devlop": "^1.0.0",
2834
+
"micromark-core-commonmark": "^2.0.0",
2835
+
"micromark-factory-space": "^2.0.0",
2836
+
"micromark-util-character": "^2.0.0",
2837
+
"micromark-util-chunked": "^2.0.0",
2838
+
"micromark-util-combine-extensions": "^2.0.0",
2839
+
"micromark-util-decode-numeric-character-reference": "^2.0.0",
2840
+
"micromark-util-encode": "^2.0.0",
2841
+
"micromark-util-normalize-identifier": "^2.0.0",
2842
+
"micromark-util-resolve-all": "^2.0.0",
2843
+
"micromark-util-sanitize-uri": "^2.0.0",
2844
+
"micromark-util-subtokenize": "^2.0.0",
2845
+
"micromark-util-symbol": "^2.0.0",
2846
+
"micromark-util-types": "^2.0.0"
2847
+
}
2848
+
},
2849
+
"node_modules/micromark-core-commonmark": {
2850
+
"version": "2.0.2",
2851
+
"funding": [
2852
+
{
2853
+
"type": "GitHub Sponsors",
2854
+
"url": "https://github.com/sponsors/unifiedjs"
2855
+
},
2856
+
{
2857
+
"type": "OpenCollective",
2858
+
"url": "https://opencollective.com/unified"
2859
+
}
2860
+
],
2861
+
"license": "MIT",
2862
+
"dependencies": {
2863
+
"decode-named-character-reference": "^1.0.0",
2864
+
"devlop": "^1.0.0",
2865
+
"micromark-factory-destination": "^2.0.0",
2866
+
"micromark-factory-label": "^2.0.0",
2867
+
"micromark-factory-space": "^2.0.0",
2868
+
"micromark-factory-title": "^2.0.0",
2869
+
"micromark-factory-whitespace": "^2.0.0",
2870
+
"micromark-util-character": "^2.0.0",
2871
+
"micromark-util-chunked": "^2.0.0",
2872
+
"micromark-util-classify-character": "^2.0.0",
2873
+
"micromark-util-html-tag-name": "^2.0.0",
2874
+
"micromark-util-normalize-identifier": "^2.0.0",
2875
+
"micromark-util-resolve-all": "^2.0.0",
2876
+
"micromark-util-subtokenize": "^2.0.0",
2877
+
"micromark-util-symbol": "^2.0.0",
2878
+
"micromark-util-types": "^2.0.0"
2879
+
}
2880
+
},
2881
+
"node_modules/micromark-extension-gfm": {
2882
+
"version": "3.0.0",
2883
+
"license": "MIT",
2884
+
"dependencies": {
2885
+
"micromark-extension-gfm-autolink-literal": "^2.0.0",
2886
+
"micromark-extension-gfm-footnote": "^2.0.0",
2887
+
"micromark-extension-gfm-strikethrough": "^2.0.0",
2888
+
"micromark-extension-gfm-table": "^2.0.0",
2889
+
"micromark-extension-gfm-tagfilter": "^2.0.0",
2890
+
"micromark-extension-gfm-task-list-item": "^2.0.0",
2891
+
"micromark-util-combine-extensions": "^2.0.0",
2892
+
"micromark-util-types": "^2.0.0"
2893
+
},
2894
+
"funding": {
2895
+
"type": "opencollective",
2896
+
"url": "https://opencollective.com/unified"
2897
+
}
2898
+
},
2899
+
"node_modules/micromark-extension-gfm-autolink-literal": {
2900
+
"version": "2.1.0",
2901
+
"license": "MIT",
2902
+
"dependencies": {
2903
+
"micromark-util-character": "^2.0.0",
2904
+
"micromark-util-sanitize-uri": "^2.0.0",
2905
+
"micromark-util-symbol": "^2.0.0",
2906
+
"micromark-util-types": "^2.0.0"
2907
+
},
2908
+
"funding": {
2909
+
"type": "opencollective",
2910
+
"url": "https://opencollective.com/unified"
2911
+
}
2912
+
},
2913
+
"node_modules/micromark-extension-gfm-footnote": {
2914
+
"version": "2.1.0",
2915
+
"license": "MIT",
2916
+
"dependencies": {
2917
+
"devlop": "^1.0.0",
2918
+
"micromark-core-commonmark": "^2.0.0",
2919
+
"micromark-factory-space": "^2.0.0",
2920
+
"micromark-util-character": "^2.0.0",
2921
+
"micromark-util-normalize-identifier": "^2.0.0",
2922
+
"micromark-util-sanitize-uri": "^2.0.0",
2923
+
"micromark-util-symbol": "^2.0.0",
2924
+
"micromark-util-types": "^2.0.0"
2925
+
},
2926
+
"funding": {
2927
+
"type": "opencollective",
2928
+
"url": "https://opencollective.com/unified"
2929
+
}
2930
+
},
2931
+
"node_modules/micromark-extension-gfm-strikethrough": {
2932
+
"version": "2.1.0",
2933
+
"license": "MIT",
2934
+
"dependencies": {
2935
+
"devlop": "^1.0.0",
2936
+
"micromark-util-chunked": "^2.0.0",
2937
+
"micromark-util-classify-character": "^2.0.0",
2938
+
"micromark-util-resolve-all": "^2.0.0",
2939
+
"micromark-util-symbol": "^2.0.0",
2940
+
"micromark-util-types": "^2.0.0"
2941
+
},
2942
+
"funding": {
2943
+
"type": "opencollective",
2944
+
"url": "https://opencollective.com/unified"
2945
+
}
2946
+
},
2947
+
"node_modules/micromark-extension-gfm-table": {
2948
+
"version": "2.1.0",
2949
+
"license": "MIT",
2950
+
"dependencies": {
2951
+
"devlop": "^1.0.0",
2952
+
"micromark-factory-space": "^2.0.0",
2953
+
"micromark-util-character": "^2.0.0",
2954
+
"micromark-util-symbol": "^2.0.0",
2955
+
"micromark-util-types": "^2.0.0"
2956
+
},
2957
+
"funding": {
2958
+
"type": "opencollective",
2959
+
"url": "https://opencollective.com/unified"
2960
+
}
2961
+
},
2962
+
"node_modules/micromark-extension-gfm-tagfilter": {
2963
+
"version": "2.0.0",
2964
+
"license": "MIT",
2965
+
"dependencies": {
2966
+
"micromark-util-types": "^2.0.0"
2967
+
},
2968
+
"funding": {
2969
+
"type": "opencollective",
2970
+
"url": "https://opencollective.com/unified"
2971
+
}
2972
+
},
2973
+
"node_modules/micromark-extension-gfm-task-list-item": {
2974
+
"version": "2.1.0",
2975
+
"license": "MIT",
2976
+
"dependencies": {
2977
+
"devlop": "^1.0.0",
2978
+
"micromark-factory-space": "^2.0.0",
2979
+
"micromark-util-character": "^2.0.0",
2980
+
"micromark-util-symbol": "^2.0.0",
2981
+
"micromark-util-types": "^2.0.0"
2982
+
},
2983
+
"funding": {
2984
+
"type": "opencollective",
2985
+
"url": "https://opencollective.com/unified"
2986
+
}
2987
+
},
2988
+
"node_modules/micromark-factory-destination": {
2989
+
"version": "2.0.1",
2990
+
"funding": [
2991
+
{
2992
+
"type": "GitHub Sponsors",
2993
+
"url": "https://github.com/sponsors/unifiedjs"
2994
+
},
2995
+
{
2996
+
"type": "OpenCollective",
2997
+
"url": "https://opencollective.com/unified"
2998
+
}
2999
+
],
3000
+
"license": "MIT",
3001
+
"dependencies": {
3002
+
"micromark-util-character": "^2.0.0",
3003
+
"micromark-util-symbol": "^2.0.0",
3004
+
"micromark-util-types": "^2.0.0"
3005
+
}
3006
+
},
3007
+
"node_modules/micromark-factory-label": {
3008
+
"version": "2.0.1",
3009
+
"funding": [
3010
+
{
3011
+
"type": "GitHub Sponsors",
3012
+
"url": "https://github.com/sponsors/unifiedjs"
3013
+
},
3014
+
{
3015
+
"type": "OpenCollective",
3016
+
"url": "https://opencollective.com/unified"
3017
+
}
3018
+
],
3019
+
"license": "MIT",
3020
+
"dependencies": {
3021
+
"devlop": "^1.0.0",
3022
+
"micromark-util-character": "^2.0.0",
3023
+
"micromark-util-symbol": "^2.0.0",
3024
+
"micromark-util-types": "^2.0.0"
3025
+
}
3026
+
},
3027
+
"node_modules/micromark-factory-space": {
3028
+
"version": "2.0.1",
3029
+
"funding": [
3030
+
{
3031
+
"type": "GitHub Sponsors",
3032
+
"url": "https://github.com/sponsors/unifiedjs"
3033
+
},
3034
+
{
3035
+
"type": "OpenCollective",
3036
+
"url": "https://opencollective.com/unified"
3037
+
}
3038
+
],
3039
+
"license": "MIT",
3040
+
"dependencies": {
3041
+
"micromark-util-character": "^2.0.0",
3042
+
"micromark-util-types": "^2.0.0"
3043
+
}
3044
+
},
3045
+
"node_modules/micromark-factory-title": {
3046
+
"version": "2.0.1",
3047
+
"funding": [
3048
+
{
3049
+
"type": "GitHub Sponsors",
3050
+
"url": "https://github.com/sponsors/unifiedjs"
3051
+
},
3052
+
{
3053
+
"type": "OpenCollective",
3054
+
"url": "https://opencollective.com/unified"
3055
+
}
3056
+
],
3057
+
"license": "MIT",
3058
+
"dependencies": {
3059
+
"micromark-factory-space": "^2.0.0",
3060
+
"micromark-util-character": "^2.0.0",
3061
+
"micromark-util-symbol": "^2.0.0",
3062
+
"micromark-util-types": "^2.0.0"
3063
+
}
3064
+
},
3065
+
"node_modules/micromark-factory-whitespace": {
3066
+
"version": "2.0.1",
3067
+
"funding": [
3068
+
{
3069
+
"type": "GitHub Sponsors",
3070
+
"url": "https://github.com/sponsors/unifiedjs"
3071
+
},
3072
+
{
3073
+
"type": "OpenCollective",
3074
+
"url": "https://opencollective.com/unified"
3075
+
}
3076
+
],
3077
+
"license": "MIT",
3078
+
"dependencies": {
3079
+
"micromark-factory-space": "^2.0.0",
3080
+
"micromark-util-character": "^2.0.0",
3081
+
"micromark-util-symbol": "^2.0.0",
3082
+
"micromark-util-types": "^2.0.0"
3083
+
}
3084
+
},
3085
+
"node_modules/micromark-util-character": {
3086
+
"version": "2.1.1",
3087
+
"funding": [
3088
+
{
3089
+
"type": "GitHub Sponsors",
3090
+
"url": "https://github.com/sponsors/unifiedjs"
3091
+
},
3092
+
{
3093
+
"type": "OpenCollective",
3094
+
"url": "https://opencollective.com/unified"
3095
+
}
3096
+
],
3097
+
"license": "MIT",
3098
+
"dependencies": {
3099
+
"micromark-util-symbol": "^2.0.0",
3100
+
"micromark-util-types": "^2.0.0"
3101
+
}
3102
+
},
3103
+
"node_modules/micromark-util-chunked": {
3104
+
"version": "2.0.1",
3105
+
"funding": [
3106
+
{
3107
+
"type": "GitHub Sponsors",
3108
+
"url": "https://github.com/sponsors/unifiedjs"
3109
+
},
3110
+
{
3111
+
"type": "OpenCollective",
3112
+
"url": "https://opencollective.com/unified"
3113
+
}
3114
+
],
3115
+
"license": "MIT",
3116
+
"dependencies": {
3117
+
"micromark-util-symbol": "^2.0.0"
3118
+
}
3119
+
},
3120
+
"node_modules/micromark-util-classify-character": {
3121
+
"version": "2.0.1",
3122
+
"funding": [
3123
+
{
3124
+
"type": "GitHub Sponsors",
3125
+
"url": "https://github.com/sponsors/unifiedjs"
3126
+
},
3127
+
{
3128
+
"type": "OpenCollective",
3129
+
"url": "https://opencollective.com/unified"
3130
+
}
3131
+
],
3132
+
"license": "MIT",
3133
+
"dependencies": {
3134
+
"micromark-util-character": "^2.0.0",
3135
+
"micromark-util-symbol": "^2.0.0",
3136
+
"micromark-util-types": "^2.0.0"
3137
+
}
3138
+
},
3139
+
"node_modules/micromark-util-combine-extensions": {
3140
+
"version": "2.0.1",
3141
+
"funding": [
3142
+
{
3143
+
"type": "GitHub Sponsors",
3144
+
"url": "https://github.com/sponsors/unifiedjs"
3145
+
},
3146
+
{
3147
+
"type": "OpenCollective",
3148
+
"url": "https://opencollective.com/unified"
3149
+
}
3150
+
],
3151
+
"license": "MIT",
3152
+
"dependencies": {
3153
+
"micromark-util-chunked": "^2.0.0",
3154
+
"micromark-util-types": "^2.0.0"
3155
+
}
3156
+
},
3157
+
"node_modules/micromark-util-decode-numeric-character-reference": {
3158
+
"version": "2.0.2",
3159
+
"funding": [
3160
+
{
3161
+
"type": "GitHub Sponsors",
3162
+
"url": "https://github.com/sponsors/unifiedjs"
3163
+
},
3164
+
{
3165
+
"type": "OpenCollective",
3166
+
"url": "https://opencollective.com/unified"
3167
+
}
3168
+
],
3169
+
"license": "MIT",
3170
+
"dependencies": {
3171
+
"micromark-util-symbol": "^2.0.0"
3172
+
}
3173
+
},
3174
+
"node_modules/micromark-util-decode-string": {
3175
+
"version": "2.0.1",
3176
+
"funding": [
3177
+
{
3178
+
"type": "GitHub Sponsors",
3179
+
"url": "https://github.com/sponsors/unifiedjs"
3180
+
},
3181
+
{
3182
+
"type": "OpenCollective",
3183
+
"url": "https://opencollective.com/unified"
3184
+
}
3185
+
],
3186
+
"license": "MIT",
3187
+
"dependencies": {
3188
+
"decode-named-character-reference": "^1.0.0",
3189
+
"micromark-util-character": "^2.0.0",
3190
+
"micromark-util-decode-numeric-character-reference": "^2.0.0",
3191
+
"micromark-util-symbol": "^2.0.0"
3192
+
}
3193
+
},
3194
+
"node_modules/micromark-util-encode": {
3195
+
"version": "2.0.1",
3196
+
"funding": [
3197
+
{
3198
+
"type": "GitHub Sponsors",
3199
+
"url": "https://github.com/sponsors/unifiedjs"
3200
+
},
3201
+
{
3202
+
"type": "OpenCollective",
3203
+
"url": "https://opencollective.com/unified"
3204
+
}
3205
+
],
3206
+
"license": "MIT"
3207
+
},
3208
+
"node_modules/micromark-util-html-tag-name": {
3209
+
"version": "2.0.1",
3210
+
"funding": [
3211
+
{
3212
+
"type": "GitHub Sponsors",
3213
+
"url": "https://github.com/sponsors/unifiedjs"
3214
+
},
3215
+
{
3216
+
"type": "OpenCollective",
3217
+
"url": "https://opencollective.com/unified"
3218
+
}
3219
+
],
3220
+
"license": "MIT"
3221
+
},
3222
+
"node_modules/micromark-util-normalize-identifier": {
3223
+
"version": "2.0.1",
3224
+
"funding": [
3225
+
{
3226
+
"type": "GitHub Sponsors",
3227
+
"url": "https://github.com/sponsors/unifiedjs"
3228
+
},
3229
+
{
3230
+
"type": "OpenCollective",
3231
+
"url": "https://opencollective.com/unified"
3232
+
}
3233
+
],
3234
+
"license": "MIT",
3235
+
"dependencies": {
3236
+
"micromark-util-symbol": "^2.0.0"
3237
+
}
3238
+
},
3239
+
"node_modules/micromark-util-resolve-all": {
3240
+
"version": "2.0.1",
3241
+
"funding": [
3242
+
{
3243
+
"type": "GitHub Sponsors",
3244
+
"url": "https://github.com/sponsors/unifiedjs"
3245
+
},
3246
+
{
3247
+
"type": "OpenCollective",
3248
+
"url": "https://opencollective.com/unified"
3249
+
}
3250
+
],
3251
+
"license": "MIT",
3252
+
"dependencies": {
3253
+
"micromark-util-types": "^2.0.0"
3254
+
}
3255
+
},
3256
+
"node_modules/micromark-util-sanitize-uri": {
3257
+
"version": "2.0.1",
3258
+
"funding": [
3259
+
{
3260
+
"type": "GitHub Sponsors",
3261
+
"url": "https://github.com/sponsors/unifiedjs"
3262
+
},
3263
+
{
3264
+
"type": "OpenCollective",
3265
+
"url": "https://opencollective.com/unified"
3266
+
}
3267
+
],
3268
+
"license": "MIT",
3269
+
"dependencies": {
3270
+
"micromark-util-character": "^2.0.0",
3271
+
"micromark-util-encode": "^2.0.0",
3272
+
"micromark-util-symbol": "^2.0.0"
3273
+
}
3274
+
},
3275
+
"node_modules/micromark-util-subtokenize": {
3276
+
"version": "2.0.3",
3277
+
"funding": [
3278
+
{
3279
+
"type": "GitHub Sponsors",
3280
+
"url": "https://github.com/sponsors/unifiedjs"
3281
+
},
3282
+
{
3283
+
"type": "OpenCollective",
3284
+
"url": "https://opencollective.com/unified"
3285
+
}
3286
+
],
3287
+
"license": "MIT",
3288
+
"dependencies": {
3289
+
"devlop": "^1.0.0",
3290
+
"micromark-util-chunked": "^2.0.0",
3291
+
"micromark-util-symbol": "^2.0.0",
3292
+
"micromark-util-types": "^2.0.0"
3293
+
}
3294
+
},
3295
+
"node_modules/micromark-util-symbol": {
3296
+
"version": "2.0.1",
3297
+
"funding": [
3298
+
{
3299
+
"type": "GitHub Sponsors",
3300
+
"url": "https://github.com/sponsors/unifiedjs"
3301
+
},
3302
+
{
3303
+
"type": "OpenCollective",
3304
+
"url": "https://opencollective.com/unified"
3305
+
}
3306
+
],
3307
+
"license": "MIT"
3308
+
},
3309
+
"node_modules/micromark-util-types": {
3310
+
"version": "2.0.1",
3311
+
"funding": [
3312
+
{
3313
+
"type": "GitHub Sponsors",
3314
+
"url": "https://github.com/sponsors/unifiedjs"
3315
+
},
3316
+
{
3317
+
"type": "OpenCollective",
3318
+
"url": "https://opencollective.com/unified"
3319
+
}
3320
+
],
3321
+
"license": "MIT"
3322
+
},
3323
+
"node_modules/micromatch": {
3324
+
"version": "4.0.8",
3325
+
"dev": true,
3326
+
"license": "MIT",
3327
+
"dependencies": {
3328
+
"braces": "^3.0.3",
3329
+
"picomatch": "^2.3.1"
3330
+
},
3331
+
"engines": {
3332
+
"node": ">=8.6"
3333
+
}
3334
+
},
3335
+
"node_modules/micromatch/node_modules/picomatch": {
3336
+
"version": "2.3.1",
3337
+
"dev": true,
3338
+
"license": "MIT",
3339
+
"engines": {
3340
+
"node": ">=8.6"
3341
+
},
3342
+
"funding": {
3343
+
"url": "https://github.com/sponsors/jonschlinkert"
3344
+
}
3345
+
},
3346
+
"node_modules/mini-svg-data-uri": {
3347
+
"version": "1.4.4",
3348
+
"dev": true,
3349
+
"license": "MIT",
3350
+
"bin": {
3351
+
"mini-svg-data-uri": "cli.js"
3352
+
}
3353
+
},
3354
+
"node_modules/minimatch": {
3355
+
"version": "3.1.2",
3356
+
"dev": true,
3357
+
"license": "ISC",
3358
+
"dependencies": {
3359
+
"brace-expansion": "^1.1.7"
3360
+
},
3361
+
"engines": {
3362
+
"node": "*"
3363
+
}
3364
+
},
3365
+
"node_modules/minipass": {
3366
+
"version": "7.1.2",
3367
+
"dev": true,
3368
+
"license": "ISC",
3369
+
"engines": {
3370
+
"node": ">=16 || 14 >=14.17"
3371
+
}
3372
+
},
3373
+
"node_modules/minizlib": {
3374
+
"version": "3.0.2",
3375
+
"dev": true,
3376
+
"license": "MIT",
3377
+
"dependencies": {
3378
+
"minipass": "^7.1.2"
3379
+
},
3380
+
"engines": {
3381
+
"node": ">= 18"
3382
+
}
3383
+
},
3384
+
"node_modules/mkdirp": {
3385
+
"version": "3.0.1",
3386
+
"dev": true,
3387
+
"license": "MIT",
3388
+
"bin": {
3389
+
"mkdirp": "dist/cjs/src/bin.js"
3390
+
},
3391
+
"engines": {
3392
+
"node": ">=10"
3393
+
},
3394
+
"funding": {
3395
+
"url": "https://github.com/sponsors/isaacs"
3396
+
}
3397
+
},
3398
+
"node_modules/mri": {
3399
+
"version": "1.2.0",
3400
+
"dev": true,
3401
+
"license": "MIT",
3402
+
"engines": {
3403
+
"node": ">=4"
3404
+
}
3405
+
},
3406
+
"node_modules/mrmime": {
3407
+
"version": "2.0.0",
3408
+
"dev": true,
3409
+
"license": "MIT",
3410
+
"engines": {
3411
+
"node": ">=10"
3412
+
}
3413
+
},
3414
+
"node_modules/ms": {
3415
+
"version": "2.1.3",
3416
+
"license": "MIT"
3417
+
},
3418
+
"node_modules/mz": {
3419
+
"version": "2.7.0",
3420
+
"dev": true,
3421
+
"license": "MIT",
3422
+
"dependencies": {
3423
+
"any-promise": "^1.0.0",
3424
+
"object-assign": "^4.0.1",
3425
+
"thenify-all": "^1.0.0"
3426
+
}
3427
+
},
3428
+
"node_modules/nanoid": {
3429
+
"version": "3.3.11",
3430
+
"funding": [
3431
+
{
3432
+
"type": "github",
3433
+
"url": "https://github.com/sponsors/ai"
3434
+
}
3435
+
],
3436
+
"license": "MIT",
3437
+
"bin": {
3438
+
"nanoid": "bin/nanoid.cjs"
3439
+
},
3440
+
"engines": {
3441
+
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
3442
+
}
3443
+
},
3444
+
"node_modules/natural-compare": {
3445
+
"version": "1.4.0",
3446
+
"dev": true,
3447
+
"license": "MIT"
3448
+
},
3449
+
"node_modules/node-fetch": {
3450
+
"version": "2.7.0",
3451
+
"dev": true,
3452
+
"license": "MIT",
3453
+
"dependencies": {
3454
+
"whatwg-url": "^5.0.0"
3455
+
},
3456
+
"engines": {
3457
+
"node": "4.x || >=6.0.0"
3458
+
},
3459
+
"peerDependencies": {
3460
+
"encoding": "^0.1.0"
3461
+
},
3462
+
"peerDependenciesMeta": {
3463
+
"encoding": {
3464
+
"optional": true
3465
+
}
3466
+
}
3467
+
},
3468
+
"node_modules/node-gyp-build": {
3469
+
"version": "4.8.4",
3470
+
"dev": true,
3471
+
"license": "MIT",
3472
+
"bin": {
3473
+
"node-gyp-build": "bin.js",
3474
+
"node-gyp-build-optional": "optional.js",
3475
+
"node-gyp-build-test": "build-test.js"
3476
+
}
3477
+
},
3478
+
"node_modules/node-releases": {
3479
+
"version": "2.0.19",
3480
+
"dev": true,
3481
+
"license": "MIT"
3482
+
},
3483
+
"node_modules/nopt": {
3484
+
"version": "8.1.0",
3485
+
"dev": true,
3486
+
"license": "ISC",
3487
+
"dependencies": {
3488
+
"abbrev": "^3.0.0"
3489
+
},
3490
+
"bin": {
3491
+
"nopt": "bin/nopt.js"
3492
+
},
3493
+
"engines": {
3494
+
"node": "^18.17.0 || >=20.5.0"
3495
+
}
3496
+
},
3497
+
"node_modules/normalize-path": {
3498
+
"version": "3.0.0",
3499
+
"dev": true,
3500
+
"license": "MIT",
3501
+
"engines": {
3502
+
"node": ">=0.10.0"
3503
+
}
3504
+
},
3505
+
"node_modules/normalize-range": {
3506
+
"version": "0.1.2",
3507
+
"dev": true,
3508
+
"license": "MIT",
3509
+
"engines": {
3510
+
"node": ">=0.10.0"
3511
+
}
3512
+
},
3513
+
"node_modules/object-assign": {
3514
+
"version": "4.1.1",
3515
+
"dev": true,
3516
+
"license": "MIT",
3517
+
"engines": {
3518
+
"node": ">=0.10.0"
3519
+
}
3520
+
},
3521
+
"node_modules/object-hash": {
3522
+
"version": "3.0.0",
3523
+
"dev": true,
3524
+
"license": "MIT",
3525
+
"engines": {
3526
+
"node": ">= 6"
3527
+
}
3528
+
},
3529
+
"node_modules/optionator": {
3530
+
"version": "0.9.4",
3531
+
"dev": true,
3532
+
"license": "MIT",
3533
+
"dependencies": {
3534
+
"deep-is": "^0.1.3",
3535
+
"fast-levenshtein": "^2.0.6",
3536
+
"levn": "^0.4.1",
3537
+
"prelude-ls": "^1.2.1",
3538
+
"type-check": "^0.4.0",
3539
+
"word-wrap": "^1.2.5"
3540
+
},
3541
+
"engines": {
3542
+
"node": ">= 0.8.0"
3543
+
}
3544
+
},
3545
+
"node_modules/p-limit": {
3546
+
"version": "3.1.0",
3547
+
"dev": true,
3548
+
"license": "MIT",
3549
+
"dependencies": {
3550
+
"yocto-queue": "^0.1.0"
3551
+
},
3552
+
"engines": {
3553
+
"node": ">=10"
3554
+
},
3555
+
"funding": {
3556
+
"url": "https://github.com/sponsors/sindresorhus"
3557
+
}
3558
+
},
3559
+
"node_modules/p-locate": {
3560
+
"version": "5.0.0",
3561
+
"dev": true,
3562
+
"license": "MIT",
3563
+
"dependencies": {
3564
+
"p-limit": "^3.0.2"
3565
+
},
3566
+
"engines": {
3567
+
"node": ">=10"
3568
+
},
3569
+
"funding": {
3570
+
"url": "https://github.com/sponsors/sindresorhus"
3571
+
}
3572
+
},
3573
+
"node_modules/package-json-from-dist": {
3574
+
"version": "1.0.1",
3575
+
"dev": true,
3576
+
"license": "BlueOak-1.0.0"
3577
+
},
3578
+
"node_modules/pako": {
3579
+
"version": "0.2.9",
3580
+
"license": "MIT"
3581
+
},
3582
+
"node_modules/parent-module": {
3583
+
"version": "1.0.1",
3584
+
"dev": true,
3585
+
"license": "MIT",
3586
+
"dependencies": {
3587
+
"callsites": "^3.0.0"
3588
+
},
3589
+
"engines": {
3590
+
"node": ">=6"
3591
+
}
3592
+
},
3593
+
"node_modules/parse-css-color": {
3594
+
"version": "0.2.1",
3595
+
"license": "MIT",
3596
+
"dependencies": {
3597
+
"color-name": "^1.1.4",
3598
+
"hex-rgb": "^4.1.0"
3599
+
}
3600
+
},
3601
+
"node_modules/parse-srcset": {
3602
+
"version": "1.0.2",
3603
+
"license": "MIT"
3604
+
},
3605
+
"node_modules/parse5": {
3606
+
"version": "7.2.1",
3607
+
"license": "MIT",
3608
+
"dependencies": {
3609
+
"entities": "^4.5.0"
3610
+
},
3611
+
"funding": {
3612
+
"url": "https://github.com/inikulin/parse5?sponsor=1"
3613
+
}
3614
+
},
3615
+
"node_modules/path-exists": {
3616
+
"version": "4.0.0",
3617
+
"dev": true,
3618
+
"license": "MIT",
3619
+
"engines": {
3620
+
"node": ">=8"
3621
+
}
3622
+
},
3623
+
"node_modules/path-key": {
3624
+
"version": "3.1.1",
3625
+
"dev": true,
3626
+
"license": "MIT",
3627
+
"engines": {
3628
+
"node": ">=8"
3629
+
}
3630
+
},
3631
+
"node_modules/path-parse": {
3632
+
"version": "1.0.7",
3633
+
"license": "MIT"
3634
+
},
3635
+
"node_modules/path-scurry": {
3636
+
"version": "1.11.1",
3637
+
"dev": true,
3638
+
"license": "BlueOak-1.0.0",
3639
+
"dependencies": {
3640
+
"lru-cache": "^10.2.0",
3641
+
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
3642
+
},
3643
+
"engines": {
3644
+
"node": ">=16 || 14 >=14.18"
3645
+
},
3646
+
"funding": {
3647
+
"url": "https://github.com/sponsors/isaacs"
3648
+
}
3649
+
},
3650
+
"node_modules/picocolors": {
3651
+
"version": "1.1.1",
3652
+
"license": "ISC"
3653
+
},
3654
+
"node_modules/picomatch": {
3655
+
"version": "4.0.2",
3656
+
"dev": true,
3657
+
"license": "MIT",
3658
+
"engines": {
3659
+
"node": ">=12"
3660
+
},
3661
+
"funding": {
3662
+
"url": "https://github.com/sponsors/jonschlinkert"
3663
+
}
3664
+
},
3665
+
"node_modules/pify": {
3666
+
"version": "2.3.0",
3667
+
"license": "MIT",
3668
+
"engines": {
3669
+
"node": ">=0.10.0"
3670
+
}
3671
+
},
3672
+
"node_modules/pirates": {
3673
+
"version": "4.0.6",
3674
+
"dev": true,
3675
+
"license": "MIT",
3676
+
"engines": {
3677
+
"node": ">= 6"
3678
+
}
3679
+
},
3680
+
"node_modules/postcss": {
3681
+
"version": "8.5.6",
3682
+
"funding": [
3683
+
{
3684
+
"type": "opencollective",
3685
+
"url": "https://opencollective.com/postcss/"
3686
+
},
3687
+
{
3688
+
"type": "tidelift",
3689
+
"url": "https://tidelift.com/funding/github/npm/postcss"
3690
+
},
3691
+
{
3692
+
"type": "github",
3693
+
"url": "https://github.com/sponsors/ai"
3694
+
}
3695
+
],
3696
+
"license": "MIT",
3697
+
"dependencies": {
3698
+
"nanoid": "^3.3.11",
3699
+
"picocolors": "^1.1.1",
3700
+
"source-map-js": "^1.2.1"
3701
+
},
3702
+
"engines": {
3703
+
"node": "^10 || ^12 || >=14"
3704
+
}
3705
+
},
3706
+
"node_modules/postcss-import": {
3707
+
"version": "16.1.1",
3708
+
"license": "MIT",
3709
+
"dependencies": {
3710
+
"postcss-value-parser": "^4.0.0",
3711
+
"read-cache": "^1.0.0",
3712
+
"resolve": "^1.1.7"
3713
+
},
3714
+
"engines": {
3715
+
"node": ">=18.0.0"
3716
+
},
3717
+
"peerDependencies": {
3718
+
"postcss": "^8.0.0"
3719
+
}
3720
+
},
3721
+
"node_modules/postcss-js": {
3722
+
"version": "4.0.1",
3723
+
"dev": true,
3724
+
"license": "MIT",
3725
+
"dependencies": {
3726
+
"camelcase-css": "^2.0.1"
3727
+
},
3728
+
"engines": {
3729
+
"node": "^12 || ^14 || >= 16"
3730
+
},
3731
+
"funding": {
3732
+
"type": "opencollective",
3733
+
"url": "https://opencollective.com/postcss/"
3734
+
},
3735
+
"peerDependencies": {
3736
+
"postcss": "^8.4.21"
3737
+
}
3738
+
},
3739
+
"node_modules/postcss-load-config": {
3740
+
"version": "3.1.4",
3741
+
"dev": true,
3742
+
"license": "MIT",
3743
+
"dependencies": {
3744
+
"lilconfig": "^2.0.5",
3745
+
"yaml": "^1.10.2"
3746
+
},
3747
+
"engines": {
3748
+
"node": ">= 10"
3749
+
},
3750
+
"funding": {
3751
+
"type": "opencollective",
3752
+
"url": "https://opencollective.com/postcss/"
3753
+
},
3754
+
"peerDependencies": {
3755
+
"postcss": ">=8.0.9",
3756
+
"ts-node": ">=9.0.0"
3757
+
},
3758
+
"peerDependenciesMeta": {
3759
+
"postcss": {
3760
+
"optional": true
3761
+
},
3762
+
"ts-node": {
3763
+
"optional": true
3764
+
}
3765
+
}
3766
+
},
3767
+
"node_modules/postcss-nested": {
3768
+
"version": "6.2.0",
3769
+
"dev": true,
3770
+
"funding": [
3771
+
{
3772
+
"type": "opencollective",
3773
+
"url": "https://opencollective.com/postcss/"
3774
+
},
3775
+
{
3776
+
"type": "github",
3777
+
"url": "https://github.com/sponsors/ai"
3778
+
}
3779
+
],
3780
+
"license": "MIT",
3781
+
"dependencies": {
3782
+
"postcss-selector-parser": "^6.1.1"
3783
+
},
3784
+
"engines": {
3785
+
"node": ">=12.0"
3786
+
},
3787
+
"peerDependencies": {
3788
+
"postcss": "^8.2.14"
3789
+
}
3790
+
},
3791
+
"node_modules/postcss-safe-parser": {
3792
+
"version": "6.0.0",
3793
+
"dev": true,
3794
+
"license": "MIT",
3795
+
"engines": {
3796
+
"node": ">=12.0"
3797
+
},
3798
+
"funding": {
3799
+
"type": "opencollective",
3800
+
"url": "https://opencollective.com/postcss/"
3801
+
},
3802
+
"peerDependencies": {
3803
+
"postcss": "^8.3.3"
3804
+
}
3805
+
},
3806
+
"node_modules/postcss-scss": {
3807
+
"version": "4.0.9",
3808
+
"dev": true,
3809
+
"funding": [
3810
+
{
3811
+
"type": "opencollective",
3812
+
"url": "https://opencollective.com/postcss/"
3813
+
},
3814
+
{
3815
+
"type": "tidelift",
3816
+
"url": "https://tidelift.com/funding/github/npm/postcss-scss"
3817
+
},
3818
+
{
3819
+
"type": "github",
3820
+
"url": "https://github.com/sponsors/ai"
3821
+
}
3822
+
],
3823
+
"license": "MIT",
3824
+
"engines": {
3825
+
"node": ">=12.0"
3826
+
},
3827
+
"peerDependencies": {
3828
+
"postcss": "^8.4.29"
3829
+
}
3830
+
},
3831
+
"node_modules/postcss-selector-parser": {
3832
+
"version": "6.1.2",
3833
+
"license": "MIT",
3834
+
"dependencies": {
3835
+
"cssesc": "^3.0.0",
3836
+
"util-deprecate": "^1.0.2"
3837
+
},
3838
+
"engines": {
3839
+
"node": ">=4"
3840
+
}
3841
+
},
3842
+
"node_modules/postcss-value-parser": {
3843
+
"version": "4.2.0",
3844
+
"license": "MIT"
3845
+
},
3846
+
"node_modules/prelude-ls": {
3847
+
"version": "1.2.1",
3848
+
"dev": true,
3849
+
"license": "MIT",
3850
+
"engines": {
3851
+
"node": ">= 0.8.0"
3852
+
}
3853
+
},
3854
+
"node_modules/property-information": {
3855
+
"version": "6.5.0",
3856
+
"license": "MIT",
3857
+
"funding": {
3858
+
"type": "github",
3859
+
"url": "https://github.com/sponsors/wooorm"
3860
+
}
3861
+
},
3862
+
"node_modules/punycode": {
3863
+
"version": "2.3.1",
3864
+
"dev": true,
3865
+
"license": "MIT",
3866
+
"engines": {
3867
+
"node": ">=6"
3868
+
}
3869
+
},
3870
+
"node_modules/queue-microtask": {
3871
+
"version": "1.2.3",
3872
+
"dev": true,
3873
+
"funding": [
3874
+
{
3875
+
"type": "github",
3876
+
"url": "https://github.com/sponsors/feross"
3877
+
},
3878
+
{
3879
+
"type": "patreon",
3880
+
"url": "https://www.patreon.com/feross"
3881
+
},
3882
+
{
3883
+
"type": "consulting",
3884
+
"url": "https://feross.org/support"
3885
+
}
3886
+
],
3887
+
"license": "MIT"
3888
+
},
3889
+
"node_modules/read-cache": {
3890
+
"version": "1.0.0",
3891
+
"license": "MIT",
3892
+
"dependencies": {
3893
+
"pify": "^2.3.0"
3894
+
}
3895
+
},
3896
+
"node_modules/readdirp": {
3897
+
"version": "4.0.2",
3898
+
"dev": true,
3899
+
"license": "MIT",
3900
+
"engines": {
3901
+
"node": ">= 14.16.0"
3902
+
},
3903
+
"funding": {
3904
+
"type": "individual",
3905
+
"url": "https://paulmillr.com/funding/"
3906
+
}
3907
+
},
3908
+
"node_modules/rehype-raw": {
3909
+
"version": "7.0.0",
3910
+
"license": "MIT",
3911
+
"dependencies": {
3912
+
"@types/hast": "^3.0.0",
3913
+
"hast-util-raw": "^9.0.0",
3914
+
"vfile": "^6.0.0"
3915
+
},
3916
+
"funding": {
3917
+
"type": "opencollective",
3918
+
"url": "https://opencollective.com/unified"
3919
+
}
3920
+
},
3921
+
"node_modules/rehype-sanitize": {
3922
+
"version": "6.0.0",
3923
+
"license": "MIT",
3924
+
"dependencies": {
3925
+
"@types/hast": "^3.0.0",
3926
+
"hast-util-sanitize": "^5.0.0"
3927
+
},
3928
+
"funding": {
3929
+
"type": "opencollective",
3930
+
"url": "https://opencollective.com/unified"
3931
+
}
3932
+
},
3933
+
"node_modules/rehype-stringify": {
3934
+
"version": "10.0.1",
3935
+
"license": "MIT",
3936
+
"dependencies": {
3937
+
"@types/hast": "^3.0.0",
3938
+
"hast-util-to-html": "^9.0.0",
3939
+
"unified": "^11.0.0"
3940
+
},
3941
+
"funding": {
3942
+
"type": "opencollective",
3943
+
"url": "https://opencollective.com/unified"
3944
+
}
3945
+
},
3946
+
"node_modules/remark": {
3947
+
"version": "15.0.1",
3948
+
"license": "MIT",
3949
+
"dependencies": {
3950
+
"@types/mdast": "^4.0.0",
3951
+
"remark-parse": "^11.0.0",
3952
+
"remark-stringify": "^11.0.0",
3953
+
"unified": "^11.0.0"
3954
+
},
3955
+
"funding": {
3956
+
"type": "opencollective",
3957
+
"url": "https://opencollective.com/unified"
3958
+
}
3959
+
},
3960
+
"node_modules/remark-gfm": {
3961
+
"version": "4.0.0",
3962
+
"license": "MIT",
3963
+
"dependencies": {
3964
+
"@types/mdast": "^4.0.0",
3965
+
"mdast-util-gfm": "^3.0.0",
3966
+
"micromark-extension-gfm": "^3.0.0",
3967
+
"remark-parse": "^11.0.0",
3968
+
"remark-stringify": "^11.0.0",
3969
+
"unified": "^11.0.0"
3970
+
},
3971
+
"funding": {
3972
+
"type": "opencollective",
3973
+
"url": "https://opencollective.com/unified"
3974
+
}
3975
+
},
3976
+
"node_modules/remark-parse": {
3977
+
"version": "11.0.0",
3978
+
"license": "MIT",
3979
+
"dependencies": {
3980
+
"@types/mdast": "^4.0.0",
3981
+
"mdast-util-from-markdown": "^2.0.0",
3982
+
"micromark-util-types": "^2.0.0",
3983
+
"unified": "^11.0.0"
3984
+
},
3985
+
"funding": {
3986
+
"type": "opencollective",
3987
+
"url": "https://opencollective.com/unified"
3988
+
}
3989
+
},
3990
+
"node_modules/remark-rehype": {
3991
+
"version": "11.1.1",
3992
+
"license": "MIT",
3993
+
"dependencies": {
3994
+
"@types/hast": "^3.0.0",
3995
+
"@types/mdast": "^4.0.0",
3996
+
"mdast-util-to-hast": "^13.0.0",
3997
+
"unified": "^11.0.0",
3998
+
"vfile": "^6.0.0"
3999
+
},
4000
+
"funding": {
4001
+
"type": "opencollective",
4002
+
"url": "https://opencollective.com/unified"
4003
+
}
4004
+
},
4005
+
"node_modules/remark-stringify": {
4006
+
"version": "11.0.0",
4007
+
"license": "MIT",
4008
+
"dependencies": {
4009
+
"@types/mdast": "^4.0.0",
4010
+
"mdast-util-to-markdown": "^2.0.0",
4011
+
"unified": "^11.0.0"
4012
+
},
4013
+
"funding": {
4014
+
"type": "opencollective",
4015
+
"url": "https://opencollective.com/unified"
4016
+
}
4017
+
},
4018
+
"node_modules/resolve": {
4019
+
"version": "1.22.8",
4020
+
"license": "MIT",
4021
+
"dependencies": {
4022
+
"is-core-module": "^2.13.0",
4023
+
"path-parse": "^1.0.7",
4024
+
"supports-preserve-symlinks-flag": "^1.0.0"
4025
+
},
4026
+
"bin": {
4027
+
"resolve": "bin/resolve"
4028
+
},
4029
+
"funding": {
4030
+
"url": "https://github.com/sponsors/ljharb"
4031
+
}
4032
+
},
4033
+
"node_modules/resolve-from": {
4034
+
"version": "4.0.0",
4035
+
"dev": true,
4036
+
"license": "MIT",
4037
+
"engines": {
4038
+
"node": ">=4"
4039
+
}
4040
+
},
4041
+
"node_modules/reusify": {
4042
+
"version": "1.0.4",
4043
+
"dev": true,
4044
+
"license": "MIT",
4045
+
"engines": {
4046
+
"iojs": ">=1.0.0",
4047
+
"node": ">=0.10.0"
4048
+
}
4049
+
},
4050
+
"node_modules/rollup": {
4051
+
"version": "4.28.0",
4052
+
"dev": true,
4053
+
"license": "MIT",
4054
+
"dependencies": {
4055
+
"@types/estree": "1.0.6"
4056
+
},
4057
+
"bin": {
4058
+
"rollup": "dist/bin/rollup"
4059
+
},
4060
+
"engines": {
4061
+
"node": ">=18.0.0",
4062
+
"npm": ">=8.0.0"
4063
+
},
4064
+
"optionalDependencies": {
4065
+
"@rollup/rollup-android-arm-eabi": "4.28.0",
4066
+
"@rollup/rollup-android-arm64": "4.28.0",
4067
+
"@rollup/rollup-darwin-arm64": "4.28.0",
4068
+
"@rollup/rollup-darwin-x64": "4.28.0",
4069
+
"@rollup/rollup-freebsd-arm64": "4.28.0",
4070
+
"@rollup/rollup-freebsd-x64": "4.28.0",
4071
+
"@rollup/rollup-linux-arm-gnueabihf": "4.28.0",
4072
+
"@rollup/rollup-linux-arm-musleabihf": "4.28.0",
4073
+
"@rollup/rollup-linux-arm64-gnu": "4.28.0",
4074
+
"@rollup/rollup-linux-arm64-musl": "4.28.0",
4075
+
"@rollup/rollup-linux-powerpc64le-gnu": "4.28.0",
4076
+
"@rollup/rollup-linux-riscv64-gnu": "4.28.0",
4077
+
"@rollup/rollup-linux-s390x-gnu": "4.28.0",
4078
+
"@rollup/rollup-linux-x64-gnu": "4.28.0",
4079
+
"@rollup/rollup-linux-x64-musl": "4.28.0",
4080
+
"@rollup/rollup-win32-arm64-msvc": "4.28.0",
4081
+
"@rollup/rollup-win32-ia32-msvc": "4.28.0",
4082
+
"@rollup/rollup-win32-x64-msvc": "4.28.0",
4083
+
"fsevents": "~2.3.2"
4084
+
}
4085
+
},
4086
+
"node_modules/run-parallel": {
4087
+
"version": "1.2.0",
4088
+
"dev": true,
4089
+
"funding": [
4090
+
{
4091
+
"type": "github",
4092
+
"url": "https://github.com/sponsors/feross"
4093
+
},
4094
+
{
4095
+
"type": "patreon",
4096
+
"url": "https://www.patreon.com/feross"
4097
+
},
4098
+
{
4099
+
"type": "consulting",
4100
+
"url": "https://feross.org/support"
4101
+
}
4102
+
],
4103
+
"license": "MIT",
4104
+
"dependencies": {
4105
+
"queue-microtask": "^1.2.2"
4106
+
}
4107
+
},
4108
+
"node_modules/sade": {
4109
+
"version": "1.8.1",
4110
+
"dev": true,
4111
+
"license": "MIT",
4112
+
"dependencies": {
4113
+
"mri": "^1.1.0"
4114
+
},
4115
+
"engines": {
4116
+
"node": ">=6"
4117
+
}
4118
+
},
4119
+
"node_modules/sanitize-html": {
4120
+
"version": "2.13.1",
4121
+
"license": "MIT",
4122
+
"dependencies": {
4123
+
"deepmerge": "^4.2.2",
4124
+
"escape-string-regexp": "^4.0.0",
4125
+
"htmlparser2": "^8.0.0",
4126
+
"is-plain-object": "^5.0.0",
4127
+
"parse-srcset": "^1.0.2",
4128
+
"postcss": "^8.3.11"
4129
+
}
4130
+
},
4131
+
"node_modules/satori": {
4132
+
"version": "0.12.2",
4133
+
"license": "MPL-2.0",
4134
+
"dependencies": {
4135
+
"@shuding/opentype.js": "1.4.0-beta.0",
4136
+
"css-background-parser": "^0.1.0",
4137
+
"css-box-shadow": "1.0.0-3",
4138
+
"css-gradient-parser": "^0.0.16",
4139
+
"css-to-react-native": "^3.0.0",
4140
+
"emoji-regex": "^10.2.1",
4141
+
"escape-html": "^1.0.3",
4142
+
"linebreak": "^1.1.0",
4143
+
"parse-css-color": "^0.2.1",
4144
+
"postcss-value-parser": "^4.2.0",
4145
+
"yoga-wasm-web": "^0.3.3"
4146
+
},
4147
+
"engines": {
4148
+
"node": ">=16"
4149
+
}
4150
+
},
4151
+
"node_modules/satori/node_modules/emoji-regex": {
4152
+
"version": "10.4.0",
4153
+
"license": "MIT"
4154
+
},
4155
+
"node_modules/semver": {
4156
+
"version": "7.6.3",
4157
+
"dev": true,
4158
+
"license": "ISC",
4159
+
"bin": {
4160
+
"semver": "bin/semver.js"
4161
+
},
4162
+
"engines": {
4163
+
"node": ">=10"
4164
+
}
4165
+
},
4166
+
"node_modules/set-cookie-parser": {
4167
+
"version": "2.7.1",
4168
+
"dev": true,
4169
+
"license": "MIT"
4170
+
},
4171
+
"node_modules/shebang-command": {
4172
+
"version": "2.0.0",
4173
+
"dev": true,
4174
+
"license": "MIT",
4175
+
"dependencies": {
4176
+
"shebang-regex": "^3.0.0"
4177
+
},
4178
+
"engines": {
4179
+
"node": ">=8"
4180
+
}
4181
+
},
4182
+
"node_modules/shebang-regex": {
4183
+
"version": "3.0.0",
4184
+
"dev": true,
4185
+
"license": "MIT",
4186
+
"engines": {
4187
+
"node": ">=8"
4188
+
}
4189
+
},
4190
+
"node_modules/signal-exit": {
4191
+
"version": "4.1.0",
4192
+
"dev": true,
4193
+
"license": "ISC",
4194
+
"engines": {
4195
+
"node": ">=14"
4196
+
},
4197
+
"funding": {
4198
+
"url": "https://github.com/sponsors/isaacs"
4199
+
}
4200
+
},
4201
+
"node_modules/sirv": {
4202
+
"version": "3.0.0",
4203
+
"dev": true,
4204
+
"license": "MIT",
4205
+
"dependencies": {
4206
+
"@polka/url": "^1.0.0-next.24",
4207
+
"mrmime": "^2.0.0",
4208
+
"totalist": "^3.0.0"
4209
+
},
4210
+
"engines": {
4211
+
"node": ">=18"
4212
+
}
4213
+
},
4214
+
"node_modules/source-map-js": {
4215
+
"version": "1.2.1",
4216
+
"license": "BSD-3-Clause",
4217
+
"engines": {
4218
+
"node": ">=0.10.0"
4219
+
}
4220
+
},
4221
+
"node_modules/space-separated-tokens": {
4222
+
"version": "2.0.2",
4223
+
"license": "MIT",
4224
+
"funding": {
4225
+
"type": "github",
4226
+
"url": "https://github.com/sponsors/wooorm"
4227
+
}
4228
+
},
4229
+
"node_modules/string-width": {
4230
+
"version": "5.1.2",
4231
+
"dev": true,
4232
+
"license": "MIT",
4233
+
"dependencies": {
4234
+
"eastasianwidth": "^0.2.0",
4235
+
"emoji-regex": "^9.2.2",
4236
+
"strip-ansi": "^7.0.1"
4237
+
},
4238
+
"engines": {
4239
+
"node": ">=12"
4240
+
},
4241
+
"funding": {
4242
+
"url": "https://github.com/sponsors/sindresorhus"
4243
+
}
4244
+
},
4245
+
"node_modules/string-width-cjs": {
4246
+
"name": "string-width",
4247
+
"version": "4.2.3",
4248
+
"dev": true,
4249
+
"license": "MIT",
4250
+
"dependencies": {
4251
+
"emoji-regex": "^8.0.0",
4252
+
"is-fullwidth-code-point": "^3.0.0",
4253
+
"strip-ansi": "^6.0.1"
4254
+
},
4255
+
"engines": {
4256
+
"node": ">=8"
4257
+
}
4258
+
},
4259
+
"node_modules/string-width-cjs/node_modules/ansi-regex": {
4260
+
"version": "5.0.1",
4261
+
"dev": true,
4262
+
"license": "MIT",
4263
+
"engines": {
4264
+
"node": ">=8"
4265
+
}
4266
+
},
4267
+
"node_modules/string-width-cjs/node_modules/emoji-regex": {
4268
+
"version": "8.0.0",
4269
+
"dev": true,
4270
+
"license": "MIT"
4271
+
},
4272
+
"node_modules/string-width-cjs/node_modules/strip-ansi": {
4273
+
"version": "6.0.1",
4274
+
"dev": true,
4275
+
"license": "MIT",
4276
+
"dependencies": {
4277
+
"ansi-regex": "^5.0.1"
4278
+
},
4279
+
"engines": {
4280
+
"node": ">=8"
4281
+
}
4282
+
},
4283
+
"node_modules/string.prototype.codepointat": {
4284
+
"version": "0.2.1",
4285
+
"license": "MIT"
4286
+
},
4287
+
"node_modules/stringify-entities": {
4288
+
"version": "4.0.4",
4289
+
"license": "MIT",
4290
+
"dependencies": {
4291
+
"character-entities-html4": "^2.0.0",
4292
+
"character-entities-legacy": "^3.0.0"
4293
+
},
4294
+
"funding": {
4295
+
"type": "github",
4296
+
"url": "https://github.com/sponsors/wooorm"
4297
+
}
4298
+
},
4299
+
"node_modules/strip-ansi": {
4300
+
"version": "7.1.0",
4301
+
"dev": true,
4302
+
"license": "MIT",
4303
+
"dependencies": {
4304
+
"ansi-regex": "^6.0.1"
4305
+
},
4306
+
"engines": {
4307
+
"node": ">=12"
4308
+
},
4309
+
"funding": {
4310
+
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
4311
+
}
4312
+
},
4313
+
"node_modules/strip-ansi-cjs": {
4314
+
"name": "strip-ansi",
4315
+
"version": "6.0.1",
4316
+
"dev": true,
4317
+
"license": "MIT",
4318
+
"dependencies": {
4319
+
"ansi-regex": "^5.0.1"
4320
+
},
4321
+
"engines": {
4322
+
"node": ">=8"
4323
+
}
4324
+
},
4325
+
"node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
4326
+
"version": "5.0.1",
4327
+
"dev": true,
4328
+
"license": "MIT",
4329
+
"engines": {
4330
+
"node": ">=8"
4331
+
}
4332
+
},
4333
+
"node_modules/strip-json-comments": {
4334
+
"version": "3.1.1",
4335
+
"dev": true,
4336
+
"license": "MIT",
4337
+
"engines": {
4338
+
"node": ">=8"
4339
+
},
4340
+
"funding": {
4341
+
"url": "https://github.com/sponsors/sindresorhus"
4342
+
}
4343
+
},
4344
+
"node_modules/sucrase": {
4345
+
"version": "3.35.0",
4346
+
"dev": true,
4347
+
"license": "MIT",
4348
+
"dependencies": {
4349
+
"@jridgewell/gen-mapping": "^0.3.2",
4350
+
"commander": "^4.0.0",
4351
+
"glob": "^10.3.10",
4352
+
"lines-and-columns": "^1.1.6",
4353
+
"mz": "^2.7.0",
4354
+
"pirates": "^4.0.1",
4355
+
"ts-interface-checker": "^0.1.9"
4356
+
},
4357
+
"bin": {
4358
+
"sucrase": "bin/sucrase",
4359
+
"sucrase-node": "bin/sucrase-node"
4360
+
},
4361
+
"engines": {
4362
+
"node": ">=16 || 14 >=14.17"
4363
+
}
4364
+
},
4365
+
"node_modules/supports-color": {
4366
+
"version": "7.2.0",
4367
+
"dev": true,
4368
+
"license": "MIT",
4369
+
"dependencies": {
4370
+
"has-flag": "^4.0.0"
4371
+
},
4372
+
"engines": {
4373
+
"node": ">=8"
4374
+
}
4375
+
},
4376
+
"node_modules/supports-preserve-symlinks-flag": {
4377
+
"version": "1.0.0",
4378
+
"license": "MIT",
4379
+
"engines": {
4380
+
"node": ">= 0.4"
4381
+
},
4382
+
"funding": {
4383
+
"url": "https://github.com/sponsors/ljharb"
4384
+
}
4385
+
},
4386
+
"node_modules/svelte": {
4387
+
"version": "5.36.2",
4388
+
"dev": true,
4389
+
"license": "MIT",
4390
+
"dependencies": {
4391
+
"@ampproject/remapping": "^2.3.0",
4392
+
"@jridgewell/sourcemap-codec": "^1.5.0",
4393
+
"@sveltejs/acorn-typescript": "^1.0.5",
4394
+
"@types/estree": "^1.0.5",
4395
+
"acorn": "^8.12.1",
4396
+
"aria-query": "^5.3.1",
4397
+
"axobject-query": "^4.1.0",
4398
+
"clsx": "^2.1.1",
4399
+
"esm-env": "^1.2.1",
4400
+
"esrap": "^2.1.0",
4401
+
"is-reference": "^3.0.3",
4402
+
"locate-character": "^3.0.0",
4403
+
"magic-string": "^0.30.11",
4404
+
"zimmerframe": "^1.1.2"
4405
+
},
4406
+
"engines": {
4407
+
"node": ">=18"
4408
+
}
4409
+
},
4410
+
"node_modules/svelte-check": {
4411
+
"version": "4.2.2",
4412
+
"dev": true,
4413
+
"license": "MIT",
4414
+
"dependencies": {
4415
+
"@jridgewell/trace-mapping": "^0.3.25",
4416
+
"chokidar": "^4.0.1",
4417
+
"fdir": "^6.2.0",
4418
+
"picocolors": "^1.0.0",
4419
+
"sade": "^1.7.4"
4420
+
},
4421
+
"bin": {
4422
+
"svelte-check": "bin/svelte-check"
4423
+
},
4424
+
"engines": {
4425
+
"node": ">= 18.0.0"
4426
+
},
4427
+
"peerDependencies": {
4428
+
"svelte": "^4.0.0 || ^5.0.0-next.0",
4429
+
"typescript": ">=5.0.0"
4430
+
}
4431
+
},
4432
+
"node_modules/svelte-eslint-parser": {
4433
+
"version": "0.43.0",
4434
+
"dev": true,
4435
+
"license": "MIT",
4436
+
"dependencies": {
4437
+
"eslint-scope": "^7.2.2",
4438
+
"eslint-visitor-keys": "^3.4.3",
4439
+
"espree": "^9.6.1",
4440
+
"postcss": "^8.4.39",
4441
+
"postcss-scss": "^4.0.9"
4442
+
},
4443
+
"engines": {
4444
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
4445
+
},
4446
+
"funding": {
4447
+
"url": "https://github.com/sponsors/ota-meshi"
4448
+
},
4449
+
"peerDependencies": {
4450
+
"svelte": "^3.37.0 || ^4.0.0 || ^5.0.0"
4451
+
},
4452
+
"peerDependenciesMeta": {
4453
+
"svelte": {
4454
+
"optional": true
4455
+
}
4456
+
}
4457
+
},
4458
+
"node_modules/svelte-eslint-parser/node_modules/eslint-scope": {
4459
+
"version": "7.2.2",
4460
+
"dev": true,
4461
+
"license": "BSD-2-Clause",
4462
+
"dependencies": {
4463
+
"esrecurse": "^4.3.0",
4464
+
"estraverse": "^5.2.0"
4465
+
},
4466
+
"engines": {
4467
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
4468
+
},
4469
+
"funding": {
4470
+
"url": "https://opencollective.com/eslint"
4471
+
}
4472
+
},
4473
+
"node_modules/svelte-eslint-parser/node_modules/eslint-visitor-keys": {
4474
+
"version": "3.4.3",
4475
+
"dev": true,
4476
+
"license": "Apache-2.0",
4477
+
"engines": {
4478
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
4479
+
},
4480
+
"funding": {
4481
+
"url": "https://opencollective.com/eslint"
4482
+
}
4483
+
},
4484
+
"node_modules/svelte-eslint-parser/node_modules/espree": {
4485
+
"version": "9.6.1",
4486
+
"dev": true,
4487
+
"license": "BSD-2-Clause",
4488
+
"dependencies": {
4489
+
"acorn": "^8.9.0",
4490
+
"acorn-jsx": "^5.3.2",
4491
+
"eslint-visitor-keys": "^3.4.1"
4492
+
},
4493
+
"engines": {
4494
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
4495
+
},
4496
+
"funding": {
4497
+
"url": "https://opencollective.com/eslint"
4498
+
}
4499
+
},
4500
+
"node_modules/tailwindcss": {
4501
+
"version": "3.4.17",
4502
+
"dev": true,
4503
+
"license": "MIT",
4504
+
"dependencies": {
4505
+
"@alloc/quick-lru": "^5.2.0",
4506
+
"arg": "^5.0.2",
4507
+
"chokidar": "^3.6.0",
4508
+
"didyoumean": "^1.2.2",
4509
+
"dlv": "^1.1.3",
4510
+
"fast-glob": "^3.3.2",
4511
+
"glob-parent": "^6.0.2",
4512
+
"is-glob": "^4.0.3",
4513
+
"jiti": "^1.21.6",
4514
+
"lilconfig": "^3.1.3",
4515
+
"micromatch": "^4.0.8",
4516
+
"normalize-path": "^3.0.0",
4517
+
"object-hash": "^3.0.0",
4518
+
"picocolors": "^1.1.1",
4519
+
"postcss": "^8.4.47",
4520
+
"postcss-import": "^15.1.0",
4521
+
"postcss-js": "^4.0.1",
4522
+
"postcss-load-config": "^4.0.2",
4523
+
"postcss-nested": "^6.2.0",
4524
+
"postcss-selector-parser": "^6.1.2",
4525
+
"resolve": "^1.22.8",
4526
+
"sucrase": "^3.35.0"
4527
+
},
4528
+
"bin": {
4529
+
"tailwind": "lib/cli.js",
4530
+
"tailwindcss": "lib/cli.js"
4531
+
},
4532
+
"engines": {
4533
+
"node": ">=14.0.0"
4534
+
}
4535
+
},
4536
+
"node_modules/tailwindcss/node_modules/chokidar": {
4537
+
"version": "3.6.0",
4538
+
"dev": true,
4539
+
"license": "MIT",
4540
+
"dependencies": {
4541
+
"anymatch": "~3.1.2",
4542
+
"braces": "~3.0.2",
4543
+
"glob-parent": "~5.1.2",
4544
+
"is-binary-path": "~2.1.0",
4545
+
"is-glob": "~4.0.1",
4546
+
"normalize-path": "~3.0.0",
4547
+
"readdirp": "~3.6.0"
4548
+
},
4549
+
"engines": {
4550
+
"node": ">= 8.10.0"
4551
+
},
4552
+
"funding": {
4553
+
"url": "https://paulmillr.com/funding/"
4554
+
},
4555
+
"optionalDependencies": {
4556
+
"fsevents": "~2.3.2"
4557
+
}
4558
+
},
4559
+
"node_modules/tailwindcss/node_modules/chokidar/node_modules/glob-parent": {
4560
+
"version": "5.1.2",
4561
+
"dev": true,
4562
+
"license": "ISC",
4563
+
"dependencies": {
4564
+
"is-glob": "^4.0.1"
4565
+
},
4566
+
"engines": {
4567
+
"node": ">= 6"
4568
+
}
4569
+
},
4570
+
"node_modules/tailwindcss/node_modules/lilconfig": {
4571
+
"version": "3.1.3",
4572
+
"dev": true,
4573
+
"license": "MIT",
4574
+
"engines": {
4575
+
"node": ">=14"
4576
+
},
4577
+
"funding": {
4578
+
"url": "https://github.com/sponsors/antonk52"
4579
+
}
4580
+
},
4581
+
"node_modules/tailwindcss/node_modules/picomatch": {
4582
+
"version": "2.3.1",
4583
+
"dev": true,
4584
+
"license": "MIT",
4585
+
"engines": {
4586
+
"node": ">=8.6"
4587
+
},
4588
+
"funding": {
4589
+
"url": "https://github.com/sponsors/jonschlinkert"
4590
+
}
4591
+
},
4592
+
"node_modules/tailwindcss/node_modules/postcss-import": {
4593
+
"version": "15.1.0",
4594
+
"dev": true,
4595
+
"license": "MIT",
4596
+
"dependencies": {
4597
+
"postcss-value-parser": "^4.0.0",
4598
+
"read-cache": "^1.0.0",
4599
+
"resolve": "^1.1.7"
4600
+
},
4601
+
"engines": {
4602
+
"node": ">=14.0.0"
4603
+
},
4604
+
"peerDependencies": {
4605
+
"postcss": "^8.0.0"
4606
+
}
4607
+
},
4608
+
"node_modules/tailwindcss/node_modules/postcss-load-config": {
4609
+
"version": "4.0.2",
4610
+
"dev": true,
4611
+
"funding": [
4612
+
{
4613
+
"type": "opencollective",
4614
+
"url": "https://opencollective.com/postcss/"
4615
+
},
4616
+
{
4617
+
"type": "github",
4618
+
"url": "https://github.com/sponsors/ai"
4619
+
}
4620
+
],
4621
+
"license": "MIT",
4622
+
"dependencies": {
4623
+
"lilconfig": "^3.0.0",
4624
+
"yaml": "^2.3.4"
4625
+
},
4626
+
"engines": {
4627
+
"node": ">= 14"
4628
+
},
4629
+
"peerDependencies": {
4630
+
"postcss": ">=8.0.9",
4631
+
"ts-node": ">=9.0.0"
4632
+
},
4633
+
"peerDependenciesMeta": {
4634
+
"postcss": {
4635
+
"optional": true
4636
+
},
4637
+
"ts-node": {
4638
+
"optional": true
4639
+
}
4640
+
}
4641
+
},
4642
+
"node_modules/tailwindcss/node_modules/readdirp": {
4643
+
"version": "3.6.0",
4644
+
"dev": true,
4645
+
"license": "MIT",
4646
+
"dependencies": {
4647
+
"picomatch": "^2.2.1"
4648
+
},
4649
+
"engines": {
4650
+
"node": ">=8.10.0"
4651
+
}
4652
+
},
4653
+
"node_modules/tailwindcss/node_modules/yaml": {
4654
+
"version": "2.6.1",
4655
+
"dev": true,
4656
+
"license": "ISC",
4657
+
"bin": {
4658
+
"yaml": "bin.mjs"
4659
+
},
4660
+
"engines": {
4661
+
"node": ">= 14"
4662
+
}
4663
+
},
4664
+
"node_modules/tar": {
4665
+
"version": "7.4.3",
4666
+
"dev": true,
4667
+
"license": "ISC",
4668
+
"dependencies": {
4669
+
"@isaacs/fs-minipass": "^4.0.0",
4670
+
"chownr": "^3.0.0",
4671
+
"minipass": "^7.1.2",
4672
+
"minizlib": "^3.0.1",
4673
+
"mkdirp": "^3.0.1",
4674
+
"yallist": "^5.0.0"
4675
+
},
4676
+
"engines": {
4677
+
"node": ">=18"
4678
+
}
4679
+
},
4680
+
"node_modules/thenify": {
4681
+
"version": "3.3.1",
4682
+
"dev": true,
4683
+
"license": "MIT",
4684
+
"dependencies": {
4685
+
"any-promise": "^1.0.0"
4686
+
}
4687
+
},
4688
+
"node_modules/thenify-all": {
4689
+
"version": "1.6.0",
4690
+
"dev": true,
4691
+
"license": "MIT",
4692
+
"dependencies": {
4693
+
"thenify": ">= 3.1.0 < 4"
4694
+
},
4695
+
"engines": {
4696
+
"node": ">=0.8"
4697
+
}
4698
+
},
4699
+
"node_modules/tiny-inflate": {
4700
+
"version": "1.0.3",
4701
+
"license": "MIT"
4702
+
},
4703
+
"node_modules/to-regex-range": {
4704
+
"version": "5.0.1",
4705
+
"dev": true,
4706
+
"license": "MIT",
4707
+
"dependencies": {
4708
+
"is-number": "^7.0.0"
4709
+
},
4710
+
"engines": {
4711
+
"node": ">=8.0"
4712
+
}
4713
+
},
4714
+
"node_modules/totalist": {
4715
+
"version": "3.0.1",
4716
+
"dev": true,
4717
+
"license": "MIT",
4718
+
"engines": {
4719
+
"node": ">=6"
4720
+
}
4721
+
},
4722
+
"node_modules/tr46": {
4723
+
"version": "0.0.3",
4724
+
"dev": true,
4725
+
"license": "MIT"
4726
+
},
4727
+
"node_modules/trim-lines": {
4728
+
"version": "3.0.1",
4729
+
"license": "MIT",
4730
+
"funding": {
4731
+
"type": "github",
4732
+
"url": "https://github.com/sponsors/wooorm"
4733
+
}
4734
+
},
4735
+
"node_modules/trough": {
4736
+
"version": "2.2.0",
4737
+
"license": "MIT",
4738
+
"funding": {
4739
+
"type": "github",
4740
+
"url": "https://github.com/sponsors/wooorm"
4741
+
}
4742
+
},
4743
+
"node_modules/ts-api-utils": {
4744
+
"version": "1.4.3",
4745
+
"dev": true,
4746
+
"license": "MIT",
4747
+
"engines": {
4748
+
"node": ">=16"
4749
+
},
4750
+
"peerDependencies": {
4751
+
"typescript": ">=4.2.0"
4752
+
}
4753
+
},
4754
+
"node_modules/ts-interface-checker": {
4755
+
"version": "0.1.13",
4756
+
"dev": true,
4757
+
"license": "Apache-2.0"
4758
+
},
4759
+
"node_modules/type-check": {
4760
+
"version": "0.4.0",
4761
+
"dev": true,
4762
+
"license": "MIT",
4763
+
"dependencies": {
4764
+
"prelude-ls": "^1.2.1"
4765
+
},
4766
+
"engines": {
4767
+
"node": ">= 0.8.0"
4768
+
}
4769
+
},
4770
+
"node_modules/typescript": {
4771
+
"version": "5.7.2",
4772
+
"dev": true,
4773
+
"license": "Apache-2.0",
4774
+
"bin": {
4775
+
"tsc": "bin/tsc",
4776
+
"tsserver": "bin/tsserver"
4777
+
},
4778
+
"engines": {
4779
+
"node": ">=14.17"
4780
+
}
4781
+
},
4782
+
"node_modules/typescript-eslint": {
4783
+
"version": "8.16.0",
4784
+
"dev": true,
4785
+
"license": "MIT",
4786
+
"dependencies": {
4787
+
"@typescript-eslint/eslint-plugin": "8.16.0",
4788
+
"@typescript-eslint/parser": "8.16.0",
4789
+
"@typescript-eslint/utils": "8.16.0"
4790
+
},
4791
+
"engines": {
4792
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
4793
+
},
4794
+
"funding": {
4795
+
"type": "opencollective",
4796
+
"url": "https://opencollective.com/typescript-eslint"
4797
+
},
4798
+
"peerDependencies": {
4799
+
"eslint": "^8.57.0 || ^9.0.0"
4800
+
},
4801
+
"peerDependenciesMeta": {
4802
+
"typescript": {
4803
+
"optional": true
4804
+
}
4805
+
}
4806
+
},
4807
+
"node_modules/undici-types": {
4808
+
"version": "6.20.0",
4809
+
"dev": true,
4810
+
"license": "MIT"
4811
+
},
4812
+
"node_modules/unicode-trie": {
4813
+
"version": "2.0.0",
4814
+
"license": "MIT",
4815
+
"dependencies": {
4816
+
"pako": "^0.2.5",
4817
+
"tiny-inflate": "^1.0.0"
4818
+
}
4819
+
},
4820
+
"node_modules/unified": {
4821
+
"version": "11.0.5",
4822
+
"license": "MIT",
4823
+
"dependencies": {
4824
+
"@types/unist": "^3.0.0",
4825
+
"bail": "^2.0.0",
4826
+
"devlop": "^1.0.0",
4827
+
"extend": "^3.0.0",
4828
+
"is-plain-obj": "^4.0.0",
4829
+
"trough": "^2.0.0",
4830
+
"vfile": "^6.0.0"
4831
+
},
4832
+
"funding": {
4833
+
"type": "opencollective",
4834
+
"url": "https://opencollective.com/unified"
4835
+
}
4836
+
},
4837
+
"node_modules/unist-util-is": {
4838
+
"version": "6.0.0",
4839
+
"license": "MIT",
4840
+
"dependencies": {
4841
+
"@types/unist": "^3.0.0"
4842
+
},
4843
+
"funding": {
4844
+
"type": "opencollective",
4845
+
"url": "https://opencollective.com/unified"
4846
+
}
4847
+
},
4848
+
"node_modules/unist-util-position": {
4849
+
"version": "5.0.0",
4850
+
"license": "MIT",
4851
+
"dependencies": {
4852
+
"@types/unist": "^3.0.0"
4853
+
},
4854
+
"funding": {
4855
+
"type": "opencollective",
4856
+
"url": "https://opencollective.com/unified"
4857
+
}
4858
+
},
4859
+
"node_modules/unist-util-stringify-position": {
4860
+
"version": "4.0.0",
4861
+
"license": "MIT",
4862
+
"dependencies": {
4863
+
"@types/unist": "^3.0.0"
4864
+
},
4865
+
"funding": {
4866
+
"type": "opencollective",
4867
+
"url": "https://opencollective.com/unified"
4868
+
}
4869
+
},
4870
+
"node_modules/unist-util-visit": {
4871
+
"version": "5.0.0",
4872
+
"license": "MIT",
4873
+
"dependencies": {
4874
+
"@types/unist": "^3.0.0",
4875
+
"unist-util-is": "^6.0.0",
4876
+
"unist-util-visit-parents": "^6.0.0"
4877
+
},
4878
+
"funding": {
4879
+
"type": "opencollective",
4880
+
"url": "https://opencollective.com/unified"
4881
+
}
4882
+
},
4883
+
"node_modules/unist-util-visit-parents": {
4884
+
"version": "6.0.1",
4885
+
"license": "MIT",
4886
+
"dependencies": {
4887
+
"@types/unist": "^3.0.0",
4888
+
"unist-util-is": "^6.0.0"
4889
+
},
4890
+
"funding": {
4891
+
"type": "opencollective",
4892
+
"url": "https://opencollective.com/unified"
4893
+
}
4894
+
},
4895
+
"node_modules/update-browserslist-db": {
4896
+
"version": "1.1.3",
4897
+
"dev": true,
4898
+
"funding": [
4899
+
{
4900
+
"type": "opencollective",
4901
+
"url": "https://opencollective.com/browserslist"
4902
+
},
4903
+
{
4904
+
"type": "tidelift",
4905
+
"url": "https://tidelift.com/funding/github/npm/browserslist"
4906
+
},
4907
+
{
4908
+
"type": "github",
4909
+
"url": "https://github.com/sponsors/ai"
4910
+
}
4911
+
],
4912
+
"license": "MIT",
4913
+
"dependencies": {
4914
+
"escalade": "^3.2.0",
4915
+
"picocolors": "^1.1.1"
4916
+
},
4917
+
"bin": {
4918
+
"update-browserslist-db": "cli.js"
4919
+
},
4920
+
"peerDependencies": {
4921
+
"browserslist": ">= 4.21.0"
4922
+
}
4923
+
},
4924
+
"node_modules/uri-js": {
4925
+
"version": "4.4.1",
4926
+
"dev": true,
4927
+
"license": "BSD-2-Clause",
4928
+
"dependencies": {
4929
+
"punycode": "^2.1.0"
4930
+
}
4931
+
},
4932
+
"node_modules/util-deprecate": {
4933
+
"version": "1.0.2",
4934
+
"license": "MIT"
4935
+
},
4936
+
"node_modules/vfile": {
4937
+
"version": "6.0.3",
4938
+
"license": "MIT",
4939
+
"dependencies": {
4940
+
"@types/unist": "^3.0.0",
4941
+
"vfile-message": "^4.0.0"
4942
+
},
4943
+
"funding": {
4944
+
"type": "opencollective",
4945
+
"url": "https://opencollective.com/unified"
4946
+
}
4947
+
},
4948
+
"node_modules/vfile-location": {
4949
+
"version": "5.0.3",
4950
+
"license": "MIT",
4951
+
"dependencies": {
4952
+
"@types/unist": "^3.0.0",
4953
+
"vfile": "^6.0.0"
4954
+
},
4955
+
"funding": {
4956
+
"type": "opencollective",
4957
+
"url": "https://opencollective.com/unified"
4958
+
}
4959
+
},
4960
+
"node_modules/vfile-message": {
4961
+
"version": "4.0.2",
4962
+
"license": "MIT",
4963
+
"dependencies": {
4964
+
"@types/unist": "^3.0.0",
4965
+
"unist-util-stringify-position": "^4.0.0"
4966
+
},
4967
+
"funding": {
4968
+
"type": "opencollective",
4969
+
"url": "https://opencollective.com/unified"
4970
+
}
4971
+
},
4972
+
"node_modules/vite": {
4973
+
"version": "5.4.11",
4974
+
"dev": true,
4975
+
"license": "MIT",
4976
+
"dependencies": {
4977
+
"esbuild": "^0.21.3",
4978
+
"postcss": "^8.4.43",
4979
+
"rollup": "^4.20.0"
4980
+
},
4981
+
"bin": {
4982
+
"vite": "bin/vite.js"
4983
+
},
4984
+
"engines": {
4985
+
"node": "^18.0.0 || >=20.0.0"
4986
+
},
4987
+
"funding": {
4988
+
"url": "https://github.com/vitejs/vite?sponsor=1"
4989
+
},
4990
+
"optionalDependencies": {
4991
+
"fsevents": "~2.3.3"
4992
+
},
4993
+
"peerDependencies": {
4994
+
"@types/node": "^18.0.0 || >=20.0.0",
4995
+
"less": "*",
4996
+
"lightningcss": "^1.21.0",
4997
+
"sass": "*",
4998
+
"sass-embedded": "*",
4999
+
"stylus": "*",
5000
+
"sugarss": "*",
5001
+
"terser": "^5.4.0"
5002
+
},
5003
+
"peerDependenciesMeta": {
5004
+
"@types/node": {
5005
+
"optional": true
5006
+
},
5007
+
"less": {
5008
+
"optional": true
5009
+
},
5010
+
"lightningcss": {
5011
+
"optional": true
5012
+
},
5013
+
"sass": {
5014
+
"optional": true
5015
+
},
5016
+
"sass-embedded": {
5017
+
"optional": true
5018
+
},
5019
+
"stylus": {
5020
+
"optional": true
5021
+
},
5022
+
"sugarss": {
5023
+
"optional": true
5024
+
},
5025
+
"terser": {
5026
+
"optional": true
5027
+
}
5028
+
}
5029
+
},
5030
+
"node_modules/vitefu": {
5031
+
"version": "1.0.4",
5032
+
"dev": true,
5033
+
"license": "MIT",
5034
+
"workspaces": [
5035
+
"tests/deps/*",
5036
+
"tests/projects/*"
5037
+
],
5038
+
"peerDependencies": {
5039
+
"vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0"
5040
+
},
5041
+
"peerDependenciesMeta": {
5042
+
"vite": {
5043
+
"optional": true
5044
+
}
5045
+
}
5046
+
},
5047
+
"node_modules/web-namespaces": {
5048
+
"version": "2.0.1",
5049
+
"license": "MIT",
5050
+
"funding": {
5051
+
"type": "github",
5052
+
"url": "https://github.com/sponsors/wooorm"
5053
+
}
5054
+
},
5055
+
"node_modules/webidl-conversions": {
5056
+
"version": "3.0.1",
5057
+
"dev": true,
5058
+
"license": "BSD-2-Clause"
5059
+
},
5060
+
"node_modules/whatwg-url": {
5061
+
"version": "5.0.0",
5062
+
"dev": true,
5063
+
"license": "MIT",
5064
+
"dependencies": {
5065
+
"tr46": "~0.0.3",
5066
+
"webidl-conversions": "^3.0.0"
5067
+
}
5068
+
},
5069
+
"node_modules/which": {
5070
+
"version": "2.0.2",
5071
+
"dev": true,
5072
+
"license": "ISC",
5073
+
"dependencies": {
5074
+
"isexe": "^2.0.0"
5075
+
},
5076
+
"bin": {
5077
+
"node-which": "bin/node-which"
5078
+
},
5079
+
"engines": {
5080
+
"node": ">= 8"
5081
+
}
5082
+
},
5083
+
"node_modules/word-wrap": {
5084
+
"version": "1.2.5",
5085
+
"dev": true,
5086
+
"license": "MIT",
5087
+
"engines": {
5088
+
"node": ">=0.10.0"
5089
+
}
5090
+
},
5091
+
"node_modules/wrap-ansi": {
5092
+
"version": "8.1.0",
5093
+
"dev": true,
5094
+
"license": "MIT",
5095
+
"dependencies": {
5096
+
"ansi-styles": "^6.1.0",
5097
+
"string-width": "^5.0.1",
5098
+
"strip-ansi": "^7.0.1"
5099
+
},
5100
+
"engines": {
5101
+
"node": ">=12"
5102
+
},
5103
+
"funding": {
5104
+
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
5105
+
}
5106
+
},
5107
+
"node_modules/wrap-ansi-cjs": {
5108
+
"name": "wrap-ansi",
5109
+
"version": "7.0.0",
5110
+
"dev": true,
5111
+
"license": "MIT",
5112
+
"dependencies": {
5113
+
"ansi-styles": "^4.0.0",
5114
+
"string-width": "^4.1.0",
5115
+
"strip-ansi": "^6.0.0"
5116
+
},
5117
+
"engines": {
5118
+
"node": ">=10"
5119
+
},
5120
+
"funding": {
5121
+
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
5122
+
}
5123
+
},
5124
+
"node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
5125
+
"version": "5.0.1",
5126
+
"dev": true,
5127
+
"license": "MIT",
5128
+
"engines": {
5129
+
"node": ">=8"
5130
+
}
5131
+
},
5132
+
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
5133
+
"version": "8.0.0",
5134
+
"dev": true,
5135
+
"license": "MIT"
5136
+
},
5137
+
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
5138
+
"version": "4.2.3",
5139
+
"dev": true,
5140
+
"license": "MIT",
5141
+
"dependencies": {
5142
+
"emoji-regex": "^8.0.0",
5143
+
"is-fullwidth-code-point": "^3.0.0",
5144
+
"strip-ansi": "^6.0.1"
5145
+
},
5146
+
"engines": {
5147
+
"node": ">=8"
5148
+
}
5149
+
},
5150
+
"node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
5151
+
"version": "6.0.1",
5152
+
"dev": true,
5153
+
"license": "MIT",
5154
+
"dependencies": {
5155
+
"ansi-regex": "^5.0.1"
5156
+
},
5157
+
"engines": {
5158
+
"node": ">=8"
5159
+
}
5160
+
},
5161
+
"node_modules/wrap-ansi/node_modules/ansi-styles": {
5162
+
"version": "6.2.1",
5163
+
"dev": true,
5164
+
"license": "MIT",
5165
+
"engines": {
5166
+
"node": ">=12"
5167
+
},
5168
+
"funding": {
5169
+
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
5170
+
}
5171
+
},
5172
+
"node_modules/yallist": {
5173
+
"version": "5.0.0",
5174
+
"dev": true,
5175
+
"license": "BlueOak-1.0.0",
5176
+
"engines": {
5177
+
"node": ">=18"
5178
+
}
5179
+
},
5180
+
"node_modules/yaml": {
5181
+
"version": "1.10.2",
5182
+
"dev": true,
5183
+
"license": "ISC",
5184
+
"engines": {
5185
+
"node": ">= 6"
5186
+
}
5187
+
},
5188
+
"node_modules/yocto-queue": {
5189
+
"version": "0.1.0",
5190
+
"dev": true,
5191
+
"license": "MIT",
5192
+
"engines": {
5193
+
"node": ">=10"
5194
+
},
5195
+
"funding": {
5196
+
"url": "https://github.com/sponsors/sindresorhus"
5197
+
}
5198
+
},
5199
+
"node_modules/yoga-wasm-web": {
5200
+
"version": "0.3.3",
5201
+
"license": "MIT"
5202
+
},
5203
+
"node_modules/zimmerframe": {
5204
+
"version": "1.1.2",
5205
+
"dev": true,
5206
+
"license": "MIT"
5207
+
},
5208
+
"node_modules/zwitch": {
5209
+
"version": "2.0.4",
5210
+
"license": "MIT",
5211
+
"funding": {
5212
+
"type": "github",
5213
+
"url": "https://github.com/sponsors/wooorm"
5214
+
}
5215
+
}
5216
+
}
5217
+
}
+48
package.json
+48
package.json
···
1
+
{
2
+
"name": "website-template",
3
+
"version": "0.0.1",
4
+
"type": "module",
5
+
"scripts": {
6
+
"dev": "vite dev",
7
+
"build": "vite build",
8
+
"preview": "vite preview -v",
9
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
10
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
11
+
"lint": "eslint ."
12
+
},
13
+
"devDependencies": {
14
+
15
+
"@sveltejs/adapter-vercel": "^5.7.0",
16
+
"@sveltejs/kit": "^2.24.0",
17
+
"@sveltejs/vite-plugin-svelte": "^4.0.4",
18
+
"@tailwindcss/forms": "^0.5.9",
19
+
"@tailwindcss/typography": "^0.5.15",
20
+
"@types/node": "^22.10.1",
21
+
"@types/sanitize-html": "^2.13.0",
22
+
"autoprefixer": "^10.4.21",
23
+
"eslint": "^9.7.0",
24
+
"eslint-plugin-svelte": "^2.36.0",
25
+
"globals": "^15.0.0",
26
+
"postcss": "^8.5.6",
27
+
"svelte": "^5.36.2",
28
+
"svelte-check": "^4.2.2",
29
+
"tailwindcss": "^3.4.17",
30
+
"typescript": "^5.0.0",
31
+
"typescript-eslint": "^8.0.0",
32
+
"vite": "^5.0.3"
33
+
},
34
+
"dependencies": {
35
+
"@resvg/resvg-js": "^2.6.2",
36
+
"@tailwindcss/nesting": "^0.0.0-insiders.565cd3e",
37
+
"postcss-import": "^16.1.1",
38
+
"rehype-raw": "^7.0.0",
39
+
"rehype-sanitize": "^6.0.0",
40
+
"rehype-stringify": "^10.0.1",
41
+
"remark": "^15.0.1",
42
+
"remark-gfm": "^4.0.0",
43
+
"remark-rehype": "^11.1.1",
44
+
"sanitize-html": "^2.13.1",
45
+
"satori": "^0.12.2",
46
+
"unified": "^11.0.5"
47
+
}
48
+
}
+8
postcss.config.js
+8
postcss.config.js
+25
src/app.html
+25
src/app.html
···
1
+
<!DOCTYPE html>
2
+
<html lang="en">
3
+
<head>
4
+
<meta charset="utf-8" />
5
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6
+
<meta name="author" content="Site Author" />
7
+
<meta name="robots" content="index, follow" />
8
+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
9
+
<meta name="format-detection" content="telephone=no" />
10
+
<meta name="mobile-web-app-capable" content="yes" />
11
+
<meta name="apple-mobile-web-app-capable" content="yes" />
12
+
<meta name="msapplication-TileColor" content="#000000" />
13
+
<meta
14
+
name="msapplication-TileImage"
15
+
content="%sveltekit.assets%/favicon/ms-icon-144x144.png"
16
+
/>
17
+
18
+
<!-- THEME LOADER - MUST BE FIRST SCRIPT -->
19
+
<script src="%sveltekit.assets%/scripts/themeLoader.js"></script>
20
+
%sveltekit.head%
21
+
</head>
22
+
<body data-sveltekit-preload-data="hover">
23
+
<div style="display: contents">%sveltekit.body%</div>
24
+
</body>
25
+
</html>
+472
src/lib/components/archive/ArchiveCard.svelte
+472
src/lib/components/archive/ArchiveCard.svelte
···
1
+
<script lang="ts">
2
+
import { fly, fade } from "svelte/transition";
3
+
import { quintOut } from "svelte/easing";
4
+
import { formatDate, formatNumber } from "$utils/formatters";
5
+
import { getMilestone } from "$utils/milestones";
6
+
import DocumentIcon from "$components/icons/utility/DocumentIcon.svelte";
7
+
import LinkExternalIcon from "$components/icons/utility/LinkExternalIcon.svelte";
8
+
import CoffeeIcon from "$components/icons/utility/CoffeeIcon.svelte";
9
+
import ClockIcon from "$components/icons/utility/ClockIcon.svelte";
10
+
import BookIcon from "$components/icons/utility/BookIcon.svelte";
11
+
import BooksIcon from "$components/icons/utility/BooksIcon.svelte";
12
+
13
+
export let type: 'post' | 'link';
14
+
export let post: any = {}; // For post type
15
+
export let title: string = ""; // For link type or post title
16
+
export let url: string = ""; // For link type
17
+
export let value: string = ""; // For link type
18
+
export let monthIndex: number = 0;
19
+
export let postIndex: number = 0;
20
+
export let localeLoaded: boolean = false;
21
+
export let postNumber: number | null = null; // New prop for milestone calculation
22
+
23
+
// Reactive variable to store the display date string for posts
24
+
let displayDate: string;
25
+
26
+
// Update displayDate whenever post.createdAt or localeLoaded changes for posts
27
+
$: {
28
+
if (type === 'post' && localeLoaded && post?.createdAt) {
29
+
const postDate = new Date(post.createdAt);
30
+
displayDate = formatDate(postDate);
31
+
} else if (type === 'post') {
32
+
displayDate = "Loading...";
33
+
}
34
+
}
35
+
36
+
// Calculate milestone information
37
+
$: milestone = type === 'post' && postNumber ? getMilestone(postNumber) : null;
38
+
39
+
// Determine the title to display based on type
40
+
$: displayTitle = type === 'post' ? post?.title : title;
41
+
$: href = type === 'post' ? `/blog/${post.rkey}` : url;
42
+
43
+
// Calculate reading time category for visual styling
44
+
$: readingTime = type === 'post' ? Math.ceil(post.wordCount / 200) : 0;
45
+
$: isLongRead = readingTime > 10;
46
+
$: isMediumRead = readingTime > 5 && readingTime <= 10;
47
+
$: isQuickRead = readingTime <= 2;
48
+
49
+
// Get appropriate icon based on reading time
50
+
$: getReadingTimeIcon = (time: number) => {
51
+
if (time <= 2) return 'quick'; // Coffee cup for quick reads
52
+
if (time <= 5) return 'short'; // Clock for short reads
53
+
if (time <= 10) return 'medium'; // Book for medium reads
54
+
return 'long'; // Stack of books for long reads
55
+
};
56
+
</script>
57
+
58
+
<div
59
+
class="archive-card group"
60
+
class:long-read={type === 'post' && isLongRead}
61
+
class:medium-read={type === 'post' && isMediumRead}
62
+
class:has-milestone={milestone}
63
+
in:fly={{
64
+
y: 15,
65
+
x: 0,
66
+
delay: 150 + monthIndex * 30 + postIndex * 50,
67
+
duration: 300,
68
+
easing: quintOut,
69
+
}}
70
+
>
71
+
<a {href} class="card-link">
72
+
<article class="card-content">
73
+
<!-- Milestone banner (appears at top if present) -->
74
+
{#if milestone}
75
+
<div
76
+
class="milestone-banner"
77
+
class:special={milestone.type === 'special'}
78
+
class:major={milestone.type === 'major'}
79
+
class:minor={milestone.type === 'minor'}
80
+
in:fade={{ delay: 300, duration: 400 }}
81
+
>
82
+
<span class="milestone-emoji" role="img" aria-label="milestone">{milestone.emoji}</span>
83
+
<span class="milestone-text">{milestone.text}</span>
84
+
</div>
85
+
{/if}
86
+
87
+
<!-- Header section with title and type indicator -->
88
+
<header class="card-header">
89
+
{#if type === 'post'}
90
+
<div class="type-indicator post-indicator">
91
+
<DocumentIcon size="14" />
92
+
<span class="sr-only">Blog post</span>
93
+
</div>
94
+
{:else}
95
+
<div class="type-indicator link-indicator">
96
+
<LinkExternalIcon size="14" />
97
+
<span class="sr-only">External link</span>
98
+
</div>
99
+
{/if}
100
+
101
+
<h3 class="card-title" title={displayTitle}>
102
+
{displayTitle}
103
+
</h3>
104
+
</header>
105
+
106
+
<!-- Main content area -->
107
+
<div class="card-body">
108
+
{#if type === 'post'}
109
+
<!-- Reading stats with visual emphasis -->
110
+
<div class="reading-stats">
111
+
<div class="stat-item words">
112
+
<span class="stat-number">{formatNumber(post.wordCount) || '0'}</span>
113
+
<span class="stat-label">words</span>
114
+
</div>
115
+
<div class="stat-divider">•</div>
116
+
<div class="stat-item time" class:highlight={isLongRead}>
117
+
<div class="reading-time-icon" class:quick={isQuickRead} class:medium={isMediumRead} class:long={isLongRead}>
118
+
{#if getReadingTimeIcon(readingTime) === 'quick'}
119
+
<CoffeeIcon size="14" />
120
+
{:else if getReadingTimeIcon(readingTime) === 'short'}
121
+
<ClockIcon size="14" />
122
+
{:else if getReadingTimeIcon(readingTime) === 'medium'}
123
+
<BookIcon size="14" />
124
+
{:else}
125
+
<BooksIcon size="14" />
126
+
{/if}
127
+
</div>
128
+
<span class="stat-number">{Math.ceil(post.wordCount / 200)}</span>
129
+
<span class="stat-label">min read</span>
130
+
</div>
131
+
</div>
132
+
{:else if type === 'link'}
133
+
<p class="link-value">{value}</p>
134
+
{/if}
135
+
</div>
136
+
137
+
<!-- Footer with metadata -->
138
+
<footer class="card-footer">
139
+
{#if type === 'post'}
140
+
<div class="date-section">
141
+
<span class="date-label">Last Updated</span>
142
+
<div class="date-value">
143
+
{#if localeLoaded && displayDate !== "Loading..."}
144
+
<span transition:fade>{displayDate}</span>
145
+
{:else}
146
+
<span class="loading">Loading...</span>
147
+
{/if}
148
+
</div>
149
+
</div>
150
+
{:else if type === 'link'}
151
+
<div class="link-domain">
152
+
{url?.replace(/^https?:\/\//, "").split("/")[0]}
153
+
</div>
154
+
{/if}
155
+
</footer>
156
+
</article>
157
+
</a>
158
+
</div>
159
+
160
+
<style>
161
+
.archive-card {
162
+
backface-visibility: hidden;
163
+
transition: all 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
164
+
}
165
+
166
+
.card-link {
167
+
display: block;
168
+
text-decoration: none;
169
+
height: 100%;
170
+
}
171
+
172
+
.card-content {
173
+
display: flex;
174
+
flex-direction: column;
175
+
height: 100%;
176
+
min-height: 140px;
177
+
padding: 20px;
178
+
border: 1px solid transparent;
179
+
border-radius: 0px;
180
+
background: var(--header-footer-bg);
181
+
transition: all 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
182
+
position: relative;
183
+
overflow: hidden;
184
+
}
185
+
186
+
.card-content::before {
187
+
content: '';
188
+
position: absolute;
189
+
top: 0;
190
+
left: 0;
191
+
right: 0;
192
+
height: 3px;
193
+
background: var(--link-color);
194
+
transform: scaleX(0);
195
+
transition: transform 0.3s ease;
196
+
transform-origin: left;
197
+
}
198
+
199
+
.group:hover .card-content {
200
+
border-color: var(--button-bg);
201
+
transform: translateY(-2px);
202
+
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
203
+
}
204
+
205
+
.group:hover .card-content::before {
206
+
transform: scaleX(1);
207
+
}
208
+
209
+
/* Disable line animation for milestone cards */
210
+
.has-milestone .card-content::before {
211
+
display: none;
212
+
}
213
+
214
+
/* Milestone Banner Styles */
215
+
.milestone-banner {
216
+
display: flex;
217
+
align-items: center;
218
+
justify-content: center;
219
+
gap: 8px;
220
+
padding: 8px 12px;
221
+
margin: -20px -20px 16px -20px;
222
+
font-size: 0.85rem;
223
+
font-weight: 600;
224
+
text-align: center;
225
+
position: relative;
226
+
overflow: hidden;
227
+
}
228
+
229
+
.milestone-banner::before {
230
+
content: '';
231
+
position: absolute;
232
+
top: 0;
233
+
left: -100%;
234
+
width: 100%;
235
+
height: 100%;
236
+
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
237
+
animation: shimmer 2s infinite;
238
+
}
239
+
240
+
@keyframes shimmer {
241
+
0% { left: -100%; }
242
+
100% { left: 100%; }
243
+
}
244
+
245
+
.milestone-banner.special {
246
+
background: linear-gradient(135deg, var(--button-bg), var(--button-hover-bg));
247
+
color: white;
248
+
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
249
+
}
250
+
251
+
.milestone-banner.major {
252
+
background: linear-gradient(135deg, var(--button-bg), var(--text-color));
253
+
color: var(--header-footer-bg);
254
+
}
255
+
256
+
.milestone-banner.minor {
257
+
background: var(--button-bg);
258
+
color: var(--text-color);
259
+
opacity: 0.9;
260
+
}
261
+
262
+
.milestone-emoji {
263
+
font-size: 1rem;
264
+
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1));
265
+
}
266
+
267
+
.milestone-text {
268
+
letter-spacing: 0.02em;
269
+
}
270
+
271
+
/* Adjust card content when milestone is present */
272
+
.has-milestone .card-header {
273
+
margin-bottom: 12px;
274
+
}
275
+
276
+
.has-milestone .card-content {
277
+
min-height: 160px;
278
+
}
279
+
280
+
/* Header Styles */
281
+
.card-header {
282
+
display: flex;
283
+
align-items: flex-start;
284
+
gap: 12px;
285
+
margin-bottom: 16px;
286
+
}
287
+
288
+
.type-indicator {
289
+
flex-shrink: 0;
290
+
padding: 6px;
291
+
border-radius: 0px;
292
+
margin-top: 2px;
293
+
transition: all 0.3s ease;
294
+
}
295
+
296
+
.post-indicator, .link-indicator {
297
+
background: var(--button-bg);
298
+
color: var(--text-color);
299
+
opacity: 0.8;
300
+
}
301
+
302
+
.group:hover .type-indicator {
303
+
transform: scale(1.1);
304
+
}
305
+
306
+
.card-title {
307
+
flex: 1;
308
+
margin: 0;
309
+
font-size: 1.1rem;
310
+
font-weight: 600;
311
+
line-height: 1.4;
312
+
color: var(--link-color);
313
+
display: -webkit-box;
314
+
-webkit-line-clamp: 3;
315
+
line-clamp: 3;
316
+
-webkit-box-orient: vertical;
317
+
overflow: hidden;
318
+
word-break: break-word;
319
+
transition: color 0.3s ease;
320
+
}
321
+
322
+
.group:hover .card-title {
323
+
color: var(--link-hover-color);
324
+
}
325
+
326
+
/* Body Styles */
327
+
.card-body {
328
+
flex: 1;
329
+
display: flex;
330
+
align-items: center;
331
+
}
332
+
333
+
.reading-stats {
334
+
display: flex;
335
+
align-items: center;
336
+
gap: 12px;
337
+
font-size: 0.95rem;
338
+
}
339
+
340
+
.stat-item {
341
+
display: flex;
342
+
align-items: center;
343
+
gap: 6px;
344
+
}
345
+
346
+
.reading-time-icon {
347
+
display: flex;
348
+
align-items: center;
349
+
color: var(--text-color);
350
+
opacity: 0.7;
351
+
transition: all 0.3s ease;
352
+
}
353
+
354
+
.stat-number {
355
+
font-weight: 600;
356
+
color: var(--text-color);
357
+
}
358
+
359
+
.stat-label {
360
+
font-size: 0.85rem;
361
+
color: var(--text-color);
362
+
opacity: 0.7;
363
+
}
364
+
365
+
.stat-item.highlight .stat-number {
366
+
color: var(--link-color);
367
+
font-weight: 700;
368
+
}
369
+
370
+
.stat-divider {
371
+
color: var(--text-color);
372
+
opacity: 0.4;
373
+
}
374
+
375
+
.link-value {
376
+
color: var(--text-color);
377
+
font-size: 0.9rem;
378
+
line-height: 1.4;
379
+
margin: 0;
380
+
}
381
+
382
+
/* Footer Styles */
383
+
.card-footer {
384
+
margin-top: auto;
385
+
padding-top: 16px;
386
+
}
387
+
388
+
.date-section {
389
+
display: flex;
390
+
flex-direction: column;
391
+
gap: 4px;
392
+
}
393
+
394
+
.date-label {
395
+
font-size: 0.75rem;
396
+
color: var(--text-color);
397
+
opacity: 0.6;
398
+
text-transform: uppercase;
399
+
letter-spacing: 0.5px;
400
+
font-weight: 500;
401
+
}
402
+
403
+
.date-value {
404
+
font-size: 0.9rem;
405
+
color: var(--text-color);
406
+
font-weight: 500;
407
+
}
408
+
409
+
.loading {
410
+
opacity: 0.5;
411
+
font-style: italic;
412
+
}
413
+
414
+
.link-domain {
415
+
font-size: 0.8rem;
416
+
color: var(--text-color);
417
+
opacity: 0.7;
418
+
font-family: monospace;
419
+
background: var(--button-bg);
420
+
padding: 4px 8px;
421
+
border-radius: 0px;
422
+
display: inline-block;
423
+
}
424
+
425
+
/* Adaptive sizing based on content */
426
+
.long-read .card-content {
427
+
min-height: 160px;
428
+
}
429
+
430
+
.medium-read .card-content {
431
+
min-height: 150px;
432
+
}
433
+
434
+
.long-read .card-content::before {
435
+
height: 4px;
436
+
background: var(--link-color);
437
+
}
438
+
439
+
/* Screen reader only content */
440
+
.sr-only {
441
+
position: absolute;
442
+
width: 1px;
443
+
height: 1px;
444
+
padding: 0;
445
+
margin: -1px;
446
+
overflow: hidden;
447
+
clip: rect(0, 0, 0, 0);
448
+
white-space: nowrap;
449
+
border: 0;
450
+
}
451
+
452
+
/* Responsive adjustments */
453
+
@media (max-width: 640px) {
454
+
.card-content {
455
+
padding: 16px;
456
+
min-height: 120px;
457
+
}
458
+
459
+
.card-title {
460
+
font-size: 1rem;
461
+
}
462
+
463
+
.reading-stats {
464
+
font-size: 0.85rem;
465
+
}
466
+
467
+
.milestone-banner {
468
+
margin: -16px -16px 12px -16px;
469
+
font-size: 0.8rem;
470
+
}
471
+
}
472
+
</style>
+35
src/lib/components/archive/MonthSection.svelte
+35
src/lib/components/archive/MonthSection.svelte
···
1
+
<script lang="ts">
2
+
import { slide } from "svelte/transition";
3
+
4
+
import { quintOut } from "svelte/easing";
5
+
import { ArchiveCard } from "./index";
6
+
import StatsDisplay from "./StatsDisplay.svelte";
7
+
8
+
export let monthName: string;
9
+
export let postsInMonth: any[];
10
+
export let monthIndex: number;
11
+
export let localeLoaded: boolean;
12
+
import { calculateTotalReadTime, calculateTotalWordCount, formatReadTime } from "$utils/tally";
13
+
14
+
$: rawTotalReadTime = calculateTotalReadTime(postsInMonth);
15
+
$: totalReadTime = formatReadTime(rawTotalReadTime);
16
+
$: totalWordCount = calculateTotalWordCount(postsInMonth);
17
+
18
+
// Calculate the number of posts
19
+
let postCount = postsInMonth.length;
20
+
</script>
21
+
22
+
<div
23
+
class="mb-12 ml-4"
24
+
in:slide={{ delay: 100 + monthIndex * 50, duration: 300, easing: quintOut }}
25
+
>
26
+
<h2 class="text-2xl font-bold mb-1 ml-2">{monthName}</h2>
27
+
<StatsDisplay {totalReadTime} {totalWordCount} {postCount} />
28
+
<div
29
+
class="grid grid-cols-[repeat(auto-fill,minmax(280px,1fr)_)] gap-x-6 gap-y-6 mx-4 my-8"
30
+
>
31
+
{#each postsInMonth as post, postIndex (post.rkey)}
32
+
<ArchiveCard type="post" {post} {monthIndex} {postIndex} {localeLoaded} postNumber={post.postNumber} />
33
+
{/each}
34
+
</div>
35
+
</div>
+24
src/lib/components/archive/StatsDisplay.svelte
+24
src/lib/components/archive/StatsDisplay.svelte
···
1
+
<script lang="ts">
2
+
import { formatNumber } from "$utils/formatters";
3
+
4
+
export let totalReadTime: string;
5
+
export let totalWordCount: number;
6
+
export let postCount: number | undefined = undefined;
7
+
8
+
// Determine singular or plural for word count
9
+
$: wordLabel = totalWordCount === 1 ? "word" : "words";
10
+
$: postLabel = postCount === 1 ? "post" : "posts";
11
+
</script>
12
+
13
+
{#if postCount !== undefined}
14
+
<p class="text-sm opacity-50 mb-4 ml-2">
15
+
{totalReadTime} read time • {formatNumber(totalWordCount)}
16
+
{wordLabel} • {formatNumber(postCount)}
17
+
{postLabel}
18
+
</p>
19
+
{:else}
20
+
<div class="mb-6 ml-4 text-sm opacity-70">
21
+
<p>Total Read Time: {totalReadTime}</p>
22
+
<p>Total Word Count: {formatNumber(totalWordCount)} {wordLabel}</p>
23
+
</div>
24
+
{/if}
+39
src/lib/components/archive/YearContent.svelte
+39
src/lib/components/archive/YearContent.svelte
···
1
+
<script lang="ts">
2
+
import { fly, fade } from "svelte/transition";
3
+
import { quintOut } from "svelte/easing";
4
+
import MonthSection from "./MonthSection.svelte";
5
+
import { calculateTotalReadTime, calculateTotalWordCount, formatReadTime } from "$utils/tally";
6
+
import StatsDisplay from "./StatsDisplay.svelte";
7
+
8
+
export const year: number = 0;
9
+
export let months: Record<string, any[]>;
10
+
export let localeLoaded: boolean;
11
+
12
+
// Calculate yearly totals
13
+
$: rawYearlyTotalReadTime = Object.values(months).reduce((total, postsInMonth) => {
14
+
return total + calculateTotalReadTime(postsInMonth);
15
+
}, 0);
16
+
$: yearlyTotalReadTime = formatReadTime(rawYearlyTotalReadTime);
17
+
18
+
$: yearlyTotalWordCount = Object.values(months).reduce((total, postsInMonth) => {
19
+
return total + calculateTotalWordCount(postsInMonth);
20
+
}, 0);
21
+
</script>
22
+
23
+
<div
24
+
in:fly={{ y: 20, duration: 300, delay: 50, easing: quintOut }}
25
+
out:fade={{ duration: 200 }}
26
+
class="year-content"
27
+
>
28
+
<StatsDisplay totalReadTime={yearlyTotalReadTime} totalWordCount={yearlyTotalWordCount} />
29
+
30
+
{#each Object.entries(months) as [monthName, postsInMonth], monthIndex}
31
+
<MonthSection
32
+
{monthName}
33
+
{postsInMonth}
34
+
{monthIndex}
35
+
{localeLoaded}
36
+
37
+
/>
38
+
{/each}
39
+
</div>
+62
src/lib/components/archive/YearTabs.svelte
+62
src/lib/components/archive/YearTabs.svelte
···
1
+
<script lang="ts">
2
+
export let groupedByYear: any[];
3
+
export let activeYear: number;
4
+
5
+
function setActiveYear(year: number) {
6
+
activeYear = year;
7
+
}
8
+
9
+
// Calculate the active tab index more reliably
10
+
$: activeTabIndex = groupedByYear.findIndex((g) => g.year === activeYear);
11
+
$: indicatorLeft = activeTabIndex >= 0 ? activeTabIndex * 100 : 0;
12
+
</script>
13
+
14
+
<div
15
+
class="flex mb-6 ml-4 border-b border-[var(--button-bg)] overflow-x-auto relative tabs-container"
16
+
>
17
+
<div class="tab-indicator-container absolute bottom-0 left-0 h-0.5 w-full">
18
+
<div
19
+
class="tab-indicator bg-[var(--link-color)] h-full absolute bottom-0 transition-all duration-300 ease-out"
20
+
style="left: {indicatorLeft}px; width: 100px;"
21
+
></div>
22
+
</div>
23
+
24
+
{#each groupedByYear as { year }, i}
25
+
<button
26
+
class="w-[100px] min-w-[100px] px-4 py-2 font-medium transition-all duration-300 relative z-10 text-center
27
+
{activeYear === year
28
+
? 'text-[var(--link-color)]'
29
+
: 'text-[var(--text-color)] opacity-80 hover:text-[var(--link-hover-color)]'}"
30
+
onclick={() => setActiveYear(year)}
31
+
>
32
+
<span
33
+
class="relative {activeYear === year
34
+
? 'transform transition-transform duration-300 scale-105'
35
+
: ''}"
36
+
>
37
+
{year}
38
+
</span>
39
+
</button>
40
+
{/each}
41
+
</div>
42
+
43
+
<style>
44
+
/* Custom scrollbar styling for tabs container */
45
+
.tabs-container::-webkit-scrollbar {
46
+
height: 6px;
47
+
}
48
+
49
+
.tabs-container::-webkit-scrollbar-track {
50
+
background: var(--header-footer-bg);
51
+
border-radius: 0px;
52
+
}
53
+
54
+
.tabs-container::-webkit-scrollbar-thumb {
55
+
background: var(--button-bg);
56
+
border-radius: 0px;
57
+
}
58
+
59
+
.tabs-container::-webkit-scrollbar-thumb:hover {
60
+
background: var(--button-hover-bg);
61
+
}
62
+
</style>
+5
src/lib/components/archive/index.ts
+5
src/lib/components/archive/index.ts
···
1
+
export { default as YearTabs } from "./YearTabs.svelte";
2
+
export { default as YearContent } from "./YearContent.svelte";
3
+
export { default as MonthSection } from "./MonthSection.svelte";
4
+
export { default as ArchiveCard } from "./ArchiveCard.svelte";
5
+
export { default as StatsDisplay } from "./StatsDisplay.svelte";
+20
src/lib/components/icons/index.ts
+20
src/lib/components/icons/index.ts
···
1
+
export { default as BlueskyIcon } from "./social/BlueskyIcon.svelte";
2
+
export { default as FacebookIcon } from "./social/FacebookIcon.svelte";
3
+
export { default as MastodonIcon } from './social/MastodonIcon.svelte';
4
+
export { default as RedditIcon } from './social/RedditIcon.svelte';
5
+
export { default as RssIcon } from './social/RssIcon.svelte';
6
+
export { default as ShareIcons } from './social/ShareIcons.svelte';
7
+
export { default as BookOpenIcon } from './utility/BookOpenIcon.svelte';
8
+
export { default as CopyLinkIcon } from './utility/CopyLinkIcon.svelte';
9
+
export { default as HomeIcon } from './utility/HomeIcon.svelte';
10
+
export { default as LinkIcon } from './utility/LinkIcon.svelte';
11
+
export { default as MoonIcon } from './utility/MoonIcon.svelte';
12
+
export { default as PostIcon } from './utility/PostIcon.svelte';
13
+
export { default as SunIcon } from './utility/SunIcon.svelte';
14
+
export { default as DocumentIcon } from "./utility/DocumentIcon.svelte";
15
+
export { default as LinkExternalIcon } from "./utility/LinkExternalIcon.svelte";
16
+
export { default as CoffeeIcon } from "./utility/CoffeeIcon.svelte";
17
+
export { default as ClockIcon } from "./utility/ClockIcon.svelte";
18
+
export { default as BookIcon } from "./utility/BookIcon.svelte";
19
+
export { default as BooksIcon } from "./utility/BooksIcon.svelte";
20
+
export { default as EditIcon } from "./utility/EditIcon.svelte";
+20
src/lib/components/icons/utility/BookIcon.svelte
+20
src/lib/components/icons/utility/BookIcon.svelte
···
1
+
<script>
2
+
export let size = "14";
3
+
export let fill = "none";
4
+
export let stroke = "currentColor";
5
+
export let strokeWidth = "2";
6
+
</script>
7
+
8
+
<svg
9
+
width={size}
10
+
height={size}
11
+
viewBox="0 0 24 24"
12
+
{fill}
13
+
{stroke}
14
+
stroke-width={strokeWidth}
15
+
stroke-linecap="round"
16
+
stroke-linejoin="round"
17
+
>
18
+
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20" />
19
+
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z" />
20
+
</svg>
+21
src/lib/components/icons/utility/BookOpenIcon.svelte
+21
src/lib/components/icons/utility/BookOpenIcon.svelte
···
1
+
<script>
2
+
export let size = "24";
3
+
export let stroke = "currentColor";
4
+
export let fill = "none";
5
+
</script>
6
+
7
+
<svg
8
+
xmlns="http://www.w3.org/2000/svg"
9
+
width={size}
10
+
height={size}
11
+
viewBox="0 0 24 24"
12
+
{fill}
13
+
{stroke}
14
+
stroke-width="2"
15
+
stroke-linecap="round"
16
+
stroke-linejoin="round"
17
+
class="feather feather-book-open"
18
+
>
19
+
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" />
20
+
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" />
21
+
</svg>
+20
src/lib/components/icons/utility/BooksIcon.svelte
+20
src/lib/components/icons/utility/BooksIcon.svelte
···
1
+
<script>
2
+
export let size = "14";
3
+
export let fill = "none";
4
+
export let stroke = "currentColor";
5
+
export let strokeWidth = "2";
6
+
</script>
7
+
8
+
<svg
9
+
width={size}
10
+
height={size}
11
+
viewBox="0 0 24 24"
12
+
{fill}
13
+
{stroke}
14
+
stroke-width={strokeWidth}
15
+
stroke-linecap="round"
16
+
stroke-linejoin="round"
17
+
>
18
+
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" />
19
+
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" />
20
+
</svg>
+20
src/lib/components/icons/utility/ClockIcon.svelte
+20
src/lib/components/icons/utility/ClockIcon.svelte
···
1
+
<script>
2
+
export let size = "14";
3
+
export let fill = "none";
4
+
export let stroke = "currentColor";
5
+
export let strokeWidth = "2";
6
+
</script>
7
+
8
+
<svg
9
+
width={size}
10
+
height={size}
11
+
viewBox="0 0 24 24"
12
+
{fill}
13
+
{stroke}
14
+
stroke-width={strokeWidth}
15
+
stroke-linecap="round"
16
+
stroke-linejoin="round"
17
+
>
18
+
<circle cx="12" cy="12" r="10" />
19
+
<polyline points="12,6 12,12 16,14" />
20
+
</svg>
+23
src/lib/components/icons/utility/CoffeeIcon.svelte
+23
src/lib/components/icons/utility/CoffeeIcon.svelte
···
1
+
<script>
2
+
export let size = "14";
3
+
export let fill = "none";
4
+
export let stroke = "currentColor";
5
+
export let strokeWidth = "2";
6
+
</script>
7
+
8
+
<svg
9
+
width={size}
10
+
height={size}
11
+
viewBox="0 0 24 24"
12
+
{fill}
13
+
{stroke}
14
+
stroke-width={strokeWidth}
15
+
stroke-linecap="round"
16
+
stroke-linejoin="round"
17
+
>
18
+
<path d="M17 8h1a4 4 0 0 1 4 4v0a4 4 0 0 1-4 4h-1" />
19
+
<path d="M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4V8Z" />
20
+
<line x1="6" y1="2" x2="6" y2="4" />
21
+
<line x1="10" y1="2" x2="10" y2="4" />
22
+
<line x1="14" y1="2" x2="14" y2="4" />
23
+
</svg>
+20
src/lib/components/icons/utility/CopyLinkIcon.svelte
+20
src/lib/components/icons/utility/CopyLinkIcon.svelte
···
1
+
<script>
2
+
export let size = "18";
3
+
export let stroke = "currentColor";
4
+
export let fill = "none";
5
+
</script>
6
+
7
+
<svg
8
+
xmlns="http://www.w3.org/2000/svg"
9
+
width={size}
10
+
height={size}
11
+
viewBox="0 0 24 24"
12
+
{fill}
13
+
{stroke}
14
+
stroke-width="2"
15
+
stroke-linecap="round"
16
+
stroke-linejoin="round"
17
+
>
18
+
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
19
+
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
20
+
</svg>
+23
src/lib/components/icons/utility/DocumentIcon.svelte
+23
src/lib/components/icons/utility/DocumentIcon.svelte
···
1
+
<script>
2
+
export let size = "14";
3
+
export let fill = "none";
4
+
export let stroke = "currentColor";
5
+
export let strokeWidth = "2";
6
+
</script>
7
+
8
+
<svg
9
+
width={size}
10
+
height={size}
11
+
viewBox="0 0 24 24"
12
+
{fill}
13
+
{stroke}
14
+
stroke-width={strokeWidth}
15
+
stroke-linecap="round"
16
+
stroke-linejoin="round"
17
+
>
18
+
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
19
+
<polyline points="14,2 14,8 20,8" />
20
+
<line x1="16" y1="13" x2="8" y2="13" />
21
+
<line x1="16" y1="17" x2="8" y2="17" />
22
+
<line x1="10" y1="9" x2="8" y2="9" />
23
+
</svg>
+21
src/lib/components/icons/utility/EditIcon.svelte
+21
src/lib/components/icons/utility/EditIcon.svelte
···
1
+
<script>
2
+
export let size = "20";
3
+
export let fill = "none";
4
+
export let stroke = "currentColor";
5
+
export let strokeWidth = "2";
6
+
</script>
7
+
8
+
<svg
9
+
width={size}
10
+
height={size}
11
+
viewBox="0 0 24 24"
12
+
{fill}
13
+
{stroke}
14
+
stroke-width={strokeWidth}
15
+
stroke-linecap="round"
16
+
stroke-linejoin="round"
17
+
>
18
+
<path d="M20.71 4.04a1 1 0 0 1 .02 1.39l-9.6 9.6c-.1.1-.21.18-.33.24l-3.76 1.25a1 1 0 0 1-1.27-1.27l1.25-3.76c.06-.12.14-.23.24-.33l9.6-9.6a1 1 0 0 1 1.41.02z" />
19
+
<path d="M14 7l3 3" />
20
+
<path d="M5 16l-2 4 4-2z" />
21
+
</svg>
+21
src/lib/components/icons/utility/HomeIcon.svelte
+21
src/lib/components/icons/utility/HomeIcon.svelte
···
1
+
<script>
2
+
export let size = "24";
3
+
export let stroke = "currentColor";
4
+
export let fill = "none";
5
+
</script>
6
+
7
+
<svg
8
+
xmlns="http://www.w3.org/2000/svg"
9
+
width={size}
10
+
height={size}
11
+
viewBox="0 0 24 24"
12
+
{fill}
13
+
{stroke}
14
+
stroke-width="2"
15
+
stroke-linecap="round"
16
+
stroke-linejoin="round"
17
+
class="feather feather-home"
18
+
>
19
+
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
20
+
<polyline points="9 22 9 12 15 12 15 22" />
21
+
</svg>
+20
src/lib/components/icons/utility/LinkExternalIcon.svelte
+20
src/lib/components/icons/utility/LinkExternalIcon.svelte
···
1
+
<script>
2
+
export let size = "14";
3
+
export let fill = "none";
4
+
export let stroke = "currentColor";
5
+
export let strokeWidth = "2";
6
+
</script>
7
+
8
+
<svg
9
+
width={size}
10
+
height={size}
11
+
viewBox="0 0 24 24"
12
+
{fill}
13
+
{stroke}
14
+
stroke-width={strokeWidth}
15
+
stroke-linecap="round"
16
+
stroke-linejoin="round"
17
+
>
18
+
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
19
+
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
20
+
</svg>
+9
src/lib/components/icons/utility/LinkIcon.svelte
+9
src/lib/components/icons/utility/LinkIcon.svelte
···
1
+
<script lang="ts">
2
+
export let size: string = "14";
3
+
export let colour: string = "currentColor";
4
+
</script>
5
+
6
+
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={colour} stroke-width="2">
7
+
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>
8
+
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
9
+
</svg>
+20
src/lib/components/icons/utility/MoonIcon.svelte
+20
src/lib/components/icons/utility/MoonIcon.svelte
···
1
+
<script lang="ts">
2
+
export let stroke: string = "currentColor";
3
+
export let width: string = "20";
4
+
export let height: string = "20";
5
+
</script>
6
+
7
+
<svg
8
+
xmlns="http://www.w3.org/2000/svg"
9
+
{width}
10
+
{height}
11
+
viewBox="0 0 24 24"
12
+
fill="none"
13
+
{stroke}
14
+
stroke-width="2"
15
+
stroke-linecap="round"
16
+
stroke-linejoin="round"
17
+
class="feather feather-moon"
18
+
>
19
+
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
20
+
</svg>
+12
src/lib/components/icons/utility/PostIcon.svelte
+12
src/lib/components/icons/utility/PostIcon.svelte
···
1
+
<script lang="ts">
2
+
export let size: string = "14";
3
+
export let colour: string = "currentColor";
4
+
</script>
5
+
6
+
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={colour} stroke-width="2">
7
+
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
8
+
<polyline points="14,2 14,8 20,8"/>
9
+
<line x1="16" y1="13" x2="8" y2="13"/>
10
+
<line x1="16" y1="17" x2="8" y2="17"/>
11
+
<line x1="10" y1="9" x2="8" y2="9"/>
12
+
</svg>
+28
src/lib/components/icons/utility/SunIcon.svelte
+28
src/lib/components/icons/utility/SunIcon.svelte
···
1
+
<script lang="ts">
2
+
export let stroke: string = "currentColor";
3
+
export let width: string = "20";
4
+
export let height: string = "20";
5
+
</script>
6
+
7
+
<svg
8
+
xmlns="http://www.w3.org/2000/svg"
9
+
{width}
10
+
{height}
11
+
viewBox="0 0 24 24"
12
+
fill="none"
13
+
{stroke}
14
+
stroke-width="2"
15
+
stroke-linecap="round"
16
+
stroke-linejoin="round"
17
+
class="feather feather-sun"
18
+
>
19
+
<circle cx="12" cy="12" r="5"></circle>
20
+
<line x1="12" y1="1" x2="12" y2="3"></line>
21
+
<line x1="12" y1="21" x2="12" y2="23"></line>
22
+
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
23
+
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
24
+
<line x1="1" y1="12" x2="3" y2="12"></line>
25
+
<line x1="21" y1="12" x2="23" y2="12"></line>
26
+
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
27
+
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
28
+
</svg>
+152
src/lib/components/layout/ThemeToggle.svelte
+152
src/lib/components/layout/ThemeToggle.svelte
···
1
+
<script lang="ts">
2
+
import { onMount } from "svelte";
3
+
import { SunIcon, MoonIcon } from "$components/icons";
4
+
import EditIcon from "$components/icons/utility/EditIcon.svelte";
5
+
import {
6
+
THEMES,
7
+
applyTheme,
8
+
getThemePreferences,
9
+
saveThemePreferences,
10
+
updateThemeColorMeta,
11
+
dispatchThemeChangeEvent,
12
+
setupSystemThemeListener
13
+
} from "../../themeLoader";
14
+
15
+
let isDarkMode: boolean = true;
16
+
let currentTheme: string = "default";
17
+
let isDropdownOpen: boolean = false;
18
+
19
+
onMount(() => {
20
+
// Retrieve current theme preferences (already applied by theme-loader)
21
+
const preferences = getThemePreferences();
22
+
isDarkMode = preferences.isDarkMode;
23
+
currentTheme = preferences.themeId;
24
+
25
+
// Update meta tag and dispatch a theme change event
26
+
updateThemeColorMeta();
27
+
dispatchThemeChangeEvent(isDarkMode, currentTheme);
28
+
29
+
// Set up system theme listener
30
+
setupSystemThemeListener();
31
+
32
+
// Close dropdown when clicking outside the theme controls
33
+
document.addEventListener("click", (e: MouseEvent) => {
34
+
const themeControls = document.querySelector(".theme-controls");
35
+
if (
36
+
isDropdownOpen &&
37
+
themeControls &&
38
+
!e.composedPath().includes(themeControls)
39
+
) {
40
+
isDropdownOpen = false;
41
+
}
42
+
});
43
+
});
44
+
45
+
function toggleTheme(): void {
46
+
isDarkMode = !isDarkMode;
47
+
applyTheme(isDarkMode, currentTheme);
48
+
saveThemePreferences(isDarkMode, currentTheme);
49
+
updateThemeColorMeta();
50
+
dispatchThemeChangeEvent(isDarkMode, currentTheme);
51
+
}
52
+
53
+
// Change the colour theme
54
+
function changeColorTheme(themeId: string): void {
55
+
currentTheme = themeId;
56
+
applyTheme(isDarkMode, currentTheme);
57
+
saveThemePreferences(isDarkMode, currentTheme);
58
+
updateThemeColorMeta();
59
+
dispatchThemeChangeEvent(isDarkMode, currentTheme);
60
+
isDropdownOpen = false;
61
+
}
62
+
63
+
// Toggle the dropdown for theme selection
64
+
function toggleDropdown(e: MouseEvent): void {
65
+
e.stopPropagation();
66
+
isDropdownOpen = !isDropdownOpen;
67
+
}
68
+
</script>
69
+
70
+
<div class="theme-controls relative">
71
+
<div class="flex items-center gap-2">
72
+
<button
73
+
onclick={toggleDropdown}
74
+
class="icon-button p-2 rounded-full transition-all duration-300 hover:scale-110"
75
+
style="background-color: var(--card-bg);"
76
+
aria-label="Change theme"
77
+
aria-expanded={isDropdownOpen}
78
+
>
79
+
<EditIcon size="20" stroke="var(--text-color)" />
80
+
</button>
81
+
82
+
<button
83
+
onclick={toggleTheme}
84
+
class="icon-button p-2 rounded-full transition-all duration-300 hover:scale-110"
85
+
style="background-color: var(--card-bg);"
86
+
aria-label={isDarkMode ? "Switch to light mode" : "Switch to dark mode"}
87
+
>
88
+
{#if isDarkMode}
89
+
<!-- Sun icon for switching to light mode -->
90
+
<SunIcon stroke="var(--text-color)" />
91
+
{:else}
92
+
<!-- Moon icon for switching to dark mode -->
93
+
<MoonIcon stroke="var(--text-color)" />
94
+
{/if}
95
+
</button>
96
+
</div>
97
+
98
+
{#if isDropdownOpen}
99
+
<div
100
+
class="theme-dropdown absolute right-0 mt-2 py-2 w-48 rounded shadow-lg z-10"
101
+
style="background-color: var(--card-bg); border: 1px solid var(--button-bg);"
102
+
>
103
+
<div class="max-h-80 overflow-y-auto">
104
+
{#each THEMES as theme}
105
+
<button
106
+
class="theme-option w-full text-left px-4 py-2 transition-colors duration-200"
107
+
class:active={currentTheme === theme.id}
108
+
onclick={() => changeColorTheme(theme.id)}
109
+
>
110
+
{theme.name}
111
+
</button>
112
+
{/each}
113
+
</div>
114
+
</div>
115
+
{/if}
116
+
</div>
117
+
118
+
<style>
119
+
/* Common icon styling */
120
+
.icon-button {
121
+
color: var(--text-color);
122
+
}
123
+
124
+
.icon-button:hover {
125
+
background-color: var(--button-hover-bg) !important;
126
+
}
127
+
128
+
.theme-option {
129
+
color: var(--text-color);
130
+
}
131
+
132
+
.theme-option:hover {
133
+
background-color: var(--button-bg);
134
+
}
135
+
136
+
.theme-option.active {
137
+
background-color: var(--button-hover-bg);
138
+
font-weight: 500;
139
+
}
140
+
141
+
.theme-dropdown {
142
+
max-height: 80vh;
143
+
}
144
+
145
+
/* Responsive adjustments */
146
+
@media (max-width: 640px) {
147
+
.theme-dropdown {
148
+
width: 12rem;
149
+
right: 0;
150
+
}
151
+
}
152
+
</style>
+3
src/lib/components/layout/index.ts
+3
src/lib/components/layout/index.ts
+24
src/lib/components/layout/main/DynamicLinks.svelte
+24
src/lib/components/layout/main/DynamicLinks.svelte
···
1
+
<script lang="ts">
2
+
import { ArchiveCard } from "$components/archive";
3
+
import type { LinkBoard } from "$components/shared";
4
+
5
+
// Export the data prop that will receive the fetched links
6
+
export let data: LinkBoard | undefined;
7
+
</script>
8
+
9
+
{#if data && data.cards.length > 0}
10
+
<div class="mb-12">
11
+
<div
12
+
class="grid grid-cols-[repeat(auto-fill,minmax(260px,1fr)_)] gap-x-6 gap-y-6 my-6"
13
+
>
14
+
{#each data.cards as link}
15
+
<ArchiveCard type="link" url={link.url} title={link.text} value={link.emoji} />
16
+
{/each}
17
+
</div>
18
+
</div>
19
+
{:else}
20
+
<!-- Placeholder for blue.linkat.board -->
21
+
<div class="mb-12 ml-4 text-center text-sm italic opacity-75">
22
+
create a <code>blue.linkat.board</code> record at <a href="https://linkat.blue/" class="text-link hover:text-link-hover">https://linkat.blue/</a>
23
+
</div>
24
+
{/if}
+102
src/lib/components/layout/main/LatestBlogPost.svelte
+102
src/lib/components/layout/main/LatestBlogPost.svelte
···
1
+
<script lang="ts">
2
+
import { slide } from "svelte/transition";
3
+
import { quintOut } from "svelte/easing";
4
+
import { ArchiveCard } from "$components/archive";
5
+
import type { Post } from "$components/shared";
6
+
7
+
export let posts: Post[] = [];
8
+
export let localeLoaded: boolean = false;
9
+
10
+
// Get the latest post with proper validation
11
+
$: latestPost = posts && posts.length > 0 ? posts[0] : null;
12
+
13
+
// Additional validation to ensure the post has valid data
14
+
$: isValidPost = latestPost &&
15
+
latestPost.title &&
16
+
latestPost.createdAt instanceof Date &&
17
+
!isNaN(latestPost.createdAt.getTime()) &&
18
+
latestPost.content;
19
+
20
+
// Use the postNumber from the latestPost object
21
+
$: postNumber = latestPost ? latestPost.postNumber : null;
22
+
</script>
23
+
24
+
{#if isValidPost}
25
+
<section
26
+
class="latest-blog-post"
27
+
in:slide={{ delay: 200, duration: 400, easing: quintOut }}
28
+
>
29
+
<div class="section-header">
30
+
<h2 class="section-title">Latest Blog Post</h2>
31
+
</div>
32
+
33
+
<div class="latest-post-container">
34
+
<ArchiveCard
35
+
type="post"
36
+
post={latestPost}
37
+
monthIndex={0}
38
+
postIndex={0}
39
+
postNumber={postNumber}
40
+
{localeLoaded}
41
+
/>
42
+
</div>
43
+
</section>
44
+
{/if}
45
+
46
+
<style>
47
+
.latest-blog-post {
48
+
margin-bottom: 3rem;
49
+
}
50
+
51
+
.section-header {
52
+
display: flex;
53
+
align-items: center;
54
+
justify-content: space-between;
55
+
margin-bottom: 1.5rem;
56
+
padding: 0;
57
+
}
58
+
59
+
.section-title {
60
+
margin: 0;
61
+
font-size: 1.75rem;
62
+
font-weight: 700;
63
+
color: var(--text-color);
64
+
position: relative;
65
+
}
66
+
67
+
.section-title::after {
68
+
content: '';
69
+
position: absolute;
70
+
bottom: -4px;
71
+
left: 0;
72
+
width: 3rem;
73
+
height: 3px;
74
+
background: var(--link-color);
75
+
border-radius: 0px;
76
+
}
77
+
78
+
.latest-post-container {
79
+
display: grid;
80
+
grid-template-columns: 1fr;
81
+
max-width: 400px;
82
+
}
83
+
84
+
/* Responsive adjustments */
85
+
@media (max-width: 640px) {
86
+
.section-header {
87
+
flex-direction: column;
88
+
align-items: flex-start;
89
+
gap: 1rem;
90
+
}
91
+
92
+
.section-title {
93
+
font-size: 1.5rem;
94
+
}
95
+
}
96
+
97
+
@media (min-width: 768px) {
98
+
.latest-post-container {
99
+
max-width: 420px;
100
+
}
101
+
}
102
+
</style>
+2
src/lib/components/layout/main/index.ts
+2
src/lib/components/layout/main/index.ts
+11
src/lib/components/post/PostContent.svelte
+11
src/lib/components/post/PostContent.svelte
···
1
+
<script lang="ts">
2
+
import type { Post } from "$components/shared";
3
+
4
+
export let post: Post;
5
+
</script>
6
+
7
+
<hr class="my-6 border-[var(--button-bg)]" />
8
+
<article class="prose dark:prose-invert mx-auto text-center">
9
+
{@html post.content}
10
+
</article>
11
+
<hr class="my-6 border-[var(--button-bg)]" />
+62
src/lib/components/post/PostHead.svelte
+62
src/lib/components/post/PostHead.svelte
···
1
+
<script lang="ts">
2
+
import { getStores } from "$app/stores";
3
+
const { page } = getStores();
4
+
import type { Post } from "$components/shared";
5
+
import { env } from "$env/dynamic/public";
6
+
7
+
export let post: Post | undefined;
8
+
</script>
9
+
10
+
<svelte:head>
11
+
{#if post !== undefined}
12
+
<title>{post?.title} - Blog - Site Name</title>
13
+
<meta name="description" content={post.excerpt} />
14
+
<meta
15
+
name="keywords"
16
+
content="personal blog, Blog - Site Name"
17
+
/>
18
+
19
+
<!-- Open Graph / Facebook -->
20
+
<meta property="og:type" content="article" />
21
+
<meta property="og:url" content={$page.url.origin + $page.url.pathname} />
22
+
<meta
23
+
property="og:title"
24
+
content={`${post.title} - Blog - Site Name`}
25
+
/>
26
+
<meta property="og:description" content={post.excerpt} />
27
+
<meta property="og:site_name" content="Blog - Site Name" />
28
+
{#if $page.url.origin}
29
+
<meta property="og:image" content={$page.url.origin + "/embed/blog.png"} />
30
+
{/if}
31
+
<meta property="og:image:width" content="1200" />
32
+
<meta property="og:image:height" content="630" />
33
+
<meta
34
+
property="article:published_time"
35
+
content={post.createdAt.toISOString()}
36
+
/>
37
+
<meta property="article:word_count" content={post.wordCount.toString()} />
38
+
39
+
<!-- Fediverse -->
40
+
{#if env.PUBLIC_ACTIVITYPUB_USER && env.PUBLIC_ACTIVITYPUB_USER.length > 0}
41
+
<meta name="fediverse:creator" content={env.PUBLIC_ACTIVITYPUB_USER}>
42
+
{/if}
43
+
44
+
<!-- Twitter -->
45
+
<meta name="twitter:card" content="summary_large_image" />
46
+
<meta name="twitter:url" content={$page.url.origin + $page.url.pathname} />
47
+
<meta
48
+
name="twitter:title"
49
+
content={`${post.title} - Blog - Site Name`}
50
+
/>
51
+
<meta name="twitter:description" content={post.excerpt} />
52
+
{#if $page.url.origin}
53
+
<meta name="twitter:image" content={$page.url.origin + "/embed/blog.png"} />
54
+
{/if}
55
+
{:else}
56
+
<title>Post Not Found - Blog - Site Name</title>
57
+
<meta
58
+
name="description"
59
+
content="The requested blog post could not be found."
60
+
/>
61
+
{/if}
62
+
</svelte:head>
+100
src/lib/components/post/PostHeader.svelte
+100
src/lib/components/post/PostHeader.svelte
···
1
+
<script lang="ts">
2
+
import { fade } from "svelte/transition";
3
+
import { formatRelativeTime, formatDate } from "$utils/formatters";
4
+
import { ShareIcons } from "$components/icons";
5
+
import { formatNumber } from "$utils/formatters";
6
+
import type { Post } from "$components/shared";
7
+
import { onMount } from 'svelte';
8
+
9
+
let { post, profile, rkey, localeLoaded } = $props<{
10
+
post: Post;
11
+
profile: any;
12
+
rkey: string;
13
+
localeLoaded: boolean;
14
+
}>();
15
+
16
+
// Determine singular or plural for word count
17
+
let wordLabel = post.wordCount === 1 ? "word" : "words";
18
+
19
+
let displayDate = $derived(localeLoaded && post.createdAt ? formatRelativeTime(post.createdAt) : 'datetime loading...');
20
+
let absoluteDisplayDate = $derived(localeLoaded && post.createdAt ? formatDate(new Date(post.createdAt)) : 'datetime loading...');
21
+
22
+
let fediverseCreator = $state('');
23
+
24
+
onMount(() => {
25
+
const metaTag = document.querySelector('meta[name="fediverse:creator"]');
26
+
if (metaTag) {
27
+
fediverseCreator = metaTag.getAttribute('content') || '';
28
+
} else if (profile?.did) {
29
+
fediverseCreator = `https://bsky.app/profile/${profile.handle}`;
30
+
}
31
+
});
32
+
</script>
33
+
34
+
<!-- Title with more breathing room -->
35
+
<div class="flex items-center justify-between">
36
+
<div class="flex-1"></div>
37
+
<h1 class="text-center my-12 flex-grow leading-relaxed">{post.title}</h1>
38
+
<div class="flex-1"></div>
39
+
</div>
40
+
41
+
<!-- Metadata section with improved spacing and grouping -->
42
+
<div class="text-center text-[var(--text-color)] opacity-80 mb-12 space-y-4">
43
+
<!-- Author and date info -->
44
+
<div class="space-y-2">
45
+
<p class="text-base">
46
+
last updated by <a
47
+
href={`https://bsky.app/profile/${profile?.handle}`}
48
+
class="text-[var(--link-color)] hover:text-[var(--link-hover-color)] font-medium"
49
+
>{#key profile?.displayName}
50
+
<span transition:fade={{ duration: 200 }}>{profile?.displayName}</span>
51
+
{/key}</a
52
+
>
53
+
<span transition:fade={{ duration: 200 }}>{displayDate}</span>
54
+
</p>
55
+
<p class="text-sm opacity-70">
56
+
<span transition:fade={{ duration: 200 }}>({absoluteDisplayDate})</span>
57
+
</p>
58
+
</div>
59
+
60
+
<!-- Links section with subtle separation -->
61
+
<div class="pt-3 mt-1">
62
+
<p class="text-sm opacity-75">
63
+
View on <a
64
+
href={`https://whtwnd.nat.vg/${profile?.did}/${rkey}`}
65
+
onerror={(e) => {
66
+
e.preventDefault();
67
+
if (e.target instanceof HTMLAnchorElement) {
68
+
e.target.href = `https://whtwnd.com/${profile?.did}/${rkey}`;
69
+
}
70
+
}}
71
+
class="hover:text-[var(--link-hover-color)] underline decoration-dotted">WhiteWind</a
72
+
>
73
+
or see the record at
74
+
<a
75
+
href={`https://atproto.at/viewer?uri=${profile?.did}/com.whtwnd.blog.entry/${rkey}`}
76
+
onerror={(e) => {
77
+
e.preventDefault();
78
+
if (e.target instanceof HTMLAnchorElement) {
79
+
e.target.href = `https://pdsls.dev/at://${profile?.did}/com.whtwnd.blog.entry/${rkey}`;
80
+
e.target.textContent = 'PDSls';
81
+
}
82
+
}}
83
+
class="hover:text-[var(--link-hover-color)] underline decoration-dotted">atproto.at</a
84
+
>
85
+
</p>
86
+
</div>
87
+
88
+
<!-- Reading time with subtle emphasis -->
89
+
<div class="px-2 py-1 inline-block">
90
+
<p class="text-sm opacity-70">
91
+
{Math.ceil(post.wordCount / 200)} min read • {formatNumber(post.wordCount)}
92
+
{wordLabel}
93
+
</p>
94
+
</div>
95
+
96
+
<!-- Share icons with reduced spacing -->
97
+
<div class="pt-1">
98
+
<ShareIcons title={post.title} {profile} />
99
+
</div>
100
+
</div>
+4
src/lib/components/post/index.ts
+4
src/lib/components/post/index.ts
+69
src/lib/components/profile/Profile.svelte
+69
src/lib/components/profile/Profile.svelte
···
1
+
<script lang="ts">
2
+
// The profile object is passed as a prop to this component.
3
+
export let profile: any;
4
+
import { Status } from ".";
5
+
</script>
6
+
7
+
<!-- Profile Banner: Displays the user's banner image. -->
8
+
<div
9
+
class="profile-banner p-4 relative rounded-none mx-2 mb-2"
10
+
style="background-image: url({profile?.banner}); background-size: cover; background-position: center; min-height: 150px;"
11
+
></div>
12
+
13
+
{#if profile}
14
+
<!-- Profile Content: Main container for avatar, user info, and status. -->
15
+
<div class="profile-content mx-2 mb-8 relative">
16
+
<!-- Mobile: Stack vertically, Desktop: Side by side -->
17
+
<div class="flex flex-col sm:flex-row sm:items-start text-left sm:gap-6">
18
+
<!-- Profile Avatar -->
19
+
<img
20
+
src={profile?.avatar}
21
+
alt="{profile?.displayName || 'User'}'s avatar"
22
+
class="rounded-none shadow-lg hover:transform-none flex-shrink-0 relative z-10
23
+
w-24 h-24 -mt-12 mx-auto mb-4
24
+
sm:w-32 sm:h-32 sm:-mt-16 sm:mx-0 sm:mb-0"
25
+
/>
26
+
27
+
<!-- User Information: Display name, handle, DID, description, status -->
28
+
<div class="flex-1 min-w-0 p-4 rounded-none overflow-hidden" style="background: var(--card-bg);">
29
+
<div class="mb-3">
30
+
<h4 class="text-lg font-semibold mb-1 leading-tight truncate text-center sm:text-left">{profile?.displayName}</h4>
31
+
<h6 class="mb-2 text-center sm:text-left">
32
+
<a
33
+
href="https://bsky.app/profile/{profile?.handle}"
34
+
class="text-link hover:text-link-hover text-sm truncate block">@{profile?.handle}</a
35
+
>
36
+
</h6>
37
+
<h6 class="opacity-40 mb-3 text-center sm:text-left">
38
+
<span class="text-xs font-mono overflow-hidden text-ellipsis whitespace-nowrap hidden sm:block">{profile?.did}</span>
39
+
</h6>
40
+
</div>
41
+
42
+
<!-- Profile Description -->
43
+
{#if profile?.description}
44
+
<div class="mb-3">
45
+
<p class="text-sm leading-relaxed text-center sm:text-left">{profile?.description}</p>
46
+
</div>
47
+
{/if}
48
+
49
+
<!-- Display consolidated status/music using the updated Status component -->
50
+
<div class="text-center sm:text-left">
51
+
<Status {profile} />
52
+
</div>
53
+
</div>
54
+
</div>
55
+
</div>
56
+
{:else}
57
+
<!-- Placeholder for app.bsky.actor.profile -->
58
+
<div
59
+
class="profile-content flex flex-col items-center justify-center text-center mx-2 p-4 relative rounded-none"
60
+
style="background: var(--card-bg);"
61
+
>
62
+
<p class="text-center text-sm italic opacity-75">
63
+
create a `app.bsky.actor.profile` record at <a
64
+
href="https://bsky.app/"
65
+
class="text-link hover:text-link-hover">https://bsky.app/</a
66
+
>
67
+
</p>
68
+
</div>
69
+
{/if}
+125
src/lib/components/profile/Status.svelte
+125
src/lib/components/profile/Status.svelte
···
1
+
<script lang="ts">
2
+
import { onMount } from "svelte";
3
+
import { env } from "$env/dynamic/public";
4
+
import { fade } from "svelte/transition";
5
+
6
+
/**
7
+
* The profile object is passed as a prop to this component.
8
+
* It should contain at least 'displayName', 'handle', or 'did' fields.
9
+
*/
10
+
export let profile: any;
11
+
12
+
// RecentFM-related state
13
+
let musicLoading = false;
14
+
let musicError: string | null = null;
15
+
let trackData: {
16
+
name: string;
17
+
artist: string;
18
+
url: string;
19
+
} | null = null;
20
+
let showContent = false;
21
+
22
+
/**
23
+
* Fetches recent music from Last.fm via RecentFM API
24
+
*/
25
+
async function fetchRecentMusic(): Promise<void> {
26
+
const lastfmUsername = env.PUBLIC_LASTFM_USERNAME;
27
+
28
+
if (!lastfmUsername) {
29
+
return;
30
+
}
31
+
32
+
musicLoading = true;
33
+
musicError = null;
34
+
35
+
try {
36
+
const params = new URLSearchParams({
37
+
username: lastfmUsername,
38
+
emoji: "🎧",
39
+
nomoji: "false",
40
+
});
41
+
42
+
const url = `https://recentfm.rknight.me/now.php?${params.toString()}`;
43
+
44
+
const response = await fetch(url);
45
+
46
+
if (!response.ok) {
47
+
throw new Error(`HTTP error! status: ${response.status}`);
48
+
}
49
+
50
+
const data = await response.json();
51
+
52
+
if (data.content) {
53
+
const parser = new DOMParser();
54
+
const doc = parser.parseFromString(data.content, "text/html");
55
+
const link = doc.querySelector("a");
56
+
57
+
if (link) {
58
+
const fullText = link.textContent || "";
59
+
const url = link.href;
60
+
61
+
const match = fullText.match(/^(.+?) by (.+)$/);
62
+
if (match) {
63
+
trackData = {
64
+
name: match[1].trim(),
65
+
artist: match[2].trim(),
66
+
url: url,
67
+
};
68
+
}
69
+
}
70
+
}
71
+
} catch (err) {
72
+
console.error("[RecentFM] Error fetching RecentFM data:", err);
73
+
musicError = "Failed to load recent tracks";
74
+
} finally {
75
+
musicLoading = false;
76
+
}
77
+
}
78
+
79
+
// Load data on mount
80
+
onMount(async () => {
81
+
await fetchRecentMusic();
82
+
83
+
// Introduce a delay before showing content
84
+
setTimeout(() => {
85
+
showContent = true;
86
+
}, 2000); // 2 second delay
87
+
});
88
+
</script>
89
+
90
+
<!-- Last.fm Music Display -->
91
+
{#if showContent && profile}
92
+
<div transition:fade={{ duration: 500 }}>
93
+
<div class="py-2">
94
+
{#if trackData}
95
+
<!-- Music Display -->
96
+
<div class="recent-track-info">
97
+
<p class="text-xs opacity-60 text-center sm:text-left">
98
+
{profile.displayName || profile.handle || profile.did} was last listening
99
+
to
100
+
</p>
101
+
<p class="text-xs font-medium text-center sm:text-left mt-0.5">
102
+
<a
103
+
href={trackData.url}
104
+
class="text-link hover:text-link-hover"
105
+
target="_blank"
106
+
rel="noopener noreferrer"
107
+
>
108
+
"{trackData.name}" by {trackData.artist}
109
+
</a>
110
+
</p>
111
+
</div>
112
+
{:else if musicError}
113
+
<p class="text-xs opacity-60 text-center sm:text-left text-red-500">
114
+
{musicError}
115
+
</p>
116
+
{/if}
117
+
118
+
{#if musicLoading}
119
+
<p class="text-xs opacity-60 italic text-center sm:text-left">
120
+
Loading recent tracks...
121
+
</p>
122
+
{/if}
123
+
</div>
124
+
</div>
125
+
{/if}
+2
src/lib/components/profile/index.ts
+2
src/lib/components/profile/index.ts
+74
src/lib/components/profile/profile.ts
+74
src/lib/components/profile/profile.ts
···
1
+
import { env } from "$env/dynamic/public";
2
+
import { getCache, setCache } from "$utils/cache";
3
+
import type { Profile } from "$components/shared";
4
+
5
+
export async function safeFetch(url: string, fetch: typeof globalThis.fetch) {
6
+
try {
7
+
const response = await fetch(url);
8
+
if (!response.ok)
9
+
throw new Error(`HTTP error! status: ${response.status}, statusText: ${response.statusText}`);
10
+
return await response.json();
11
+
} catch (error: unknown) {
12
+
// Catch network errors (e.g., connection refused, timeout)
13
+
console.error(`Network error fetching ${url}:`, error);
14
+
if (error instanceof Error) {
15
+
throw new Error(`Failed to fetch ${url}: ${error.message}`);
16
+
} else {
17
+
throw new Error(`Failed to fetch ${url}: An unknown error occurred`);
18
+
}
19
+
}
20
+
}
21
+
22
+
export async function getProfile(fetch: typeof globalThis.fetch): Promise<Profile> {
23
+
const cacheKey = `profile_${env.PUBLIC_ATPROTOCOL_USER}`;
24
+
let profile: Profile | null = getCache<Profile>(cacheKey);
25
+
26
+
if (profile) {
27
+
return profile;
28
+
}
29
+
30
+
try {
31
+
const fetchProfile = await safeFetch(
32
+
`https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=${env.PUBLIC_ATPROTOCOL_USER}`,
33
+
fetch
34
+
);
35
+
const split = fetchProfile["did"].split(":");
36
+
let diddoc;
37
+
if (split[0] === "did") {
38
+
if (split[1] === "plc") {
39
+
diddoc = await safeFetch(`https://plc.directory/${fetchProfile["did"]}`, fetch);
40
+
} else if (split[1] === "web") {
41
+
diddoc = await safeFetch("https://" + split[2] + "/.well-known/did.json", fetch);
42
+
}
43
+
} else {
44
+
throw new Error("Invalid DID, malformed");
45
+
}
46
+
let pdsurl;
47
+
for (const service of diddoc["service"]) {
48
+
if (service["id"] === "#atproto_pds") {
49
+
pdsurl = service["serviceEndpoint"];
50
+
}
51
+
}
52
+
if (!pdsurl) {
53
+
throw new Error("DID lacks #atproto_pds service");
54
+
}
55
+
profile = {
56
+
avatar: fetchProfile["avatar"],
57
+
banner: fetchProfile["banner"],
58
+
displayName: fetchProfile["displayName"],
59
+
did: fetchProfile["did"],
60
+
handle: fetchProfile["handle"],
61
+
description: fetchProfile["description"],
62
+
pds: pdsurl,
63
+
};
64
+
setCache(cacheKey, profile);
65
+
return profile;
66
+
} catch (error: unknown) {
67
+
console.error("Error fetching profile:", error);
68
+
if (error instanceof Error) {
69
+
throw error; // Re-throw the error after logging
70
+
} else {
71
+
throw new Error("An unknown error occurred while fetching profile");
72
+
}
73
+
}
74
+
}
+310
src/lib/css/app.css
+310
src/lib/css/app.css
···
1
+
@import "$css/variables.css";
2
+
3
+
/* Minimalist flat styles with gentle dark pastel green theme */
4
+
@tailwind base;
5
+
@tailwind components;
6
+
@tailwind utilities;
7
+
8
+
@layer base {
9
+
10
+
/* Scrollbar styling */
11
+
::-webkit-scrollbar {
12
+
width: 10px;
13
+
height: 10px;
14
+
}
15
+
16
+
::-webkit-scrollbar-track {
17
+
background: var(--header-footer-bg);
18
+
}
19
+
20
+
::-webkit-scrollbar-thumb {
21
+
background: var(--button-bg);
22
+
border-radius: 0px;
23
+
}
24
+
25
+
::-webkit-scrollbar-thumb:hover {
26
+
background: var(--button-hover-bg);
27
+
}
28
+
29
+
* {
30
+
color: var(--text-color);
31
+
}
32
+
33
+
body {
34
+
background-color: var(--background-color);
35
+
color: var(--text-color);
36
+
font-family: "Recursive", sans-serif;
37
+
font-variation-settings: "MONO" 0, "CASL" 0, "wght" 300, "slnt" 0,
38
+
"CRSV" 0.5;
39
+
}
40
+
41
+
h1 {
42
+
@apply text-4xl font-bold;
43
+
font-variation-settings: "MONO" 0, "CASL" 0.8, "wght" 700, "slnt" 0,
44
+
"CRSV" 0.9;
45
+
}
46
+
47
+
h2 {
48
+
@apply text-3xl font-bold;
49
+
font-variation-settings: "MONO" 0, "CASL" 0.7, "wght" 650, "slnt" 0,
50
+
"CRSV" 0.85;
51
+
}
52
+
53
+
h3 {
54
+
@apply text-2xl font-bold;
55
+
font-variation-settings: "MONO" 0, "CASL" 0.6, "wght" 600, "slnt" 0,
56
+
"CRSV" 0.8;
57
+
}
58
+
59
+
h4 {
60
+
@apply text-xl font-semibold;
61
+
font-variation-settings: "MONO" 0, "CASL" 0.5, "wght" 550, "slnt" 0,
62
+
"CRSV" 0.75;
63
+
}
64
+
65
+
h5 {
66
+
@apply text-lg font-semibold;
67
+
font-variation-settings: "MONO" 0, "CASL" 0.4, "wght" 500, "slnt" 0,
68
+
"CRSV" 0.7;
69
+
}
70
+
71
+
h6 {
72
+
@apply text-sm font-semibold;
73
+
font-variation-settings: "MONO" 0, "CASL" 0.3, "wght" 450, "slnt" 0,
74
+
"CRSV" 0.6;
75
+
}
76
+
77
+
a {
78
+
@apply text-[var(--link-color)] hover:text-[var(--link-hover-color)] no-underline;
79
+
font-variation-settings: "MONO" 0, "CASL" 0, "wght" 450, "slnt" 0,
80
+
"CRSV" 0.5;
81
+
/* Reduced transition complexity */
82
+
transition: color 0.2s ease;
83
+
}
84
+
85
+
a:hover {
86
+
font-variation-settings: "MONO" 0, "CASL" 0, "wght" 600, "slnt" 0,
87
+
"CRSV" 0.5;
88
+
}
89
+
90
+
a,
91
+
a:visited,
92
+
a:hover,
93
+
a:active,
94
+
a:focus {
95
+
text-decoration: none !important;
96
+
}
97
+
98
+
/* Last.FM info styles */
99
+
.recent-played {
100
+
@apply text-center;
101
+
}
102
+
103
+
.recent-track-info {
104
+
@apply space-y-0.5;
105
+
}
106
+
107
+
/* Header links - simplified transitions */
108
+
header a,
109
+
a.font-medium {
110
+
@apply no-underline hover:no-underline inline-block;
111
+
/* Only animate on hover, not constantly */
112
+
transition: transform 0.2s ease;
113
+
}
114
+
115
+
header a:hover,
116
+
a.font-medium:hover {
117
+
transform: scale(1.1);
118
+
}
119
+
120
+
/* Typography styles for blog content */
121
+
.prose {
122
+
@apply max-w-none;
123
+
}
124
+
125
+
/* Left-align headings in blog posts */
126
+
.prose h1,
127
+
.prose h2,
128
+
.prose h3,
129
+
.prose h4,
130
+
.prose h5,
131
+
.prose h6 {
132
+
@apply text-left;
133
+
}
134
+
135
+
/* Left-align paragraphs and lists */
136
+
.prose p,
137
+
.prose ul,
138
+
.prose ol {
139
+
@apply text-left;
140
+
}
141
+
142
+
.prose img {
143
+
@apply border-0 rounded-lg mx-auto;
144
+
}
145
+
146
+
a {
147
+
color: var(--link-color);
148
+
text-decoration: none;
149
+
}
150
+
151
+
a:hover {
152
+
color: var(--link-hover-color);
153
+
text-decoration: none;
154
+
}
155
+
156
+
/* Update prose elements to use variables */
157
+
.prose a {
158
+
color: var(--link-color);
159
+
text-decoration: none;
160
+
}
161
+
162
+
.prose a:hover {
163
+
color: var(--link-hover-color);
164
+
}
165
+
166
+
.prose blockquote {
167
+
@apply border-l-2 pl-4 rounded-none mx-auto;
168
+
border-color: var(--button-bg);
169
+
background-color: var(--card-bg);
170
+
}
171
+
172
+
.prose code {
173
+
background-color: var(--card-bg);
174
+
@apply rounded px-1 font-mono;
175
+
}
176
+
177
+
.prose pre {
178
+
background-color: var(--card-bg);
179
+
@apply rounded-md mx-auto text-left font-mono p-4 overflow-x-auto;
180
+
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.3);
181
+
line-height: 1.5;
182
+
}
183
+
184
+
.prose pre code {
185
+
@apply bg-transparent p-0;
186
+
}
187
+
188
+
/* Post grid and cards - simplified animations */
189
+
.post-card {
190
+
@apply border-0 rounded-none p-4 shadow-md;
191
+
background-color: var(--card-bg);
192
+
color: var(--text-color);
193
+
/* Only animate specific properties on hover */
194
+
transition: transform 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
195
+
}
196
+
197
+
.post-card:hover {
198
+
background-color: var(--header-footer-bg);
199
+
transform: scale(1.05);
200
+
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
201
+
}
202
+
203
+
/* Profile banner - simplified */
204
+
.profile-banner {
205
+
@apply bg-cover bg-center rounded-md border-0;
206
+
transition: opacity 0.2s ease;
207
+
}
208
+
209
+
.profile-banner:hover {
210
+
opacity: 0.95;
211
+
}
212
+
213
+
/* Profile section elements */
214
+
.flex-wrap {
215
+
@apply gap-4 items-center;
216
+
}
217
+
218
+
div.items-center {
219
+
@apply p-3;
220
+
}
221
+
222
+
.rounded-full {
223
+
@apply shadow-none;
224
+
transition: transform 0.2s ease;
225
+
}
226
+
227
+
.rounded-full:hover {
228
+
transform: scale(1.05);
229
+
}
230
+
231
+
/* Account handle and related info */
232
+
h4.content-around,
233
+
h6.content-around {
234
+
@apply my-2 text-left;
235
+
}
236
+
237
+
/* Links styling within the profile section - simplified */
238
+
.profile-banner a,
239
+
.flex-wrap a,
240
+
div.items-center a,
241
+
h4.content-around a,
242
+
h6.content-around a {
243
+
@apply no-underline hover:no-underline inline-block;
244
+
color: var(--link-color);
245
+
transition: color 0.2s ease, transform 0.2s ease;
246
+
}
247
+
248
+
.profile-banner a:hover,
249
+
.flex-wrap a:hover,
250
+
div.items-center a:hover,
251
+
h4.content-around a:hover,
252
+
h6.content-around a:hover {
253
+
color: var(--link-hover-color);
254
+
transform: scale(1.1);
255
+
}
256
+
257
+
/* Remove any borders throughout profile section */
258
+
.profile-banner,
259
+
.profile-banner *,
260
+
.flex-wrap *,
261
+
div.items-center *,
262
+
.rounded-full,
263
+
h4.content-around *,
264
+
h6.content-around * {
265
+
@apply border-0;
266
+
}
267
+
268
+
/* Comments section styling */
269
+
:global(.comments-section) {
270
+
@apply mt-8 p-4 border-0 rounded-md bg-[var(--header-footer-bg)];
271
+
}
272
+
273
+
:global(.comments-section input, .comments-section button) {
274
+
@apply bg-[var(--button-bg)] border-0 rounded-sm;
275
+
}
276
+
}
277
+
278
+
.prose+.flex.justify-between {
279
+
@apply mt-12 mb-8;
280
+
}
281
+
282
+
.prose+.flex.justify-between a {
283
+
@apply max-w-[45%] truncate;
284
+
}
285
+
286
+
.prose+.flex.justify-between a:first-child {
287
+
@apply text-left;
288
+
}
289
+
290
+
.prose+.flex.justify-between a:last-child {
291
+
@apply text-right;
292
+
}
293
+
294
+
.text-link {
295
+
color: var(--link-color);
296
+
}
297
+
298
+
.text-link-hover,
299
+
.text-link:hover {
300
+
color: var(--link-hover-color);
301
+
}
302
+
303
+
.text-primary {
304
+
color: var(--text-color);
305
+
}
306
+
307
+
.text-muted {
308
+
color: var(--link-color);
309
+
opacity: 0.8;
310
+
}
+24
src/lib/css/variables.css
+24
src/lib/css/variables.css
···
1
+
/* Default mono theme - dark mode */
2
+
:root {
3
+
font-family: sans-serif, system-ui;
4
+
--background-color: #1a1a1a;
5
+
--text-color: #f0f0f0;
6
+
--header-footer-bg: #2a2a2a;
7
+
--button-bg: #4a4a4a;
8
+
--button-hover-bg: #6a6a6a;
9
+
--link-color: #9a9a9a;
10
+
--link-hover-color: #b0b0b0;
11
+
--card-bg: #2a2a2a;
12
+
}
13
+
14
+
/* Default mono theme - light mode */
15
+
:root.light {
16
+
--background-color: #f0f0f0;
17
+
--text-color: #1a1a1a;
18
+
--header-footer-bg: #e0e0e0;
19
+
--button-bg: #b0b0b0;
20
+
--button-hover-bg: #9a9a9a;
21
+
--link-color: #6a6a6a;
22
+
--link-hover-color: #4a4a4a;
23
+
--card-bg: #e0e0e0;
24
+
}
+3
src/lib/parser/index.ts
+3
src/lib/parser/index.ts
+34
src/lib/parser/parser.ts
+34
src/lib/parser/parser.ts
···
1
+
import { extractTextFromMarkdown, calculateWordCount } from "$utils/textProcessor";
2
+
import { createMarkdownProcessor } from "./processor";
3
+
import type { Post, MarkdownPost } from "./types";
4
+
5
+
export async function parse(mdposts: Map<string, MarkdownPost>): Promise<Map<string, Post>> {
6
+
const posts: Map<string, Post> = new Map();
7
+
const processor = createMarkdownProcessor();
8
+
9
+
for (const [rkey, post] of mdposts) {
10
+
const parsedHtml = String(
11
+
await processor.process(post.mdcontent)
12
+
);
13
+
14
+
// Ensure mdcontent is a string before processing
15
+
const markdownContent = post.mdcontent || '';
16
+
17
+
// Extract plain text for excerpt
18
+
const excerpt = await extractTextFromMarkdown(markdownContent);
19
+
20
+
// Calculate word count from markdown content
21
+
const wordCount = calculateWordCount(markdownContent);
22
+
23
+
posts.set(rkey, {
24
+
title: post.title,
25
+
rkey: post.rkey,
26
+
createdAt: post.createdAt,
27
+
content: parsedHtml,
28
+
excerpt,
29
+
wordCount,
30
+
});
31
+
}
32
+
33
+
return posts;
34
+
}
+25
src/lib/parser/plugins.ts
+25
src/lib/parser/plugins.ts
···
1
+
import type { Node, Root, Element, Plugin } from "./types";
2
+
3
+
// Automatically enforce https on PDS images. Heavily inspired by WhiteWind's blob replacer:
4
+
// https://github.com/whtwnd/whitewind-blog/blob/7eb8d4623eea617fd562b93d66a0e235323a2f9a/frontend/src/services/DocProvider.tsx#L90
5
+
// In theory we could also use their cache, but I'd like to rely on their API as little as possible, opting to pull from the PDS instead.
6
+
const upgradeImage = (child: Node): void => {
7
+
if (child.type !== "element") {
8
+
return;
9
+
}
10
+
const elem = child as Element;
11
+
if (elem.tagName === "img") {
12
+
// Ensure https
13
+
const src = elem.properties.src;
14
+
if (src !== undefined && typeof src === "string") {
15
+
elem.properties.src = src.replace(/http:\/\//, "https://");
16
+
}
17
+
}
18
+
elem.children.forEach((child) => upgradeImage(child));
19
+
};
20
+
21
+
export const rehypeUpgradeImage: Plugin<[], Root, Node> = () => {
22
+
return (tree) => {
23
+
tree.children.forEach((child) => upgradeImage(child));
24
+
};
25
+
};
+21
src/lib/parser/processor.ts
+21
src/lib/parser/processor.ts
···
1
+
import rehypeStringify from "rehype-stringify";
2
+
import remarkParse from "remark-parse";
3
+
import remarkGfm from "remark-gfm";
4
+
import remarkRehype from "remark-rehype";
5
+
import rehypeSanitize from "rehype-sanitize";
6
+
import rehypeRaw from "rehype-raw";
7
+
import { unified } from "unified";
8
+
import { customSchema } from "./schema";
9
+
import { rehypeUpgradeImage } from "./plugins";
10
+
import type { Schema } from "./types";
11
+
12
+
export const createMarkdownProcessor = () => {
13
+
return unified()
14
+
.use(remarkParse, { fragment: true }) // Parse the MD
15
+
.use(remarkGfm) // Parse GH specific MD
16
+
.use(remarkRehype, { allowDangerousHtml: true }) // Convert to HTML
17
+
.use(rehypeRaw) // Parse HTML that exists as raw text leftover from MD parse
18
+
.use(rehypeUpgradeImage)
19
+
.use(rehypeSanitize, customSchema as Schema) // Sanitize the HTML
20
+
.use(rehypeStringify); // Stringify
21
+
};
+43
src/lib/parser/schema.ts
+43
src/lib/parser/schema.ts
···
1
+
import { defaultSchema } from "rehype-sanitize";
2
+
import type { Schema } from "./types";
3
+
4
+
// WhiteWind's own custom schema:
5
+
// https://github.com/whtwnd/whitewind-blog/blob/7eb8d4623eea617fd562b93d66a0e235323a2f9a/frontend/src/services/DocProvider.tsx#L122
6
+
export const customSchema: Schema = {
7
+
...defaultSchema,
8
+
attributes: {
9
+
...defaultSchema.attributes,
10
+
font: [...(defaultSchema.attributes?.font ?? []), "color"],
11
+
blockquote: [
12
+
...(defaultSchema.attributes?.blockquote ?? []),
13
+
// bluesky
14
+
"className",
15
+
"dataBlueskyUri",
16
+
"dataBlueskyCid",
17
+
// instagram
18
+
"dataInstgrmCaptioned",
19
+
"dataInstgrmPermalink",
20
+
"dataInstgrmVersion",
21
+
],
22
+
iframe: [
23
+
"width",
24
+
"height",
25
+
"title",
26
+
"frameborder",
27
+
"allow",
28
+
"referrerpolicy",
29
+
"allowfullscreen",
30
+
"style",
31
+
"seamless",
32
+
["src", /https:\/\/(www.youtube.com|bandcamp.com)\/.*/],
33
+
],
34
+
section: ["dataFootnotes", "className"],
35
+
},
36
+
tagNames: [
37
+
...(defaultSchema.tagNames ?? []),
38
+
"font",
39
+
"mark",
40
+
"iframe",
41
+
"section",
42
+
],
43
+
};
+8
src/lib/parser/types.ts
+8
src/lib/parser/types.ts
···
1
+
import type { Schema } from "../../../node_modules/rehype-sanitize/lib";
2
+
import type { Node } from "unist";
3
+
import type { Root, Element } from "hast";
4
+
import type { Plugin } from "unified";
5
+
import type { Post, MarkdownPost } from "$components/shared";
6
+
7
+
export type { Schema, Node, Root, Element, Plugin, Post, MarkdownPost };
8
+
+189
src/lib/services/blogService.ts
+189
src/lib/services/blogService.ts
···
1
+
import { getProfile } from "$components/profile/profile";
2
+
import type { Profile, MarkdownPost, Post, BlogServiceResult } from "$components/shared";
3
+
import { parse } from "$lib/parser";
4
+
5
+
// Cache for blog data
6
+
let profile: Profile;
7
+
let allPosts: Map<string, Post>;
8
+
let sortedPosts: Post[] = [];
9
+
10
+
/**
11
+
* Validates and processes a single blog record
12
+
*/
13
+
function processRecord(data: any): MarkdownPost | null {
14
+
const matches = data["uri"].split("/");
15
+
const rkey = matches[matches.length - 1];
16
+
17
+
// Enhanced debugging for development
18
+
if (process.env.NODE_ENV === 'development') {
19
+
console.log('=== Record Debug Info ===');
20
+
console.log('URI:', data["uri"]);
21
+
console.log('Data structure keys:', Object.keys(data));
22
+
}
23
+
24
+
// Try both access patterns to be safe
25
+
const record = data["value"] || data.value;
26
+
27
+
if (!record) {
28
+
console.warn(`No record value found for ${rkey}`, {
29
+
dataKeys: Object.keys(data),
30
+
});
31
+
return null;
32
+
}
33
+
34
+
// Validate URI format and visibility
35
+
if (
36
+
!matches ||
37
+
matches.length !== 5 ||
38
+
!record ||
39
+
(record["visibility"] && record["visibility"] !== "public")
40
+
) {
41
+
if (process.env.NODE_ENV === 'development') {
42
+
console.warn('Post skipped due to validation failure:', {
43
+
rkey,
44
+
matchesLength: matches?.length,
45
+
hasRecord: !!record,
46
+
visibility: record?.["visibility"],
47
+
});
48
+
}
49
+
return null;
50
+
}
51
+
52
+
// Extract fields with fallback patterns
53
+
const content = record["content"] || record.content || (record.value && record.value.content);
54
+
const title = record["title"] || record.title || (record.value && record.value.title);
55
+
const createdAt = record["createdAt"] || record.createdAt || (record.value && record.value.createdAt);
56
+
57
+
// Skip if missing required content
58
+
if (!content) {
59
+
console.warn(`Skipping post with missing content: ${rkey}`);
60
+
return null;
61
+
}
62
+
63
+
// Handle createdAt - use current time as fallback for missing dates
64
+
let createdAtDate: Date;
65
+
if (!createdAt) {
66
+
console.warn(`Post missing createdAt, using current time: ${rkey}`);
67
+
createdAtDate = new Date();
68
+
} else {
69
+
createdAtDate = new Date(createdAt);
70
+
71
+
// Skip posts with invalid dates
72
+
if (isNaN(createdAtDate.getTime())) {
73
+
console.warn(`Skipping post with invalid date: ${rkey}`, {
74
+
rawCreatedAt: createdAt,
75
+
});
76
+
return null;
77
+
}
78
+
}
79
+
80
+
// Use title if available, otherwise generate one
81
+
const finalTitle = title || `Untitled Post (${rkey})`;
82
+
83
+
return {
84
+
title: finalTitle,
85
+
createdAt: createdAtDate,
86
+
mdcontent: content,
87
+
rkey,
88
+
};
89
+
}
90
+
91
+
/**
92
+
* Fetches and processes all blog posts
93
+
*/
94
+
export async function loadAllPosts(fetch: typeof window.fetch): Promise<BlogServiceResult> {
95
+
try {
96
+
// Load profile if not cached
97
+
if (profile === undefined) {
98
+
profile = await getProfile(fetch);
99
+
}
100
+
101
+
// Load posts if not cached
102
+
if (allPosts === undefined) {
103
+
const rawResponse = await fetch(
104
+
`${profile.pds}/xrpc/com.atproto.repo.listRecords?repo=${profile.did}&collection=com.whtwnd.blog.entry`
105
+
);
106
+
107
+
if (!rawResponse.ok) {
108
+
throw new Error(`Failed to fetch posts: ${rawResponse.status}`);
109
+
}
110
+
111
+
const response = await rawResponse.json();
112
+
113
+
if (!response.records || response.records.length === 0) {
114
+
allPosts = new Map();
115
+
sortedPosts = [];
116
+
} else {
117
+
const mdposts: Map<string, MarkdownPost> = new Map();
118
+
119
+
// Process all records
120
+
for (const data of response.records) {
121
+
const processedPost = processRecord(data);
122
+
if (processedPost) {
123
+
mdposts.set(processedPost.rkey, processedPost);
124
+
}
125
+
}
126
+
127
+
console.log(`Successfully processed ${mdposts.size} posts out of ${response.records.length} total records`);
128
+
129
+
// Parse markdown content
130
+
allPosts = await parse(mdposts);
131
+
sortedPosts = Array.from(allPosts.values()).sort(
132
+
(a, b) => b.createdAt.getTime() - a.createdAt.getTime()
133
+
);
134
+
135
+
// Assign postNumber based on chronological order (1 = latest, 2 = second latest, etc.)
136
+
// Assign postNumber based on reverse chronological order (total posts = latest, 1 = oldest)
137
+
const totalPosts = sortedPosts.length;
138
+
sortedPosts.forEach((post, index) => {
139
+
post.postNumber = totalPosts - index;
140
+
});
141
+
}
142
+
}
143
+
144
+
return {
145
+
posts: allPosts,
146
+
profile,
147
+
sortedPosts,
148
+
getPost: (rkey: string) => allPosts.get(rkey) || null,
149
+
getAdjacentPosts: (rkey: string): { previous: Post | null; next: Post | null } => {
150
+
const index = sortedPosts.findIndex((post) => post.rkey === rkey);
151
+
return {
152
+
previous: index > 0 ? sortedPosts[index - 1] : null,
153
+
next: index < sortedPosts.length - 1 ? sortedPosts[index + 1] : null,
154
+
};
155
+
},
156
+
};
157
+
} catch (error) {
158
+
console.error("Error in loadAllPosts:", error);
159
+
return {
160
+
posts: new Map(),
161
+
profile: profile || ({} as Profile),
162
+
sortedPosts: [],
163
+
getPost: () => null,
164
+
getAdjacentPosts: () => ({ previous: null, next: null }),
165
+
};
166
+
}
167
+
}
168
+
169
+
/**
170
+
* Gets the latest N blog posts (for homepage display)
171
+
*/
172
+
export async function getLatestPosts(fetch: typeof window.fetch, limit: number = 3): Promise<Post[]> {
173
+
try {
174
+
const { sortedPosts } = await loadAllPosts(fetch);
175
+
return sortedPosts.slice(0, limit);
176
+
} catch (error) {
177
+
console.error("Error fetching latest posts:", error);
178
+
return [];
179
+
}
180
+
}
181
+
182
+
/**
183
+
* Clears the cache (useful for testing or force refresh)
184
+
*/
185
+
export function clearCache(): void {
186
+
profile = undefined as any;
187
+
allPosts = undefined as any;
188
+
sortedPosts = [];
189
+
}
+130
src/lib/themeLoader.ts
+130
src/lib/themeLoader.ts
···
1
+
import type { Theme } from "$components/shared/interfaces";
2
+
3
+
// Theme configuration - single source of truth
4
+
export const THEMES: Theme[] = [
5
+
{ id: "default", name: "Green (Default)" },
6
+
];
7
+
8
+
export const THEME_STORAGE_KEYS = {
9
+
MODE: "theme-mode",
10
+
COLOR: "color-theme",
11
+
} as const;
12
+
13
+
export type ThemeMode = "light" | "dark";
14
+
15
+
/**
16
+
* Applies theme classes to the document element
17
+
*/
18
+
export function applyTheme(isDarkMode: boolean, themeId: string): void {
19
+
// Remove all existing theme classes
20
+
document.documentElement.classList.remove("light");
21
+
THEMES.forEach((theme) => {
22
+
if (theme.id !== "default") {
23
+
document.documentElement.classList.remove(theme.id);
24
+
}
25
+
});
26
+
27
+
// Apply light mode class if needed
28
+
if (!isDarkMode) {
29
+
document.documentElement.classList.add("light");
30
+
}
31
+
32
+
// Apply color theme class if not default
33
+
if (themeId !== "default") {
34
+
document.documentElement.classList.add(themeId);
35
+
}
36
+
}
37
+
38
+
/**
39
+
* Gets the user's theme preferences from localStorage and system
40
+
*/
41
+
export function getThemePreferences(): { isDarkMode: boolean; themeId: string } {
42
+
const savedThemeMode = localStorage.getItem(THEME_STORAGE_KEYS.MODE);
43
+
const savedColorTheme = localStorage.getItem(THEME_STORAGE_KEYS.COLOR);
44
+
45
+
let isDarkMode: boolean;
46
+
if (savedThemeMode) {
47
+
isDarkMode = savedThemeMode === "dark";
48
+
} else {
49
+
// Use system preference as default
50
+
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
51
+
isDarkMode = prefersDark;
52
+
}
53
+
54
+
const themeId = savedColorTheme || "default";
55
+
56
+
return { isDarkMode, themeId };
57
+
}
58
+
59
+
/**
60
+
* Saves theme preferences to localStorage
61
+
*/
62
+
export function saveThemePreferences(isDarkMode: boolean, themeId: string): void {
63
+
localStorage.setItem(THEME_STORAGE_KEYS.MODE, isDarkMode ? "dark" : "light");
64
+
localStorage.setItem(THEME_STORAGE_KEYS.COLOR, themeId);
65
+
}
66
+
67
+
/**
68
+
* Updates the theme-color meta tag for browser UI
69
+
*/
70
+
export function updateThemeColorMeta(): void {
71
+
const themeColor = getComputedStyle(document.documentElement)
72
+
.getPropertyValue("--background-color")
73
+
.trim();
74
+
75
+
let metaTag = document.querySelector('meta[name="theme-color"]') as HTMLMetaElement;
76
+
if (!metaTag) {
77
+
metaTag = document.createElement("meta");
78
+
metaTag.setAttribute("name", "theme-color");
79
+
document.head.appendChild(metaTag);
80
+
}
81
+
metaTag.setAttribute("content", themeColor);
82
+
}
83
+
84
+
/**
85
+
* Dispatches a custom event when theme changes
86
+
*/
87
+
export function dispatchThemeChangeEvent(isDarkMode: boolean, themeId: string): void {
88
+
document.dispatchEvent(
89
+
new CustomEvent("themeChanged", {
90
+
detail: { isDarkMode, theme: themeId },
91
+
})
92
+
);
93
+
}
94
+
95
+
/**
96
+
* Sets up system theme change listener
97
+
*/
98
+
export function setupSystemThemeListener(): void {
99
+
window
100
+
.matchMedia("(prefers-color-scheme: dark)")
101
+
.addEventListener("change", (e) => {
102
+
// Only apply system preference if user hasn't set a manual preference
103
+
if (localStorage.getItem(THEME_STORAGE_KEYS.MODE) === null) {
104
+
const { themeId } = getThemePreferences();
105
+
applyTheme(e.matches, themeId);
106
+
updateThemeColorMeta();
107
+
dispatchThemeChangeEvent(e.matches, themeId);
108
+
}
109
+
});
110
+
}
111
+
112
+
/**
113
+
* Initializes theme system - should be called as early as possible
114
+
*/
115
+
export function initializeTheme(): void {
116
+
const { isDarkMode, themeId } = getThemePreferences();
117
+
applyTheme(isDarkMode, themeId);
118
+
119
+
// Update meta tag when DOM is ready
120
+
if (document.readyState === "loading") {
121
+
document.addEventListener("DOMContentLoaded", updateThemeColorMeta);
122
+
} else {
123
+
updateThemeColorMeta();
124
+
}
125
+
}
126
+
127
+
// Auto-initialize when script loads (for inline usage)
128
+
if (typeof window !== "undefined") {
129
+
initializeTheme();
130
+
}
+39
src/lib/utils/cache.ts
+39
src/lib/utils/cache.ts
···
1
+
/**
2
+
* Caches data in localStorage with a specified expiry time.
3
+
* @param key The key to store the data under.
4
+
* @param data The data to store.
5
+
* @param ttl The time-to-live for the cache in milliseconds (default: 1 hour).
6
+
*/
7
+
export function setCache<T>(key: string, data: T, ttl: number = 3600000): void {
8
+
if (typeof window === 'undefined') {
9
+
return; // Don't cache on the server
10
+
}
11
+
const now = new Date().getTime();
12
+
const item = {
13
+
data: data,
14
+
expiry: now + ttl,
15
+
};
16
+
localStorage.setItem(key, JSON.stringify(item));
17
+
}
18
+
19
+
/**
20
+
* Retrieves data from localStorage if it's not expired.
21
+
* @param key The key to retrieve the data from.
22
+
* @returns The cached data, or null if expired or not found.
23
+
*/
24
+
export function getCache<T>(key: string): T | null {
25
+
if (typeof window === 'undefined') {
26
+
return null; // No cache on the server
27
+
}
28
+
const itemStr = localStorage.getItem(key);
29
+
if (!itemStr) {
30
+
return null;
31
+
}
32
+
const item = JSON.parse(itemStr);
33
+
const now = new Date().getTime();
34
+
if (now > item.expiry) {
35
+
localStorage.removeItem(key);
36
+
return null;
37
+
}
38
+
return item.data;
39
+
}
+121
src/lib/utils/formatters.ts
+121
src/lib/utils/formatters.ts
···
1
+
/**
2
+
* Returns the ordinal suffix for a given number (e.g., "st", "nd", "rd", "th").
3
+
* @param num The number to get the ordinal suffix for.
4
+
* @returns The ordinal suffix.
5
+
*/
6
+
export function getOrdinalSuffix(num: number): string {
7
+
const lastDigit = num % 10;
8
+
const lastTwoDigits = num % 100;
9
+
10
+
if (lastTwoDigits >= 11 && lastTwoDigits <= 13) {
11
+
return 'th';
12
+
}
13
+
14
+
switch (lastDigit) {
15
+
case 1:
16
+
return 'st';
17
+
case 2:
18
+
return 'nd';
19
+
case 3:
20
+
return 'rd';
21
+
default:
22
+
return 'th';
23
+
}
24
+
}
25
+
26
+
export function formatDate(
27
+
date: Date | string,
28
+
locale: string = typeof window !== "undefined"
29
+
? window.navigator.language
30
+
: "en-GB"
31
+
): string {
32
+
const dateObj = new Date(date);
33
+
34
+
const options: Intl.DateTimeFormatOptions = {
35
+
year: "numeric",
36
+
month: "long",
37
+
day: "numeric",
38
+
hour: "2-digit",
39
+
minute: "2-digit",
40
+
};
41
+
42
+
const formattedDate = new Intl.DateTimeFormat(locale, options).format(
43
+
dateObj
44
+
);
45
+
46
+
// Only add ordinal suffix for English locales
47
+
if (locale.startsWith("en")) {
48
+
const day = dateObj.getDate();
49
+
return formattedDate.replace(/(\d+)/, `$1${getOrdinalSuffix(day)}`);
50
+
}
51
+
52
+
return formattedDate;
53
+
}
54
+
55
+
export function formatMonthYear(
56
+
date: Date | string,
57
+
locale: string = typeof window !== "undefined"
58
+
? window.navigator.language
59
+
: "en-GB"
60
+
): string {
61
+
const dateObj = new Date(date);
62
+
63
+
const options: Intl.DateTimeFormatOptions = {
64
+
year: "numeric",
65
+
month: "long",
66
+
};
67
+
68
+
return new Intl.DateTimeFormat(locale, options).format(dateObj);
69
+
}
70
+
71
+
// Function to format a date relative to the current time (e.g., '2 hours ago')
72
+
export function formatRelativeTime(
73
+
date: Date | string,
74
+
locale: string = typeof window !== "undefined"
75
+
? window.navigator.language
76
+
: "en-GB"
77
+
): string {
78
+
const dateObj = new Date(date);
79
+
const now = new Date();
80
+
const diffInSeconds = Math.round((now.getTime() - dateObj.getTime()) / 1000);
81
+
82
+
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
83
+
84
+
const units: { unit: Intl.RelativeTimeFormatUnit; seconds: number }[] = [
85
+
{ unit: "year", seconds: 31536000 },
86
+
{ unit: "month", seconds: 2592000 },
87
+
{ unit: "week", seconds: 604800 },
88
+
{ unit: "day", seconds: 86400 },
89
+
{ unit: "hour", seconds: 3600 },
90
+
{ unit: "minute", seconds: 60 },
91
+
{ unit: "second", seconds: 1 },
92
+
];
93
+
94
+
for (const { unit, seconds } of units) {
95
+
if (Math.abs(diffInSeconds) >= seconds) {
96
+
const value = Math.round(diffInSeconds / seconds);
97
+
return rtf.format(-value, unit);
98
+
}
99
+
}
100
+
101
+
return rtf.format(0, "second"); // Should ideally not happen if date is in the past
102
+
}
103
+
104
+
/**
105
+
* Formats a number according to the specified locale and options.
106
+
* @param value The number to format.
107
+
* @param locale The locale to use for formatting (e.g., 'en-GB', 'en-US'). Defaults to 'en-GB'.
108
+
* @param options Options for number formatting, conforming to Intl.NumberFormatOptions.
109
+
* @returns The formatted number string.
110
+
*/
111
+
export function formatNumber(value: number, locale: string = 'en-GB', options?: Intl.NumberFormatOptions): string {
112
+
if (typeof value !== 'number') {
113
+
return String(value); // Return as string if not a number
114
+
}
115
+
try {
116
+
return new Intl.NumberFormat(locale, options).format(value);
117
+
} catch (error) {
118
+
console.error(`Error formatting number for locale ${locale}:`, error);
119
+
return value.toLocaleString(locale); // Fallback to basic toLocaleString on error
120
+
}
121
+
}
+100
src/lib/utils/milestones.ts
+100
src/lib/utils/milestones.ts
···
1
+
import { formatNumber, getOrdinalSuffix } from './formatters';
2
+
3
+
4
+
export interface Milestone {
5
+
text: string;
6
+
emoji: string;
7
+
type: 'special' | 'major' | 'minor';
8
+
}
9
+
10
+
/**
11
+
* Determines if a post number represents a milestone and returns milestone info
12
+
*/
13
+
export function getMilestone(postNumber: number): Milestone | null {
14
+
// Special milestones defined in a more maintainable structure.
15
+
const specialMilestones: { number: number; text: string; emoji: string; }[] = [
16
+
{ number: 1, text: "First Post!", emoji: "🎉" },
17
+
{ number: 100, text: "Centennial Post!", emoji: "💯" },
18
+
{ number: 365, text: "Daily Dose Complete!", emoji: "�" },
19
+
{ number: 500, text: "Half Thousand!", emoji: "🏆" },
20
+
{ number: 1000, text: "One Thousand Posts!", emoji: "🌟" },
21
+
{ number: 10000, text: "Ten Thousand Posts!", emoji: "🚀" },
22
+
{ number: 200, text: "Double Century!", emoji: "🎉🎉" },
23
+
{ number: 250, text: "Quarter Thousand!", emoji: "✨✨" },
24
+
{ number: 750, text: "Three-Quarter Thousand!", emoji: "💫💫" },
25
+
];
26
+
27
+
for (const milestone of specialMilestones) {
28
+
if (postNumber === milestone.number) {
29
+
return {
30
+
text: milestone.text,
31
+
emoji: milestone.emoji,
32
+
type: 'special'
33
+
};
34
+
}
35
+
}
36
+
37
+
// Major milestones (every 50 posts after 100)
38
+
if (postNumber > 100 && postNumber % 250 === 0) {
39
+
return {
40
+
text: `${formatNumber(postNumber)} Posts!`,
41
+
emoji: "�",
42
+
type: 'major'
43
+
};
44
+
}
45
+
46
+
if (postNumber > 100 && postNumber % 50 === 0) {
47
+
return {
48
+
text: `${formatNumber(postNumber)} Posts!`,
49
+
emoji: "🎯",
50
+
type: 'major'
51
+
};
52
+
}
53
+
54
+
// Specific major milestone that doesn't fit the general rule.
55
+
if (postNumber === 150) {
56
+
return {
57
+
text: "One Hundred Fifty Posts!",
58
+
emoji: "🎉",
59
+
type: 'major'
60
+
};
61
+
}
62
+
63
+
// Minor milestones (every 10 posts, but not major milestones).
64
+
// This check should come after special and major milestones to ensure correct precedence.
65
+
if (postNumber % 10 === 0 && postNumber % 50 !== 0) {
66
+
const ordinal = getOrdinal(postNumber);
67
+
return {
68
+
text: `${ordinal} Post!`,
69
+
emoji: "✨",
70
+
type: 'minor'
71
+
};
72
+
}
73
+
74
+
// Very special fun ones that are not part of the main special milestones array.
75
+
const funMilestones: { number: number; text: string; emoji: string; }[] = [
76
+
{ number: 404, text: "Post Not Found!", emoji: "🔍" },
77
+
{ number: 123, text: "One Two Three!", emoji: "🔢" },
78
+
{ number: 333, text: "Triple Three!", emoji: "✨✨✨" },
79
+
];
80
+
81
+
for (const milestone of funMilestones) {
82
+
if (postNumber === milestone.number) {
83
+
return {
84
+
text: milestone.text,
85
+
emoji: milestone.emoji,
86
+
type: 'special'
87
+
};
88
+
}
89
+
}
90
+
91
+
return null;
92
+
}
93
+
94
+
/**
95
+
* Converts a number to its ordinal form (1st, 2nd, 3rd, etc.)
96
+
*/
97
+
function getOrdinal(num: number): string {
98
+
const formatted = formatNumber(num);
99
+
return formatted + getOrdinalSuffix(num);
100
+
}
+40
src/lib/utils/tally.ts
+40
src/lib/utils/tally.ts
···
1
+
/**
2
+
* Calculates the total read time for a given array of posts.
3
+
* Assumes an average reading speed of 200 words per minute.
4
+
* @param posts - An array of post objects, each with a 'wordCount' property.
5
+
* @returns The total read time in minutes, rounded up.
6
+
*/
7
+
export function calculateTotalReadTime(posts: { wordCount: number }[]): number {
8
+
return posts.reduce((total, post) => {
9
+
return total + Math.ceil(post.wordCount / 200);
10
+
}, 0);
11
+
}
12
+
13
+
/**
14
+
* Calculates the total word count for a given array of posts.
15
+
* @param posts - An array of post objects, each with a 'wordCount' property.
16
+
* @returns The total word count.
17
+
*/
18
+
export function calculateTotalWordCount(posts: { wordCount: number }[]): number {
19
+
return posts.reduce((total, post) => total + post.wordCount, 0);
20
+
}
21
+
22
+
/**
23
+
* Formats a given number of minutes into a human-readable string (e.g., "2 hours", "3 days").
24
+
* @param minutes - The total number of minutes.
25
+
* @returns A formatted string representing the time.
26
+
*/
27
+
export function formatReadTime(minutes: number): string {
28
+
if (minutes < 60) {
29
+
return `${minutes} min`;
30
+
} else if (minutes < 60 * 24) {
31
+
const hours = Math.round(minutes / 60);
32
+
return `${hours} hour${hours === 1 ? '' : 's'}`;
33
+
} else if (minutes < 60 * 24 * 7) {
34
+
const days = Math.round(minutes / (60 * 24));
35
+
return `${days} day${days === 1 ? '' : 's'}`;
36
+
} else {
37
+
const weeks = Math.round(minutes / (60 * 24 * 7));
38
+
return `${weeks} week${weeks === 1 ? '' : 's'}`;
39
+
}
40
+
}
+69
src/lib/utils/textProcessor.ts
+69
src/lib/utils/textProcessor.ts
···
1
+
import remarkParse from "remark-parse";
2
+
import remarkGfm from "remark-gfm";
3
+
import remarkRehype from "remark-rehype";
4
+
import rehypeStringify from "rehype-stringify";
5
+
import { unified } from "unified";
6
+
import type { Node } from "unist";
7
+
8
+
type TextNode = Node & { type: "text"; value: string };
9
+
type ParentNode = Node & { children: Node[] };
10
+
11
+
/**
12
+
* Extracts plain text from markdown content and truncates it to a specified length.
13
+
* @param markdown The markdown content to process.
14
+
* @param maxLength The maximum length of the extracted text (default: 160).
15
+
* @returns A promise that resolves to the extracted and truncated plain text.
16
+
*/
17
+
export async function extractTextFromMarkdown(
18
+
markdown: string,
19
+
maxLength: number = 160
20
+
): Promise<string> {
21
+
// Process the markdown to get plain text
22
+
const plainText = String(
23
+
await unified()
24
+
.use(remarkParse, { fragment: true })
25
+
.use(remarkGfm)
26
+
.use(() => (tree) => {
27
+
// Simple transformer that visits all nodes and removes everything but text
28
+
const visit = (node: Node): string => {
29
+
if (node.type === "text") {
30
+
const textNode = node as TextNode;
31
+
return textNode.value;
32
+
}
33
+
if ("children" in node) {
34
+
const parentNode = node as ParentNode;
35
+
return parentNode.children.map(visit).filter(Boolean).join(" ");
36
+
}
37
+
return "";
38
+
};
39
+
40
+
// Replace tree with just text content
41
+
return {
42
+
type: "root",
43
+
children: [{ type: "text", value: visit(tree) }],
44
+
};
45
+
})
46
+
.use(remarkRehype)
47
+
.use(rehypeStringify)
48
+
.process(markdown)
49
+
);
50
+
51
+
// Clean up the text
52
+
let cleaned = plainText.replace(/\s+/g, " ").trim();
53
+
54
+
// Truncate to maxLength if necessary
55
+
if (cleaned.length > maxLength) {
56
+
cleaned = cleaned.substring(0, maxLength) + "...";
57
+
}
58
+
59
+
return cleaned;
60
+
}
61
+
62
+
/**
63
+
* Calculates the word count of a given string.
64
+
* @param text The input string.
65
+
* @returns The number of words in the string.
66
+
*/
67
+
export function calculateWordCount(text: string): number {
68
+
return text.split(/\s+/).filter((word) => word.length > 0).length;
69
+
}
+28
src/routes/+layout.svelte
+28
src/routes/+layout.svelte
···
1
+
<script lang="ts">
2
+
import "$css/app.css";
3
+
import { getStores } from "$app/stores";
4
+
const { page } = getStores();
5
+
import Profile from "$components/profile/Profile.svelte";
6
+
import { Navigation, Footer } from "$components/layout";
7
+
8
+
let { data, children } = $props();
9
+
10
+
// Check if we're on the home page or blog page using $derived
11
+
const showProfile = $derived(
12
+
$page.route.id ? ["/", "/blog"].includes($page.route.id) : false
13
+
);
14
+
const isHomePage = $derived($page.route.id === "/");
15
+
const isBlogIndex = $derived($page.route.id === "/blog");
16
+
</script>
17
+
18
+
<div class="box-border mx-auto px-4 sm:px-8 max-w-[1000px] pb-8">
19
+
<Navigation {isHomePage} {isBlogIndex} />
20
+
21
+
{#if showProfile}
22
+
<Profile profile={data.profile} />
23
+
{/if}
24
+
25
+
{@render children()}
26
+
27
+
<Footer profile={data.profile} posts={data.posts} />
28
+
</div>
+40
src/routes/+layout.ts
+40
src/routes/+layout.ts
···
1
+
import { getProfile } from "$components/profile/profile";
2
+
import { getLatestPosts } from "$services/blogService";
3
+
import type { Profile, LinkBoard } from "$components/shared";
4
+
5
+
// Profile data cache
6
+
let profile: Profile;
7
+
let dynamicLinks: LinkBoard | undefined;
8
+
9
+
export async function load({ fetch }) {
10
+
if (profile === undefined) {
11
+
profile = await getProfile(fetch);
12
+
}
13
+
14
+
// Fetch dynamic links only if not already cached
15
+
if (dynamicLinks === undefined) {
16
+
try {
17
+
const rawResponse = await fetch(
18
+
`${profile.pds}/xrpc/com.atproto.repo.listRecords?repo=${profile.did}&collection=blue.linkat.board&rkey=self`
19
+
);
20
+
const response = await rawResponse.json();
21
+
if (response && response.records && response.records.length > 0) {
22
+
dynamicLinks = response.records[0].value as LinkBoard;
23
+
}
24
+
} catch (error) {
25
+
console.error("Error fetching dynamic links:", error);
26
+
}
27
+
}
28
+
29
+
// Fetch latest blog posts using the consolidated service
30
+
const latestPosts = await getLatestPosts(fetch, 3);
31
+
32
+
return {
33
+
profile,
34
+
pdsUrl: profile.pds,
35
+
did: profile.did,
36
+
posts: new Map(), // Keep this for compatibility with existing Footer component
37
+
dynamicLinks,
38
+
latestPosts,
39
+
};
40
+
}
+63
src/routes/+page.svelte
+63
src/routes/+page.svelte
···
1
+
<script lang="ts">
2
+
import { onMount } from "svelte";
3
+
import { getStores } from "$app/stores";
4
+
const { page } = getStores();
5
+
import { DynamicLinks, LatestBlogPost } from "$components/layout/main";
6
+
7
+
let { data } = $props();
8
+
9
+
// State to track if locale has been properly loaded
10
+
let localeLoaded = $state(false);
11
+
12
+
onMount(() => {
13
+
// Set a brief timeout to ensure the browser has time to determine locale
14
+
setTimeout(() => {
15
+
localeLoaded = true;
16
+
}, 10);
17
+
});
18
+
</script>
19
+
20
+
<svelte:head>
21
+
<title>Site Name</title>
22
+
<meta
23
+
name="description"
24
+
content="Welcome to Site Name - A personal space where I share my thoughts on coding, technology, and life."
25
+
/>
26
+
<meta
27
+
name="keywords"
28
+
content="Ewan, personal website, coding, technology, programming, tech blog, Site Name"
29
+
/>
30
+
31
+
<!-- Open Graph / Facebook -->
32
+
<meta property="og:type" content="website" />
33
+
<meta property="og:url" content={$page.url.origin + $page.url.pathname} />
34
+
<meta property="og:title" content="Site Title" />
35
+
<meta
36
+
property="og:description"
37
+
content="Welcome to Site Name - A personal space where I share my thoughts on coding, technology, and life."
38
+
/>
39
+
<meta property="og:site_name" content="Site Name" />
40
+
{#if $page.url.origin}
41
+
<meta property="og:image" content={$page.url.origin + "/embed/main.png"} />
42
+
{/if}
43
+
<meta property="og:image:width" content="1200" />
44
+
<meta property="og:image:height" content="630" />
45
+
46
+
<!-- Twitter -->
47
+
<meta name="twitter:card" content="summary_large_image" />
48
+
<meta name="twitter:url" content={$page.url.origin + $page.url.pathname} />
49
+
<meta name="twitter:title" content="Site Title" />
50
+
<meta
51
+
name="twitter:description" content="A personal space where I share my thoughts on coding, technology, and life."
52
+
/>
53
+
{#if $page.url.origin}
54
+
<meta name="twitter:image" content={$page.url.origin + "/embed/main.png"} />
55
+
{/if}
56
+
</svelte:head>
57
+
58
+
<!-- Latest Blog Post section (only show if we have posts) -->
59
+
{#if data.latestPosts && data.latestPosts.length > 0}
60
+
<LatestBlogPost posts={data.latestPosts} {localeLoaded} />
61
+
{/if}
62
+
63
+
<DynamicLinks data={data.dynamicLinks} />
+8
src/routes/blog/+layout.ts
+8
src/routes/blog/+layout.ts
+199
src/routes/blog/+page.svelte
+199
src/routes/blog/+page.svelte
···
1
+
<script lang="ts">
2
+
import { onMount } from "svelte";
3
+
import YearTabs from "$components/archive/YearTabs.svelte";
4
+
import YearContent from "$components/archive/YearContent.svelte";
5
+
import { getStores } from "$app/stores";
6
+
const { page } = getStores();
7
+
const { data } = $props();
8
+
import type { Post } from "$components/shared";
9
+
10
+
// Get posts from data with enhanced validation
11
+
const posts = $derived(
12
+
Array.from((data.posts || new Map()).values() as Iterable<Post>)
13
+
.filter((post) => {
14
+
// Enhanced validation for posts
15
+
const hasValidTitle = post.title && typeof post.title === 'string';
16
+
const hasValidDate = post.createdAt instanceof Date && !isNaN(post.createdAt.getTime());
17
+
const hasValidContent = post.content && typeof post.content === 'string';
18
+
const hasValidRkey = post.rkey && typeof post.rkey === 'string';
19
+
20
+
const isValid = hasValidTitle && hasValidDate && hasValidContent && hasValidRkey;
21
+
22
+
if (!isValid && process.env.NODE_ENV === 'development') {
23
+
console.warn('Invalid post filtered out:', {
24
+
title: post.title,
25
+
rkey: post.rkey,
26
+
hasValidTitle,
27
+
hasValidDate,
28
+
hasValidContent,
29
+
hasValidRkey,
30
+
createdAt: post.createdAt,
31
+
});
32
+
}
33
+
34
+
return isValid;
35
+
})
36
+
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
37
+
);
38
+
39
+
// State to track if locale has been properly loaded
40
+
let localeLoaded = $state(false);
41
+
42
+
onMount(() => {
43
+
// Set a brief timeout to ensure the browser has time to determine locale
44
+
setTimeout(() => {
45
+
localeLoaded = true;
46
+
}, 10);
47
+
});
48
+
49
+
// Helper function to get only month name
50
+
function getMonthName(date: Date): string {
51
+
try {
52
+
return new Intl.DateTimeFormat(
53
+
typeof window !== "undefined" ? window.navigator.language : "en-GB",
54
+
{ month: "long" }
55
+
).format(date);
56
+
} catch (error) {
57
+
console.warn('Error formatting month name:', error);
58
+
return date.toLocaleDateString('en-GB', { month: 'long' });
59
+
}
60
+
}
61
+
62
+
// Group posts by year and month
63
+
type YearMonthGroup = {
64
+
year: number;
65
+
months: Record<string, Post[]>;
66
+
};
67
+
68
+
const groupedByYear = $derived(
69
+
(() => {
70
+
if (!posts || posts.length === 0) {
71
+
return [];
72
+
}
73
+
74
+
const groups: Record<number, Record<string, Post[]>> = {};
75
+
76
+
posts.forEach((post) => {
77
+
try {
78
+
const year = post.createdAt.getFullYear();
79
+
const month = getMonthName(post.createdAt);
80
+
81
+
if (!groups[year]) groups[year] = {};
82
+
if (!groups[year][month]) groups[year][month] = [];
83
+
84
+
groups[year][month].push(post);
85
+
} catch (error) {
86
+
console.warn('Error grouping post:', { post, error });
87
+
}
88
+
});
89
+
90
+
// Convert to array of year groups sorted by year (descending)
91
+
return Object.entries(groups)
92
+
.sort(([yearA], [yearB]) => Number(yearB) - Number(yearA))
93
+
.map(([year, months]) => ({
94
+
year: Number(year),
95
+
months,
96
+
}));
97
+
})() as YearMonthGroup[]
98
+
);
99
+
100
+
// State for active year tab
101
+
let activeYear = $state(0);
102
+
103
+
// Set initial active year when data is loaded
104
+
$effect(() => {
105
+
if (groupedByYear.length > 0) {
106
+
activeYear = groupedByYear[0].year;
107
+
}
108
+
});
109
+
110
+
// Computed loading and error states
111
+
const isLoading = $derived(!localeLoaded);
112
+
const hasData = $derived(data && data.posts && data.posts.size > 0);
113
+
const hasValidPosts = $derived(posts && posts.length > 0);
114
+
const hasProfile = $derived(data && data.profile);
115
+
</script>
116
+
117
+
<svelte:head>
118
+
<title>Blog - Site Name</title>
119
+
<meta
120
+
name="description"
121
+
content="Welcome to Blog - Site Name - Keywords"
122
+
/>
123
+
<meta
124
+
name="keywords"
125
+
content="personal blog, Blog - Site Name"
126
+
/>
127
+
<link
128
+
rel="alternate"
129
+
type="application/rss+xml"
130
+
title="Blog - Site Name RSS Feed"
131
+
href="{$page.url.origin}/blog/rss"
132
+
/>
133
+
134
+
<!-- Open Graph / Facebook -->
135
+
<meta property="og:type" content="website" />
136
+
<meta property="og:url" content={$page.url.origin + $page.url.pathname} />
137
+
<meta property="og:title" content="Blog - Site Title" />
138
+
<meta
139
+
property="og:description"
140
+
content="Welcome to Blog - Site Name - Keywords"
141
+
/>
142
+
<meta property="og:site_name" content="Blog - Site Name" />
143
+
{#if $page.url.origin}
144
+
<meta property="og:image" content={$page.url.origin + "/embed/blog.png"} />
145
+
{/if}
146
+
<meta property="og:image:width" content="1200" />
147
+
<meta property="og:image:height" content="630" />
148
+
149
+
<!-- Twitter -->
150
+
<meta name="twitter:card" content="summary_large_image" />
151
+
<meta name="twitter:url" content={$page.url.origin + $page.url.pathname} />
152
+
<meta name="twitter:title" content="Blog - Site Title" />
153
+
<meta
154
+
name="twitter:description" content="Keywords"
155
+
/>
156
+
{#if $page.url.origin}
157
+
<meta name="twitter:image" content={$page.url.origin + "/embed/blog.png"} />
158
+
{/if}
159
+
</svelte:head>
160
+
161
+
{#if isLoading}
162
+
<div
163
+
class="flex justify-center items-center min-h-[200px] text-lg text-[var(--text-color)] opacity-70"
164
+
>
165
+
Loading...
166
+
</div>
167
+
{:else if !hasProfile}
168
+
<div
169
+
class="flex flex-col items-center justify-center min-h-[200px] text-lg text-[var(--text-color)] opacity-70 text-center"
170
+
>
171
+
<p>Unable to load profile data.</p>
172
+
<p class="mt-2 text-sm">Please try refreshing the page.</p>
173
+
</div>
174
+
{:else if !hasData}
175
+
<div
176
+
class="flex flex-col items-center justify-center min-h-[200px] text-lg text-[var(--text-color)] opacity-70 text-center"
177
+
>
178
+
<p>No blog data available.</p>
179
+
<p class="mt-2 text-sm">This blog uses the <a href="https://whtwnd.com">WhiteWind</a> blogging lexicon,
180
+
<code>com.whtwnd.blog.entry</code>, but there seem to be no records available.</p>
181
+
</div>
182
+
{:else if !hasValidPosts}
183
+
<div
184
+
class="flex flex-col items-center justify-center min-h-[200px] text-lg text-[var(--text-color)] opacity-70 text-center"
185
+
>
186
+
<p>No valid blog posts found.</p>
187
+
<p class="mt-2 text-sm">Posts were found but none have valid content, titles, and dates.</p>
188
+
</div>
189
+
{:else}
190
+
<!-- Year tabs with animated indicator -->
191
+
<YearTabs {groupedByYear} bind:activeYear />
192
+
193
+
<!-- Content for active year with animations -->
194
+
{#each groupedByYear as { year, months } (year)}
195
+
{#if year === activeYear}
196
+
<YearContent {year} {months} {localeLoaded} />
197
+
{/if}
198
+
{/each}
199
+
{/if}
+50
src/routes/blog/[rkey]/+page.svelte
+50
src/routes/blog/[rkey]/+page.svelte
···
1
+
<script lang="ts" module>
2
+
declare global {
3
+
interface Window {
4
+
$page: {
5
+
url: URL;
6
+
};
7
+
}
8
+
}
9
+
</script>
10
+
11
+
<script lang="ts">
12
+
import { onMount } from "svelte";
13
+
import type { Post } from "$components/shared";
14
+
import {
15
+
PostHead,
16
+
PostHeader,
17
+
PostContent,
18
+
PostNavigation,
19
+
} from "$components/post";
20
+
import { NotFoundMessage } from "$components/shared";
21
+
22
+
let { data }: { data: any } = $props();
23
+
let post = $derived(data.post as Post);
24
+
let adjacentPosts = $derived(data.adjacentPosts);
25
+
26
+
// State to track if locale has been properly loaded
27
+
let localeLoaded = $state(false);
28
+
29
+
onMount(() => {
30
+
// Set localeLoaded to true when component is mounted in the browser
31
+
localeLoaded = true;
32
+
});
33
+
</script>
34
+
35
+
<PostHead {post} />
36
+
37
+
{#if post !== undefined}
38
+
<div class="max-w-4xl mx-auto px-4">
39
+
<PostHeader
40
+
{post}
41
+
profile={data.profile}
42
+
rkey={data.rkey}
43
+
{localeLoaded}
44
+
/>
45
+
<PostContent {post} />
46
+
<PostNavigation {adjacentPosts} />
47
+
</div>
48
+
{:else}
49
+
<NotFoundMessage />
50
+
{/if}
+20
src/routes/blog/[rkey]/+page.ts
+20
src/routes/blog/[rkey]/+page.ts
···
1
+
export const prerender = false;
2
+
3
+
export const load = async ({ parent, params }) => {
4
+
const { getPost, profile, getAdjacentPosts } = await parent();
5
+
const post = getPost(params.rkey);
6
+
7
+
if (!post) return { status: 404 };
8
+
9
+
// Get adjacent posts for navigation
10
+
const adjacentPosts = getAdjacentPosts(params.rkey);
11
+
12
+
return {
13
+
post,
14
+
rkey: params.rkey,
15
+
posts: new Map([[params.rkey, post]]),
16
+
profile,
17
+
adjacentPosts,
18
+
getAdjacentPosts: () => adjacentPosts,
19
+
};
20
+
};
+133
src/routes/blog/rss/+server.ts
+133
src/routes/blog/rss/+server.ts
···
1
+
import type { RequestHandler } from "./$types";
2
+
import { dev } from "$app/environment";
3
+
import { parse } from "$lib/parser";
4
+
import type { MarkdownPost } from "$components/shared";
5
+
import { getProfile } from "$components/profile/profile"; // Import getProfile
6
+
7
+
export const GET: RequestHandler = async ({ url, fetch }) => {
8
+
try {
9
+
// Use getProfile to get profile data
10
+
const profileData = await getProfile(fetch);
11
+
12
+
const did = profileData.did;
13
+
const pdsUrl = profileData.pds;
14
+
15
+
if (!pdsUrl) throw new Error("Could not find PDS URL");
16
+
17
+
// Get blog posts
18
+
const postsResponse = await fetch(
19
+
`${pdsUrl}/xrpc/com.atproto.repo.listRecords?repo=${did}&collection=com.whtwnd.blog.entry`
20
+
);
21
+
if (!postsResponse.ok)
22
+
throw new Error(`Posts fetch failed: ${postsResponse.status}`);
23
+
const postsData = await postsResponse.json();
24
+
25
+
// Process posts
26
+
const mdposts: Map<string, MarkdownPost> = new Map();
27
+
for (const data of postsData.records) {
28
+
const matches = data.uri.split("/");
29
+
const rkey = matches[matches.length - 1];
30
+
const record = data.value;
31
+
32
+
if (
33
+
matches &&
34
+
matches.length === 5 &&
35
+
record &&
36
+
(record.visibility === "public" || !record.visibility)
37
+
) {
38
+
mdposts.set(rkey, {
39
+
title: record.title,
40
+
createdAt: new Date(record.createdAt),
41
+
mdcontent: record.content,
42
+
rkey,
43
+
});
44
+
}
45
+
}
46
+
47
+
// Parse markdown posts to HTML
48
+
const posts = await parse(mdposts);
49
+
50
+
// Sort posts by date (newest first)
51
+
const sortedPosts = Array.from(posts.values()).sort(
52
+
(a, b) => b.createdAt.getTime() - a.createdAt.getTime()
53
+
);
54
+
55
+
// Build the RSS XML
56
+
const baseUrl = dev ? url.origin : "https://example.com"; // Update with your production domain
57
+
const rssXml = `<?xml version="1.0" encoding="UTF-8" ?>
58
+
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/">
59
+
<channel>
60
+
<title>Blog - Site Name</title>
61
+
<description>Keywords</description>
62
+
<link>${baseUrl}/blog</link>
63
+
<atom:link href="${baseUrl}/blog/rss" rel="self" type="application/rss+xml" />
64
+
<image>
65
+
${baseUrl ? `<url>${baseUrl}/embed/blog.png</url>` : ''}
66
+
<title>Blog - Site Name</title>
67
+
<link>${baseUrl}/blog</link>
68
+
</image>
69
+
<lastBuildDate>${new Date().toUTCString()}</lastBuildDate>
70
+
${sortedPosts
71
+
.map(
72
+
(post) => `
73
+
<item>
74
+
<title>${escapeXml(post.title)}</title>
75
+
<link>${baseUrl}/blog/${post.rkey}</link>
76
+
<guid isPermaLink="true">${baseUrl}/blog/${post.rkey}</guid>
77
+
<pubDate>${new Date(post.createdAt).toUTCString()}</pubDate>
78
+
<description><![CDATA[${post.excerpt || ""}]]></description>
79
+
<content:encoded><![CDATA[${post.content || ""}]]></content:encoded>
80
+
<author>${profileData.displayName || profileData.handle} (${
81
+
profileData.handle
82
+
})</author>
83
+
${baseUrl ? `<media:content url="${baseUrl}/embed/blog.png" medium="image" />` : ''}
84
+
</item>`
85
+
)
86
+
.join("")}
87
+
</channel>
88
+
</rss>`;
89
+
90
+
return new Response(rssXml, {
91
+
headers: {
92
+
"Content-Type": "application/xml",
93
+
"Cache-Control": "max-age=0, s-maxage=3600",
94
+
},
95
+
});
96
+
} catch (error) {
97
+
console.error("Error generating RSS feed:", error);
98
+
99
+
// Return a minimal valid RSS feed in case of error
100
+
return new Response(
101
+
`<?xml version="1.0" encoding="UTF-8" ?>
102
+
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
103
+
<channel>
104
+
<title>Blog - Site Name</title>
105
+
<description>Keywords</description>
106
+
<link>${url.origin}/blog</link>
107
+
<atom:link href="${
108
+
url.origin
109
+
}/blog/rss" rel="self" type="application/rss+xml" />
110
+
<lastBuildDate>${new Date().toUTCString()}</lastBuildDate>
111
+
<!-- Error occurred while generating feed items -->
112
+
</channel>
113
+
</rss>`,
114
+
{
115
+
headers: {
116
+
"Content-Type": "application/xml",
117
+
"Cache-Control": "no-cache",
118
+
},
119
+
}
120
+
);
121
+
}
122
+
};
123
+
124
+
// Helper function to escape XML special characters
125
+
function escapeXml(unsafe: string): string {
126
+
if (!unsafe) return "";
127
+
return unsafe
128
+
.replace(/&/g, "&")
129
+
.replace(/</g, "<")
130
+
.replace(/>/g, ">")
131
+
.replace(/"/g, """)
132
+
.replace(/'/g, "'");
133
+
}
+1
static/.well-known/atproto-did
+1
static/.well-known/atproto-did
···
1
+
placeholder - replace with your DID
+79
static/scripts/themeLoader.js
+79
static/scripts/themeLoader.js
···
1
+
// Inline theme loader - compiled from themeLoader.ts
2
+
// This runs immediately when the script loads
3
+
(function() {
4
+
'use strict';
5
+
6
+
// Theme configuration - single source of truth
7
+
const THEMES = [
8
+
{ id: "default", name: "Green (Default)" }
9
+
];
10
+
11
+
const THEME_STORAGE_KEYS = {
12
+
MODE: "theme-mode",
13
+
COLOR: "color-theme",
14
+
};
15
+
16
+
/**
17
+
* Applies theme classes to the document element
18
+
*/
19
+
function applyTheme(isDarkMode, themeId) {
20
+
// Remove all existing theme classes
21
+
document.documentElement.classList.remove("light");
22
+
THEMES.forEach((theme) => {
23
+
if (theme.id !== "default") {
24
+
document.documentElement.classList.remove(theme.id);
25
+
}
26
+
});
27
+
28
+
// Apply light mode class if needed
29
+
if (!isDarkMode) {
30
+
document.documentElement.classList.add("light");
31
+
}
32
+
33
+
// Apply color theme class if not default
34
+
if (themeId !== "default") {
35
+
document.documentElement.classList.add(themeId);
36
+
}
37
+
}
38
+
39
+
/**
40
+
* Gets the user's theme preferences from localStorage and system
41
+
*/
42
+
function getThemePreferences() {
43
+
const savedThemeMode = localStorage.getItem(THEME_STORAGE_KEYS.MODE);
44
+
const savedColorTheme = localStorage.getItem(THEME_STORAGE_KEYS.COLOR);
45
+
46
+
let isDarkMode;
47
+
if (savedThemeMode) {
48
+
isDarkMode = savedThemeMode === "dark";
49
+
} else {
50
+
// Use system preference as default
51
+
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
52
+
isDarkMode = prefersDark;
53
+
}
54
+
55
+
const themeId = savedColorTheme || "default";
56
+
57
+
return { isDarkMode, themeId };
58
+
}
59
+
60
+
/**
61
+
* Initializes theme system - runs immediately
62
+
*/
63
+
function initializeTheme() {
64
+
const { isDarkMode, themeId } = getThemePreferences();
65
+
applyTheme(isDarkMode, themeId);
66
+
}
67
+
68
+
// Initialize theme immediately
69
+
initializeTheme();
70
+
71
+
// Make theme functions available globally for the Svelte component
72
+
window.__themeLoader = {
73
+
THEMES,
74
+
THEME_STORAGE_KEYS,
75
+
applyTheme,
76
+
getThemePreferences,
77
+
initializeTheme
78
+
};
79
+
})();
+28
svelte.config.js
+28
svelte.config.js
···
1
+
import adapter from '@sveltejs/adapter-vercel';
2
+
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
3
+
4
+
/** @type {import('@sveltejs/kit').Config} */
5
+
const config = {
6
+
// Consult https://svelte.dev/docs/kit/integrations
7
+
// for more information about preprocessors
8
+
preprocess: vitePreprocess(),
9
+
10
+
kit: {
11
+
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
12
+
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
13
+
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
14
+
adapter: adapter(),
15
+
prerender: {
16
+
entries: ['*'],
17
+
origin: 'https://ewancroft.uk'
18
+
},
19
+
alias: {
20
+
'$components': './src/lib/components',
21
+
'$css': './src/lib/css',
22
+
'$services': './src/lib/services',
23
+
'$utils': './src/lib/utils'
24
+
}
25
+
}
26
+
};
27
+
28
+
export default config;
+99
tailwind.config.ts
+99
tailwind.config.ts
···
1
+
import forms from "@tailwindcss/forms";
2
+
import typography from "@tailwindcss/typography";
3
+
4
+
import type { Config } from "tailwindcss";
5
+
6
+
export default {
7
+
content: ["./src/**/*.{html,js,svelte,ts}"],
8
+
9
+
theme: {
10
+
extend: {
11
+
colors: {
12
+
primary: "var(--link-color)",
13
+
"primary-hover": "var(--link-hover-color)",
14
+
background: "var(--background-color)",
15
+
text: "var(--text-color)",
16
+
card: "var(--card-bg)",
17
+
"header-footer": "var(--header-footer-bg)",
18
+
button: "var(--button-bg)",
19
+
"button-hover": "var(--button-hover-bg)",
20
+
},
21
+
typography: {
22
+
DEFAULT: {
23
+
css: {
24
+
color: "var(--text-color)",
25
+
a: {
26
+
color: "var(--link-color)",
27
+
"&:hover": {
28
+
color: "var(--link-hover-color)",
29
+
},
30
+
},
31
+
h1: {
32
+
color: "var(--text-color)",
33
+
},
34
+
h2: {
35
+
color: "var(--text-color)",
36
+
},
37
+
h3: {
38
+
color: "var(--text-color)",
39
+
},
40
+
h4: {
41
+
color: "var(--text-color)",
42
+
},
43
+
h5: {
44
+
color: "var(--text-color)",
45
+
},
46
+
h6: {
47
+
color: "var(--text-color)",
48
+
},
49
+
strong: {
50
+
color: "var(--text-color)",
51
+
},
52
+
code: {
53
+
color: "var(--text-color)",
54
+
backgroundColor: "var(--background-color)",
55
+
borderRadius: "0.25rem",
56
+
padding: "0.1rem 0.3rem",
57
+
},
58
+
blockquote: {
59
+
color: "var(--text-color)",
60
+
borderLeftColor: "var(--button-bg)",
61
+
backgroundColor: "var(--background-color)",
62
+
borderRadius: "0 0.5rem 0.5rem 0",
63
+
padding: "0.5rem 1rem",
64
+
},
65
+
".task-list-item": {
66
+
"list-style-type": "none",
67
+
},
68
+
'[type="checkbox"]': {
69
+
"border-radius": "0px",
70
+
"border-color": "var(--button-bg)",
71
+
},
72
+
img: {
73
+
"border-style": "solid",
74
+
"border-color": "var(--button-bg)",
75
+
"border-width": "1px",
76
+
"border-radius": "0em",
77
+
},
78
+
hr: {
79
+
borderColor: "var(--button-bg)",
80
+
},
81
+
pre: {
82
+
backgroundColor: "var(--background-color)",
83
+
color: "var(--text-color)",
84
+
},
85
+
},
86
+
},
87
+
invert: {
88
+
css: {
89
+
code: {
90
+
backgroundColor: "var(--background-color)",
91
+
},
92
+
},
93
+
},
94
+
},
95
+
},
96
+
},
97
+
98
+
plugins: [forms, typography],
99
+
} satisfies Config;
+20
tsconfig.json
+20
tsconfig.json
···
1
+
{
2
+
"extends": "./.svelte-kit/tsconfig.json",
3
+
"compilerOptions": {
4
+
"allowJs": true,
5
+
"checkJs": true,
6
+
"esModuleInterop": true,
7
+
"forceConsistentCasingInFileNames": true,
8
+
"resolveJsonModule": true,
9
+
"skipLibCheck": true,
10
+
"sourceMap": true,
11
+
"strict": true,
12
+
"moduleResolution": "bundler",
13
+
"allowImportingTsExtensions": true
14
+
}
15
+
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
16
+
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
17
+
//
18
+
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
19
+
// from the referenced tsconfig.json - TypeScript does not merge them in
20
+
}