+9
.changeset/README.md
+9
.changeset/README.md
···
1
+
# Changesets
2
+
3
+
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool
4
+
that works with multi-package repos, or single-package repos to help you version and publish your
5
+
code. You can find the full documentation for it
6
+
[in our repository](https://github.com/changesets/changesets)
7
+
8
+
We have a quick list of common questions to get you started engaging with this project in
9
+
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
+11
.changeset/config.json
+11
.changeset/config.json
···
1
+
{
2
+
"$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
3
+
"changelog": "@changesets/cli/changelog",
4
+
"commit": false,
5
+
"fixed": [],
6
+
"linked": [],
7
+
"access": "restricted",
8
+
"baseBranch": "trunk",
9
+
"updateInternalDependencies": "patch",
10
+
"ignore": ["svelte-site"]
11
+
}
+8
.changeset/six-rabbits-admire.md
+8
.changeset/six-rabbits-admire.md
-13
.editorconfig
-13
.editorconfig
+1
-2
.gitignore
+1
-2
.gitignore
-1
.mailmap
-1
.mailmap
···
1
-
Mary <git@mary.my.id> <pineapplecreamcheese@skiff.com>
+7
.prettierignore
+7
.prettierignore
+9
-2
.prettierrc
+9
-2
.prettierrc
···
6
6
"semi": true,
7
7
"singleQuote": true,
8
8
"bracketSpacing": true,
9
-
"plugins": ["prettier-plugin-css-order"],
9
+
"plugins": ["prettier-plugin-svelte", "prettier-plugin-css-order"],
10
10
"overrides": [
11
11
{
12
-
"files": ["tsconfig.json", "jsconfig.json"],
12
+
"files": ["tsconfig.json", "jsconfig.json", "tsconfig.*.json"],
13
13
"options": {
14
14
"parser": "jsonc"
15
+
}
16
+
},
17
+
{
18
+
"files": ["*.md"],
19
+
"options": {
20
+
"printWidth": 100,
21
+
"proseWrap": "always"
15
22
}
16
23
}
17
24
]
+1
-1
.vscode/extensions.json
+1
-1
.vscode/extensions.json
+1
-1
.vscode/settings.json
+1
-1
.vscode/settings.json
+11
-73
README.md
+11
-73
README.md
···
1
-
# <bluesky-post>
2
-
3
-
A custom element for embedding Bluesky posts. [Live demo](https://mary-ext.github.io/bluesky-post-embed)
4
-
5
-
- **Lightweight**, the entire package + dependencies is only 24 KB (7 KB gzipped)
6
-
- **Standalone**, no additional middleman involved, connects straight to Bluesky's API
7
-
8
-
## Installation
9
-
10
-
### via package manager
11
-
12
-
This custom element is available on npm.
13
-
14
-
```
15
-
npm install bluesky-post-embed
16
-
```
17
-
18
-
Then, import the package on your app.
19
-
20
-
```js
21
-
import 'bluesky-post-embed';
22
-
```
23
-
24
-
### via CDN
25
-
26
-
If you like, you can also rely on CDN services like esm.sh or JSDelivr.
27
-
28
-
```html
29
-
<script type="module" src="https://esm.sh/bluesky-post-embed@~0.1.0"></script>
30
-
```
31
-
32
-
## Usage
33
-
34
-
Bluesky posts can be embedded like so:
35
-
36
-
```html
37
-
<bluesky-post src="https://bsky.app/profile/pfrazee.com/post/3kj2umze7zj2n" theme="light">
38
-
<blockquote class="bluesky-post-fallback">
39
-
<p dir="auto">angel mode</p>
40
-
— Paul Frazee ๐ฆ (@pfrazee.com)
41
-
<a href="https://bsky.app/profile/pfrazee.com/post/3kj2umze7zj2n">January 16, 2024</a>
42
-
</blockquote>
43
-
</bluesky-post>
44
-
```
45
-
46
-
Adding a fallback content like above is heavily recommended for progressive enhancement.
47
-
48
-
### Attributes
49
-
50
-
- `src` **Required**
51
-
A `bsky.app` URL of the post.
52
-
Heavily recommended that the URL points to a DID instead of a handle, otherwise, it would have to
53
-
resolve the handle into DID first (and suffer from issues like users changing their handles, etc)
54
-
An example of such URL would be: https://bsky.app/profile/did:plc:ragtjsm2j2vknwkz3zp4oxrd/post/3kj2umze7zj2n
55
-
- `contextless` **Optional**
56
-
Prevent displaying of context when `src` points to a reply.
57
-
- `theme` **Semi-required**
58
-
The color palette that it should use, either `light` or `dark`.
59
-
Set this to blank if you're setting a custom color palette.
60
-
- `service-uri` **Optional**
61
-
URL to an AppView service, defaults to `https://public.api.bsky.app`
62
-
63
-
### Events
1
+
# <bluesky-embed>
64
2
65
-
- `loaded`
66
-
Fired when the embed has successfully loaded the post
67
-
- `error`
68
-
Fired when the embed fails to load the post
3
+
A custom element for embedding Bluesky posts.
69
4
70
-
## SSR usage
5
+
- **Lightweight**, the entire package + dependencies is only ~20 KB (~6 KB gzipped)
6
+
- **Standalone**, no middleman involved, directly calls Bluesky's API
7
+
- **Server-side rendering possible**, allows for no-JavaScript usage
71
8
72
-
The core of this package is a static HTML renderer, so long as your framework of
73
-
choice has the means to deal with declarative shadow DOM, then it's possible to
74
-
generate a completely static embed without any JavaScript in the client.
9
+
| Packages |
10
+
| ------------------------------------------------------------------------------------------------ |
11
+
| [`bluesky-post-embed`](./packages/bluesky-post-embed): displays a post embed |
12
+
| [`bluesky-profile-card-embed`](./packages/bluesky-profile-card-embed): displays a user's profile |
13
+
| [`bluesky-profile-feed-embed`](./packages/bluesky-profile-feed-embed): displays a user's feed |
75
14
76
-
See [this repository](https://github.com/mary-ext/astro-bluesky-post) for an
77
-
SSR demo made in Astro.
15
+

-33
index.html
-33
index.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.0" />
6
-
<title>Bluesky post embeds</title>
7
-
<script type="module" src="./src/index.ts"></script>
8
-
</head>
9
-
<body>
10
-
<h1><bluesky-post></h1>
11
-
<p>A custom element for embedding Bluesky posts.</p>
12
-
<p>See <a href="https://github.com/mary-ext/bluesky-post-embed">GitHub repository</a> for details.</p>
13
-
14
-
<bluesky-post src="https://bsky.app/profile/pfrazee.com/post/3kj2umze7zj2n" theme="light">
15
-
<blockquote class="bluesky-post-fallback">
16
-
<p dir="auto">angel mode</p>
17
-
— Paul Frazee ๐ฆ (@pfrazee.com)
18
-
<a href="https://bsky.app/profile/pfrazee.com/post/3kj2umze7zj2n">January 16, 2024</a>
19
-
</blockquote>
20
-
</bluesky-post>
21
-
22
-
<pre>
23
-
<bluesky-post src="https://bsky.app/profile/pfrazee.com/post/3kj2umze7zj2n" theme="light">
24
-
<blockquote class="bluesky-post-fallback">
25
-
<p dir="auto">angel mode</p>
26
-
&mdash; Paul Frazee ๐ฆ (@pfrazee.com)
27
-
<a href="https://bsky.app/profile/pfrazee.com/post/3kj2umze7zj2n">January 16, 2024</a>
28
-
</blockquote>
29
-
</bluesky-post>
30
-
<script type="module" src="https://esm.sh/bluesky-post-embed@~0.1.0"></script></pre
31
-
>
32
-
</body>
33
-
</html>
-686
lib/core.tsx
-686
lib/core.tsx
···
1
-
import type { TrustedHTML } from '@intrnl/jsx-to-string';
2
-
3
-
import { BskyXRPC } from '@externdefs/bluesky-client';
4
-
import type {
5
-
AppBskyEmbedExternal,
6
-
AppBskyEmbedImages,
7
-
AppBskyEmbedRecord,
8
-
AppBskyFeedDefs,
9
-
AppBskyFeedGetPostThread,
10
-
AppBskyFeedPost,
11
-
AppBskyGraphDefs,
12
-
} from '@externdefs/bluesky-client/lexicons';
13
-
14
-
import './style.css';
15
-
16
-
import { segment_richtext } from './utils/richtext/segmentize.ts';
17
-
import type { Facet } from './utils/richtext/types.ts';
18
-
19
-
import { abs_long, abs_short, format_date } from './utils/date.ts';
20
-
import { format_compact } from './utils/number.ts';
21
-
22
-
type ThreadResponse = AppBskyFeedGetPostThread.Output;
23
-
type PostData = AppBskyFeedDefs.PostView;
24
-
type PostRecord = AppBskyFeedPost.Record;
25
-
26
-
/// Fetch post
27
-
const parse_src = (src: string): [actor: string, rkey: string] => {
28
-
const { protocol, host, pathname } = new URL(src);
29
-
30
-
if (protocol === 'https:' || protocol === 'http:') {
31
-
if (host === 'bsky.app' || host === 'staging.bsky.app') {
32
-
const match = /\/profile\/([^/]+)\/post\/([^/]+)\/?$/.exec(pathname);
33
-
34
-
if (match) {
35
-
return [match[1], match[2]];
36
-
}
37
-
}
38
-
}
39
-
40
-
throw new RangeError(`Invalid src: ${src}`);
41
-
};
42
-
43
-
export const get = async (
44
-
src: string,
45
-
contextless: boolean,
46
-
service: string = 'https://public.api.bsky.app',
47
-
): Promise<ThreadResponse> => {
48
-
const rpc = new BskyXRPC({ service });
49
-
50
-
const [actor, rkey] = parse_src(src);
51
-
52
-
let did: string;
53
-
if (actor.startsWith('did:')) {
54
-
did = actor;
55
-
} else {
56
-
const response = await rpc.get('com.atproto.identity.resolveHandle', {
57
-
params: {
58
-
handle: actor,
59
-
},
60
-
});
61
-
62
-
did = response.data.did;
63
-
}
64
-
65
-
const response = await rpc.get('app.bsky.feed.getPostThread', {
66
-
params: {
67
-
uri: `at://${did}/app.bsky.feed.post/${rkey}`,
68
-
parentHeight: !contextless ? 2 : 1,
69
-
depth: 0,
70
-
},
71
-
});
72
-
73
-
const data = response.data;
74
-
75
-
return data;
76
-
};
77
-
78
-
/// Renderer
79
-
const get_record_key = (uri: string) => {
80
-
const idx = uri.lastIndexOf('/');
81
-
return uri.slice(idx + 1);
82
-
};
83
-
84
-
const get_collection_ns = (uri: string) => {
85
-
const first = uri.indexOf('/', 5);
86
-
const second = uri.indexOf('/', first + 1);
87
-
88
-
return uri.slice(first + 1, second);
89
-
};
90
-
91
-
const get_profile_url = (author: string) => {
92
-
return `https://bsky.app/profile/${author}`;
93
-
};
94
-
95
-
const get_post_url = (author: string, rkey: string) => {
96
-
return `https://bsky.app/profile/${author}/post/${rkey}`;
97
-
};
98
-
99
-
export const render = (resp: ThreadResponse, contextless: boolean): TrustedHTML => {
100
-
const posts = unwrap_thread(resp, contextless);
101
-
const len = posts.length;
102
-
103
-
if (len === 0) {
104
-
return (
105
-
<div class="root">
106
-
<p class="not-available">This post is unavailable</p>
107
-
</div>
108
-
);
109
-
}
110
-
111
-
const reply_to_icon = (
112
-
<svg viewBox="0 0 24 24" class="icon">
113
-
<path fill="currentColor" d="M10 9V5l-7 7l7 7v-4.1c5 0 8.5 1.6 11 5.1c-1-5-4-10-11-11" />
114
-
</svg>
115
-
);
116
-
117
-
return (
118
-
<div class="root">
119
-
<div class="timeline">
120
-
{posts.map(({ post, parent }, i) => {
121
-
const main = i === len - 1;
122
-
123
-
const record = post.record as PostRecord;
124
-
const author = post.author;
125
-
126
-
const author_url = get_profile_url(author.did);
127
-
const post_url = get_post_url(author.did, get_record_key(post.uri));
128
-
129
-
if (main) {
130
-
return (
131
-
<div class="main-post">
132
-
<div class="main-post__header">
133
-
<a href={author_url} target="_blank" class="main-post__avatar-wrapper">
134
-
{author.avatar ? (
135
-
<img loading="lazy" src={author.avatar} class="main-post__avatar" />
136
-
) : null}
137
-
</a>
138
-
139
-
<a href={author_url} target="_blank" class="main-post__name-wrapper">
140
-
<bdi class="main-post__display-name-wrapper">
141
-
<span class="main-post__display-name">{author.displayName}</span>
142
-
</bdi>
143
-
<span class="main-post__handle">@{author.handle}</span>
144
-
</a>
145
-
</div>
146
-
147
-
{i === 0 && record.reply ? (
148
-
<p class="main-post__context">
149
-
{reply_to_icon}
150
-
{parent ? (
151
-
<span>
152
-
Reply to{' '}
153
-
<a
154
-
href={`https://bsky.app/profile/${parent.author.handle}`}
155
-
target="_blank"
156
-
class="main-post__context-link"
157
-
>
158
-
{parent.author.displayName || '@' + parent.author.handle}
159
-
</a>
160
-
</span>
161
-
) : (
162
-
<span>Reply to an unknown post</span>
163
-
)}
164
-
</p>
165
-
) : null}
166
-
167
-
<div class="main-post__body">
168
-
<RichTextRenderer text={record.text} facets={record.facets} />
169
-
</div>
170
-
171
-
{post.embed ? <Embeds embed={post.embed} large={true} /> : null}
172
-
173
-
{record.tags ? (
174
-
<div class="main-post__tags">
175
-
{record.tags.map((tag) => (
176
-
<div class="main-post__tag">
177
-
<span>#</span>
178
-
<span class="main-post__tag-text">{tag}</span>
179
-
</div>
180
-
))}
181
-
</div>
182
-
) : null}
183
-
184
-
<time datetime={record.createdAt} class="main-post__date">
185
-
{format_date(abs_long, record.createdAt)}
186
-
</time>
187
-
188
-
<div class="main-post__stats">
189
-
<span class="main-post__stat" title={`${post.likeCount || 0} likes`}>
190
-
<svg viewBox="0 0 24 24" class="icon">
191
-
<path
192
-
fill="currentColor"
193
-
d="M16.5 3c-1.74 0-3.41.81-4.5 2.09C10.91 3.81 9.24 3 7.5 3C4.42 3 2 5.42 2 8.5c0 3.78 3.4 6.86 8.55 11.54L12 21.35l1.45-1.32C18.6 15.36 22 12.28 22 8.5C22 5.42 19.58 3 16.5 3m-4.4 15.55l-.1.1l-.1-.1C7.14 14.24 4 11.39 4 8.5C4 6.5 5.5 5 7.5 5c1.54 0 3.04.99 3.57 2.36h1.87C13.46 5.99 14.96 5 16.5 5c2 0 3.5 1.5 3.5 3.5c0 2.89-3.14 5.74-7.9 10.05"
194
-
></path>
195
-
</svg>
196
-
<span>{format_compact(post.likeCount || 0)}</span>
197
-
</span>
198
-
199
-
<span class="main-post__stat" title={`${post.repostCount || 0} reposts`}>
200
-
<svg viewBox="0 0 24 24" class="icon">
201
-
<path
202
-
fill="currentColor"
203
-
d="M7 7h10v3l4-4l-4-4v3H5v6h2zm10 10H7v-3l-4 4l4 4v-3h12v-6h-2z"
204
-
></path>
205
-
</svg>
206
-
<span>{format_compact(post.repostCount || 0)}</span>
207
-
</span>
208
-
209
-
<div class="gap"></div>
210
-
211
-
<a href={post_url} target="_blank" class="permalink">
212
-
<span>Read {format_compact(post.replyCount || 0)} replies on Bluesky</span>
213
-
</a>
214
-
</div>
215
-
</div>
216
-
);
217
-
} else {
218
-
return (
219
-
<div class="reply-post">
220
-
<div class="reply-post__aside">
221
-
<a href={author_url} target="_blank" class="reply-post__avatar-wrapper">
222
-
{author.avatar ? (
223
-
<img loading="lazy" src={author.avatar} class="reply-post__avatar" />
224
-
) : null}
225
-
</a>
226
-
227
-
<div class="reply-post__line"></div>
228
-
</div>
229
-
230
-
<div class="reply-post__main">
231
-
<div class="reply-post__header">
232
-
<a href={author_url} target="_blank" class="reply-post__name-wrapper">
233
-
<bdi class="reply-post__display-name-wrapper">
234
-
<span class="reply-post__display-name">{author.displayName}</span>
235
-
</bdi>
236
-
237
-
<span class="reply-post__handle">@{author.handle}</span>
238
-
</a>
239
-
240
-
<span aria-hidden="true" class="dot">
241
-
ยท
242
-
</span>
243
-
244
-
<a
245
-
href={post_url}
246
-
target="_blank"
247
-
title={format_date(abs_long, record.createdAt)}
248
-
class="reply-post__date"
249
-
>
250
-
<time datetime={record.createdAt}>{format_date(abs_short, record.createdAt)}</time>
251
-
</a>
252
-
</div>
253
-
254
-
{i === 0 && record.reply ? (
255
-
<p class="reply-post__context">
256
-
{reply_to_icon}
257
-
{parent ? (
258
-
<span>
259
-
Reply to{' '}
260
-
<a
261
-
href={`https://bsky.app/profile/${parent.author.handle}`}
262
-
target="_blank"
263
-
class="reply-post__context-link"
264
-
>
265
-
{parent.author.displayName || '@' + parent.author.handle}
266
-
</a>
267
-
</span>
268
-
) : (
269
-
<span>Reply to an unknown post</span>
270
-
)}
271
-
</p>
272
-
) : null}
273
-
274
-
<div class="reply-post__body">
275
-
<RichTextRenderer text={record.text} facets={record.facets} />
276
-
</div>
277
-
278
-
{post.embed ? <Embeds embed={post.embed} large={false} /> : null}
279
-
</div>
280
-
</div>
281
-
);
282
-
}
283
-
})}
284
-
</div>
285
-
</div>
286
-
);
287
-
};
288
-
289
-
const unwrap_thread = (resp: ThreadResponse, contextless: boolean) => {
290
-
const items: { post: PostData; parent: PostData | undefined }[] = [];
291
-
292
-
let i = 0;
293
-
let il = contextless ? 1 : 2;
294
-
295
-
let curr: typeof resp.thread | undefined = resp.thread;
296
-
while (curr) {
297
-
if (curr.$type === 'app.bsky.feed.defs#notFoundPost' || curr.$type === 'app.bsky.feed.defs#blockedPost') {
298
-
break;
299
-
}
300
-
301
-
const post = curr.post;
302
-
303
-
if (i !== 0) {
304
-
items[i - 1].parent = post;
305
-
}
306
-
307
-
if (++i > il) {
308
-
break;
309
-
}
310
-
311
-
const author = post.author;
312
-
if (author.labels?.some((def) => def.val === '!no-unauthenticated')) {
313
-
break;
314
-
}
315
-
316
-
items.push({ post: post, parent: undefined });
317
-
curr = curr.parent;
318
-
}
319
-
320
-
return items.reverse();
321
-
};
322
-
323
-
/// <RichTextRenderer />
324
-
const RichTextRenderer = ({ text, facets }: { text: string; facets?: Facet[] }) => {
325
-
const segments = segment_richtext(text, facets);
326
-
327
-
return (
328
-
<>
329
-
{segments.map((segment) => {
330
-
const text = segment.text;
331
-
332
-
const link = segment.link;
333
-
const mention = segment.mention;
334
-
const tag = segment.tag;
335
-
336
-
if (link) {
337
-
return (
338
-
<a href={link.uri} target="_blank" class="link">
339
-
{text}
340
-
</a>
341
-
);
342
-
} else if (mention) {
343
-
return (
344
-
<a href={`https://bsky.app/profile/${mention.did}`} target="_blank" class="mention">
345
-
{text}
346
-
</a>
347
-
);
348
-
} else if (tag) {
349
-
return <span class="hashtag">{text}</span>;
350
-
}
351
-
352
-
return text;
353
-
})}
354
-
</>
355
-
);
356
-
};
357
-
358
-
/// <Embeds />
359
-
type EmbeddedImage = AppBskyEmbedImages.ViewImage;
360
-
type EmbeddedLink = AppBskyEmbedExternal.ViewExternal;
361
-
type EmbeddedRecord = AppBskyEmbedRecord.View['record'];
362
-
363
-
const Embeds = ({ embed, large }: { embed: NonNullable<PostData['embed']>; large: boolean }) => {
364
-
let images: EmbeddedImage[] | undefined;
365
-
let link: EmbeddedLink | undefined;
366
-
let record: EmbeddedRecord | undefined;
367
-
368
-
{
369
-
const $type = embed.$type;
370
-
371
-
if ($type === 'app.bsky.embed.images#view') {
372
-
images = embed.images;
373
-
} else if ($type === 'app.bsky.embed.external#view') {
374
-
link = embed.external;
375
-
} else if ($type === 'app.bsky.embed.record#view') {
376
-
record = embed.record;
377
-
} else if ($type === 'app.bsky.embed.recordWithMedia#view') {
378
-
const rec = embed.record.record;
379
-
380
-
const media = embed.media;
381
-
const mediatype = media.$type;
382
-
383
-
record = rec;
384
-
385
-
if (mediatype === 'app.bsky.embed.images#view') {
386
-
images = media.images;
387
-
} else if (mediatype === 'app.bsky.embed.external#view') {
388
-
link = media.external;
389
-
}
390
-
}
391
-
}
392
-
393
-
return (
394
-
<div class="embeds">
395
-
{link ? <EmbedLink link={link} /> : null}
396
-
{images ? <EmbedImage images={images} is_bordered={true} allow_standalone_ratio={true} /> : null}
397
-
{record ? render_record(record, large) : null}
398
-
</div>
399
-
);
400
-
};
401
-
402
-
const render_record = (record: EmbeddedRecord, large: boolean) => {
403
-
const $type = record.$type;
404
-
405
-
if ($type === 'app.bsky.embed.record#viewNotFound' || $type === 'app.bsky.embed.record#viewBlocked') {
406
-
return <EmbedNotFound uri={record.uri} />;
407
-
}
408
-
409
-
if ($type === 'app.bsky.embed.record#viewRecord') {
410
-
const author = record.author;
411
-
if (author.labels?.some((def) => def.val === '!no-unauthenticated')) {
412
-
return <EmbedNotFound uri={record.uri} />;
413
-
}
414
-
415
-
return <EmbedPost post={record} large={large} />;
416
-
}
417
-
418
-
if ($type === 'app.bsky.feed.defs#generatorView') {
419
-
return <EmbedFeed feed={record} />;
420
-
}
421
-
422
-
if ($type === 'app.bsky.graph.defs#listView') {
423
-
return <EmbedList list={record} />;
424
-
}
425
-
426
-
return null;
427
-
};
428
-
429
-
/// <EmbedNotFound />
430
-
const EmbedNotFound = ({ uri }: { uri: string }) => {
431
-
const ns = get_collection_ns(uri);
432
-
const resource =
433
-
ns === 'app.bsky.feed.post'
434
-
? 'post'
435
-
: ns === 'app.bsky.feed.generator'
436
-
? 'feed'
437
-
: ns === 'app.bsky.graph.list'
438
-
? 'list'
439
-
: 'record';
440
-
441
-
return <p class="embed-not-found">This {resource} is unavailable</p>;
442
-
};
443
-
444
-
/// <EmbedLink />
445
-
const EmbedLink = ({ link }: { link: EmbeddedLink }) => {
446
-
return (
447
-
<a href={link.uri} target="_blank" rel="noopener noreferrer nofollow" class="embed-link interactive">
448
-
{link.thumb ? <img loading="lazy" src={link.thumb} class="embed-link__thumb" /> : null}
449
-
450
-
<div class="embed-link__main">
451
-
<p class="embed-link__domain">{get_domain(link.uri)}</p>
452
-
<p class="embed-link__title">{link.title}</p>
453
-
454
-
<div class="embed-link__desktop-only">
455
-
<p class="embed-link__summary">{link.description}</p>
456
-
</div>
457
-
</div>
458
-
</a>
459
-
);
460
-
};
461
-
462
-
const get_domain = (url: string) => {
463
-
try {
464
-
const host = new URL(url).host;
465
-
return host.startsWith('www.') ? host.slice(4) : host;
466
-
} catch {
467
-
return url;
468
-
}
469
-
};
470
-
471
-
/// <EmbedImage />
472
-
const enum RenderMode {
473
-
MULTIPLE,
474
-
STANDALONE,
475
-
STANDALONE_RATIO,
476
-
}
477
-
478
-
const EmbedImage = ({
479
-
images,
480
-
is_bordered,
481
-
allow_standalone_ratio,
482
-
}: {
483
-
images: EmbeddedImage[];
484
-
is_bordered: boolean;
485
-
allow_standalone_ratio: boolean;
486
-
}) => {
487
-
const length = images.length;
488
-
const is_standalone_image = allow_standalone_ratio && length === 1 && 'aspectRatio' in images[0];
489
-
490
-
return (
491
-
<div
492
-
class={
493
-
'embed-image' +
494
-
(is_bordered ? ' embed-image--bordered' : '') +
495
-
(is_standalone_image ? ' embed-image--standalone' : '')
496
-
}
497
-
>
498
-
{is_standalone_image ? (
499
-
render_img(images[0], RenderMode.STANDALONE_RATIO)
500
-
) : length === 1 ? (
501
-
render_img(images[0], RenderMode.STANDALONE)
502
-
) : length === 2 ? (
503
-
<div class="embed-image__grid">
504
-
<div class="embed-image__col">{render_img(images[0], RenderMode.MULTIPLE)}</div>
505
-
<div class="embed-image__col">{render_img(images[1], RenderMode.MULTIPLE)}</div>
506
-
</div>
507
-
) : length === 3 ? (
508
-
<div class="embed-image__grid">
509
-
<div class="embed-image__col">
510
-
{render_img(images[0], RenderMode.MULTIPLE)}
511
-
{render_img(images[1], RenderMode.MULTIPLE)}
512
-
</div>
513
-
514
-
<div class="embed-image__col">{render_img(images[2], RenderMode.MULTIPLE)}</div>
515
-
</div>
516
-
) : length === 4 ? (
517
-
<div class="embed-image__grid">
518
-
<div class="embed-image__col">
519
-
{render_img(images[0], RenderMode.MULTIPLE)}
520
-
{render_img(images[2], RenderMode.MULTIPLE)}
521
-
</div>
522
-
523
-
<div class="embed-image__col">
524
-
{render_img(images[1], RenderMode.MULTIPLE)}
525
-
{render_img(images[3], RenderMode.MULTIPLE)}
526
-
</div>
527
-
</div>
528
-
) : null}
529
-
</div>
530
-
);
531
-
};
532
-
533
-
const render_img = (img: EmbeddedImage, mode: RenderMode) => {
534
-
// FIXME: with STANDALONE_RATIO, we are resizing the image to make it fit
535
-
// the container with our given constraints, but this doesn't work when the
536
-
// image hasn't had its metadata loaded yet, the browser will snap to the
537
-
// smallest possible size for our layout.
538
-
539
-
const alt = img.alt;
540
-
const aspectRatio = img.aspectRatio;
541
-
542
-
let cn: string | undefined;
543
-
let ratio: string | undefined;
544
-
545
-
if (mode === RenderMode.MULTIPLE) {
546
-
cn = `embed-image__image-wrapper--multiple`;
547
-
} else if (mode === RenderMode.STANDALONE) {
548
-
cn = `embed-image__image-wrapper--standalone`;
549
-
} else if (mode === RenderMode.STANDALONE_RATIO) {
550
-
cn = `embed-image__image-wrapper--standalone-ratio`;
551
-
ratio = `${aspectRatio!.width}/${aspectRatio!.height}`;
552
-
}
553
-
554
-
return (
555
-
<div class={'embed-image__image-wrapper ' + cn} style={{ 'aspect-ratio': ratio }}>
556
-
<img loading="lazy" src={img.thumb} alt={alt} class="embed-image__image" />
557
-
{mode === RenderMode.STANDALONE_RATIO ? <div class="embed-image__placeholder"></div> : null}
558
-
</div>
559
-
);
560
-
};
561
-
562
-
/// <EmbedPost />
563
-
const EmbedPost = ({ post, large }: { post: AppBskyEmbedRecord.ViewRecord; large: boolean }) => {
564
-
const author = post.author;
565
-
566
-
const record = post.value as PostRecord;
567
-
const text = record.text;
568
-
const images = get_post_images(post);
569
-
570
-
const show_large_images = images !== undefined && (large || !text);
571
-
572
-
const post_url = get_post_url(author.did, get_record_key(post.uri));
573
-
574
-
return (
575
-
<a href={post_url} target="_blank" class="embed-post interactive">
576
-
<div class="embed-post__header">
577
-
<div class="embed-post__avatar-wrapper">
578
-
{author.avatar ? <img loading="lazy" src={author.avatar} class="embed-post__avatar" /> : null}
579
-
</div>
580
-
581
-
<span class="embed-post__name-wrapper">
582
-
{author.displayName ? (
583
-
<bdi class="embed-post__display-name-wrapper">
584
-
<span class="embed-post__display-name">{author.displayName}</span>
585
-
</bdi>
586
-
) : null}
587
-
588
-
<span class="embed-post__handle">@{author.handle}</span>
589
-
</span>
590
-
591
-
<span aria-hidden="true" class="dot">
592
-
ยท
593
-
</span>
594
-
595
-
<time datetime={record.createdAt} class="embed-post__date">
596
-
{format_date(abs_short, record.createdAt)}
597
-
</time>
598
-
</div>
599
-
600
-
{text ? (
601
-
<div class="embed-post__body">
602
-
{images && !large ? (
603
-
<div class="embed-post__image-aside">
604
-
<EmbedImage images={images} is_bordered={true} allow_standalone_ratio={false} />
605
-
</div>
606
-
) : null}
607
-
608
-
<div class="embed-post__text">{text}</div>
609
-
</div>
610
-
) : null}
611
-
612
-
{show_large_images ? (
613
-
<>
614
-
{text ? <div class="embed-post__divider"></div> : null}
615
-
<EmbedImage images={images} is_bordered={false} allow_standalone_ratio={false} />
616
-
</>
617
-
) : null}
618
-
</a>
619
-
);
620
-
};
621
-
622
-
const get_post_images = (post: AppBskyEmbedRecord.ViewRecord) => {
623
-
const embeds = post.embeds;
624
-
625
-
if (embeds && embeds.length > 0) {
626
-
const val = embeds[0];
627
-
628
-
if (val.$type === 'app.bsky.embed.images#view') {
629
-
return val.images;
630
-
} else if (val.$type === 'app.bsky.embed.recordWithMedia#view') {
631
-
const media = val.media;
632
-
633
-
if (media.$type === 'app.bsky.embed.images#view') {
634
-
return media.images;
635
-
}
636
-
}
637
-
}
638
-
};
639
-
640
-
/// <EmbedFeed />
641
-
const EmbedFeed = ({ feed }: { feed: AppBskyFeedDefs.GeneratorView }) => {
642
-
const creator = feed.creator;
643
-
644
-
const feed_url = `https://bsky.app/profile/${creator.handle}/feed/${get_record_key(feed.uri)}`;
645
-
646
-
return (
647
-
<a href={feed_url} target="_blank" class="embed-feed interactive">
648
-
<div class="embed-feed__avatar-wrapper">
649
-
{feed.avatar ? <img loading="lazy" src={feed.avatar} class="embed-feed__avatar" /> : null}
650
-
</div>
651
-
652
-
<div class="embed-feed__main">
653
-
<p class="embed-feed__name">{feed.displayName}</p>
654
-
<p class="embed-feed__type">{`Feed by @${creator.handle}`}</p>
655
-
</div>
656
-
</a>
657
-
);
658
-
};
659
-
660
-
/// <EmbedList />
661
-
const EmbedList = ({ list }: { list: AppBskyGraphDefs.ListView }) => {
662
-
const creator = list.creator;
663
-
664
-
const raw_purpose = list.purpose;
665
-
const purpose =
666
-
raw_purpose === 'app.bsky.graph.defs#curatelist'
667
-
? `Curation list`
668
-
: raw_purpose === 'app.bsky.graph.defs#modlist'
669
-
? `Moderation list`
670
-
: `Unknown list`;
671
-
672
-
const list_url = `https://bsky.app/profile/${creator.handle}/list/${get_record_key(list.uri)}`;
673
-
674
-
return (
675
-
<a href={list_url} target="_blank" class="embed-list interactive">
676
-
<div class="embed-list__avatar-wrapper">
677
-
{list.avatar ? <img loading="lazy" src={list.avatar} class="embed-list__avatar" /> : null}
678
-
</div>
679
-
680
-
<div class="embed-list__main">
681
-
<p class="embed-list__name">{list.name}</p>
682
-
<p class="embed-list__type">{`${purpose} by ${creator.handle}`}</p>
683
-
</div>
684
-
</a>
685
-
);
686
-
};
-1
lib/env.d.ts
-1
lib/env.d.ts
···
1
-
/// <reference types="vite/client" />
-35
lib/index.ts
-35
lib/index.ts
···
1
-
import rawStyles from './style.css?inline';
2
-
import { get, render } from './core.tsx';
3
-
4
-
let _style: CSSStyleSheet;
5
-
const get_style = () => {
6
-
if (!_style) {
7
-
_style = new CSSStyleSheet();
8
-
_style.replaceSync(rawStyles);
9
-
}
10
-
11
-
return _style;
12
-
};
13
-
14
-
export class BlueskyPost extends HTMLElement {
15
-
connectedCallback() {
16
-
this.load().then(
17
-
() => this.dispatchEvent(new CustomEvent('loaded')),
18
-
(err) => this.dispatchEvent(new CustomEvent('error', { detail: err })),
19
-
);
20
-
}
21
-
22
-
async load() {
23
-
const src = this.getAttribute('src')!;
24
-
const service = this.getAttribute('service-uri') || undefined;
25
-
const contextless = this.getAttribute('contextless') !== null;
26
-
27
-
const data = await get(src, contextless, service);
28
-
29
-
const root = this.attachShadow({ mode: 'open' });
30
-
root.adoptedStyleSheets = [get_style()];
31
-
root.innerHTML = render(data, contextless).value;
32
-
}
33
-
}
34
-
35
-
customElements.define('bluesky-post', BlueskyPost);
-677
lib/style.css
-677
lib/style.css
···
1
-
:host {
2
-
--font-size: 1rem;
3
-
display: block;
4
-
}
5
-
6
-
:host([theme='light']) {
7
-
--text-primary: #000000;
8
-
--text-secondary: #455668;
9
-
--text-link: #1083fe;
10
-
--background-primary: #ffffff;
11
-
--background-secondary: #455668;
12
-
--divider-hover: #c0ccd6;
13
-
--divider: #c2ccd6;
14
-
}
15
-
:host([theme='dark']) {
16
-
--text-primary: #ffffff;
17
-
--text-secondary: #9aaabc;
18
-
--text-link: #1083fe;
19
-
--background-primary: #161e27;
20
-
--background-secondary: #212d3b;
21
-
--divider-hover: #42566c;
22
-
--divider: #324458;
23
-
}
24
-
25
-
*,
26
-
*::before,
27
-
*::after {
28
-
box-sizing: border-box;
29
-
}
30
-
31
-
p {
32
-
margin: 0;
33
-
}
34
-
35
-
.root {
36
-
margin: 0 auto;
37
-
border: 1px solid var(--divider);
38
-
border-radius: 8px;
39
-
background: var(--background-primary);
40
-
min-width: 250px;
41
-
max-width: 550px;
42
-
overflow: hidden;
43
-
color: var(--text-primary);
44
-
font-size: calc(var(--font-size) * 0.875);
45
-
line-height: calc(var(--font-size) * 1.25);
46
-
}
47
-
48
-
.link,
49
-
.mention {
50
-
color: var(--text-link);
51
-
text-decoration: none;
52
-
53
-
&:hover {
54
-
text-decoration: underline;
55
-
}
56
-
}
57
-
58
-
.dot {
59
-
margin: 0 4px;
60
-
user-select: none;
61
-
}
62
-
63
-
.icon {
64
-
flex-shrink: 0;
65
-
width: 1em;
66
-
height: 1em;
67
-
}
68
-
69
-
.interactive {
70
-
cursor: pointer;
71
-
color: inherit;
72
-
text-decoration: none;
73
-
}
74
-
75
-
.not-available {
76
-
padding: 12px;
77
-
color: var(--text-secondary);
78
-
}
79
-
80
-
/** main-post */
81
-
.main-post {
82
-
padding: 16px;
83
-
}
84
-
85
-
.main-post__header {
86
-
display: flex;
87
-
align-items: center;
88
-
margin: 0 0 12px 0;
89
-
color: var(--text-secondary);
90
-
}
91
-
92
-
.main-post__avatar-wrapper {
93
-
display: block;
94
-
flex-shrink: 0;
95
-
margin: 0 12px 0 0;
96
-
border-radius: 9999px;
97
-
background: var(--background-secondary);
98
-
width: 40px;
99
-
height: 40px;
100
-
overflow: hidden;
101
-
102
-
&:hover {
103
-
filter: brightness(0.85);
104
-
}
105
-
}
106
-
.main-post__avatar {
107
-
width: 100%;
108
-
height: 100%;
109
-
object-fit: cover;
110
-
}
111
-
112
-
.main-post__name-wrapper {
113
-
display: block;
114
-
max-width: 100%;
115
-
overflow: hidden;
116
-
color: inherit;
117
-
text-decoration: none;
118
-
text-overflow: ellipsis;
119
-
white-space: nowrap;
120
-
}
121
-
.main-post__display-name-wrapper {
122
-
overflow: hidden;
123
-
text-overflow: ellipsis;
124
-
125
-
.main-post__name-wrapper:hover & {
126
-
text-decoration: underline;
127
-
}
128
-
}
129
-
.main-post__display-name {
130
-
color: var(--text-primary);
131
-
font-weight: 700;
132
-
}
133
-
.main-post__handle {
134
-
display: block;
135
-
overflow: hidden;
136
-
text-overflow: ellipsis;
137
-
white-space: nowrap;
138
-
}
139
-
140
-
.main-post__context {
141
-
display: flex;
142
-
align-items: center;
143
-
gap: 6px;
144
-
margin: 0 0 4px 0;
145
-
color: var(--text-secondary);
146
-
147
-
span {
148
-
overflow: hidden;
149
-
text-overflow: ellipsis;
150
-
white-space: nowrap;
151
-
}
152
-
153
-
a {
154
-
color: inherit;
155
-
text-decoration: none;
156
-
157
-
&:hover {
158
-
text-decoration: underline;
159
-
}
160
-
}
161
-
}
162
-
.main-post__body {
163
-
overflow: hidden;
164
-
font-size: calc(var(--font-size) * 1);
165
-
line-height: calc(var(--font-size) * 1.5);
166
-
white-space: pre-wrap;
167
-
overflow-wrap: break-word;
168
-
169
-
&:empty {
170
-
display: none;
171
-
}
172
-
}
173
-
174
-
.main-post__tags {
175
-
display: flex;
176
-
flex-wrap: wrap;
177
-
gap: 6px;
178
-
margin: 12px 0 0 0;
179
-
}
180
-
181
-
.main-post__tag {
182
-
display: flex;
183
-
align-items: center;
184
-
gap: 4px;
185
-
border-radius: 9999px;
186
-
background: var(--text-secondary);
187
-
padding: 0 8px;
188
-
min-width: 0px;
189
-
line-height: calc(var(--font-size) * 1.5);
190
-
}
191
-
192
-
.main-post__tag-text {
193
-
overflow: hidden;
194
-
text-overflow: ellipsis;
195
-
white-space: nowrap;
196
-
}
197
-
198
-
.main-post__date {
199
-
display: flex;
200
-
flex-wrap: wrap;
201
-
align-items: center;
202
-
gap: 8px;
203
-
margin: 12px 0 0;
204
-
border-bottom: 1px solid var(--divider);
205
-
padding: 0 0 12px 0;
206
-
color: var(--text-secondary);
207
-
}
208
-
.main-post__stats {
209
-
display: flex;
210
-
flex-wrap: wrap;
211
-
align-items: center;
212
-
gap: 8px 16px;
213
-
margin: 0 0 -16px 0;
214
-
padding: 12px 0;
215
-
color: var(--text-secondary);
216
-
217
-
.gap {
218
-
flex: 1 1 auto;
219
-
}
220
-
221
-
.permalink {
222
-
display: flex;
223
-
align-items: center;
224
-
gap: 4px;
225
-
color: var(--text-link);
226
-
font-weight: 700;
227
-
text-decoration: none;
228
-
229
-
&:hover {
230
-
text-decoration: underline;
231
-
}
232
-
}
233
-
}
234
-
.main-post__stat {
235
-
display: flex;
236
-
align-items: center;
237
-
gap: 8px;
238
-
}
239
-
240
-
/** reply-post */
241
-
.reply-post {
242
-
display: flex;
243
-
position: relative;
244
-
gap: 12px;
245
-
padding: 12px 16px 0 16px;
246
-
}
247
-
248
-
.reply-post__aside {
249
-
flex-shrink: 0;
250
-
}
251
-
252
-
.reply-post__avatar-wrapper {
253
-
display: block;
254
-
border-radius: 9999px;
255
-
background: var(--background-secondary);
256
-
width: 40px;
257
-
height: 40px;
258
-
overflow: hidden;
259
-
260
-
&:hover {
261
-
filter: brightness(0.85);
262
-
}
263
-
}
264
-
.reply-post__avatar {
265
-
width: 100%;
266
-
height: 100%;
267
-
object-fit: cover;
268
-
}
269
-
.reply-post__line {
270
-
position: absolute;
271
-
top: 56px;
272
-
bottom: -12px;
273
-
left: 35px;
274
-
border-left: 2px solid var(--divider);
275
-
}
276
-
277
-
.reply-post__main {
278
-
display: flex;
279
-
flex-grow: 1;
280
-
flex-direction: column;
281
-
min-width: 0px;
282
-
}
283
-
284
-
.reply-post__header {
285
-
display: flex;
286
-
align-items: center;
287
-
margin: 0 0 2px 0;
288
-
color: var(--text-secondary);
289
-
}
290
-
.reply-post__name-wrapper {
291
-
display: flex;
292
-
gap: 4px;
293
-
max-width: 100%;
294
-
overflow: hidden;
295
-
color: inherit;
296
-
text-decoration: none;
297
-
text-overflow: ellipsis;
298
-
white-space: nowrap;
299
-
}
300
-
.reply-post__display-name-wrapper {
301
-
overflow: hidden;
302
-
text-overflow: ellipsis;
303
-
304
-
.reply-post__name-wrapper:hover & {
305
-
text-decoration: underline;
306
-
}
307
-
}
308
-
.reply-post__display-name {
309
-
color: var(--text-primary);
310
-
font-weight: 700;
311
-
}
312
-
.reply-post__handle {
313
-
display: block;
314
-
overflow: hidden;
315
-
text-overflow: ellipsis;
316
-
white-space: nowrap;
317
-
}
318
-
.reply-post__date {
319
-
color: inherit;
320
-
text-decoration: none;
321
-
white-space: nowrap;
322
-
323
-
&:hover {
324
-
text-decoration: underline;
325
-
}
326
-
}
327
-
328
-
.reply-post__context {
329
-
display: flex;
330
-
align-items: center;
331
-
gap: 4px;
332
-
margin: 0 0 4px 0;
333
-
color: var(--text-secondary);
334
-
335
-
span {
336
-
overflow: hidden;
337
-
text-overflow: ellipsis;
338
-
white-space: nowrap;
339
-
}
340
-
341
-
a {
342
-
color: inherit;
343
-
text-decoration: none;
344
-
345
-
&:hover {
346
-
text-decoration: underline;
347
-
}
348
-
}
349
-
}
350
-
.reply-post__body {
351
-
white-space: pre-wrap;
352
-
overflow-wrap: break-word;
353
-
}
354
-
355
-
/** embeds */
356
-
.embeds {
357
-
display: flex;
358
-
flex-direction: column;
359
-
gap: 12px;
360
-
margin: 12px 0 0 0;
361
-
}
362
-
363
-
.embeds:empty {
364
-
display: none;
365
-
}
366
-
367
-
/** embed-feed */
368
-
.embed-feed {
369
-
display: flex;
370
-
gap: 12px;
371
-
border: 1px solid var(--divider);
372
-
border-radius: 6px;
373
-
padding: 12px;
374
-
375
-
&:hover {
376
-
border-color: var(--divider-hover);
377
-
}
378
-
}
379
-
380
-
.embed-feed__avatar-wrapper {
381
-
margin: 2px 0 0 0;
382
-
border-radius: 6px;
383
-
background: var(--background-secondary);
384
-
width: 36px;
385
-
height: 36px;
386
-
overflow: hidden;
387
-
}
388
-
389
-
.embed-feed__avatar {
390
-
width: 100%;
391
-
height: 100%;
392
-
object-fit: cover;
393
-
}
394
-
395
-
.embed-feed__main {
396
-
}
397
-
398
-
.embed-feed__name {
399
-
font-weight: 700;
400
-
}
401
-
402
-
.embed-feed__type {
403
-
color: var(--text-secondary);
404
-
}
405
-
406
-
/** embed-image */
407
-
.embed-image {
408
-
}
409
-
410
-
.embed-image--bordered {
411
-
border: 1px solid var(--divider);
412
-
border-radius: 6px;
413
-
overflow: hidden;
414
-
}
415
-
416
-
.embed-image--standalone {
417
-
align-self: baseline;
418
-
max-width: 100%;
419
-
}
420
-
421
-
.embed-image__grid {
422
-
display: flex;
423
-
gap: 2px;
424
-
aspect-ratio: 16 / 9;
425
-
}
426
-
427
-
.embed-image__col {
428
-
display: flex;
429
-
flex-grow: 1;
430
-
flex-basis: 0px;
431
-
flex-direction: column;
432
-
gap: 2px;
433
-
}
434
-
435
-
.embed-image__image-wrapper {
436
-
position: relative;
437
-
}
438
-
439
-
.embed-image__image-wrapper--multiple {
440
-
flex-grow: 1;
441
-
flex-basis: 0px;
442
-
min-height: 0px;
443
-
overflow: hidden;
444
-
}
445
-
446
-
.embed-image__image-wrapper--standalone {
447
-
aspect-ratio: 16 / 9;
448
-
overflow: hidden;
449
-
}
450
-
451
-
.embed-image__image-wrapper--standalone-ratio {
452
-
min-width: 64px;
453
-
max-width: 100%;
454
-
min-height: 64px;
455
-
max-height: 320px;
456
-
overflow: hidden;
457
-
}
458
-
459
-
.embed-image__image {
460
-
width: 100%;
461
-
height: 100%;
462
-
object-fit: cover;
463
-
font-size: 0px;
464
-
}
465
-
466
-
.embed-image__placeholder {
467
-
width: 100vw;
468
-
height: 100vh;
469
-
}
470
-
471
-
/** embed-link */
472
-
.embed-link {
473
-
display: flex;
474
-
border: 1px solid var(--divider);
475
-
border-radius: 6px;
476
-
overflow: hidden;
477
-
478
-
&:hover {
479
-
border-color: var(--divider-hover);
480
-
}
481
-
}
482
-
483
-
.embed-link__thumb {
484
-
flex-shrink: 0;
485
-
box-sizing: content-box;
486
-
border-right-width: 1px;
487
-
aspect-ratio: 1 / 1;
488
-
width: 86px;
489
-
object-fit: cover;
490
-
}
491
-
492
-
.embed-link__main {
493
-
display: flex;
494
-
flex-direction: column;
495
-
justify-content: center;
496
-
gap: 2px;
497
-
padding: 12px;
498
-
min-width: 0px;
499
-
}
500
-
501
-
.embed-link__domain {
502
-
overflow: hidden;
503
-
color: var(--text-secondary);
504
-
text-overflow: ellipsis;
505
-
}
506
-
507
-
.embed-link__title {
508
-
display: -webkit-box;
509
-
overflow: hidden;
510
-
overflow-wrap: break-word;
511
-
-webkit-box-orient: vertical;
512
-
-webkit-line-clamp: 2;
513
-
}
514
-
515
-
.embed-link__title:empty {
516
-
display: none;
517
-
}
518
-
519
-
.embed-link__desktop-only {
520
-
display: none;
521
-
522
-
@media (min-width: 640px) {
523
-
display: block;
524
-
}
525
-
}
526
-
527
-
.embed-link__summary {
528
-
display: -webkit-box;
529
-
overflow: hidden;
530
-
-webkit-box-orient: vertical;
531
-
-webkit-line-clamp: 2;
532
-
color: var(--text-secondary);
533
-
}
534
-
535
-
.embed-link__summary:empty {
536
-
display: none;
537
-
}
538
-
539
-
/** embed-list */
540
-
.embed-list {
541
-
display: flex;
542
-
gap: 12px;
543
-
border: 1px solid var(--divider);
544
-
border-radius: 6px;
545
-
padding: 12px;
546
-
547
-
&:hover {
548
-
border-color: var(--divider-hover);
549
-
}
550
-
}
551
-
552
-
.embed-list__avatar-wrapper {
553
-
margin: 2px 0 0 0;
554
-
border-radius: 6px;
555
-
background: var(--background-secondary);
556
-
width: 36px;
557
-
height: 36px;
558
-
overflow: hidden;
559
-
}
560
-
561
-
.embed-list__avatar {
562
-
width: 100%;
563
-
height: 100%;
564
-
object-fit: cover;
565
-
}
566
-
567
-
.embed-list__main {
568
-
}
569
-
570
-
.embed-list__name {
571
-
font-weight: 700;
572
-
}
573
-
574
-
.embed-list__type {
575
-
color: var(--text-secondary);
576
-
}
577
-
578
-
/** embed-not-found */
579
-
.embed-not-found {
580
-
border: 1px solid var(--divider);
581
-
border-radius: 6px;
582
-
padding: 12px;
583
-
color: var(--text-secondary);
584
-
}
585
-
586
-
/** embed-post */
587
-
.embed-post {
588
-
border: 1px solid var(--divider);
589
-
border-radius: 6px;
590
-
overflow: hidden;
591
-
592
-
&:hover {
593
-
border-color: var(--divider-hover);
594
-
}
595
-
}
596
-
597
-
.embed-post__header {
598
-
display: flex;
599
-
margin: 12px 12px 0 12px;
600
-
color: var(--text-secondary);
601
-
}
602
-
.embed-post__header + .embed-image {
603
-
margin: 12px 0 0 0;
604
-
}
605
-
606
-
.embed-post__avatar-wrapper {
607
-
flex-shrink: 0;
608
-
margin: 0 8px 0 0;
609
-
border-radius: 9999px;
610
-
background: var(--background-secondary);
611
-
width: 20px;
612
-
height: 20px;
613
-
overflow: hidden;
614
-
}
615
-
.embed-post__avatar {
616
-
width: 100%;
617
-
height: 100%;
618
-
}
619
-
620
-
.embed-post__name-wrapper {
621
-
display: flex;
622
-
gap: 4px;
623
-
max-width: 100%;
624
-
overflow: hidden;
625
-
text-overflow: ellipsis;
626
-
white-space: nowrap;
627
-
}
628
-
.embed-post__display-name-wrapper {
629
-
overflow: hidden;
630
-
text-overflow: ellipsis;
631
-
}
632
-
.embed-post__display-name {
633
-
color: var(--text-primary);
634
-
font-weight: 700;
635
-
}
636
-
.embed-post__handle {
637
-
display: block;
638
-
overflow: hidden;
639
-
text-overflow: ellipsis;
640
-
white-space: nowrap;
641
-
}
642
-
643
-
.embed-post__date {
644
-
white-space: nowrap;
645
-
}
646
-
647
-
.embed-post__body {
648
-
display: flex;
649
-
align-items: flex-start;
650
-
}
651
-
652
-
.embed-post__image-aside {
653
-
flex-grow: 1;
654
-
flex-basis: 0px;
655
-
margin: 8px 0 12px 12px;
656
-
}
657
-
658
-
.embed-post__text {
659
-
display: -webkit-box;
660
-
margin: 4px 12px 12px 12px;
661
-
overflow: hidden;
662
-
-webkit-box-orient: vertical;
663
-
-webkit-line-clamp: 6;
664
-
flex-grow: 4;
665
-
flex-basis: 0px;
666
-
min-width: 0px;
667
-
white-space: pre-wrap;
668
-
overflow-wrap: break-word;
669
-
}
670
-
671
-
.embed-post__text:empty {
672
-
display: none;
673
-
}
674
-
675
-
.embed-post__divider {
676
-
margin: 12px 0 0 0;
677
-
}
-12
lib/utils/date.ts
-12
lib/utils/date.ts
···
1
-
export const abs_long = new Intl.DateTimeFormat('en-US', { dateStyle: 'long', timeStyle: 'short' });
2
-
export const abs_short = new Intl.DateTimeFormat('en-US', { dateStyle: 'medium' });
3
-
4
-
export const format_date = (formatter: Intl.DateTimeFormat, date: string | number) => {
5
-
const inst = new Date(date);
6
-
7
-
if (isNaN(inst.getTime())) {
8
-
return 'N/A';
9
-
}
10
-
11
-
return formatter.format(inst);
12
-
};
-14
lib/utils/number.ts
-14
lib/utils/number.ts
···
1
-
const long = new Intl.NumberFormat('en-US');
2
-
const compact = new Intl.NumberFormat('en-US', { notation: 'compact' });
3
-
4
-
export const format_compact = (value: number) => {
5
-
if (value < 1_000) {
6
-
return '' + value;
7
-
}
8
-
9
-
if (value < 100_000) {
10
-
return long.format(value);
11
-
}
12
-
13
-
return compact.format(value);
14
-
};
-82
lib/utils/richtext/segmentize.ts
-82
lib/utils/richtext/segmentize.ts
···
1
-
import { create_utf_string, get_utf8_length, slice_utf8 } from './unicode.ts';
2
-
import type { Facet, LinkFeature, MentionFeature, TagFeature } from './types.ts';
3
-
4
-
export interface RichTextSegment {
5
-
text: string;
6
-
link?: LinkFeature;
7
-
mention?: MentionFeature;
8
-
tag?: TagFeature;
9
-
}
10
-
11
-
const create_segment = (text: string, facet?: Facet): RichTextSegment => {
12
-
let link: LinkFeature | undefined;
13
-
let mention: MentionFeature | undefined;
14
-
let tag: TagFeature | undefined;
15
-
16
-
if (facet) {
17
-
const features = facet.features;
18
-
19
-
for (let idx = 0, len = features.length; idx < len; idx++) {
20
-
const feature = features[idx];
21
-
const type = feature.$type;
22
-
23
-
if (type === 'app.bsky.richtext.facet#link') {
24
-
link = feature;
25
-
} else if (type === 'app.bsky.richtext.facet#mention') {
26
-
mention = feature;
27
-
} else if (type === 'app.bsky.richtext.facet#tag') {
28
-
tag = feature;
29
-
}
30
-
}
31
-
}
32
-
33
-
return { text, link, mention, tag };
34
-
};
35
-
36
-
export const segment_richtext = (text: string, facets: Facet[] | undefined) => {
37
-
if (!facets || facets.length < 1) {
38
-
return [create_segment(text)];
39
-
}
40
-
41
-
const ustr = create_utf_string(text);
42
-
43
-
const segments: RichTextSegment[] = [];
44
-
const length = get_utf8_length(ustr);
45
-
46
-
const facets_length = facets.length;
47
-
48
-
let text_cursor = 0;
49
-
let facet_cursor = 0;
50
-
51
-
do {
52
-
const facet = facets[facet_cursor];
53
-
const { byteStart, byteEnd } = facet.index;
54
-
55
-
if (text_cursor < byteStart) {
56
-
segments.push(create_segment(slice_utf8(ustr, text_cursor, byteStart)));
57
-
} else if (text_cursor > byteStart) {
58
-
facet_cursor++;
59
-
continue;
60
-
}
61
-
62
-
if (byteStart < byteEnd) {
63
-
const subtext = slice_utf8(ustr, byteStart, byteEnd);
64
-
65
-
if (!subtext.trim()) {
66
-
// dont empty string entities
67
-
segments.push(create_segment(subtext));
68
-
} else {
69
-
segments.push(create_segment(subtext, facet));
70
-
}
71
-
}
72
-
73
-
text_cursor = byteEnd;
74
-
facet_cursor++;
75
-
} while (facet_cursor < facets_length);
76
-
77
-
if (text_cursor < length) {
78
-
segments.push(create_segment(slice_utf8(ustr, text_cursor, length)));
79
-
}
80
-
81
-
return segments;
82
-
};
-6
lib/utils/richtext/types.ts
-6
lib/utils/richtext/types.ts
···
1
-
import type { AppBskyRichtextFacet } from '@externdefs/bluesky-client/lexicons';
2
-
3
-
export type Facet = AppBskyRichtextFacet.Main;
4
-
export type LinkFeature = AppBskyRichtextFacet.Link;
5
-
export type MentionFeature = AppBskyRichtextFacet.Mention;
6
-
export type TagFeature = AppBskyRichtextFacet.Tag;
-22
lib/utils/richtext/unicode.ts
-22
lib/utils/richtext/unicode.ts
···
1
-
const encoder = new TextEncoder();
2
-
const decoder = new TextDecoder();
3
-
4
-
export interface UtfString {
5
-
u16: string;
6
-
u8: Uint8Array;
7
-
}
8
-
9
-
export const create_utf_string = (utf16: string): UtfString => {
10
-
return {
11
-
u16: utf16,
12
-
u8: encoder.encode(utf16),
13
-
};
14
-
};
15
-
16
-
export const get_utf8_length = (utf: UtfString) => {
17
-
return utf.u8.byteLength;
18
-
};
19
-
20
-
export const slice_utf8 = (utf: UtfString, start?: number, end?: number) => {
21
-
return decoder.decode(utf.u8.slice(start, end));
22
-
};
+11
-33
package.json
+11
-33
package.json
···
1
1
{
2
-
"type": "module",
3
-
"name": "bluesky-post-embed",
4
-
"description": "Custom element for embedding Bluesky posts",
5
-
"version": "0.1.6",
6
-
"author": "externdefs",
7
-
"license": "MIT",
8
-
"repository": {
9
-
"type": "git",
10
-
"url": "git+https://codeberg.org/mary-ext/bluesky-post-embed.git"
11
-
},
12
-
"files": [
13
-
"dist/"
14
-
],
2
+
"private": true,
15
3
"scripts": {
16
-
"dev": "vite",
17
-
"build": "tsc && vite build",
18
-
"fmt": "prettier --cache --write .",
19
-
"prepublishOnly": "pnpm run build"
20
-
},
21
-
"exports": {
22
-
".": "./dist/element.js",
23
-
"./core": "./dist/core.js",
24
-
"./style.css": "./dist/style.css"
25
-
},
26
-
"dependencies": {
27
-
"@externdefs/bluesky-client": "^0.5.8"
4
+
"fmt": "prettier --cache --write ."
28
5
},
29
6
"devDependencies": {
30
-
"@babel/core": "^7.24.4",
31
-
"@babel/plugin-syntax-typescript": "^7.24.1",
32
-
"@intrnl/jsx-to-string": "^0.1.6",
33
-
"@rollup/plugin-babel": "^6.0.4",
34
-
"prettier": "^3.2.5",
7
+
"@changesets/cli": "^2.29.8",
8
+
"prettier": "^3.7.4",
35
9
"prettier-plugin-css-order": "^2.1.2",
36
-
"typescript": "~5.4.5",
37
-
"vite": "^5.2.8",
38
-
"vite-plugin-dts": "^3.8.1"
10
+
"prettier-plugin-svelte": "^3.4.0",
11
+
"typescript": "~5.8.3"
12
+
},
13
+
"pnpm": {
14
+
"patchedDependencies": {
15
+
"svelte": "patches/svelte.patch"
16
+
}
39
17
}
40
18
}
+1
packages/bluesky-post-embed/.gitignore
+1
packages/bluesky-post-embed/.gitignore
···
1
+
themes/
+83
packages/bluesky-post-embed/README.md
+83
packages/bluesky-post-embed/README.md
···
1
+
# <bluesky-post-embed>
2
+
3
+
A custom element for embedding Bluesky posts.
4
+
5
+
## Installation
6
+
7
+
### via npm
8
+
9
+
```
10
+
npm install bluesky-post-embed
11
+
```
12
+
13
+
then, import the package on your app.
14
+
15
+
```js
16
+
import 'bluesky-post-embed';
17
+
18
+
import 'bluesky-post-embed/style.css';
19
+
import 'bluesky-post-embed/themes/light.css';
20
+
```
21
+
22
+
## Usage
23
+
24
+
```html
25
+
<bluesky-post src="at://did:plc:ragtjsm2j2vknwkz3zp4oxrd/app.bsky.feed.post/3kj2umze7zj2n">
26
+
<blockquote class="bluesky-post-fallback">
27
+
<p dir="auto">angel mode</p>
28
+
โ Paul Frazee (@pfrazee.com)
29
+
<a href="https://bsky.app/profile/did:plc:ragtjsm2j2vknwkz3zp4oxrd/post/3kj2umze7zj2n"
30
+
>January 16, 2024 at 9:11 AM</a
31
+
>
32
+
</blockquote>
33
+
</bluesky-post>
34
+
```
35
+
36
+
### Attributes
37
+
38
+
- `src` **Required**
39
+
AT-URI of the post record
40
+
- `contextless` **Optional**
41
+
Whether to show the post without any context (no parent reply)
42
+
- `allow-unauthenticated` **Optional**
43
+
Whether to allow unauthenticated viewing
44
+
- `service-uri` **Optional**
45
+
URL to an AppView service, defaults to `https://public.api.bsky.app`
46
+
47
+
### Events
48
+
49
+
- `loaded`
50
+
Fired when the embed has successfully loaded the post
51
+
- `error`
52
+
Fired when the embed fails to load the post
53
+
54
+
## SSR usage
55
+
56
+
The embeds are powered by a static HTML renderer, this renderer can be used directly in your
57
+
server-rendering framework of choice for a zero-JS experience.
58
+
59
+
```tsx
60
+
import { fetchPost, renderPost } from 'bluesky-post-embed/core';
61
+
62
+
import 'bluesky-post-embed/style.css';
63
+
import 'bluesky-post-embed/themes/light.css';
64
+
65
+
// fetch the post
66
+
const controller = new AbortController();
67
+
const data = await fetchPost({
68
+
src: `at://did:plc:ragtjsm2j2vknwkz3zp4oxrd/app.bsky.feed.post/3kj2umze7zj2n`,
69
+
signal: controller.signal,
70
+
});
71
+
72
+
// render the post
73
+
const html = renderPost(data);
74
+
return (
75
+
<bluesky-post
76
+
src={data.thread?.post.uri}
77
+
dangerouslySetInnerHTML={{ __html: html }}
78
+
></bluesky-post>
79
+
);
80
+
```
81
+
82
+
Check out examples for [Astro](https://github.com/mary-ext/bluesky-embed-astro) and
83
+
[SvelteKit](https://github.com/mary-ext/bluesky-embed-sveltekit).
+11
packages/bluesky-post-embed/index.html
+11
packages/bluesky-post-embed/index.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.0" />
6
+
<title>Bluesky post embed</title>
7
+
</head>
8
+
<body>
9
+
<script type="module" src="./src/main.tsx"></script>
10
+
</body>
11
+
</html>
+95
packages/bluesky-post-embed/lib/bluesky-post.svelte
+95
packages/bluesky-post-embed/lib/bluesky-post.svelte
···
1
+
<script lang="ts" module>
2
+
import type { AppBskyFeedDefs, AppBskyFeedGetPostThread } from '@atcute/client/lexicons';
3
+
4
+
type ThreadData = AppBskyFeedGetPostThread.Output['thread'];
5
+
type PostView = AppBskyFeedDefs.PostView;
6
+
7
+
const unwrapPostThread = (data: ThreadData, contextless: boolean, allowUnauthenticated: boolean) => {
8
+
const items: { post: PostView; parent: PostView | null }[] = [];
9
+
10
+
let i = 0;
11
+
let il = contextless ? 1 : 2;
12
+
13
+
let curr: typeof data | undefined = data;
14
+
while (curr) {
15
+
if (
16
+
curr.$type === 'app.bsky.feed.defs#notFoundPost' ||
17
+
curr.$type === 'app.bsky.feed.defs#blockedPost'
18
+
) {
19
+
break;
20
+
}
21
+
22
+
const post = curr.post;
23
+
24
+
if (i !== 0) {
25
+
items[i - 1].parent = post;
26
+
}
27
+
28
+
if (++i > il) {
29
+
break;
30
+
}
31
+
32
+
const author = post.author;
33
+
if (!allowUnauthenticated && author.labels?.some((def) => def.val === '!no-unauthenticated')) {
34
+
break;
35
+
}
36
+
37
+
items.push({ post: post, parent: null });
38
+
curr = curr.parent;
39
+
}
40
+
return items.reverse();
41
+
};
42
+
</script>
43
+
44
+
<script lang="ts">
45
+
import EmbedFrame from 'internal/components/embed-frame.svelte';
46
+
import HighlightedPost from 'internal/components/highlighted-post.svelte';
47
+
import Post from 'internal/components/post.svelte';
48
+
49
+
import { NO_UNAUTHENTICATED_LABEL } from 'internal/utils/constants.js';
50
+
import type { PostData } from 'internal/types/post.js';
51
+
52
+
const { thread, contextless, allowUnauthenticated }: PostData = $props();
53
+
54
+
const isPwiForbidden =
55
+
!allowUnauthenticated &&
56
+
thread !== null &&
57
+
thread.$type === 'app.bsky.feed.defs#threadViewPost' &&
58
+
thread.post.author.labels?.some((label) => label.val === NO_UNAUTHENTICATED_LABEL);
59
+
</script>
60
+
61
+
{#if thread === null}
62
+
{@render Message(`The post can't be found, it may have been deleted.`)}
63
+
{:else if isPwiForbidden}
64
+
{@render Message(`The author has requested for their posts to not be displayed on external sites.`)}
65
+
{:else}
66
+
{@const posts = unwrapPostThread(thread, contextless, allowUnauthenticated)}
67
+
68
+
<EmbedFrame>
69
+
{#each posts as { post, parent }, idx}
70
+
{@const hasPrevious = idx !== 0}
71
+
72
+
{#if idx === posts.length - 1}
73
+
<HighlightedPost {post} {parent} prev={hasPrevious} />
74
+
{:else}
75
+
<Post {post} {parent} prev={hasPrevious} />
76
+
{/if}
77
+
{/each}
78
+
</EmbedFrame>
79
+
{/if}
80
+
81
+
{#snippet Message(msg: string)}
82
+
<EmbedFrame>
83
+
<div class="message">{msg}</div>
84
+
</EmbedFrame>
85
+
{/snippet}
86
+
87
+
<style>
88
+
.message {
89
+
margin: 0 auto;
90
+
padding: 32px 16px;
91
+
max-width: 380px;
92
+
color: var(--text-secondary);
93
+
text-align: center;
94
+
}
95
+
</style>
+70
packages/bluesky-post-embed/lib/core.ts
+70
packages/bluesky-post-embed/lib/core.ts
···
1
+
import '@atcute/bluesky/lexicons';
2
+
3
+
import { simpleFetchHandler, XRPC, XRPCError } from '@atcute/client';
4
+
import type { At } from '@atcute/client/lexicons';
5
+
import { render } from 'svelte/server';
6
+
7
+
import type { PostData } from 'internal/types/post.js';
8
+
import { DEFAULT_APPVIEW_URL } from 'internal/utils/constants.js';
9
+
10
+
import BlueskyPost from './bluesky-post.svelte';
11
+
12
+
export type { PostData };
13
+
14
+
export interface PostFetchOptions {
15
+
/**
16
+
* AT-URI of the post in question
17
+
*/
18
+
uri: string;
19
+
/**
20
+
* Abort signal to cancel the request
21
+
*/
22
+
signal?: AbortSignal;
23
+
/**
24
+
* Whether to fetch post without context (no parent replies)
25
+
* @default false
26
+
*/
27
+
contextless?: boolean;
28
+
/**
29
+
* Whether to allow unauthenticated viewing
30
+
* @default false
31
+
*/
32
+
allowUnauthenticated?: boolean;
33
+
/**
34
+
* AppView service to use
35
+
* @default "https://public.api.bsky.app"
36
+
*/
37
+
serviceUri?: string;
38
+
}
39
+
40
+
export const fetchPost = async (opts: PostFetchOptions): Promise<PostData> => {
41
+
const rpc = new XRPC({ handler: simpleFetchHandler({ service: opts.serviceUri ?? DEFAULT_APPVIEW_URL }) });
42
+
const contextless = opts.contextless ?? false;
43
+
44
+
const { data } = await rpc
45
+
.get('app.bsky.feed.getPostThread', {
46
+
signal: opts.signal,
47
+
params: {
48
+
uri: opts.uri as At.ResourceUri,
49
+
parentHeight: !contextless ? 2 : 1,
50
+
depth: 0,
51
+
},
52
+
})
53
+
.catch((err) => {
54
+
if (err instanceof XRPCError) {
55
+
if (err.kind === 'NotFound') {
56
+
return { data: null };
57
+
}
58
+
}
59
+
60
+
return Promise.reject(err);
61
+
});
62
+
63
+
const thread = data?.thread.$type === 'app.bsky.feed.defs#threadViewPost' ? data.thread : null;
64
+
65
+
return { thread, contextless, allowUnauthenticated: opts.allowUnauthenticated ?? false };
66
+
};
67
+
68
+
export const renderPost = (data: PostData): string => {
69
+
return render(BlueskyPost, { props: data }).body;
70
+
};
+59
packages/bluesky-post-embed/lib/wc.ts
+59
packages/bluesky-post-embed/lib/wc.ts
···
1
+
import { fetchPost, renderPost } from './core';
2
+
3
+
export class BlueskyPost extends HTMLElement {
4
+
connectedCallback() {
5
+
this.load().then(
6
+
() => this.dispatchEvent(new CustomEvent('loaded')),
7
+
(err) => {
8
+
const defaulted = this.dispatchEvent(new CustomEvent('error', { detail: err }));
9
+
if (defaulted) {
10
+
throw err;
11
+
}
12
+
},
13
+
);
14
+
}
15
+
16
+
async load() {
17
+
const src = this.getAttribute('src')!;
18
+
const serviceUri = this.getAttribute('service-uri') || undefined;
19
+
const contextless = this.getAttribute('contextless') !== null;
20
+
const allowUnauthenticated = this.getAttribute('allow-unauthenticated') !== null;
21
+
const silent = this.getAttribute('silent') !== null;
22
+
23
+
const data = await fetchPost({ uri: src, contextless, allowUnauthenticated, serviceUri }).catch(
24
+
(error) => {
25
+
if (silent) {
26
+
console.warn('Failed to fetch post:', error);
27
+
return null;
28
+
}
29
+
throw error;
30
+
},
31
+
);
32
+
33
+
if (data === null) {
34
+
return;
35
+
}
36
+
37
+
const html = renderPost(data);
38
+
39
+
const root = this.shadowRoot;
40
+
41
+
if (!root) {
42
+
this.innerHTML = html;
43
+
} else {
44
+
const template = document.createElement('template');
45
+
template.innerHTML = html;
46
+
47
+
const fragment = template.content;
48
+
const slot = root.querySelector('slot');
49
+
50
+
if (slot) {
51
+
slot.replaceWith(fragment);
52
+
} else {
53
+
root.appendChild(fragment);
54
+
}
55
+
}
56
+
}
57
+
}
58
+
59
+
customElements.define('bluesky-post', BlueskyPost);
+45
packages/bluesky-post-embed/package.json
+45
packages/bluesky-post-embed/package.json
···
1
+
{
2
+
"type": "module",
3
+
"name": "bluesky-post-embed",
4
+
"description": "Custom element for embedding Bluesky posts",
5
+
"version": "1.0.6",
6
+
"author": "externdefs",
7
+
"license": "MIT",
8
+
"repository": {
9
+
"type": "git",
10
+
"url": "https://github.com/mary-ext/bluesky-embed",
11
+
"directory": "packages/bluesky-post-embed"
12
+
},
13
+
"files": [
14
+
"dist/",
15
+
"themes/"
16
+
],
17
+
"exports": {
18
+
".": "./dist/wc.js",
19
+
"./core": "./dist/core.js",
20
+
"./style.css": "./dist/core.css",
21
+
"./themes/*": "./themes/*"
22
+
},
23
+
"scripts": {
24
+
"dev": "vite",
25
+
"build": "pnpm run check && vite build && rsync -aHAX --delete ../../themes/ themes/",
26
+
"check": "svelte-check --tsconfig ./tsconfig.json && tsc -p tsconfig.node.json",
27
+
"prepack": "pnpm run build"
28
+
},
29
+
"dependencies": {
30
+
"@atcute/bluesky": "^2.1.1",
31
+
"@atcute/bluesky-richtext-segmenter": "^2.0.4",
32
+
"@atcute/client": "^3.1.0"
33
+
},
34
+
"devDependencies": {
35
+
"@preact/preset-vite": "^2.10.2",
36
+
"@tsconfig/svelte": "^5.0.6",
37
+
"@types/node": "^24.10.1",
38
+
"internal": "workspace:^",
39
+
"preact": "^10.28.0",
40
+
"svelte": "catalog:",
41
+
"svelte-check": "^4.3.4",
42
+
"vite": "^7.2.6",
43
+
"vite-plugin-dts": "^4.5.4"
44
+
}
45
+
}
+53
packages/bluesky-post-embed/src/app.tsx
+53
packages/bluesky-post-embed/src/app.tsx
···
1
+
import { useEffect, useMemo, useState } from 'preact/hooks';
2
+
3
+
import type { PostData } from 'internal/types/post.js';
4
+
import { fetchPost, renderPost } from '../lib/core';
5
+
6
+
const uri = `at://did:plc:ragtjsm2j2vknwkz3zp4oxrd/app.bsky.feed.post/3kj2umze7zj2n`;
7
+
8
+
const App = () => {
9
+
const [state, setState] = useState<{ uri: string; data: PostData }>();
10
+
11
+
useEffect(() => {
12
+
if (state && state.uri === uri) {
13
+
return;
14
+
}
15
+
16
+
const controller = new AbortController();
17
+
const promise = fetchPost({
18
+
uri: uri,
19
+
signal: controller.signal,
20
+
allowUnauthenticated: true,
21
+
});
22
+
23
+
promise.then((data) => {
24
+
setState({ uri, data });
25
+
});
26
+
27
+
return () => {
28
+
controller.abort();
29
+
};
30
+
}, [uri]);
31
+
32
+
return <div class="app">{state && <BlueskyPost data={state.data} />}</div>;
33
+
};
34
+
35
+
export default App;
36
+
37
+
const BlueskyPost = ({ data }: { data: PostData }) => {
38
+
const html = useMemo(() => renderPost(data), [data]);
39
+
40
+
return <bluesky-post src={data.thread?.post.uri} dangerouslySetInnerHTML={{ __html: html }}></bluesky-post>;
41
+
};
42
+
43
+
declare module 'preact' {
44
+
namespace JSX {
45
+
interface BlueskyPostAttributes extends HTMLAttributes<HTMLElement> {
46
+
src?: string;
47
+
}
48
+
49
+
interface IntrinsicElements {
50
+
'bluesky-post': BlueskyPostAttributes;
51
+
}
52
+
}
53
+
}
+8
packages/bluesky-post-embed/src/main.tsx
+8
packages/bluesky-post-embed/src/main.tsx
+8
packages/bluesky-post-embed/src/styles/main.css
+8
packages/bluesky-post-embed/src/styles/main.css
+199
packages/bluesky-post-embed/src/styles/normalize.css
+199
packages/bluesky-post-embed/src/styles/normalize.css
···
1
+
/*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize */
2
+
3
+
/*
4
+
Document
5
+
========
6
+
*/
7
+
8
+
/**
9
+
Use a better box model (opinionated).
10
+
*/
11
+
12
+
*,
13
+
::before,
14
+
::after {
15
+
box-sizing: border-box;
16
+
}
17
+
18
+
html {
19
+
line-height: 1.15; /* 1. Correct the line height in all browsers. */
20
+
/* Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) */
21
+
font-family: 'Inter', 'Roboto', ui-sans-serif, sans-serif, 'Noto Color Emoji', 'Twemoji Mozilla';
22
+
-webkit-text-size-adjust: 100%; /* 2. Prevent adjustments of font size after orientation changes in iOS. */
23
+
tab-size: 4; /* 3. Use a more readable tab size (opinionated). */
24
+
}
25
+
26
+
/*
27
+
Sections
28
+
========
29
+
*/
30
+
31
+
body {
32
+
margin: 0; /* Remove the margin in all browsers. */
33
+
}
34
+
35
+
/*
36
+
Text-level semantics
37
+
====================
38
+
*/
39
+
40
+
/**
41
+
Add the correct font weight in Chrome and Safari.
42
+
*/
43
+
44
+
b,
45
+
strong {
46
+
font-weight: bolder;
47
+
}
48
+
49
+
/**
50
+
1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3)
51
+
2. Correct the odd 'em' font sizing in all browsers.
52
+
*/
53
+
54
+
code,
55
+
kbd,
56
+
samp,
57
+
pre {
58
+
font-size: 1em; /* 2 */
59
+
font-family: 'JetBrains Mono NL', ui-monospace, monospace; /* 1 */
60
+
}
61
+
62
+
/**
63
+
Add the correct font size in all browsers.
64
+
*/
65
+
66
+
small {
67
+
font-size: 80%;
68
+
}
69
+
70
+
/**
71
+
Prevent 'sub' and 'sup' elements from affecting the line height in all browsers.
72
+
*/
73
+
74
+
sub,
75
+
sup {
76
+
position: relative;
77
+
vertical-align: baseline;
78
+
font-size: 75%;
79
+
line-height: 0;
80
+
}
81
+
82
+
sub {
83
+
bottom: -0.25em;
84
+
}
85
+
86
+
sup {
87
+
top: -0.5em;
88
+
}
89
+
90
+
/*
91
+
Tabular data
92
+
============
93
+
*/
94
+
95
+
/**
96
+
Correct table border color inheritance in Chrome and Safari. (https://issues.chromium.org/issues/40615503, https://bugs.webkit.org/show_bug.cgi?id=195016)
97
+
*/
98
+
99
+
table {
100
+
border-color: currentcolor;
101
+
}
102
+
103
+
/*
104
+
Forms
105
+
=====
106
+
*/
107
+
108
+
/**
109
+
1. Change the font styles in all browsers.
110
+
2. Remove the margin in Firefox and Safari.
111
+
*/
112
+
113
+
button,
114
+
input,
115
+
optgroup,
116
+
select,
117
+
textarea {
118
+
margin: 0; /* 2 */
119
+
font-size: 100%; /* 1 */
120
+
line-height: 1.15; /* 1 */
121
+
font-family: inherit; /* 1 */
122
+
}
123
+
124
+
/**
125
+
Correct the inability to style clickable types in iOS and Safari.
126
+
*/
127
+
128
+
button,
129
+
[type='button'],
130
+
[type='reset'],
131
+
[type='submit'] {
132
+
-webkit-appearance: button;
133
+
}
134
+
135
+
/**
136
+
Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers.
137
+
*/
138
+
139
+
legend {
140
+
padding: 0;
141
+
}
142
+
143
+
/**
144
+
Add the correct vertical alignment in Chrome and Firefox.
145
+
*/
146
+
147
+
progress {
148
+
vertical-align: baseline;
149
+
}
150
+
151
+
/**
152
+
Correct the cursor style of increment and decrement buttons in Safari.
153
+
*/
154
+
155
+
::-webkit-inner-spin-button,
156
+
::-webkit-outer-spin-button {
157
+
height: auto;
158
+
}
159
+
160
+
/**
161
+
1. Correct the odd appearance in Chrome and Safari.
162
+
2. Correct the outline style in Safari.
163
+
*/
164
+
165
+
[type='search'] {
166
+
-webkit-appearance: textfield; /* 1 */
167
+
outline-offset: -2px; /* 2 */
168
+
}
169
+
170
+
/**
171
+
Remove the inner padding in Chrome and Safari on macOS.
172
+
*/
173
+
174
+
::-webkit-search-decoration {
175
+
-webkit-appearance: none;
176
+
}
177
+
178
+
/**
179
+
1. Correct the inability to style clickable types in iOS and Safari.
180
+
2. Change font properties to 'inherit' in Safari.
181
+
*/
182
+
183
+
::-webkit-file-upload-button {
184
+
-webkit-appearance: button; /* 1 */
185
+
font: inherit; /* 2 */
186
+
}
187
+
188
+
/*
189
+
Interactive
190
+
===========
191
+
*/
192
+
193
+
/*
194
+
Add the correct display in Chrome and Safari.
195
+
*/
196
+
197
+
summary {
198
+
display: list-item;
199
+
}
+8
packages/bluesky-post-embed/svelte.config.js
+8
packages/bluesky-post-embed/svelte.config.js
+14
packages/bluesky-post-embed/tsconfig.build.json
+14
packages/bluesky-post-embed/tsconfig.build.json
···
1
+
{
2
+
"extends": "@tsconfig/svelte/tsconfig.json",
3
+
"compilerOptions": {
4
+
"types": [],
5
+
"target": "ESNext",
6
+
"useDefineForClassFields": true,
7
+
"module": "ESNext",
8
+
"resolveJsonModule": true,
9
+
"isolatedModules": true,
10
+
"moduleDetection": "force",
11
+
"noEmit": true,
12
+
},
13
+
"include": ["lib"],
14
+
}
+17
packages/bluesky-post-embed/tsconfig.json
+17
packages/bluesky-post-embed/tsconfig.json
···
1
+
{
2
+
"extends": "@tsconfig/svelte/tsconfig.json",
3
+
"compilerOptions": {
4
+
"types": [],
5
+
"target": "ESNext",
6
+
"useDefineForClassFields": true,
7
+
"module": "ESNext",
8
+
"resolveJsonModule": true,
9
+
"isolatedModules": true,
10
+
"moduleDetection": "force",
11
+
"noEmit": true,
12
+
"jsx": "react-jsx",
13
+
"jsxImportSource": "preact",
14
+
},
15
+
"include": ["lib", "src"],
16
+
"references": [{ "path": "./tsconfig.node.json" }],
17
+
}
+14
packages/bluesky-post-embed/tsconfig.node.json
+14
packages/bluesky-post-embed/tsconfig.node.json
···
1
+
{
2
+
"compilerOptions": {
3
+
"composite": true,
4
+
"types": ["node"],
5
+
"skipLibCheck": true,
6
+
"module": "ESNext",
7
+
"target": "ESNext",
8
+
"moduleResolution": "Bundler",
9
+
"strict": true,
10
+
"noEmit": true,
11
+
"noUncheckedSideEffectImports": true,
12
+
},
13
+
"include": ["vite.config.ts"],
14
+
}
+103
packages/bluesky-post-embed/vite.config.ts
+103
packages/bluesky-post-embed/vite.config.ts
···
1
+
import * as path from 'node:path';
2
+
3
+
import { compile as compileSvelte } from 'svelte/compiler';
4
+
import { type Plugin, createFilter, defineConfig } from 'vite';
5
+
6
+
import preact from '@preact/preset-vite';
7
+
import dts from 'vite-plugin-dts';
8
+
9
+
export default defineConfig({
10
+
base: './',
11
+
build: {
12
+
outDir: 'dist/',
13
+
target: 'esnext',
14
+
minify: false,
15
+
cssMinify: false,
16
+
cssCodeSplit: true,
17
+
lib: {
18
+
entry: {
19
+
core: 'lib/core.ts',
20
+
wc: 'lib/wc.ts',
21
+
},
22
+
formats: ['es'],
23
+
},
24
+
rollupOptions: {
25
+
external: ['@atcute/client', '@atcute/bluesky-richtext-segmenter'],
26
+
},
27
+
},
28
+
esbuild: {
29
+
target: 'esnext',
30
+
},
31
+
plugins: [
32
+
svelte(),
33
+
preact(),
34
+
dts({
35
+
rollupTypes: true,
36
+
tsconfigPath: 'tsconfig.build.json',
37
+
beforeWriteFile(filePath, content) {
38
+
if (filePath.endsWith('/core.d.ts')) {
39
+
// Make sure the relevant types are present
40
+
return { content: `import '@atcute/bluesky/lexicons';\n${content}` };
41
+
}
42
+
},
43
+
}),
44
+
],
45
+
});
46
+
47
+
function svelte(): Plugin {
48
+
const filter = createFilter('**/*.svelte');
49
+
const stylesheets = new Map<string, string>();
50
+
51
+
return {
52
+
name: 'svelte',
53
+
resolveId(id) {
54
+
return stylesheets.has(id) ? id : null;
55
+
},
56
+
load(id) {
57
+
const css = stylesheets.get(id);
58
+
if (css !== undefined) {
59
+
this.addWatchFile(id.slice(0, -4));
60
+
return { code: css };
61
+
}
62
+
63
+
return null;
64
+
},
65
+
transform(source, id) {
66
+
if (!filter(id)) {
67
+
return null;
68
+
}
69
+
70
+
const result = compileSvelte(source, {
71
+
generate: 'server',
72
+
css: 'external',
73
+
cssHash({ hash, filename }) {
74
+
const prefix = `github:mary-ext/bluesky-post-embed/`;
75
+
return `s-` + hash(prefix + path.relative(__dirname, filename));
76
+
},
77
+
runes: true,
78
+
filename: id,
79
+
});
80
+
81
+
{
82
+
const { js, css, warnings } = result;
83
+
84
+
let jsCode = js.code;
85
+
86
+
if (css) {
87
+
const cssId = `${id}.css`;
88
+
jsCode = jsCode + `\nimport ${JSON.stringify(cssId)};\n`;
89
+
stylesheets.set(cssId, css.code);
90
+
}
91
+
92
+
for (const warn of warnings) {
93
+
if (warn.code === 'state_referenced_locally') {
94
+
continue;
95
+
}
96
+
this.warn(warn);
97
+
}
98
+
99
+
return { code: jsCode };
100
+
}
101
+
},
102
+
};
103
+
}
+1
packages/bluesky-profile-card-embed/.gitignore
+1
packages/bluesky-profile-card-embed/.gitignore
···
1
+
themes/
+81
packages/bluesky-profile-card-embed/README.md
+81
packages/bluesky-profile-card-embed/README.md
···
1
+
# <bluesky-profile-card-embed>
2
+
3
+
A custom element for embedding Bluesky profile cards.
4
+
5
+
## Installation
6
+
7
+
### via npm
8
+
9
+
```
10
+
npm install bluesky-profile-card-embed
11
+
```
12
+
13
+
then, import the package on your app.
14
+
15
+
```js
16
+
import 'bluesky-profile-card-embed';
17
+
18
+
import 'bluesky-profile-card-embed/style.css';
19
+
import 'bluesky-profile-card-embed/themes/light.css';
20
+
```
21
+
22
+
## Usage
23
+
24
+
```html
25
+
<bluesky-profile-card actor="did:plc:2gkh62xvzokhlf6li4ol3b3d">
26
+
<a
27
+
target="_blank"
28
+
href="https://bsky.app/profile/did:plc:2gkh62xvzokhlf6li4ol3b3d"
29
+
class="bluesky-profile-card-fallback"
30
+
>
31
+
@patak.dev's Bluesky profile
32
+
</a>
33
+
</bluesky-profile-card>
34
+
```
35
+
36
+
### Attributes
37
+
38
+
- `actor` **Required**
39
+
DID or handle of the account
40
+
- `allow-unauthenticated` **Optional**
41
+
Whether to allow unauthenticated viewing
42
+
- `service-uri` **Optional**
43
+
URL to an AppView service, defaults to `https://public.api.bsky.app`
44
+
45
+
### Events
46
+
47
+
- `loaded`
48
+
Fired when the embed has successfully loaded the profile card
49
+
- `error`
50
+
Fired when the embed fails to load the profile card
51
+
52
+
## SSR usage
53
+
54
+
The embeds are powered by a static HTML renderer, this renderer can be used directly in your
55
+
server-rendering framework of choice for a zero-JS experience.
56
+
57
+
```tsx
58
+
import { fetchProfileCard, renderProfileCard } from 'bluesky-profile-card-embed/core';
59
+
60
+
import 'bluesky-profile-card-embed/style.css';
61
+
import 'bluesky-profile-card-embed/themes/light.css';
62
+
63
+
// fetch the profile
64
+
const controller = new AbortController();
65
+
const data = await fetchProfileCard({
66
+
actor: `did:plc:ragtjsm2j2vknwkz3zp4oxrd`,
67
+
signal: controller.signal,
68
+
});
69
+
70
+
// render the profile
71
+
const html = renderProfileCard(data);
72
+
return (
73
+
<bluesky-profile-card
74
+
actor={data.profile?.did}
75
+
dangerouslySetInnerHTML={{ __html: html }}
76
+
></bluesky-profile-card>
77
+
);
78
+
```
79
+
80
+
Check out examples for [Astro](https://github.com/mary-ext/bluesky-embed-astro) and
81
+
[SvelteKit](https://github.com/mary-ext/bluesky-embed-sveltekit).
+11
packages/bluesky-profile-card-embed/index.html
+11
packages/bluesky-profile-card-embed/index.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.0" />
6
+
<title>Bluesky profile card embed</title>
7
+
</head>
8
+
<body>
9
+
<script type="module" src="./src/main.tsx"></script>
10
+
</body>
11
+
</html>
+38
packages/bluesky-profile-card-embed/lib/bluesky-profile-card.svelte
+38
packages/bluesky-profile-card-embed/lib/bluesky-profile-card.svelte
···
1
+
<script lang="ts">
2
+
import EmbedFrame from 'internal/components/embed-frame.svelte';
3
+
import ProfileCard from 'internal/components/profile-card.svelte';
4
+
5
+
import type { ProfileCardData } from 'internal/types/profile-card.js';
6
+
import { NO_UNAUTHENTICATED_LABEL } from 'internal/utils/constants.js';
7
+
8
+
const { profile, allowUnauthenticated }: ProfileCardData = $props();
9
+
10
+
const isPwiForbidden =
11
+
!allowUnauthenticated && profile?.labels?.some((label) => label.val === NO_UNAUTHENTICATED_LABEL);
12
+
</script>
13
+
14
+
{#if profile === null}
15
+
{@render Message(`The profile can't be found, it may have been deleted.`)}
16
+
{:else if isPwiForbidden}
17
+
{@render Message(`The user has requested for their profile to not be displayed on external sites.`)}
18
+
{:else}
19
+
<EmbedFrame>
20
+
<ProfileCard {profile} />
21
+
</EmbedFrame>
22
+
{/if}
23
+
24
+
{#snippet Message(msg: string)}
25
+
<EmbedFrame>
26
+
<div class="message">{msg}</div>
27
+
</EmbedFrame>
28
+
{/snippet}
29
+
30
+
<style>
31
+
.message {
32
+
margin: 0 auto;
33
+
padding: 32px 16px;
34
+
max-width: 380px;
35
+
color: var(--text-secondary);
36
+
text-align: center;
37
+
}
38
+
</style>
+61
packages/bluesky-profile-card-embed/lib/core.ts
+61
packages/bluesky-profile-card-embed/lib/core.ts
···
1
+
import '@atcute/bluesky/lexicons';
2
+
3
+
import { simpleFetchHandler, XRPC, XRPCError } from '@atcute/client';
4
+
import type { At } from '@atcute/client/lexicons';
5
+
import { render } from 'svelte/server';
6
+
7
+
import type { ProfileCardData } from 'internal/types/profile-card.js';
8
+
import { DEFAULT_APPVIEW_URL } from 'internal/utils/constants.js';
9
+
10
+
import BlueskyProfileCard from './bluesky-profile-card.svelte';
11
+
12
+
export type { ProfileCardData };
13
+
14
+
export interface ProfileCardFetchOptions {
15
+
/**
16
+
* Handle or DID identifier of the user
17
+
*/
18
+
actor: string;
19
+
/**
20
+
* Abort signal to cancel the request
21
+
*/
22
+
signal?: AbortSignal;
23
+
/**
24
+
* Allow unauthenticated viewing
25
+
* @default false
26
+
*/
27
+
allowUnauthenticated?: boolean;
28
+
/**
29
+
* AppView service to use
30
+
* @default "https://public.api.bsky.app"
31
+
*/
32
+
serviceUri?: string;
33
+
}
34
+
35
+
export const fetchProfileCard = async (opts: ProfileCardFetchOptions): Promise<ProfileCardData> => {
36
+
const actor = opts.actor;
37
+
const allowUnauthenticated = opts.allowUnauthenticated ?? false;
38
+
39
+
const rpc = new XRPC({ handler: simpleFetchHandler({ service: opts.serviceUri ?? DEFAULT_APPVIEW_URL }) });
40
+
41
+
const { data: profile } = await rpc
42
+
.get('app.bsky.actor.getProfile', {
43
+
signal: opts.signal,
44
+
params: { actor: actor as At.Identifier },
45
+
})
46
+
.catch((err) => {
47
+
if (err instanceof XRPCError) {
48
+
if (err.kind === 'InvalidRequest' && err.description === 'Profile not found') {
49
+
return { data: null };
50
+
}
51
+
}
52
+
53
+
return Promise.reject(err);
54
+
});
55
+
56
+
return { profile: profile, allowUnauthenticated };
57
+
};
58
+
59
+
export const renderProfileCard = (data: ProfileCardData): string => {
60
+
return render(BlueskyProfileCard, { props: data }).body;
61
+
};
+56
packages/bluesky-profile-card-embed/lib/wc.ts
+56
packages/bluesky-profile-card-embed/lib/wc.ts
···
1
+
import { fetchProfileCard, renderProfileCard } from './core';
2
+
3
+
export class BlueskyProfileCard extends HTMLElement {
4
+
connectedCallback() {
5
+
this.load().then(
6
+
() => this.dispatchEvent(new CustomEvent('loaded')),
7
+
(err) => {
8
+
const defaulted = this.dispatchEvent(new CustomEvent('error', { detail: err }));
9
+
if (defaulted) {
10
+
throw err;
11
+
}
12
+
},
13
+
);
14
+
}
15
+
16
+
async load() {
17
+
const actor = this.getAttribute('actor')!;
18
+
const serviceUri = this.getAttribute('service-uri') || undefined;
19
+
const allowUnauthenticated = this.getAttribute('allow-unauthenticated') !== null;
20
+
const silent = this.getAttribute('silent') !== null;
21
+
22
+
const data = await fetchProfileCard({ actor, allowUnauthenticated, serviceUri }).catch((error) => {
23
+
if (silent) {
24
+
console.warn('Failed to fetch profile card:', error);
25
+
return null;
26
+
}
27
+
throw error;
28
+
});
29
+
30
+
if (data === null) {
31
+
return;
32
+
}
33
+
34
+
const html = renderProfileCard(data);
35
+
36
+
const root = this.shadowRoot;
37
+
38
+
if (!root) {
39
+
this.innerHTML = html;
40
+
} else {
41
+
const template = document.createElement('template');
42
+
template.innerHTML = html;
43
+
44
+
const fragment = template.content;
45
+
const slot = root.querySelector('slot');
46
+
47
+
if (slot) {
48
+
slot.replaceWith(fragment);
49
+
} else {
50
+
root.appendChild(fragment);
51
+
}
52
+
}
53
+
}
54
+
}
55
+
56
+
customElements.define('bluesky-profile-card', BlueskyProfileCard);
+45
packages/bluesky-profile-card-embed/package.json
+45
packages/bluesky-profile-card-embed/package.json
···
1
+
{
2
+
"type": "module",
3
+
"name": "bluesky-profile-card-embed",
4
+
"description": "Custom element for embedding Bluesky profile cards",
5
+
"version": "1.0.1",
6
+
"author": "externdefs",
7
+
"license": "MIT",
8
+
"repository": {
9
+
"type": "git",
10
+
"url": "https://github.com/mary-ext/bluesky-embed",
11
+
"directory": "packages/bluesky-profile-card-embed"
12
+
},
13
+
"files": [
14
+
"dist/",
15
+
"themes/"
16
+
],
17
+
"exports": {
18
+
".": "./dist/wc.js",
19
+
"./core": "./dist/core.js",
20
+
"./style.css": "./dist/core.css",
21
+
"./themes/*": "./themes/*"
22
+
},
23
+
"scripts": {
24
+
"dev": "vite",
25
+
"build": "pnpm run check && vite build && rsync -aHAX --delete ../../themes/ themes/",
26
+
"check": "svelte-check --tsconfig ./tsconfig.json && tsc -p tsconfig.node.json",
27
+
"prepack": "pnpm run build"
28
+
},
29
+
"dependencies": {
30
+
"@atcute/bluesky": "^2.1.1",
31
+
"@atcute/bluesky-richtext-parser": "^1.0.7",
32
+
"@atcute/client": "^3.1.0"
33
+
},
34
+
"devDependencies": {
35
+
"@preact/preset-vite": "^2.10.2",
36
+
"@tsconfig/svelte": "^5.0.6",
37
+
"@types/node": "^24.10.1",
38
+
"internal": "workspace:^",
39
+
"preact": "^10.28.0",
40
+
"svelte": "catalog:",
41
+
"svelte-check": "^4.3.4",
42
+
"vite": "^7.2.6",
43
+
"vite-plugin-dts": "^4.5.4"
44
+
}
45
+
}
+58
packages/bluesky-profile-card-embed/src/app.tsx
+58
packages/bluesky-profile-card-embed/src/app.tsx
···
1
+
import { useEffect, useMemo, useState } from 'preact/hooks';
2
+
3
+
import type { ProfileCardData } from 'internal/types/profile-card.js';
4
+
import { fetchProfileCard, renderProfileCard } from '../lib/core';
5
+
6
+
const actor = `patak.dev`;
7
+
8
+
const App = () => {
9
+
const [state, setState] = useState<{ actor: string; data: ProfileCardData }>();
10
+
11
+
useEffect(() => {
12
+
if (state && state.actor === actor) {
13
+
return;
14
+
}
15
+
16
+
const controller = new AbortController();
17
+
const promise = fetchProfileCard({
18
+
actor: actor,
19
+
signal: controller.signal,
20
+
allowUnauthenticated: true,
21
+
});
22
+
23
+
promise.then((data) => {
24
+
setState({ actor, data });
25
+
});
26
+
27
+
return () => {
28
+
controller.abort();
29
+
};
30
+
}, [actor]);
31
+
32
+
return <div class="app">{state && <BlueskyProfileCard data={state.data} />}</div>;
33
+
};
34
+
35
+
export default App;
36
+
37
+
const BlueskyProfileCard = ({ data }: { data: ProfileCardData }) => {
38
+
const html = useMemo(() => renderProfileCard(data), [data, renderProfileCard]);
39
+
40
+
return (
41
+
<bluesky-profile-card
42
+
actor={data.profile?.did}
43
+
dangerouslySetInnerHTML={{ __html: html }}
44
+
></bluesky-profile-card>
45
+
);
46
+
};
47
+
48
+
declare module 'preact' {
49
+
namespace JSX {
50
+
interface BlueskyProfileCardAttributes extends HTMLAttributes<HTMLElement> {
51
+
actor?: string;
52
+
}
53
+
54
+
interface IntrinsicElements {
55
+
'bluesky-profile-card': BlueskyProfileCardAttributes;
56
+
}
57
+
}
58
+
}
+8
packages/bluesky-profile-card-embed/src/main.tsx
+8
packages/bluesky-profile-card-embed/src/main.tsx
+8
packages/bluesky-profile-card-embed/src/styles/main.css
+8
packages/bluesky-profile-card-embed/src/styles/main.css
+199
packages/bluesky-profile-card-embed/src/styles/normalize.css
+199
packages/bluesky-profile-card-embed/src/styles/normalize.css
···
1
+
/*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize */
2
+
3
+
/*
4
+
Document
5
+
========
6
+
*/
7
+
8
+
/**
9
+
Use a better box model (opinionated).
10
+
*/
11
+
12
+
*,
13
+
::before,
14
+
::after {
15
+
box-sizing: border-box;
16
+
}
17
+
18
+
html {
19
+
line-height: 1.15; /* 1. Correct the line height in all browsers. */
20
+
/* Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) */
21
+
font-family: 'Inter', 'Roboto', ui-sans-serif, sans-serif, 'Noto Color Emoji', 'Twemoji Mozilla';
22
+
-webkit-text-size-adjust: 100%; /* 2. Prevent adjustments of font size after orientation changes in iOS. */
23
+
tab-size: 4; /* 3. Use a more readable tab size (opinionated). */
24
+
}
25
+
26
+
/*
27
+
Sections
28
+
========
29
+
*/
30
+
31
+
body {
32
+
margin: 0; /* Remove the margin in all browsers. */
33
+
}
34
+
35
+
/*
36
+
Text-level semantics
37
+
====================
38
+
*/
39
+
40
+
/**
41
+
Add the correct font weight in Chrome and Safari.
42
+
*/
43
+
44
+
b,
45
+
strong {
46
+
font-weight: bolder;
47
+
}
48
+
49
+
/**
50
+
1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3)
51
+
2. Correct the odd 'em' font sizing in all browsers.
52
+
*/
53
+
54
+
code,
55
+
kbd,
56
+
samp,
57
+
pre {
58
+
font-size: 1em; /* 2 */
59
+
font-family: 'JetBrains Mono NL', ui-monospace, monospace; /* 1 */
60
+
}
61
+
62
+
/**
63
+
Add the correct font size in all browsers.
64
+
*/
65
+
66
+
small {
67
+
font-size: 80%;
68
+
}
69
+
70
+
/**
71
+
Prevent 'sub' and 'sup' elements from affecting the line height in all browsers.
72
+
*/
73
+
74
+
sub,
75
+
sup {
76
+
position: relative;
77
+
vertical-align: baseline;
78
+
font-size: 75%;
79
+
line-height: 0;
80
+
}
81
+
82
+
sub {
83
+
bottom: -0.25em;
84
+
}
85
+
86
+
sup {
87
+
top: -0.5em;
88
+
}
89
+
90
+
/*
91
+
Tabular data
92
+
============
93
+
*/
94
+
95
+
/**
96
+
Correct table border color inheritance in Chrome and Safari. (https://issues.chromium.org/issues/40615503, https://bugs.webkit.org/show_bug.cgi?id=195016)
97
+
*/
98
+
99
+
table {
100
+
border-color: currentcolor;
101
+
}
102
+
103
+
/*
104
+
Forms
105
+
=====
106
+
*/
107
+
108
+
/**
109
+
1. Change the font styles in all browsers.
110
+
2. Remove the margin in Firefox and Safari.
111
+
*/
112
+
113
+
button,
114
+
input,
115
+
optgroup,
116
+
select,
117
+
textarea {
118
+
margin: 0; /* 2 */
119
+
font-size: 100%; /* 1 */
120
+
line-height: 1.15; /* 1 */
121
+
font-family: inherit; /* 1 */
122
+
}
123
+
124
+
/**
125
+
Correct the inability to style clickable types in iOS and Safari.
126
+
*/
127
+
128
+
button,
129
+
[type='button'],
130
+
[type='reset'],
131
+
[type='submit'] {
132
+
-webkit-appearance: button;
133
+
}
134
+
135
+
/**
136
+
Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers.
137
+
*/
138
+
139
+
legend {
140
+
padding: 0;
141
+
}
142
+
143
+
/**
144
+
Add the correct vertical alignment in Chrome and Firefox.
145
+
*/
146
+
147
+
progress {
148
+
vertical-align: baseline;
149
+
}
150
+
151
+
/**
152
+
Correct the cursor style of increment and decrement buttons in Safari.
153
+
*/
154
+
155
+
::-webkit-inner-spin-button,
156
+
::-webkit-outer-spin-button {
157
+
height: auto;
158
+
}
159
+
160
+
/**
161
+
1. Correct the odd appearance in Chrome and Safari.
162
+
2. Correct the outline style in Safari.
163
+
*/
164
+
165
+
[type='search'] {
166
+
-webkit-appearance: textfield; /* 1 */
167
+
outline-offset: -2px; /* 2 */
168
+
}
169
+
170
+
/**
171
+
Remove the inner padding in Chrome and Safari on macOS.
172
+
*/
173
+
174
+
::-webkit-search-decoration {
175
+
-webkit-appearance: none;
176
+
}
177
+
178
+
/**
179
+
1. Correct the inability to style clickable types in iOS and Safari.
180
+
2. Change font properties to 'inherit' in Safari.
181
+
*/
182
+
183
+
::-webkit-file-upload-button {
184
+
-webkit-appearance: button; /* 1 */
185
+
font: inherit; /* 2 */
186
+
}
187
+
188
+
/*
189
+
Interactive
190
+
===========
191
+
*/
192
+
193
+
/*
194
+
Add the correct display in Chrome and Safari.
195
+
*/
196
+
197
+
summary {
198
+
display: list-item;
199
+
}
+8
packages/bluesky-profile-card-embed/svelte.config.js
+8
packages/bluesky-profile-card-embed/svelte.config.js
+14
packages/bluesky-profile-card-embed/tsconfig.build.json
+14
packages/bluesky-profile-card-embed/tsconfig.build.json
···
1
+
{
2
+
"extends": "@tsconfig/svelte/tsconfig.json",
3
+
"compilerOptions": {
4
+
"types": [],
5
+
"target": "ESNext",
6
+
"useDefineForClassFields": true,
7
+
"module": "ESNext",
8
+
"resolveJsonModule": true,
9
+
"isolatedModules": true,
10
+
"moduleDetection": "force",
11
+
"noEmit": true,
12
+
},
13
+
"include": ["lib"],
14
+
}
+17
packages/bluesky-profile-card-embed/tsconfig.json
+17
packages/bluesky-profile-card-embed/tsconfig.json
···
1
+
{
2
+
"extends": "@tsconfig/svelte/tsconfig.json",
3
+
"compilerOptions": {
4
+
"types": [],
5
+
"target": "ESNext",
6
+
"useDefineForClassFields": true,
7
+
"module": "ESNext",
8
+
"resolveJsonModule": true,
9
+
"isolatedModules": true,
10
+
"moduleDetection": "force",
11
+
"noEmit": true,
12
+
"jsx": "react-jsx",
13
+
"jsxImportSource": "preact",
14
+
},
15
+
"include": ["lib", "src"],
16
+
"references": [{ "path": "./tsconfig.node.json" }],
17
+
}
+14
packages/bluesky-profile-card-embed/tsconfig.node.json
+14
packages/bluesky-profile-card-embed/tsconfig.node.json
···
1
+
{
2
+
"compilerOptions": {
3
+
"composite": true,
4
+
"types": ["node"],
5
+
"skipLibCheck": true,
6
+
"module": "ESNext",
7
+
"target": "ESNext",
8
+
"moduleResolution": "Bundler",
9
+
"strict": true,
10
+
"noEmit": true,
11
+
"noUncheckedSideEffectImports": true,
12
+
},
13
+
"include": ["vite.config.ts"],
14
+
}
+103
packages/bluesky-profile-card-embed/vite.config.ts
+103
packages/bluesky-profile-card-embed/vite.config.ts
···
1
+
import * as path from 'node:path';
2
+
3
+
import { compile as compileSvelte } from 'svelte/compiler';
4
+
import { type Plugin, createFilter, defineConfig } from 'vite';
5
+
6
+
import preact from '@preact/preset-vite';
7
+
import dts from 'vite-plugin-dts';
8
+
9
+
export default defineConfig({
10
+
base: './',
11
+
build: {
12
+
outDir: 'dist/',
13
+
target: 'esnext',
14
+
minify: false,
15
+
cssMinify: false,
16
+
cssCodeSplit: true,
17
+
lib: {
18
+
entry: {
19
+
core: 'lib/core.ts',
20
+
wc: 'lib/wc.ts',
21
+
},
22
+
formats: ['es'],
23
+
},
24
+
rollupOptions: {
25
+
external: ['@atcute/client', '@atcute/bluesky-richtext-parser'],
26
+
},
27
+
},
28
+
esbuild: {
29
+
target: 'esnext',
30
+
},
31
+
plugins: [
32
+
svelte(),
33
+
preact(),
34
+
dts({
35
+
rollupTypes: true,
36
+
tsconfigPath: 'tsconfig.build.json',
37
+
beforeWriteFile(filePath, content) {
38
+
if (filePath.endsWith('/core.d.ts')) {
39
+
// Make sure the relevant types are present
40
+
return { content: `import '@atcute/bluesky/lexicons';\n${content}` };
41
+
}
42
+
},
43
+
}),
44
+
],
45
+
});
46
+
47
+
function svelte(): Plugin {
48
+
const filter = createFilter('**/*.svelte');
49
+
const stylesheets = new Map<string, string>();
50
+
51
+
return {
52
+
name: 'svelte',
53
+
resolveId(id) {
54
+
return stylesheets.has(id) ? id : null;
55
+
},
56
+
load(id) {
57
+
const css = stylesheets.get(id);
58
+
if (css !== undefined) {
59
+
this.addWatchFile(id.slice(0, -4));
60
+
return { code: css };
61
+
}
62
+
63
+
return null;
64
+
},
65
+
transform(source, id) {
66
+
if (!filter(id)) {
67
+
return null;
68
+
}
69
+
70
+
const result = compileSvelte(source, {
71
+
generate: 'server',
72
+
css: 'external',
73
+
cssHash({ hash, filename }) {
74
+
const prefix = `github:mary-ext/bluesky-profile-card-embed/`;
75
+
return `s-` + hash(prefix + path.relative(__dirname, filename));
76
+
},
77
+
runes: true,
78
+
filename: id,
79
+
});
80
+
81
+
{
82
+
const { js, css, warnings } = result;
83
+
84
+
let jsCode = js.code;
85
+
86
+
if (css) {
87
+
const cssId = `${id}.css`;
88
+
jsCode = jsCode + `\nimport ${JSON.stringify(cssId)};\n`;
89
+
stylesheets.set(cssId, css.code);
90
+
}
91
+
92
+
for (const warn of warnings) {
93
+
if (warn.code === 'state_referenced_locally') {
94
+
continue;
95
+
}
96
+
this.warn(warn);
97
+
}
98
+
99
+
return { code: jsCode };
100
+
}
101
+
},
102
+
};
103
+
}
+1
packages/bluesky-profile-feed-embed/.gitignore
+1
packages/bluesky-profile-feed-embed/.gitignore
···
1
+
themes/
+84
packages/bluesky-profile-feed-embed/README.md
+84
packages/bluesky-profile-feed-embed/README.md
···
1
+
# <bluesky-profile-feed-embed>
2
+
3
+
A custom element for embedding Bluesky profile feeds.
4
+
5
+
## Installation
6
+
7
+
### via npm
8
+
9
+
```
10
+
npm install bluesky-profile-feed-embed
11
+
```
12
+
13
+
then, import the package on your app.
14
+
15
+
```js
16
+
import 'bluesky-profile-feed-embed';
17
+
18
+
import 'bluesky-profile-feed-embed/style.css';
19
+
import 'bluesky-profile-feed-embed/themes/light.css';
20
+
```
21
+
22
+
## Usage
23
+
24
+
```html
25
+
<bluesky-profile-feed actor="did:plc:ragtjsm2j2vknwkz3zp4oxrd" include-pins>
26
+
<a
27
+
target="_blank"
28
+
href="https://bsky.app/profile/did:plc:ragtjsm2j2vknwkz3zp4oxrd"
29
+
class="bluesky-profile-feed-fallback"
30
+
>
31
+
Posts by Paul Frazee (@pfrazee.com)
32
+
</a>
33
+
</bluesky-profile-feed>
34
+
```
35
+
36
+
### Attributes
37
+
38
+
- `actor` **Required**
39
+
DID or handle of the account
40
+
- `include-pins` **Optional**
41
+
Whether to show pinned posts
42
+
- `allow-unauthenticated` **Optional**
43
+
Whether to allow unauthenticated viewing
44
+
- `service-uri` **Optional**
45
+
URL to an AppView service, defaults to `https://public.api.bsky.app`
46
+
47
+
### Events
48
+
49
+
- `loaded`
50
+
Fired when the embed has successfully loaded the post
51
+
- `error`
52
+
Fired when the embed fails to load the post
53
+
54
+
## SSR usage
55
+
56
+
The embeds are powered by a static HTML renderer, this renderer can be used directly in your
57
+
server-rendering framework of choice for a zero-JS experience.
58
+
59
+
```tsx
60
+
import { fetchProfileFeed, renderProfileFeed } from 'bluesky-profile-feed-embed/core';
61
+
62
+
import 'bluesky-post-embed/style.css';
63
+
import 'bluesky-post-embed/themes/light.css';
64
+
65
+
// fetch the profile
66
+
const controller = new AbortController();
67
+
const data = await fetchProfileFeed({
68
+
actor: `did:plc:ragtjsm2j2vknwkz3zp4oxrd`,
69
+
includePins: true,
70
+
signal: controller.signal,
71
+
});
72
+
73
+
// render the profile
74
+
const html = renderProfileFeed(data);
75
+
return (
76
+
<bluesky-profile-feed
77
+
src={data.thread?.post.uri}
78
+
dangerouslySetInnerHTML={{ __html: html }}
79
+
></bluesky-profile-feed>
80
+
);
81
+
```
82
+
83
+
Check out examples for [Astro](https://github.com/mary-ext/bluesky-embed-astro) and
84
+
[SvelteKit](https://github.com/mary-ext/bluesky-embed-sveltekit).
+100
packages/bluesky-profile-feed-embed/lib/bluesky-profile-feed.svelte
+100
packages/bluesky-profile-feed-embed/lib/bluesky-profile-feed.svelte
···
1
+
<script lang="ts">
2
+
import EmbedFrame from 'internal/components/embed-frame.svelte';
3
+
import FeedPost from 'internal/components/feed-post.svelte';
4
+
import ProfileFeedHeader from 'internal/components/profile-feed-header.svelte';
5
+
6
+
import type { ProfileFeedData } from 'internal/types/profile-feed.js';
7
+
import { NO_UNAUTHENTICATED_LABEL } from 'internal/utils/constants.js';
8
+
9
+
const { profile, feed, allowUnauthenticated }: ProfileFeedData = $props();
10
+
11
+
const isPwiForbidden =
12
+
!allowUnauthenticated && profile?.labels?.some((label) => label.val === NO_UNAUTHENTICATED_LABEL);
13
+
14
+
const items = feed.filter((item) => {
15
+
if (!profile) {
16
+
return false;
17
+
}
18
+
19
+
const reason = item.reason;
20
+
if (reason) {
21
+
if (reason.$type === 'app.bsky.feed.defs#reasonPin') {
22
+
return true;
23
+
}
24
+
25
+
if (reason.$type === 'app.bsky.feed.defs#reasonRepost') {
26
+
const author = item.post.author;
27
+
28
+
if (author.did !== profile.did) {
29
+
return (
30
+
allowUnauthenticated || !author.labels?.some((label) => label.val === NO_UNAUTHENTICATED_LABEL)
31
+
);
32
+
}
33
+
34
+
return true;
35
+
}
36
+
37
+
// Don't show anything we don't recognize
38
+
return false;
39
+
}
40
+
41
+
return !item.reply;
42
+
});
43
+
</script>
44
+
45
+
{#if profile === null}
46
+
{@render Message(`The profile can't be found, it may have been deleted.`)}
47
+
{:else if isPwiForbidden}
48
+
{@render Message(`The user has requested for their posts to not be displayed on external sites.`)}
49
+
{:else}
50
+
<EmbedFrame>
51
+
<ProfileFeedHeader {profile} />
52
+
53
+
{#if items.length > 0}
54
+
<div class="feed">
55
+
{#each items as item}
56
+
<FeedPost {item} />
57
+
{/each}
58
+
59
+
<div class="end-marker">
60
+
<div class="dot"></div>
61
+
</div>
62
+
</div>
63
+
{:else}
64
+
<div class="message">This user has not made any posts.</div>
65
+
{/if}
66
+
</EmbedFrame>
67
+
{/if}
68
+
69
+
{#snippet Message(msg: string)}
70
+
<EmbedFrame>
71
+
<div class="message">{msg}</div>
72
+
</EmbedFrame>
73
+
{/snippet}
74
+
75
+
<style>
76
+
.message {
77
+
margin: 0 auto;
78
+
padding: 32px 16px;
79
+
max-width: 380px;
80
+
color: var(--text-secondary);
81
+
text-align: center;
82
+
}
83
+
84
+
.feed {
85
+
max-height: var(--max-feed-height);
86
+
overflow-y: auto;
87
+
}
88
+
.end-marker {
89
+
display: grid;
90
+
place-items: center;
91
+
height: 48px;
92
+
93
+
.dot {
94
+
border-radius: 50%;
95
+
background: var(--text-secondary);
96
+
width: 4px;
97
+
height: 4px;
98
+
}
99
+
}
100
+
</style>
+87
packages/bluesky-profile-feed-embed/lib/core.ts
+87
packages/bluesky-profile-feed-embed/lib/core.ts
···
1
+
import '@atcute/bluesky/lexicons';
2
+
3
+
import { simpleFetchHandler, XRPC, XRPCError } from '@atcute/client';
4
+
import type { At } from '@atcute/client/lexicons';
5
+
import { render } from 'svelte/server';
6
+
7
+
import type { ProfileFeedData } from 'internal/types/profile-feed.js';
8
+
import { DEFAULT_APPVIEW_URL } from 'internal/utils/constants.js';
9
+
10
+
import BlueskyProfileFeed from './bluesky-profile-feed.svelte';
11
+
12
+
export type { ProfileFeedData };
13
+
14
+
export interface ProfileFeedFetchOptions {
15
+
/**
16
+
* Handle or DID identifier of the user
17
+
*/
18
+
actor: string;
19
+
/**
20
+
* Abort signal to cancel the request
21
+
*/
22
+
signal?: AbortSignal;
23
+
/**
24
+
* Include pinned posts
25
+
* @default false
26
+
*/
27
+
includePins?: boolean;
28
+
/**
29
+
* Allow unauthenticated viewing
30
+
* @default false
31
+
*/
32
+
allowUnauthenticated?: boolean;
33
+
/**
34
+
* AppView service to use
35
+
* @default "https://public.api.bsky.app"
36
+
*/
37
+
serviceUri?: string;
38
+
}
39
+
40
+
export const fetchProfileFeed = async (opts: ProfileFeedFetchOptions): Promise<ProfileFeedData> => {
41
+
const actor = opts.actor as At.Identifier;
42
+
const allowUnauthenticated = opts.allowUnauthenticated ?? false;
43
+
44
+
const rpc = new XRPC({ handler: simpleFetchHandler({ service: opts.serviceUri ?? DEFAULT_APPVIEW_URL }) });
45
+
46
+
const [{ data: profile }, { data: timeline }] = await Promise.all([
47
+
rpc
48
+
.get('app.bsky.actor.getProfile', {
49
+
signal: opts.signal,
50
+
params: { actor: actor as At.Identifier },
51
+
})
52
+
.catch((err) => {
53
+
if (err instanceof XRPCError) {
54
+
if (err.kind === 'InvalidRequest' && err.description === 'Profile not found') {
55
+
return { data: null };
56
+
}
57
+
}
58
+
59
+
return Promise.reject(err);
60
+
}),
61
+
rpc
62
+
.get('app.bsky.feed.getAuthorFeed', {
63
+
signal: opts.signal,
64
+
params: {
65
+
actor,
66
+
filter: 'posts_no_replies',
67
+
includePins: opts.includePins,
68
+
limit: 30,
69
+
},
70
+
})
71
+
.catch((err) => {
72
+
if (err instanceof XRPCError) {
73
+
if (err.kind === 'InvalidRequest' && err.description === 'Profile not found') {
74
+
return { data: { feed: [] } };
75
+
}
76
+
}
77
+
78
+
return Promise.reject(err);
79
+
}),
80
+
]);
81
+
82
+
return { profile: profile, feed: timeline.feed, allowUnauthenticated };
83
+
};
84
+
85
+
export const renderProfileFeed = (data: ProfileFeedData): string => {
86
+
return render(BlueskyProfileFeed, { props: data }).body;
87
+
};
+59
packages/bluesky-profile-feed-embed/lib/wc.ts
+59
packages/bluesky-profile-feed-embed/lib/wc.ts
···
1
+
import { fetchProfileFeed, renderProfileFeed } from './core';
2
+
3
+
export class BlueskyProfileFeed extends HTMLElement {
4
+
connectedCallback() {
5
+
this.load().then(
6
+
() => this.dispatchEvent(new CustomEvent('loaded')),
7
+
(err) => {
8
+
const defaulted = this.dispatchEvent(new CustomEvent('error', { detail: err }));
9
+
if (defaulted) {
10
+
throw err;
11
+
}
12
+
},
13
+
);
14
+
}
15
+
16
+
async load() {
17
+
const actor = this.getAttribute('actor')!;
18
+
const serviceUri = this.getAttribute('service-uri') || undefined;
19
+
const allowUnauthenticated = this.getAttribute('allow-unauthenticated') !== null;
20
+
const includePins = this.getAttribute('include-pins') !== null;
21
+
const silent = this.getAttribute('silent') !== null;
22
+
23
+
const data = await fetchProfileFeed({ actor, allowUnauthenticated, includePins, serviceUri }).catch(
24
+
(error) => {
25
+
if (silent) {
26
+
console.warn('Failed to fetch profile feed:', error);
27
+
return null;
28
+
}
29
+
throw error;
30
+
},
31
+
);
32
+
33
+
if (data === null) {
34
+
return;
35
+
}
36
+
37
+
const html = renderProfileFeed(data);
38
+
39
+
const root = this.shadowRoot;
40
+
41
+
if (!root) {
42
+
this.innerHTML = html;
43
+
} else {
44
+
const template = document.createElement('template');
45
+
template.innerHTML = html;
46
+
47
+
const fragment = template.content;
48
+
const slot = root.querySelector('slot');
49
+
50
+
if (slot) {
51
+
slot.replaceWith(fragment);
52
+
} else {
53
+
root.appendChild(fragment);
54
+
}
55
+
}
56
+
}
57
+
}
58
+
59
+
customElements.define('bluesky-profile-feed', BlueskyProfileFeed);
+43
packages/bluesky-profile-feed-embed/package.json
+43
packages/bluesky-profile-feed-embed/package.json
···
1
+
{
2
+
"type": "module",
3
+
"name": "bluesky-profile-feed-embed",
4
+
"description": "Custom element for embedding Bluesky profile feeds",
5
+
"version": "1.0.4",
6
+
"author": "externdefs",
7
+
"license": "MIT",
8
+
"repository": {
9
+
"type": "git",
10
+
"url": "https://github.com/mary-ext/bluesky-embed",
11
+
"directory": "packages/bluesky-profile-feed-embed"
12
+
},
13
+
"files": [
14
+
"dist/",
15
+
"themes/"
16
+
],
17
+
"exports": {
18
+
".": "./dist/wc.js",
19
+
"./core": "./dist/core.js",
20
+
"./style.css": "./dist/core.css",
21
+
"./themes/*": "./themes/*"
22
+
},
23
+
"scripts": {
24
+
"dev": "vite",
25
+
"build": "pnpm run check && vite build && rsync -aHAX --delete ../../themes/ themes/",
26
+
"check": "svelte-check --tsconfig ./tsconfig.json && tsc -p tsconfig.node.json",
27
+
"prepack": "pnpm run build"
28
+
},
29
+
"dependencies": {
30
+
"@atcute/bluesky": "^2.1.1",
31
+
"@atcute/bluesky-richtext-segmenter": "^2.0.4",
32
+
"@atcute/client": "^3.1.0"
33
+
},
34
+
"devDependencies": {
35
+
"@tsconfig/svelte": "^5.0.6",
36
+
"@types/node": "^24.10.1",
37
+
"internal": "workspace:^",
38
+
"svelte": "catalog:",
39
+
"svelte-check": "^4.3.4",
40
+
"vite": "^7.2.6",
41
+
"vite-plugin-dts": "^4.5.4"
42
+
}
43
+
}
+8
packages/bluesky-profile-feed-embed/svelte.config.js
+8
packages/bluesky-profile-feed-embed/svelte.config.js
+15
packages/bluesky-profile-feed-embed/tsconfig.json
+15
packages/bluesky-profile-feed-embed/tsconfig.json
···
1
+
{
2
+
"extends": "@tsconfig/svelte/tsconfig.json",
3
+
"compilerOptions": {
4
+
"types": [],
5
+
"target": "ESNext",
6
+
"useDefineForClassFields": true,
7
+
"module": "ESNext",
8
+
"resolveJsonModule": true,
9
+
"isolatedModules": true,
10
+
"moduleDetection": "force",
11
+
"noEmit": true,
12
+
},
13
+
"include": ["lib"],
14
+
"references": [{ "path": "./tsconfig.node.json" }],
15
+
}
+14
packages/bluesky-profile-feed-embed/tsconfig.node.json
+14
packages/bluesky-profile-feed-embed/tsconfig.node.json
···
1
+
{
2
+
"compilerOptions": {
3
+
"composite": true,
4
+
"types": ["node"],
5
+
"skipLibCheck": true,
6
+
"module": "ESNext",
7
+
"target": "ESNext",
8
+
"moduleResolution": "Bundler",
9
+
"strict": true,
10
+
"noEmit": true,
11
+
"noUncheckedSideEffectImports": true,
12
+
},
13
+
"include": ["vite.config.ts"],
14
+
}
+100
packages/bluesky-profile-feed-embed/vite.config.ts
+100
packages/bluesky-profile-feed-embed/vite.config.ts
···
1
+
import * as path from 'node:path';
2
+
3
+
import { compile as compileSvelte } from 'svelte/compiler';
4
+
import { type Plugin, createFilter, defineConfig } from 'vite';
5
+
6
+
import dts from 'vite-plugin-dts';
7
+
8
+
export default defineConfig({
9
+
base: './',
10
+
build: {
11
+
outDir: 'dist/',
12
+
target: 'esnext',
13
+
minify: false,
14
+
cssMinify: false,
15
+
cssCodeSplit: true,
16
+
lib: {
17
+
entry: {
18
+
core: 'lib/core.ts',
19
+
wc: 'lib/wc.ts',
20
+
},
21
+
formats: ['es'],
22
+
},
23
+
rollupOptions: {
24
+
external: ['@atcute/client', '@atcute/bluesky-richtext-segmenter'],
25
+
},
26
+
},
27
+
esbuild: {
28
+
target: 'esnext',
29
+
},
30
+
plugins: [
31
+
svelte(),
32
+
dts({
33
+
rollupTypes: true,
34
+
beforeWriteFile(filePath, content) {
35
+
if (filePath.endsWith('/core.d.ts')) {
36
+
// Make sure the relevant types are present
37
+
return { content: `import '@atcute/bluesky/lexicons';\n${content}` };
38
+
}
39
+
},
40
+
}),
41
+
],
42
+
});
43
+
44
+
function svelte(): Plugin {
45
+
const filter = createFilter('**/*.svelte');
46
+
const stylesheets = new Map<string, string>();
47
+
48
+
return {
49
+
name: 'svelte',
50
+
resolveId(id) {
51
+
return stylesheets.has(id) ? id : null;
52
+
},
53
+
load(id) {
54
+
const css = stylesheets.get(id);
55
+
if (css !== undefined) {
56
+
this.addWatchFile(id.slice(0, -4));
57
+
return { code: css };
58
+
}
59
+
60
+
return null;
61
+
},
62
+
transform(source, id) {
63
+
if (!filter(id)) {
64
+
return null;
65
+
}
66
+
67
+
const result = compileSvelte(source, {
68
+
generate: 'server',
69
+
css: 'external',
70
+
cssHash({ hash, filename }) {
71
+
const prefix = `github:mary-ext/bluesky-profile-feed-embed/`;
72
+
return `s-` + hash(prefix + path.relative(__dirname, filename));
73
+
},
74
+
runes: true,
75
+
filename: id,
76
+
});
77
+
78
+
{
79
+
const { js, css, warnings } = result;
80
+
81
+
let jsCode = js.code;
82
+
83
+
if (css) {
84
+
const cssId = `${id}.css`;
85
+
jsCode = jsCode + `\nimport ${JSON.stringify(cssId)};\n`;
86
+
stylesheets.set(cssId, css.code);
87
+
}
88
+
89
+
for (const warn of warnings) {
90
+
if (warn.code === 'state_referenced_locally') {
91
+
continue;
92
+
}
93
+
this.warn(warn);
94
+
}
95
+
96
+
return { code: jsCode };
97
+
}
98
+
},
99
+
};
100
+
}
+88
packages/internal/components/content-hider.svelte
+88
packages/internal/components/content-hider.svelte
···
1
+
<script lang="ts">
2
+
import type { Snippet } from 'svelte';
3
+
import type { LabelDefinition } from '../utils/labels';
4
+
5
+
interface Props {
6
+
warning: LabelDefinition | undefined;
7
+
children: Snippet;
8
+
}
9
+
10
+
const { warning, children }: Props = $props();
11
+
</script>
12
+
13
+
{#if !warning}
14
+
{@render children()}
15
+
{:else}
16
+
<details class="content-hider">
17
+
<summary class="gate">
18
+
<svg class="icon" fill="none" viewBox="0 0 24 24">
19
+
<path
20
+
stroke="currentColor"
21
+
stroke-linecap="square"
22
+
stroke-width="2"
23
+
d="M11 11h1v5m9-4a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
24
+
/>
25
+
<path
26
+
fill="currentColor"
27
+
stroke="currentColor"
28
+
stroke-width=".5"
29
+
d="M11.5 7.25h-.25v1.5h1.5v-1.5H11.5Z"
30
+
/>
31
+
</svg>
32
+
33
+
<span class="label">{warning.name}</span>
34
+
35
+
<span class="action"></span>
36
+
</summary>
37
+
38
+
{@render children()}
39
+
</details>
40
+
{/if}
41
+
42
+
<style>
43
+
.gate {
44
+
display: flex;
45
+
align-items: center;
46
+
gap: 12px;
47
+
cursor: pointer;
48
+
border: 1px solid var(--divider);
49
+
border-radius: 6px;
50
+
padding: 0 12px;
51
+
height: 44px;
52
+
53
+
.content-hider[open] & {
54
+
margin-bottom: 12px;
55
+
}
56
+
57
+
&:hover {
58
+
border-color: var(--divider-hover);
59
+
}
60
+
}
61
+
62
+
.icon {
63
+
width: 18px;
64
+
height: 18px;
65
+
color: var(--text-secondary);
66
+
}
67
+
.label {
68
+
flex-grow: 1;
69
+
overflow: hidden;
70
+
font-weight: 500;
71
+
user-select: none;
72
+
text-overflow: ellipsis;
73
+
}
74
+
75
+
.action {
76
+
color: var(--text-link);
77
+
font-weight: 500;
78
+
font-size: calc(var(--font-size) * 0.8125);
79
+
line-height: calc(var(--font-size) * 1.25);
80
+
81
+
&::before {
82
+
content: 'Show';
83
+
}
84
+
.content-hider[open] &::before {
85
+
content: 'Hide';
86
+
}
87
+
}
88
+
</style>
+50
packages/internal/components/embed-frame.svelte
+50
packages/internal/components/embed-frame.svelte
···
1
+
<script lang="ts">
2
+
import type { Snippet } from 'svelte';
3
+
4
+
interface Props {
5
+
children: Snippet;
6
+
}
7
+
8
+
const { children }: Props = $props();
9
+
</script>
10
+
11
+
<div class="bluesky-embed">
12
+
{@render children()}
13
+
</div>
14
+
15
+
<style>
16
+
.bluesky-embed {
17
+
position: relative;
18
+
box-sizing: border-box;
19
+
margin: 0 auto;
20
+
border: 1px solid var(--divider);
21
+
border-radius: 8px;
22
+
background: var(--background-primary);
23
+
min-width: 250px;
24
+
max-width: 550px;
25
+
overflow: hidden;
26
+
color: var(--text-primary);
27
+
font-weight: 400;
28
+
font-size: calc(var(--font-size) * 0.875);
29
+
line-height: calc(var(--font-size) * 1.25);
30
+
font-family: var(--font-family);
31
+
32
+
:global(:where(*)),
33
+
:global(:where(*::before)),
34
+
:global(:where(*::after)) {
35
+
box-sizing: border-box;
36
+
margin: 0;
37
+
padding: 0;
38
+
}
39
+
:global(:where(a)) {
40
+
color: inherit;
41
+
text-decoration: none;
42
+
}
43
+
44
+
:global(:where(.icon)) {
45
+
flex-shrink: 0;
46
+
width: 1em;
47
+
height: 1em;
48
+
}
49
+
}
50
+
</style>
+126
packages/internal/components/embeds/embeds.svelte
+126
packages/internal/components/embeds/embeds.svelte
···
1
+
<script lang="ts" module>
2
+
const collectionToLabel = (collection: string): string | null => {
3
+
switch (collection) {
4
+
case 'app.bsky.feed.post':
5
+
return 'post';
6
+
case 'app.bsky.feed.generator':
7
+
return 'feed';
8
+
case 'app.bsky.graph.list':
9
+
return 'list';
10
+
case 'app.bsky.graph.starterpack':
11
+
return 'starter pack';
12
+
case 'app.bsky.labeler.service':
13
+
return 'labeler';
14
+
}
15
+
16
+
return null;
17
+
};
18
+
</script>
19
+
20
+
<script lang="ts">
21
+
import type {
22
+
AppBskyEmbedExternal,
23
+
AppBskyEmbedImages,
24
+
AppBskyEmbedRecord,
25
+
AppBskyEmbedVideo,
26
+
AppBskyFeedDefs,
27
+
Brand,
28
+
} from '@atcute/client/lexicons';
29
+
30
+
import { findLabel } from '../../utils/labels';
31
+
import { parseAtUri } from '../../utils/syntax/at-url';
32
+
33
+
import ContentHider from '../content-hider.svelte';
34
+
35
+
import ExternalEmbed from './external-embed.svelte';
36
+
import FeedEmbed from './feed-embed.svelte';
37
+
import ImageEmbed from './image-embed.svelte';
38
+
import ListEmbed from './list-embed.svelte';
39
+
import QuoteEmbed from './quote-embed.svelte';
40
+
import StarterpackEmbed from './starterpack-embed.svelte';
41
+
import VideoEmbed from './video-embed.svelte';
42
+
43
+
type Embed = NonNullable<AppBskyFeedDefs.PostView['embed']>;
44
+
type MediaEmbed = Brand.Union<AppBskyEmbedExternal.View | AppBskyEmbedImages.View | AppBskyEmbedVideo.View>;
45
+
type RecordEmbed = AppBskyEmbedRecord.View;
46
+
47
+
interface Props {
48
+
post?: AppBskyFeedDefs.PostView;
49
+
embed: Embed;
50
+
large?: boolean;
51
+
}
52
+
53
+
const { post, embed, large = false }: Props = $props();
54
+
</script>
55
+
56
+
<div class="embeds">
57
+
{#if embed.$type === 'app.bsky.embed.recordWithMedia#view'}
58
+
{@render Media(embed.media)}
59
+
{@render Record(embed.record)}
60
+
{:else if embed.$type === 'app.bsky.embed.record#view'}
61
+
{@render Record(embed)}
62
+
{:else}
63
+
{@render Media(embed)}
64
+
{/if}
65
+
</div>
66
+
67
+
{#snippet Media(embed: MediaEmbed)}
68
+
{@const warning = post && findLabel(post.labels, post.author.did)}
69
+
70
+
<ContentHider {warning}>
71
+
{#if embed.$type === 'app.bsky.embed.external#view'}
72
+
<ExternalEmbed {embed} />
73
+
{:else if embed.$type === 'app.bsky.embed.images#view'}
74
+
<ImageEmbed {embed} standalone />
75
+
{:else if embed.$type === 'app.bsky.embed.video#view'}
76
+
<VideoEmbed {post} {embed} standalone />
77
+
{:else}
78
+
{@render Message(`Unsupported media embed`)}
79
+
{/if}
80
+
</ContentHider>
81
+
{/snippet}
82
+
83
+
{#snippet Record(embed: RecordEmbed)}
84
+
{@const record = embed.record}
85
+
86
+
{#if record.$type === 'app.bsky.embed.record#viewRecord'}
87
+
<QuoteEmbed embed={record} {large} />
88
+
{:else if record.$type === 'app.bsky.feed.defs#generatorView'}
89
+
<FeedEmbed embed={record} />
90
+
{:else if record.$type === 'app.bsky.graph.defs#listView'}
91
+
<ListEmbed embed={record} />
92
+
{:else if record.$type === 'app.bsky.graph.defs#starterPackViewBasic'}
93
+
<StarterpackEmbed embed={record} {large} />
94
+
{:else}
95
+
{@const uri = parseAtUri(record.uri)}
96
+
{@const resource = collectionToLabel(uri.collection)}
97
+
98
+
{@const isUnavailable =
99
+
resource &&
100
+
(record.$type === 'app.bsky.embed.record#viewNotFound' ||
101
+
record.$type === 'app.bsky.embed.record#viewBlocked' ||
102
+
record.$type === 'app.bsky.embed.record#viewDetached')}
103
+
104
+
{@render Message(isUnavailable ? `This ${resource} is unavailable` : `Unsupported record embed`)}
105
+
{/if}
106
+
{/snippet}
107
+
108
+
{#snippet Message(message: string)}
109
+
<div class="message">{message}</div>
110
+
{/snippet}
111
+
112
+
<style>
113
+
.embeds {
114
+
display: flex;
115
+
flex-direction: column;
116
+
gap: 12px;
117
+
margin: 12px 0 0 0;
118
+
}
119
+
120
+
.message {
121
+
border: 1px solid var(--divider);
122
+
border-radius: 6px;
123
+
padding: 12px;
124
+
color: var(--text-secondary);
125
+
}
126
+
</style>
+129
packages/internal/components/embeds/external-embed.svelte
+129
packages/internal/components/embeds/external-embed.svelte
···
1
+
<script lang="ts" module>
2
+
const safeParseUrl = (str: string): URL | null => {
3
+
let url: URL | null | undefined;
4
+
if ('parse' in URL) {
5
+
url = URL.parse(str);
6
+
} else {
7
+
try {
8
+
// @ts-expect-error: `'parse' in URL` is giving truthy
9
+
url = new URL(str);
10
+
} catch {}
11
+
}
12
+
13
+
if (url && (url.protocol === 'https:' || url.protocol === 'http:')) {
14
+
return url;
15
+
}
16
+
17
+
return null;
18
+
};
19
+
</script>
20
+
21
+
<script lang="ts">
22
+
import type { AppBskyEmbedExternal } from '@atcute/client/lexicons';
23
+
24
+
interface Props {
25
+
embed: AppBskyEmbedExternal.View;
26
+
}
27
+
28
+
const { embed }: Props = $props();
29
+
30
+
const external = embed.external;
31
+
32
+
const domain = safeParseUrl(external.uri)?.host;
33
+
</script>
34
+
35
+
<a target="_blank" href={domain && external.uri} rel="noopener noreferrer nofollow" class="external-embed">
36
+
{#if external.thumb}
37
+
<img loading="lazy" src={external.thumb} alt="" class="thumbnail" />
38
+
{/if}
39
+
40
+
<div class="meta">
41
+
<p class="title">{external.title}</p>
42
+
<p class="description">{external.description}</p>
43
+
44
+
{#if domain}
45
+
<div class="domain">
46
+
<!-- earth -->
47
+
<svg class="icon" fill="none" viewBox="0 0 24 24">
48
+
<path
49
+
stroke="currentColor"
50
+
stroke-linecap="round"
51
+
stroke-width="2"
52
+
d="m4.172 8.07 3.94 2.957.977-1.941 3.887-.978 1.15-4.6M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-6.078 4.865.973-1.946-2.869-1.928-1.89-.12-1.08 1.075 1.947 2.919h2.919Z"
53
+
/>
54
+
</svg>
55
+
56
+
<span class="domain-name">{domain}</span>
57
+
</div>
58
+
{/if}
59
+
</div>
60
+
</a>
61
+
62
+
<style>
63
+
.external-embed {
64
+
display: block;
65
+
border: 1px solid var(--divider);
66
+
border-radius: 6px;
67
+
overflow: hidden;
68
+
69
+
&:hover {
70
+
border-color: var(--divider-hover);
71
+
}
72
+
}
73
+
74
+
.thumbnail {
75
+
display: block;
76
+
border-bottom: 1px solid var(--divider);
77
+
background: #000000;
78
+
aspect-ratio: 1.91;
79
+
width: 100%;
80
+
81
+
.external-embed:hover & {
82
+
border-color: var(--divider-hover);
83
+
}
84
+
}
85
+
86
+
.meta {
87
+
padding: 12px;
88
+
}
89
+
90
+
.title {
91
+
display: -webkit-box;
92
+
overflow: hidden;
93
+
font-weight: 700;
94
+
white-space: pre-wrap;
95
+
-webkit-box-orient: vertical;
96
+
-webkit-line-clamp: 2;
97
+
line-clamp: 2;
98
+
overflow-wrap: break-word;
99
+
100
+
&:empty {
101
+
display: none;
102
+
}
103
+
}
104
+
.description {
105
+
display: -webkit-box;
106
+
overflow: hidden;
107
+
color: var(--text-secondary);
108
+
font-size: calc(var(--font-size) * 0.8125);
109
+
white-space: pre-wrap;
110
+
-webkit-box-orient: vertical;
111
+
-webkit-line-clamp: 2;
112
+
line-clamp: 2;
113
+
overflow-wrap: break-word;
114
+
115
+
&:empty {
116
+
display: none;
117
+
}
118
+
}
119
+
120
+
.domain {
121
+
display: flex;
122
+
align-items: center;
123
+
gap: 6px;
124
+
margin: 6px 0 0 0;
125
+
color: var(--text-secondary);
126
+
font-weight: 500;
127
+
font-size: calc(var(--font-size) * 0.75);
128
+
}
129
+
</style>
+100
packages/internal/components/embeds/feed-embed.svelte
+100
packages/internal/components/embeds/feed-embed.svelte
···
1
+
<script lang="ts">
2
+
import type { AppBskyFeedDefs } from '@atcute/client/lexicons';
3
+
4
+
import { getFeedUrl } from '../../utils/bsky-url';
5
+
import { parseAtUri } from '../../utils/syntax/at-url';
6
+
7
+
interface Props {
8
+
embed: AppBskyFeedDefs.GeneratorView;
9
+
}
10
+
11
+
const { embed: feed }: Props = $props();
12
+
13
+
const creator = feed.creator;
14
+
15
+
const feedUrl = getFeedUrl(creator.did, parseAtUri(feed.uri).rkey);
16
+
</script>
17
+
18
+
<a target="_blank" href={feedUrl} class="feed-embed">
19
+
<div class="main">
20
+
<div class="avatar-wrapper">
21
+
{#if feed.avatar}
22
+
<img loading="lazy" src={feed.avatar} alt="" class="avatar" />
23
+
{:else}
24
+
<svg viewBox="0 0 32 32" class="avatar">
25
+
<path fill="#0070FF" d="M0 0h32v32H0z" />
26
+
<path
27
+
fill="#fff"
28
+
d="M22.153 22.354a9.328 9.328 0 0 0 3.837-.491 3.076 3.076 0 0 0-4.802-2.79m.965 3.281a6.128 6.128 0 0 0-.965-3.28Zm-11.342-3.28a3.077 3.077 0 0 0-4.801 2.79 9.21 9.21 0 0 0 3.835.49m.966-3.28a6.127 6.127 0 0 0-.966 3.28Zm8.265-8.997a3.076 3.076 0 1 1-6.153 0 3.076 3.076 0 0 1 6.153 0Zm6.154 3.077a2.307 2.307 0 1 1-4.615 0 2.307 2.307 0 0 1 4.615 0Zm-13.847 0a2.307 2.307 0 1 1-4.614 0 2.307 2.307 0 0 1 4.614 0Z"
29
+
/>
30
+
<path fill="#fff" d="M22 22c0 3.314-2.686 3.5-6 3.5s-6-.186-6-3.5a6 6 0 0 1 12 0Z" />
31
+
</svg>
32
+
{/if}
33
+
</div>
34
+
35
+
<div class="info">
36
+
<p class="name">{feed.displayName}</p>
37
+
<p class="creator">Feed by @{creator.handle}</p>
38
+
</div>
39
+
</div>
40
+
41
+
<p class="description">{feed.description}</p>
42
+
</a>
43
+
44
+
<style>
45
+
.feed-embed {
46
+
display: flex;
47
+
flex-direction: column;
48
+
gap: 12px;
49
+
border: 1px solid var(--divider);
50
+
border-radius: 6px;
51
+
padding: 12px;
52
+
53
+
&:hover {
54
+
border-color: var(--divider-hover);
55
+
}
56
+
}
57
+
58
+
.main {
59
+
display: flex;
60
+
gap: 12px;
61
+
}
62
+
63
+
.avatar-wrapper {
64
+
margin: 2px 0 0 0;
65
+
border-radius: 6px;
66
+
background: var(--background-secondary);
67
+
width: 36px;
68
+
height: 36px;
69
+
overflow: hidden;
70
+
}
71
+
.avatar {
72
+
width: 100%;
73
+
height: 100%;
74
+
object-fit: cover;
75
+
}
76
+
77
+
.name {
78
+
font-weight: 700;
79
+
}
80
+
81
+
.creator {
82
+
color: var(--text-secondary);
83
+
font-size: calc(var(--font-size) * 0.8125);
84
+
}
85
+
86
+
.description {
87
+
display: -webkit-box;
88
+
overflow: hidden;
89
+
font-size: calc(var(--font-size) * 0.8125);
90
+
white-space: pre-wrap;
91
+
-webkit-box-orient: vertical;
92
+
-webkit-line-clamp: 2;
93
+
line-clamp: 2;
94
+
overflow-wrap: break-word;
95
+
96
+
&:empty {
97
+
display: none;
98
+
}
99
+
}
100
+
</style>
+183
packages/internal/components/embeds/image-embed.svelte
+183
packages/internal/components/embeds/image-embed.svelte
···
1
+
<script lang="ts" module>
2
+
const DEFAULT_RATIO = { width: 16, height: 9 };
3
+
</script>
4
+
5
+
<script lang="ts">
6
+
import type { AppBskyEmbedImages } from '@atcute/client/lexicons';
7
+
8
+
interface Props {
9
+
embed: AppBskyEmbedImages.View;
10
+
borderless?: boolean;
11
+
standalone?: boolean;
12
+
blur?: boolean;
13
+
}
14
+
15
+
const { embed, borderless, standalone, blur }: Props = $props();
16
+
17
+
const images = embed.images;
18
+
const length = images.length;
19
+
</script>
20
+
21
+
<div
22
+
class={'image-embed' +
23
+
(!borderless ? ` is-bordered` : ``) +
24
+
(standalone && length === 1 ? ` is-aligned` : ``)}
25
+
>
26
+
{#if length === 4}
27
+
<div class="grid">
28
+
<div class="col">
29
+
<div class="item wide tl">
30
+
{@render Image(0)}
31
+
</div>
32
+
<div class="item wide bl">
33
+
{@render Image(2)}
34
+
</div>
35
+
</div>
36
+
<div class="col">
37
+
<div class="item wide tr">
38
+
{@render Image(1)}
39
+
</div>
40
+
<div class="item wide br">
41
+
{@render Image(3)}
42
+
</div>
43
+
</div>
44
+
</div>
45
+
{:else if length === 3}
46
+
<div class="grid">
47
+
<div class="col square">
48
+
<div class="item tl bl">
49
+
{@render Image(0)}
50
+
</div>
51
+
</div>
52
+
<div class="col square">
53
+
<div class="item tr">
54
+
{@render Image(1)}
55
+
</div>
56
+
<div class="item br">
57
+
{@render Image(2)}
58
+
</div>
59
+
</div>
60
+
</div>
61
+
{:else if length === 2}
62
+
<div class="grid">
63
+
<div class="col">
64
+
<div class="item square tl bl">
65
+
{@render Image(0)}
66
+
</div>
67
+
</div>
68
+
<div class="col">
69
+
<div class="item square tr br">
70
+
{@render Image(1)}
71
+
</div>
72
+
</div>
73
+
</div>
74
+
{:else if length === 1}
75
+
{@const ratio = standalone && (images[0].aspectRatio || DEFAULT_RATIO)}
76
+
77
+
<div
78
+
class={`single-item tl tr bl br` + (ratio ? ` is-standalone` : ``)}
79
+
style={ratio ? `aspect-ratio: ${ratio.width}/${ratio.height}` : ``}
80
+
>
81
+
{@render Image(0)}
82
+
83
+
{#if ratio}
84
+
<div class="placeholder"></div>
85
+
{/if}
86
+
</div>
87
+
{/if}
88
+
</div>
89
+
90
+
{#snippet Image(index: number)}
91
+
{@const image = images[index]}
92
+
93
+
<img loading="lazy" src={image.thumb} alt={image.alt} class={`image` + (blur ? ` is-blurred` : ``)} />
94
+
{/snippet}
95
+
96
+
<style>
97
+
.is-aligned {
98
+
align-self: baseline;
99
+
max-width: 100%;
100
+
}
101
+
102
+
.grid {
103
+
display: flex;
104
+
gap: 2px;
105
+
}
106
+
.col {
107
+
display: flex;
108
+
flex: 1;
109
+
flex-direction: column;
110
+
gap: 2px;
111
+
}
112
+
113
+
.square {
114
+
aspect-ratio: 1;
115
+
}
116
+
.wide {
117
+
aspect-ratio: 1.5;
118
+
}
119
+
120
+
.item {
121
+
position: relative;
122
+
flex-grow: 1;
123
+
flex-shrink: 0;
124
+
overflow: hidden;
125
+
}
126
+
127
+
.is-bordered {
128
+
.tl,
129
+
.tr,
130
+
.bl,
131
+
.br {
132
+
border: 1px solid var(--divider);
133
+
}
134
+
135
+
.tl {
136
+
border-top-left-radius: 6px;
137
+
}
138
+
.tr {
139
+
border-top-right-radius: 6px;
140
+
}
141
+
.bl {
142
+
border-bottom-left-radius: 6px;
143
+
}
144
+
.br {
145
+
border-bottom-right-radius: 6px;
146
+
}
147
+
}
148
+
149
+
.single-item {
150
+
position: relative;
151
+
aspect-ratio: 16 / 9;
152
+
overflow: hidden;
153
+
154
+
.image {
155
+
object-fit: contain;
156
+
}
157
+
}
158
+
.is-standalone {
159
+
min-width: 64px;
160
+
max-width: 100%;
161
+
min-height: 64px;
162
+
max-height: 320px;
163
+
}
164
+
165
+
.image {
166
+
position: absolute;
167
+
inset: 0;
168
+
background: #000000;
169
+
width: 100%;
170
+
height: 100%;
171
+
object-fit: cover;
172
+
font-size: 0px;
173
+
}
174
+
.is-blurred {
175
+
scale: 125%;
176
+
filter: blur(24px);
177
+
}
178
+
179
+
.placeholder {
180
+
width: 100vw;
181
+
height: 100vh;
182
+
}
183
+
</style>
+113
packages/internal/components/embeds/list-embed.svelte
+113
packages/internal/components/embeds/list-embed.svelte
···
1
+
<script lang="ts" module>
2
+
const getPurpose = (purpose: AppBskyGraphDefs.ListPurpose) => {
3
+
switch (purpose) {
4
+
case 'app.bsky.graph.defs#curatelist':
5
+
return `User list`;
6
+
case 'app.bsky.graph.defs#modlist':
7
+
return `Moderation list`;
8
+
}
9
+
10
+
return `Unknown list`;
11
+
};
12
+
</script>
13
+
14
+
<script lang="ts">
15
+
import type { AppBskyGraphDefs } from '@atcute/client/lexicons';
16
+
17
+
import { getFeedUrl } from '../../utils/bsky-url';
18
+
import { parseAtUri } from '../../utils/syntax/at-url';
19
+
20
+
interface Props {
21
+
embed: AppBskyGraphDefs.ListView;
22
+
}
23
+
24
+
const { embed: list }: Props = $props();
25
+
26
+
const creator = list.creator;
27
+
28
+
const listUrl = getFeedUrl(creator.did, parseAtUri(list.uri).rkey);
29
+
</script>
30
+
31
+
<a target="_blank" href={listUrl} class="list-embed">
32
+
<div class="main">
33
+
<div class="avatar-wrapper">
34
+
{#if list.avatar}
35
+
<img loading="lazy" src={list.avatar} alt="" class="avatar" />
36
+
{:else}
37
+
<svg viewBox="0 0 32 32" class="avatar">
38
+
<path fill="#0070FF" d="M0 0h32v32H0z" />
39
+
<path
40
+
fill="#fff"
41
+
d="M22.153 22.354a9.328 9.328 0 0 0 3.837-.491 3.076 3.076 0 0 0-4.802-2.79m.965 3.281a6.128 6.128 0 0 0-.965-3.28Zm-11.342-3.28a3.077 3.077 0 0 0-4.801 2.79 9.21 9.21 0 0 0 3.835.49m.966-3.28a6.127 6.127 0 0 0-.966 3.28Zm8.265-8.997a3.076 3.076 0 1 1-6.153 0 3.076 3.076 0 0 1 6.153 0Zm6.154 3.077a2.307 2.307 0 1 1-4.615 0 2.307 2.307 0 0 1 4.615 0Zm-13.847 0a2.307 2.307 0 1 1-4.614 0 2.307 2.307 0 0 1 4.614 0Z"
42
+
/>
43
+
<path fill="#fff" d="M22 22c0 3.314-2.686 3.5-6 3.5s-6-.186-6-3.5a6 6 0 0 1 12 0Z" />
44
+
</svg>
45
+
{/if}
46
+
</div>
47
+
48
+
<div class="info">
49
+
<p class="name">{list.name}</p>
50
+
<p class="creator">{getPurpose(list.purpose)} by @{creator.handle}</p>
51
+
</div>
52
+
</div>
53
+
54
+
<p class="description">{list.description}</p>
55
+
</a>
56
+
57
+
<style>
58
+
.list-embed {
59
+
display: flex;
60
+
flex-direction: column;
61
+
gap: 12px;
62
+
border: 1px solid var(--divider);
63
+
border-radius: 6px;
64
+
padding: 12px;
65
+
66
+
&:hover {
67
+
border-color: var(--divider-hover);
68
+
}
69
+
}
70
+
71
+
.main {
72
+
display: flex;
73
+
gap: 12px;
74
+
}
75
+
76
+
.avatar-wrapper {
77
+
margin: 2px 0 0 0;
78
+
border-radius: 6px;
79
+
background: var(--background-secondary);
80
+
width: 36px;
81
+
height: 36px;
82
+
overflow: hidden;
83
+
}
84
+
.avatar {
85
+
width: 100%;
86
+
height: 100%;
87
+
object-fit: cover;
88
+
}
89
+
90
+
.name {
91
+
font-weight: 700;
92
+
}
93
+
94
+
.creator {
95
+
color: var(--text-secondary);
96
+
font-size: calc(var(--font-size) * 0.8125);
97
+
}
98
+
99
+
.description {
100
+
display: -webkit-box;
101
+
overflow: hidden;
102
+
font-size: calc(var(--font-size) * 0.8125);
103
+
white-space: pre-wrap;
104
+
-webkit-box-orient: vertical;
105
+
-webkit-line-clamp: 2;
106
+
line-clamp: 2;
107
+
overflow-wrap: break-word;
108
+
109
+
&:empty {
110
+
display: none;
111
+
}
112
+
}
113
+
</style>
+213
packages/internal/components/embeds/quote-embed.svelte
+213
packages/internal/components/embeds/quote-embed.svelte
···
1
+
<script lang="ts" module>
2
+
const getPostImage = (embed: AppBskyFeedDefs.PostView['embed']): AppBskyEmbedImages.View | undefined => {
3
+
if (embed) {
4
+
if (embed.$type === 'app.bsky.embed.images#view') {
5
+
return embed;
6
+
}
7
+
8
+
if (embed.$type === 'app.bsky.embed.recordWithMedia#view') {
9
+
return getPostImage(embed.media);
10
+
}
11
+
}
12
+
};
13
+
14
+
const getPostVideo = (embed: AppBskyFeedDefs.PostView['embed']): AppBskyEmbedVideo.View | undefined => {
15
+
if (embed) {
16
+
if (embed.$type === 'app.bsky.embed.video#view') {
17
+
return embed;
18
+
}
19
+
20
+
if (embed.$type === 'app.bsky.embed.recordWithMedia#view') {
21
+
return getPostVideo(embed.media);
22
+
}
23
+
}
24
+
};
25
+
</script>
26
+
27
+
<script lang="ts">
28
+
import type {
29
+
AppBskyEmbedImages,
30
+
AppBskyEmbedRecord,
31
+
AppBskyEmbedVideo,
32
+
AppBskyFeedDefs,
33
+
AppBskyFeedPost,
34
+
} from '@atcute/client/lexicons';
35
+
36
+
import { getPostUrl } from '../../utils/bsky-url';
37
+
import { formatShortDate } from '../../utils/date';
38
+
import { findLabel } from '../../utils/labels';
39
+
import { parseAtUri } from '../../utils/syntax/at-url';
40
+
41
+
import ImageEmbed from './image-embed.svelte';
42
+
import VideoEmbed from './video-embed.svelte';
43
+
44
+
interface Props {
45
+
embed: AppBskyEmbedRecord.ViewRecord;
46
+
large?: boolean;
47
+
}
48
+
49
+
const { embed: quote, large = false }: Props = $props();
50
+
51
+
const record = quote.value as AppBskyFeedPost.Record;
52
+
const text = record.text.trim();
53
+
54
+
const author = quote.author;
55
+
const authorName = author.displayName?.trim();
56
+
57
+
const embed = quote.embeds?.[0];
58
+
const image = getPostImage(embed);
59
+
const video = getPostVideo(embed);
60
+
61
+
const postUrl = getPostUrl(author.did, parseAtUri(quote.uri).rkey);
62
+
63
+
const isMediaBlurred = !!findLabel(quote.labels, author.did);
64
+
</script>
65
+
66
+
<a target="_blank" href={postUrl} class="quote-embed">
67
+
<div class="meta">
68
+
<div class="avatar-wrapper">
69
+
{#if author.avatar}
70
+
<img loading="lazy" src={author.avatar} alt="" class="avatar" />
71
+
{/if}
72
+
</div>
73
+
74
+
<span class="name-wrapper">
75
+
{#if authorName}
76
+
<bdi class="display-name-wrapper">
77
+
<span class="display-name">{authorName}</span>
78
+
</bdi>
79
+
{/if}
80
+
81
+
<span class="handle">@{author.handle}</span>
82
+
</span>
83
+
84
+
<span aria-hidden="true" class="dot">ยท</span>
85
+
86
+
<time datetime={record.createdAt} class="date">
87
+
{formatShortDate(record.createdAt)}
88
+
</time>
89
+
</div>
90
+
91
+
{#if text}
92
+
<div class="body">
93
+
{#if !large}
94
+
{#if image}
95
+
<div class="aside">
96
+
<ImageEmbed embed={image} blur={isMediaBlurred} />
97
+
</div>
98
+
{:else if video}
99
+
<div class="aside">
100
+
<VideoEmbed embed={video} blur={isMediaBlurred} />
101
+
</div>
102
+
{/if}
103
+
{/if}
104
+
105
+
<p class="text">{text}</p>
106
+
</div>
107
+
{:else}
108
+
<div class="divide"></div>
109
+
{/if}
110
+
111
+
{#if large || !text}
112
+
{#if image}
113
+
<ImageEmbed embed={image} borderless blur={isMediaBlurred} />
114
+
{:else if video}
115
+
<VideoEmbed embed={video} borderless blur={isMediaBlurred} />
116
+
{/if}
117
+
{/if}
118
+
</a>
119
+
120
+
<style>
121
+
.quote-embed {
122
+
display: block;
123
+
border: 1px solid var(--divider);
124
+
border-radius: 6px;
125
+
overflow: hidden;
126
+
127
+
&:hover {
128
+
border-color: var(--divider-hover);
129
+
}
130
+
}
131
+
132
+
.meta {
133
+
display: flex;
134
+
padding: 12px 12px 0 12px;
135
+
color: var(--text-secondary);
136
+
137
+
.avatar-wrapper {
138
+
flex-shrink: 0;
139
+
margin: 0 8px 0 0;
140
+
border-radius: 9999px;
141
+
background: var(--background-secondary);
142
+
width: 20px;
143
+
height: 20px;
144
+
overflow: hidden;
145
+
}
146
+
.avatar {
147
+
width: 100%;
148
+
height: 100%;
149
+
}
150
+
151
+
.name-wrapper {
152
+
display: flex;
153
+
gap: 4px;
154
+
max-width: 100%;
155
+
overflow: hidden;
156
+
text-overflow: ellipsis;
157
+
white-space: nowrap;
158
+
}
159
+
.display-name-wrapper {
160
+
overflow: hidden;
161
+
text-overflow: ellipsis;
162
+
}
163
+
.display-name {
164
+
color: var(--text-primary);
165
+
font-weight: 700;
166
+
}
167
+
.handle {
168
+
display: block;
169
+
overflow: hidden;
170
+
text-overflow: ellipsis;
171
+
white-space: nowrap;
172
+
}
173
+
174
+
.dot {
175
+
flex-shrink: 0;
176
+
margin: 0 6px;
177
+
}
178
+
179
+
.date {
180
+
white-space: nowrap;
181
+
}
182
+
}
183
+
184
+
.body {
185
+
display: flex;
186
+
align-items: flex-start;
187
+
}
188
+
189
+
.aside {
190
+
flex-grow: 1;
191
+
flex-basis: 0;
192
+
margin: 8px 0 12px 12px;
193
+
max-width: 20%;
194
+
}
195
+
196
+
.text {
197
+
display: -webkit-box;
198
+
margin: 8px 12px 12px 12px;
199
+
overflow: hidden;
200
+
-webkit-box-orient: vertical;
201
+
flex-grow: 4;
202
+
flex-basis: 0px;
203
+
min-width: 0px;
204
+
-webkit-line-clamp: 6;
205
+
line-clamp: 6;
206
+
white-space: pre-wrap;
207
+
overflow-wrap: break-word;
208
+
}
209
+
210
+
.divide {
211
+
padding: 6px 0;
212
+
}
213
+
</style>
+122
packages/internal/components/embeds/starterpack-embed.svelte
+122
packages/internal/components/embeds/starterpack-embed.svelte
···
1
+
<script lang="ts">
2
+
import type { AppBskyGraphDefs, AppBskyGraphStarterpack } from '@atcute/client/lexicons';
3
+
4
+
import { getStarterpackImgUrl, getStarterpackUrl } from '../../utils/bsky-url';
5
+
import { parseAtUri } from '../../utils/syntax/at-url';
6
+
7
+
interface Props {
8
+
embed: AppBskyGraphDefs.StarterPackViewBasic;
9
+
large?: boolean;
10
+
}
11
+
12
+
const { embed: pack, large = false }: Props = $props();
13
+
14
+
const record = pack.record as AppBskyGraphStarterpack.Record;
15
+
16
+
const creator = pack.creator;
17
+
const creatorDid = creator.did;
18
+
19
+
const rkey = parseAtUri(pack.uri).rkey;
20
+
const packUrl = getStarterpackUrl(creatorDid, rkey);
21
+
</script>
22
+
23
+
<a target="_blank" href={packUrl} class="starterpack-embed">
24
+
{#if large}
25
+
{@const imageUrl = getStarterpackImgUrl(creatorDid, rkey)}
26
+
27
+
<img loading="lazy" src={imageUrl} alt="" class="banner" />
28
+
{/if}
29
+
30
+
<div class="meta">
31
+
<div class="main">
32
+
<svg fill="none" viewBox="0 0 24 24" class="avatar">
33
+
<defs>
34
+
<linearGradient id="a" x1="0" x2="100%" y1="0" y2="0" gradientTransform="rotate(45)">
35
+
<stop offset="0" stop-color="#0A7AFF" />
36
+
<stop offset="1" stop-color="#59B9FF" />
37
+
</linearGradient>
38
+
</defs>
39
+
<path
40
+
fill="url(#a)"
41
+
fill-rule="evenodd"
42
+
d="M11.26 5.227 5.02 6.899c-.734.197-1.17.95-.973 1.685l1.672 6.24c.197.734.951 1.17 1.685.973l6.24-1.672a1.376 1.376 0 0 0 .973-1.685L12.945 6.2a1.375 1.375 0 0 0-1.685-.973Zm-6.566.459a2.632 2.632 0 0 0-1.86 3.223l1.672 6.24a2.632 2.632 0 0 0 3.223 1.861l6.24-1.672a2.631 2.631 0 0 0 1.861-3.223l-1.672-6.24a2.632 2.632 0 0 0-3.223-1.861l-6.24 1.672Z"
43
+
clip-rule="evenodd"
44
+
/>
45
+
<path
46
+
fill="url(#a)"
47
+
fill-rule="evenodd"
48
+
d="M15.138 18.411a4.606 4.606 0 1 0 0-9.211 4.606 4.606 0 0 0 0 9.211Zm0 1.257a5.862 5.862 0 1 0 0-11.724 5.862 5.862 0 0 0 0 11.724Z"
49
+
clip-rule="evenodd"
50
+
/>
51
+
</svg>
52
+
53
+
<div class="info">
54
+
<p class="name">{record.name}</p>
55
+
<p class="creator">Starter pack by @{creator.handle}</p>
56
+
</div>
57
+
</div>
58
+
59
+
<p class="description">{record.description}</p>
60
+
</div>
61
+
</a>
62
+
63
+
<style>
64
+
.starterpack-embed {
65
+
display: block;
66
+
border: 1px solid var(--divider);
67
+
border-radius: 6px;
68
+
overflow: hidden;
69
+
70
+
&:hover {
71
+
border-color: var(--divider-hover);
72
+
}
73
+
}
74
+
75
+
.banner {
76
+
display: block;
77
+
aspect-ratio: 1.91;
78
+
width: 100%;
79
+
}
80
+
81
+
.meta {
82
+
display: flex;
83
+
flex-direction: column;
84
+
gap: 12px;
85
+
padding: 12px;
86
+
}
87
+
88
+
.main {
89
+
display: flex;
90
+
gap: 12px;
91
+
}
92
+
93
+
.avatar {
94
+
margin: 2px;
95
+
width: 36px;
96
+
height: 36px;
97
+
}
98
+
99
+
.name {
100
+
font-weight: 700;
101
+
}
102
+
103
+
.creator {
104
+
color: var(--text-secondary);
105
+
font-size: calc(var(--font-size) * 0.8125);
106
+
}
107
+
108
+
.description {
109
+
display: -webkit-box;
110
+
overflow: hidden;
111
+
font-size: calc(var(--font-size) * 0.8125);
112
+
white-space: pre-wrap;
113
+
-webkit-box-orient: vertical;
114
+
-webkit-line-clamp: 2;
115
+
line-clamp: 2;
116
+
overflow-wrap: break-word;
117
+
118
+
&:empty {
119
+
display: none;
120
+
}
121
+
}
122
+
</style>
+120
packages/internal/components/embeds/video-embed.svelte
+120
packages/internal/components/embeds/video-embed.svelte
···
1
+
<script lang="ts">
2
+
import type { AppBskyEmbedVideo, AppBskyFeedDefs } from '@atcute/client/lexicons';
3
+
4
+
import { getPostUrl } from '../../utils/bsky-url';
5
+
import { parseAtUri } from '../../utils/syntax/at-url';
6
+
7
+
interface Props {
8
+
post?: AppBskyFeedDefs.PostView;
9
+
embed: AppBskyEmbedVideo.View;
10
+
borderless?: boolean;
11
+
standalone?: boolean;
12
+
blur?: boolean;
13
+
}
14
+
15
+
const { post, embed: video, borderless, standalone, blur }: Props = $props();
16
+
17
+
const ratio = standalone && video.aspectRatio;
18
+
19
+
const postUrl = post && getPostUrl(post.author.did, parseAtUri(post.uri).rkey);
20
+
</script>
21
+
22
+
{#if standalone}
23
+
<a
24
+
target="_blank"
25
+
href={postUrl}
26
+
class={`video-embed` + (!borderless ? ` is-bordered` : ``) + (standalone ? ` is-standalone` : ``)}
27
+
>
28
+
<div class="constrainer" style={ratio ? `aspect-ratio: ${ratio.width}/${ratio.height}` : ``}>
29
+
{@render Content()}
30
+
</div>
31
+
</a>
32
+
{:else}
33
+
<div
34
+
class={`video-embed` + (!borderless ? ` is-bordered` : ``)}
35
+
style={ratio ? `aspect-ratio: ${ratio.width}/${ratio.height}` : ``}
36
+
>
37
+
{@render Content()}
38
+
</div>
39
+
{/if}
40
+
41
+
{#snippet Content()}
42
+
<img loading="lazy" src={video.thumbnail} alt="" class={`thumbnail` + (blur ? ` is-blurred` : ``)} />
43
+
44
+
{#if ratio}
45
+
<div class="placeholder"></div>
46
+
{/if}
47
+
48
+
<div class="play">
49
+
<!-- play -->
50
+
<svg class="icon" fill="none" viewBox="0 0 24 24">
51
+
<path fill="currentColor" d="M22 12 5 2v20l17-10Z" />
52
+
</svg>
53
+
</div>
54
+
{/snippet}
55
+
56
+
<style>
57
+
.video-embed {
58
+
display: block;
59
+
position: relative;
60
+
background: #000000;
61
+
aspect-ratio: 16 / 9;
62
+
overflow: hidden;
63
+
}
64
+
.is-bordered {
65
+
border: 1px solid var(--divider);
66
+
border-radius: 6px;
67
+
}
68
+
.is-standalone {
69
+
align-self: baseline;
70
+
aspect-ratio: auto;
71
+
max-width: 100%;
72
+
}
73
+
74
+
.constrainer {
75
+
min-width: 64px;
76
+
max-width: 100%;
77
+
min-height: 64px;
78
+
max-height: 320px;
79
+
}
80
+
81
+
.thumbnail {
82
+
width: 100%;
83
+
height: 100%;
84
+
object-fit: contain;
85
+
}
86
+
.is-blurred {
87
+
scale: 125%;
88
+
filter: blur(24px);
89
+
}
90
+
91
+
.placeholder {
92
+
width: 100vw;
93
+
height: 100vh;
94
+
}
95
+
96
+
.play {
97
+
display: grid;
98
+
position: absolute;
99
+
top: 50%;
100
+
left: 50%;
101
+
place-items: center;
102
+
translate: -50% -50%;
103
+
border-radius: 50%;
104
+
background: rgba(64, 64, 64, 0.6);
105
+
aspect-ratio: 1 / 1;
106
+
height: 40%;
107
+
max-height: 48px;
108
+
color: #ffffff;
109
+
font-size: 20px;
110
+
111
+
.icon {
112
+
width: 40%;
113
+
height: 40%;
114
+
}
115
+
116
+
.is-standalone &:hover {
117
+
background: rgba(64, 64, 64, 0.8);
118
+
}
119
+
}
120
+
</style>
+400
packages/internal/components/feed-post.svelte
+400
packages/internal/components/feed-post.svelte
···
1
+
<script lang="ts">
2
+
import type { AppBskyFeedDefs, AppBskyFeedPost } from '@atcute/client/lexicons';
3
+
4
+
import { getPostUrl, getProfileUrl } from '../utils/bsky-url';
5
+
import { formatLongDate, formatShortDate } from '../utils/date';
6
+
import { parseAtUri } from '../utils/syntax/at-url';
7
+
8
+
import { formatCompactNumber, formatLongNumber } from '../utils/number';
9
+
import Embeds from './embeds/embeds.svelte';
10
+
import RichtextRenderer from './richtext-renderer.svelte';
11
+
12
+
interface Props {
13
+
item: AppBskyFeedDefs.FeedViewPost;
14
+
prev?: boolean;
15
+
next?: boolean;
16
+
}
17
+
18
+
const { item, prev = false, next = false }: Props = $props();
19
+
20
+
const reason = item.reason;
21
+
const post = item.post;
22
+
const parent = item.reply?.parent;
23
+
24
+
const author = post.author;
25
+
const authorUrl = getProfileUrl(author.did);
26
+
const authorName = author.displayName?.trim();
27
+
28
+
const record = post.record as AppBskyFeedPost.Record;
29
+
const postUrl = getPostUrl(author.did, parseAtUri(post.uri).rkey);
30
+
31
+
const replyCount = post.replyCount || 0;
32
+
const likeCount = post.likeCount || 0;
33
+
const repostCount = (post.repostCount || 0) + (post.quoteCount || 0);
34
+
</script>
35
+
36
+
<div class={`feed-post` + (!next ? ` is-leaf` : ``)}>
37
+
<div class="contexts">
38
+
{#if prev}
39
+
<div class="ascendant-line-wrapper">
40
+
<div class="line"></div>
41
+
</div>
42
+
{/if}
43
+
44
+
{#if reason}
45
+
{#if reason.$type === 'app.bsky.feed.defs#reasonRepost'}
46
+
{@const by = reason.by}
47
+
48
+
<div class="context">
49
+
<div class="aside">
50
+
<svg class="icon" viewBox="0 0 24 24" fill="none">
51
+
<path
52
+
d="M17 3L20 6L17 9M7 21L4 18L7 15M5 18H20V13M4 11V6H19"
53
+
stroke="currentColor"
54
+
stroke-width="2"
55
+
stroke-linecap="square"
56
+
/>
57
+
</svg>
58
+
</div>
59
+
<a href={getProfileUrl(by.did)} class="main">
60
+
<span dir="auto" class="name">{by.displayName}</span>
61
+
<span class="affix">{' '}reposted</span>
62
+
</a>
63
+
</div>
64
+
{:else if reason.$type === 'app.bsky.feed.defs#reasonPin'}
65
+
<div class="context">
66
+
<div class="aside">
67
+
<svg class="icon" fill="none" viewBox="0 0 24 24">
68
+
<path
69
+
stroke="currentColor"
70
+
stroke-linecap="square"
71
+
stroke-width="2"
72
+
d="M12 15H5v-2.5l.377-.377A7.25 7.25 0 0 0 7.5 6.997V3h9v3.997a7.25 7.25 0 0 0 2.123 5.127L19 12.5V15h-7Zm0 0v6"
73
+
/>
74
+
</svg>
75
+
</div>
76
+
<span class="main">Pinned</span>
77
+
</div>
78
+
{/if}
79
+
{/if}
80
+
</div>
81
+
82
+
<div class="content">
83
+
<div class="aside">
84
+
<a target="_blank" href={authorUrl} class="avatar-wrapper">
85
+
{#if author.avatar}
86
+
<img loading="lazy" src={author.avatar} alt="" class="avatar" />
87
+
{/if}
88
+
</a>
89
+
90
+
{#if next}
91
+
<div class="descendant-line"></div>
92
+
{/if}
93
+
</div>
94
+
95
+
<div class="main">
96
+
<div class="meta">
97
+
<a href={authorUrl} target="_blank" class="name-wrapper">
98
+
{#if authorName}
99
+
<bdi class="display-name-wrapper">
100
+
<span class="display-name">{authorName}</span>
101
+
</bdi>
102
+
{/if}
103
+
104
+
<span class="handle">@{author.handle}</span>
105
+
</a>
106
+
107
+
<span aria-hidden="true" class="dot"> ยท </span>
108
+
109
+
<a target="_blank" href={postUrl} title={formatLongDate(record.createdAt)} class="date">
110
+
<time datetime={record.createdAt}>{formatShortDate(record.createdAt)}</time>
111
+
</a>
112
+
</div>
113
+
114
+
{#if !prev && record.reply}
115
+
<p class="reply-context">
116
+
{#if parent && parent.$type === 'app.bsky.feed.defs#postView'}
117
+
{@const author = parent.author}
118
+
119
+
Replying to
120
+
<a target="_blank" href={getProfileUrl(author.did)} dir="auto">
121
+
{author.displayName?.trim() || `@${author.handle}`}
122
+
</a>
123
+
{:else}
124
+
Replying to an unknown post
125
+
{/if}
126
+
</p>
127
+
{/if}
128
+
129
+
<RichtextRenderer text={record.text} facets={record.facets} />
130
+
131
+
{#if post.embed}
132
+
<Embeds {post} embed={post.embed} />
133
+
{/if}
134
+
135
+
<div class="metrics">
136
+
<div
137
+
title={replyCount === 1
138
+
? `${formatLongNumber(replyCount)} reply`
139
+
: `${formatLongNumber(replyCount)} replies`}
140
+
class="stat"
141
+
>
142
+
<svg class="icon" fill="none" viewBox="0 0 24 24">
143
+
<path
144
+
stroke="currentColor"
145
+
stroke-linecap="square"
146
+
stroke-width="2"
147
+
d="M3.002 4h18v14h-9l-5 3v-3h-4V4Z"
148
+
/>
149
+
</svg>
150
+
151
+
<span class="count">
152
+
{formatCompactNumber(replyCount)}
153
+
</span>
154
+
</div>
155
+
156
+
<div
157
+
title={repostCount === 1
158
+
? `${formatLongNumber(repostCount)} repost`
159
+
: `${formatLongNumber(repostCount)} reposts`}
160
+
class="stat"
161
+
>
162
+
<svg class="icon" fill="none" viewBox="0 0 24 24">
163
+
<path
164
+
stroke="currentColor"
165
+
stroke-linecap="square"
166
+
stroke-width="2"
167
+
d="m17 3 3 3-3 3M7 21l-3-3 3-3m-2 3h15v-5M4 11V6h15"
168
+
/>
169
+
</svg>
170
+
171
+
<span class="count">
172
+
{formatCompactNumber(repostCount)}
173
+
</span>
174
+
</div>
175
+
176
+
<div
177
+
title={likeCount === 1
178
+
? `${formatLongNumber(likeCount)} like`
179
+
: `${formatLongNumber(likeCount)} likes`}
180
+
class="stat"
181
+
>
182
+
<svg class="icon" fill="none" viewBox="0 0 24 24">
183
+
<path
184
+
stroke="currentColor"
185
+
stroke-width="2"
186
+
d="M12 5.768c6.162-6.25 16.725 5.358 0 14.732C-4.725 11.126 5.838-.482 12 5.768Z"
187
+
/>
188
+
</svg>
189
+
190
+
<span class="count">
191
+
{formatCompactNumber(likeCount)}
192
+
</span>
193
+
</div>
194
+
</div>
195
+
</div>
196
+
</div>
197
+
</div>
198
+
199
+
<style>
200
+
.feed-post {
201
+
padding: 0 16px;
202
+
}
203
+
.is-leaf {
204
+
border-bottom: 1px solid var(--divider);
205
+
}
206
+
207
+
.ascendant-line-wrapper {
208
+
display: flex;
209
+
flex-direction: column;
210
+
align-items: center;
211
+
width: 36px;
212
+
213
+
.line {
214
+
position: absolute;
215
+
top: 0;
216
+
bottom: 4px;
217
+
flex-grow: 1;
218
+
border-left: 2px solid var(--divider);
219
+
}
220
+
}
221
+
.descendant-line {
222
+
flex-grow: 1;
223
+
margin-top: 4px;
224
+
border-left: 2px solid var(--divider);
225
+
}
226
+
227
+
.contexts {
228
+
display: flex;
229
+
position: relative;
230
+
flex-direction: column;
231
+
padding: 8px 0 4px 0;
232
+
}
233
+
.context {
234
+
display: flex;
235
+
align-items: center;
236
+
gap: 12px;
237
+
color: var(--text-secondary);
238
+
font-size: 0.8125rem;
239
+
line-height: 1.25rem;
240
+
241
+
.aside {
242
+
display: flex;
243
+
flex-shrink: 0;
244
+
justify-content: flex-end;
245
+
width: 36px;
246
+
}
247
+
248
+
.main {
249
+
display: flex;
250
+
min-width: 0px;
251
+
252
+
&:hover {
253
+
text-decoration-line: underline;
254
+
}
255
+
}
256
+
257
+
.name {
258
+
overflow: hidden;
259
+
font-weight: 500;
260
+
text-overflow: ellipsis;
261
+
white-space: nowrap;
262
+
}
263
+
264
+
.affix {
265
+
flex-shrink: 0;
266
+
white-space: pre;
267
+
}
268
+
}
269
+
270
+
.content {
271
+
display: flex;
272
+
gap: 12px;
273
+
274
+
.aside {
275
+
display: flex;
276
+
flex-shrink: 0;
277
+
flex-direction: column;
278
+
align-items: center;
279
+
}
280
+
281
+
.main {
282
+
flex-grow: 1;
283
+
padding-bottom: 12px;
284
+
min-width: 0;
285
+
}
286
+
}
287
+
288
+
.avatar-wrapper {
289
+
display: block;
290
+
border-radius: 9999px;
291
+
background: var(--background-secondary);
292
+
width: 36px;
293
+
height: 36px;
294
+
overflow: hidden;
295
+
296
+
&:hover {
297
+
filter: brightness(0.85);
298
+
}
299
+
}
300
+
.avatar {
301
+
width: 100%;
302
+
height: 100%;
303
+
object-fit: cover;
304
+
}
305
+
306
+
.meta {
307
+
display: flex;
308
+
align-items: center;
309
+
margin: 0 0 2px 0;
310
+
color: var(--text-secondary);
311
+
312
+
.name-wrapper {
313
+
display: flex;
314
+
gap: 4px;
315
+
max-width: 100%;
316
+
overflow: hidden;
317
+
color: inherit;
318
+
text-decoration: none;
319
+
text-overflow: ellipsis;
320
+
white-space: nowrap;
321
+
}
322
+
323
+
.display-name-wrapper {
324
+
overflow: hidden;
325
+
text-overflow: ellipsis;
326
+
327
+
.name-wrapper:hover & {
328
+
text-decoration: underline;
329
+
}
330
+
}
331
+
332
+
.display-name {
333
+
color: var(--text-primary);
334
+
font-weight: 700;
335
+
}
336
+
337
+
.handle {
338
+
display: block;
339
+
overflow: hidden;
340
+
text-overflow: ellipsis;
341
+
white-space: nowrap;
342
+
}
343
+
344
+
.dot {
345
+
flex-shrink: 0;
346
+
margin: 0 6px;
347
+
}
348
+
349
+
.date {
350
+
color: inherit;
351
+
text-decoration: none;
352
+
white-space: nowrap;
353
+
354
+
&:hover {
355
+
text-decoration: underline;
356
+
}
357
+
}
358
+
}
359
+
360
+
.reply-context {
361
+
overflow: hidden;
362
+
color: var(--text-secondary);
363
+
font-size: calc(var(--font-size) * 0.8125);
364
+
text-overflow: ellipsis;
365
+
white-space: nowrap;
366
+
367
+
a {
368
+
color: inherit;
369
+
font-weight: 500;
370
+
371
+
&:hover {
372
+
text-decoration: underline;
373
+
}
374
+
}
375
+
}
376
+
377
+
.metrics {
378
+
display: flex;
379
+
align-items: center;
380
+
gap: 16px;
381
+
margin-top: 12px;
382
+
color: var(--text-secondary);
383
+
}
384
+
.stat {
385
+
display: flex;
386
+
align-items: center;
387
+
gap: 8px;
388
+
min-width: 0px;
389
+
max-width: 100%;
390
+
391
+
.count {
392
+
padding-right: 8px;
393
+
overflow: hidden;
394
+
font-size: calc(var(--font-size) * 0.8125);
395
+
line-height: calc(var(--font-size) * 1.25);
396
+
text-overflow: ellipsis;
397
+
white-space: nowrap;
398
+
}
399
+
}
400
+
</style>
+271
packages/internal/components/highlighted-post.svelte
+271
packages/internal/components/highlighted-post.svelte
···
1
+
<script lang="ts">
2
+
import type { AppBskyFeedDefs, AppBskyFeedPost } from '@atcute/client/lexicons';
3
+
4
+
import { getPostUrl, getProfileUrl } from '../utils/bsky-url';
5
+
import { formatLongDate } from '../utils/date';
6
+
import { findLabel } from '../utils/labels';
7
+
import { formatCompactNumber, formatLongNumber } from '../utils/number';
8
+
import { parseAtUri } from '../utils/syntax/at-url';
9
+
10
+
import Embeds from './embeds/embeds.svelte';
11
+
import RichTextRenderer from './richtext-renderer.svelte';
12
+
13
+
interface Props {
14
+
post: AppBskyFeedDefs.PostView;
15
+
parent: AppBskyFeedDefs.PostView | null;
16
+
prev?: boolean;
17
+
}
18
+
19
+
const { post, parent, prev = false }: Props = $props();
20
+
21
+
const author = post.author;
22
+
const authorUrl = getProfileUrl(author.did);
23
+
const authorName = author.displayName?.trim();
24
+
25
+
const record = post.record as AppBskyFeedPost.Record;
26
+
const postUrl = getPostUrl(author.did, parseAtUri(post.uri).rkey);
27
+
28
+
const replyCount = post.replyCount || 0;
29
+
const likeCount = post.likeCount || 0;
30
+
const repostCount = (post.repostCount || 0) + (post.quoteCount || 0);
31
+
32
+
const isAuthorBlurred = !!findLabel(author.labels, author.did);
33
+
</script>
34
+
35
+
<div class="highlighted-post">
36
+
<div class="meta">
37
+
<a href={authorUrl} target="_blank" class="avatar-wrapper">
38
+
{#if author.avatar}
39
+
<img
40
+
loading="lazy"
41
+
src={author.avatar}
42
+
alt=""
43
+
class={`avatar` + (isAuthorBlurred ? ` is-blurred` : ``)}
44
+
/>
45
+
{/if}
46
+
</a>
47
+
48
+
<a href={authorUrl} target="_blank" class="name-wrapper">
49
+
{#if authorName}
50
+
<bdi class="display-name-wrapper">
51
+
<span class="display-name">{authorName}</span>
52
+
</bdi>
53
+
{/if}
54
+
<span class="handle">@{author.handle}</span>
55
+
</a>
56
+
57
+
{#if !prev}
58
+
<svg class="logo" fill="none" viewBox="0 0 320 286">
59
+
<path
60
+
fill="#0A7AFF"
61
+
d="M69.364 19.146c36.687 27.806 76.147 84.186 90.636 114.439 14.489-30.253 53.948-86.633 90.636-114.439C277.107-.917 320-16.44 320 32.957c0 9.865-5.603 82.875-8.889 94.729-11.423 41.208-53.045 51.719-90.071 45.357 64.719 11.12 81.182 47.953 45.627 84.785-80 82.874-106.667-44.333-106.667-44.333s-26.667 127.207-106.667 44.333c-35.555-36.832-19.092-73.665 45.627-84.785-37.026 6.362-78.648-4.149-90.071-45.357C5.603 115.832 0 42.822 0 32.957 0-16.44 42.893-.917 69.364 19.147Z"
62
+
/>
63
+
</svg>
64
+
{/if}
65
+
</div>
66
+
67
+
{#if !prev && record.reply}
68
+
<p class="context">
69
+
{#if parent}
70
+
{@const author = parent.author}
71
+
72
+
Replying to
73
+
<a target="_blank" href={getProfileUrl(author.did)} dir="auto">
74
+
{author.displayName?.trim() || `@${author.handle}`}
75
+
</a>
76
+
{:else}
77
+
Replying to an unknown post
78
+
{/if}
79
+
</p>
80
+
{/if}
81
+
82
+
<RichTextRenderer text={record.text} facets={record.facets} large />
83
+
84
+
{#if post.embed}
85
+
<Embeds {post} embed={post.embed} large />
86
+
{/if}
87
+
88
+
<time datetime={record.createdAt} class="date">
89
+
{formatLongDate(record.createdAt)}
90
+
</time>
91
+
92
+
<div class="stats">
93
+
<span
94
+
class="stat"
95
+
title={likeCount === 1 ? `${formatLongNumber(likeCount)} like` : `${formatLongNumber(likeCount)} likes`}
96
+
>
97
+
<!-- heart-2 -->
98
+
<svg class="icon" fill="none" viewBox="0 0 24 24">
99
+
<path
100
+
stroke="currentColor"
101
+
stroke-width="2"
102
+
d="M12 5.768c6.162-6.25 16.725 5.358 0 14.732C-4.725 11.126 5.838-.482 12 5.768Z"
103
+
/>
104
+
</svg>
105
+
106
+
<span>{formatCompactNumber(likeCount)}</span>
107
+
</span>
108
+
109
+
<span
110
+
class="stat"
111
+
title={repostCount === 1
112
+
? `${formatLongNumber(repostCount)} repost`
113
+
: `${formatLongNumber(repostCount)} reposts`}
114
+
>
115
+
<!-- arrows-repeat-right-left -->
116
+
<svg class="icon" fill="none" viewBox="0 0 24 24">
117
+
<path
118
+
stroke="currentColor"
119
+
stroke-linecap="square"
120
+
stroke-width="2"
121
+
d="m17 3 3 3-3 3M7 21l-3-3 3-3m-2 3h15v-5M4 11V6h15"
122
+
/>
123
+
</svg>
124
+
125
+
<span>{formatCompactNumber(repostCount)}</span>
126
+
</span>
127
+
128
+
<div class="gap"></div>
129
+
130
+
<a href={postUrl} target="_blank" class="permalink">
131
+
<span>
132
+
{!replyCount
133
+
? `View on Bluesky`
134
+
: replyCount === 1
135
+
? `Read ${formatCompactNumber(replyCount)} reply on Bluesky`
136
+
: `Read ${formatCompactNumber(replyCount)} replies on Bluesky`}
137
+
</span>
138
+
</a>
139
+
</div>
140
+
</div>
141
+
142
+
<style>
143
+
.highlighted-post {
144
+
padding: 16px;
145
+
}
146
+
147
+
.meta {
148
+
display: flex;
149
+
align-items: center;
150
+
gap: 12px;
151
+
margin: 0 0 12px 0;
152
+
color: var(--text-secondary);
153
+
}
154
+
155
+
.avatar-wrapper {
156
+
display: block;
157
+
flex-shrink: 0;
158
+
border-radius: 9999px;
159
+
background: var(--background-secondary);
160
+
width: 40px;
161
+
height: 40px;
162
+
overflow: hidden;
163
+
164
+
&:hover {
165
+
filter: brightness(0.85);
166
+
}
167
+
}
168
+
169
+
.avatar {
170
+
width: 100%;
171
+
height: 100%;
172
+
object-fit: cover;
173
+
}
174
+
.is-blurred {
175
+
scale: 125%;
176
+
filter: blur(4px);
177
+
}
178
+
179
+
.name-wrapper {
180
+
display: block;
181
+
flex-grow: 1;
182
+
max-width: 100%;
183
+
overflow: hidden;
184
+
color: inherit;
185
+
text-overflow: ellipsis;
186
+
white-space: nowrap;
187
+
}
188
+
.display-name-wrapper {
189
+
overflow: hidden;
190
+
text-overflow: ellipsis;
191
+
192
+
.name-wrapper:hover & {
193
+
text-decoration: underline;
194
+
}
195
+
}
196
+
.display-name {
197
+
color: var(--text-primary);
198
+
font-weight: 700;
199
+
}
200
+
.handle {
201
+
display: block;
202
+
overflow: hidden;
203
+
text-overflow: ellipsis;
204
+
white-space: nowrap;
205
+
}
206
+
207
+
.logo {
208
+
width: 32px;
209
+
height: 32px;
210
+
}
211
+
212
+
.context {
213
+
overflow: hidden;
214
+
color: var(--text-secondary);
215
+
font-size: calc(var(--font-size) * 0.8125);
216
+
text-overflow: ellipsis;
217
+
white-space: nowrap;
218
+
219
+
a {
220
+
color: inherit;
221
+
font-weight: 500;
222
+
223
+
&:hover {
224
+
text-decoration: underline;
225
+
}
226
+
}
227
+
}
228
+
229
+
.date {
230
+
display: flex;
231
+
flex-wrap: wrap;
232
+
align-items: center;
233
+
gap: 8px;
234
+
margin: 12px 0 0;
235
+
border-bottom: 1px solid var(--divider);
236
+
padding: 0 0 12px 0;
237
+
color: var(--text-secondary);
238
+
}
239
+
240
+
.stats {
241
+
display: flex;
242
+
flex-wrap: wrap;
243
+
align-items: center;
244
+
gap: 8px 16px;
245
+
margin: 0 0 -16px 0;
246
+
padding: 12px 0;
247
+
color: var(--text-secondary);
248
+
249
+
.gap {
250
+
flex: 1 1 auto;
251
+
}
252
+
253
+
.permalink {
254
+
display: flex;
255
+
align-items: center;
256
+
gap: 4px;
257
+
color: var(--text-link);
258
+
font-weight: 700;
259
+
260
+
&:hover {
261
+
text-decoration: underline;
262
+
}
263
+
}
264
+
}
265
+
.stat {
266
+
display: flex;
267
+
align-items: center;
268
+
gap: 8px;
269
+
font-weight: 500;
270
+
}
271
+
</style>
+226
packages/internal/components/post.svelte
+226
packages/internal/components/post.svelte
···
1
+
<script lang="ts">
2
+
import type { AppBskyFeedDefs, AppBskyFeedPost } from '@atcute/client/lexicons';
3
+
4
+
import { getPostUrl, getProfileUrl } from '../utils/bsky-url';
5
+
import { formatLongDate, formatShortDate } from '../utils/date';
6
+
import { findLabel } from '../utils/labels';
7
+
import { parseAtUri } from '../utils/syntax/at-url';
8
+
9
+
import Embeds from './embeds/embeds.svelte';
10
+
import RichtextRenderer from './richtext-renderer.svelte';
11
+
12
+
interface Props {
13
+
post: AppBskyFeedDefs.PostView;
14
+
parent?: AppBskyFeedDefs.PostView | null;
15
+
prev?: boolean;
16
+
}
17
+
18
+
const { post, parent, prev }: Props = $props();
19
+
20
+
const author = post.author;
21
+
const authorUrl = getProfileUrl(author.did);
22
+
const authorName = author.displayName?.trim();
23
+
24
+
const record = post.record as AppBskyFeedPost.Record;
25
+
const postUrl = getPostUrl(author.did, parseAtUri(post.uri).rkey);
26
+
27
+
const isAuthorBlurred = !!findLabel(author.labels, author.did);
28
+
</script>
29
+
30
+
<div class="post">
31
+
{#if !prev}
32
+
<svg class="logo" fill="none" viewBox="0 0 320 286">
33
+
<path
34
+
fill="#0A7AFF"
35
+
d="M69.364 19.146c36.687 27.806 76.147 84.186 90.636 114.439 14.489-30.253 53.948-86.633 90.636-114.439C277.107-.917 320-16.44 320 32.957c0 9.865-5.603 82.875-8.889 94.729-11.423 41.208-53.045 51.719-90.071 45.357 64.719 11.12 81.182 47.953 45.627 84.785-80 82.874-106.667-44.333-106.667-44.333s-26.667 127.207-106.667 44.333c-35.555-36.832-19.092-73.665 45.627-84.785-37.026 6.362-78.648-4.149-90.071-45.357C5.603 115.832 0 42.822 0 32.957 0-16.44 42.893-.917 69.364 19.147Z"
36
+
/>
37
+
</svg>
38
+
{/if}
39
+
40
+
<div class="aside">
41
+
<a target="_blank" href={authorUrl} class="avatar-wrapper">
42
+
{#if author.avatar}
43
+
<img
44
+
loading="lazy"
45
+
src={author.avatar}
46
+
alt=""
47
+
class={`avatar` + (isAuthorBlurred ? ` is-blurred` : ``)}
48
+
/>
49
+
{/if}
50
+
</a>
51
+
52
+
<div class="line"></div>
53
+
</div>
54
+
55
+
<div class="main">
56
+
<div class="meta">
57
+
<a href={authorUrl} target="_blank" class="name-wrapper">
58
+
{#if authorName}
59
+
<bdi class="display-name-wrapper">
60
+
<span class="display-name">{authorName}</span>
61
+
</bdi>
62
+
{/if}
63
+
64
+
<span class="handle">@{author.handle}</span>
65
+
</a>
66
+
67
+
<span aria-hidden="true" class="dot"> ยท </span>
68
+
69
+
<a target="_blank" href={postUrl} title={formatLongDate(record.createdAt)} class="date">
70
+
<time datetime={record.createdAt}>{formatShortDate(record.createdAt)}</time>
71
+
</a>
72
+
</div>
73
+
74
+
{#if !prev && record.reply}
75
+
<p class="context">
76
+
{#if parent}
77
+
{@const author = parent.author}
78
+
79
+
Replying to
80
+
<a target="_blank" href={getProfileUrl(author.did)} dir="auto">
81
+
{author.displayName?.trim() || `@${author.handle}`}
82
+
</a>
83
+
{:else}
84
+
Replying to an unknown post
85
+
{/if}
86
+
</p>
87
+
{/if}
88
+
89
+
<RichtextRenderer text={record.text} facets={record.facets} />
90
+
91
+
{#if post.embed}
92
+
<Embeds {post} embed={post.embed} />
93
+
{/if}
94
+
</div>
95
+
</div>
96
+
97
+
<style>
98
+
.post {
99
+
display: flex;
100
+
position: relative;
101
+
gap: 12px;
102
+
padding: 12px 16px 0 16px;
103
+
}
104
+
105
+
.logo {
106
+
position: absolute;
107
+
top: 12px;
108
+
right: 12px;
109
+
width: 24px;
110
+
height: 24px;
111
+
}
112
+
113
+
.aside {
114
+
flex-shrink: 0;
115
+
}
116
+
117
+
.avatar-wrapper {
118
+
display: block;
119
+
border-radius: 9999px;
120
+
background: var(--background-secondary);
121
+
width: 40px;
122
+
height: 40px;
123
+
overflow: hidden;
124
+
125
+
&:hover {
126
+
filter: brightness(0.85);
127
+
}
128
+
}
129
+
130
+
.avatar {
131
+
width: 100%;
132
+
height: 100%;
133
+
object-fit: cover;
134
+
}
135
+
.is-blurred {
136
+
scale: 125%;
137
+
filter: blur(4px);
138
+
}
139
+
140
+
.line {
141
+
position: absolute;
142
+
top: 56px;
143
+
bottom: -12px;
144
+
left: 35px;
145
+
border-left: 2px solid var(--divider);
146
+
}
147
+
148
+
.main {
149
+
display: flex;
150
+
flex-grow: 1;
151
+
flex-direction: column;
152
+
min-width: 0px;
153
+
}
154
+
155
+
.meta {
156
+
display: flex;
157
+
align-items: center;
158
+
margin: 0 0 2px 0;
159
+
padding: 0 calc(24px + 8px) 0 0;
160
+
color: var(--text-secondary);
161
+
162
+
.name-wrapper {
163
+
display: flex;
164
+
gap: 4px;
165
+
max-width: 100%;
166
+
overflow: hidden;
167
+
color: inherit;
168
+
text-decoration: none;
169
+
text-overflow: ellipsis;
170
+
white-space: nowrap;
171
+
}
172
+
173
+
.display-name-wrapper {
174
+
overflow: hidden;
175
+
text-overflow: ellipsis;
176
+
177
+
.name-wrapper:hover & {
178
+
text-decoration: underline;
179
+
}
180
+
}
181
+
182
+
.display-name {
183
+
color: var(--text-primary);
184
+
font-weight: 700;
185
+
}
186
+
187
+
.handle {
188
+
display: block;
189
+
overflow: hidden;
190
+
text-overflow: ellipsis;
191
+
white-space: nowrap;
192
+
}
193
+
194
+
.dot {
195
+
flex-shrink: 0;
196
+
margin: 0 6px;
197
+
}
198
+
199
+
.date {
200
+
color: inherit;
201
+
text-decoration: none;
202
+
white-space: nowrap;
203
+
204
+
&:hover {
205
+
text-decoration: underline;
206
+
}
207
+
}
208
+
}
209
+
210
+
.context {
211
+
overflow: hidden;
212
+
color: var(--text-secondary);
213
+
font-size: calc(var(--font-size) * 0.8125);
214
+
text-overflow: ellipsis;
215
+
white-space: nowrap;
216
+
217
+
a {
218
+
color: inherit;
219
+
font-weight: 500;
220
+
221
+
&:hover {
222
+
text-decoration: underline;
223
+
}
224
+
}
225
+
}
226
+
</style>
+204
packages/internal/components/profile-card.svelte
+204
packages/internal/components/profile-card.svelte
···
1
+
<script lang="ts">
2
+
import type { AppBskyActorDefs } from '@atcute/client/lexicons';
3
+
4
+
import { getProfileUrl } from '../utils/bsky-url';
5
+
import { findLabel } from '../utils/labels';
6
+
import { formatCompactNumber } from '../utils/number';
7
+
import RichtextRawRenderer from './richtext-raw-renderer.svelte';
8
+
9
+
interface Props {
10
+
profile: AppBskyActorDefs.ProfileViewDetailed;
11
+
}
12
+
13
+
const { profile }: Props = $props();
14
+
15
+
const url = getProfileUrl(profile.did);
16
+
const isBlurred = findLabel(profile.labels, profile.did);
17
+
</script>
18
+
19
+
<div class="profile-card has-banner">
20
+
<div class="banner-wrapper">
21
+
{#if profile.banner}
22
+
<img loading="lazy" src={profile.banner} alt="" class={`banner` + (isBlurred ? ` is-blurred` : ``)} />
23
+
{/if}
24
+
</div>
25
+
26
+
<div class="contents">
27
+
<div class="header">
28
+
<a href={url} target="_blank" class="avatar-wrapper">
29
+
{#if profile.avatar}
30
+
<img
31
+
loading="lazy"
32
+
src={profile.avatar}
33
+
alt=""
34
+
class={`avatar` + (isBlurred ? ` is-blurred` : ``)}
35
+
/>
36
+
{/if}
37
+
</a>
38
+
39
+
<div class="actions">
40
+
<a href={url} target="_blank" class="follow-button">
41
+
<svg class="icon" fill="none" viewBox="0 0 24 24">
42
+
<path
43
+
stroke="currentColor"
44
+
stroke-linecap="square"
45
+
stroke-width="2"
46
+
d="M12 4v8m0 0v8m0-8H4m8 0h8"
47
+
/>
48
+
</svg>
49
+
<span>Follow</span>
50
+
</a>
51
+
52
+
<svg class="logo" fill="none" viewBox="0 0 320 286">
53
+
<path
54
+
fill="#0A7AFF"
55
+
d="M69.364 19.146c36.687 27.806 76.147 84.186 90.636 114.439 14.489-30.253 53.948-86.633 90.636-114.439C277.107-.917 320-16.44 320 32.957c0 9.865-5.603 82.875-8.889 94.729-11.423 41.208-53.045 51.719-90.071 45.357 64.719 11.12 81.182 47.953 45.627 84.785-80 82.874-106.667-44.333-106.667-44.333s-26.667 127.207-106.667 44.333c-35.555-36.832-19.092-73.665 45.627-84.785-37.026 6.362-78.648-4.149-90.071-45.357C5.603 115.832 0 42.822 0 32.957 0-16.44 42.893-.917 69.364 19.147Z"
56
+
/>
57
+
</svg>
58
+
</div>
59
+
</div>
60
+
61
+
<div class="name-wrapper">
62
+
<p dir="auto" class="display-name">{profile.displayName?.trim() || profile.handle.slice(0, 64)}</p>
63
+
<p class="handle">@{profile.handle}</p>
64
+
</div>
65
+
66
+
<div class="stats">
67
+
<span class="stat-entry">
68
+
<span class="stat-count">{formatCompactNumber(profile.followersCount || 0)}</span>
69
+
<span> {profile.followersCount === 1 ? `Follower` : `Followers`}</span>
70
+
</span>
71
+
72
+
<span class="stat-entry">
73
+
<span class="stat-count">{formatCompactNumber(profile.followsCount || 0)}</span>
74
+
<span> Following</span>
75
+
</span>
76
+
</div>
77
+
78
+
{#if profile.description?.trim()}
79
+
<RichtextRawRenderer text={profile.description} />
80
+
{/if}
81
+
</div>
82
+
</div>
83
+
84
+
<style>
85
+
.profile-card {
86
+
display: flex;
87
+
flex-direction: column;
88
+
}
89
+
90
+
.is-blurred {
91
+
scale: 125%;
92
+
filter: blur(4px);
93
+
}
94
+
95
+
.banner-wrapper {
96
+
background: var(--background-secondary);
97
+
aspect-ratio: 3 / 1;
98
+
overflow: hidden;
99
+
}
100
+
.banner {
101
+
width: 100%;
102
+
height: 100%;
103
+
object-fit: cover;
104
+
}
105
+
106
+
.contents {
107
+
display: flex;
108
+
position: relative;
109
+
flex-direction: column;
110
+
gap: 8px;
111
+
padding: 12px 16px 16px;
112
+
}
113
+
.logo {
114
+
width: 24px;
115
+
height: 24px;
116
+
}
117
+
118
+
.header {
119
+
display: flex;
120
+
justify-content: space-between;
121
+
align-items: end;
122
+
}
123
+
.actions {
124
+
display: flex;
125
+
align-items: center;
126
+
gap: 16px;
127
+
}
128
+
129
+
.avatar-wrapper {
130
+
display: block;
131
+
flex-shrink: 0;
132
+
outline: 2px solid var(--background-primary);
133
+
border-radius: 9999px;
134
+
background: var(--background-secondary);
135
+
width: 90px;
136
+
height: 90px;
137
+
overflow: hidden;
138
+
139
+
.has-banner & {
140
+
margin-top: calc(-90px + 34px);
141
+
}
142
+
}
143
+
.avatar {
144
+
width: 100%;
145
+
height: 100%;
146
+
object-fit: cover;
147
+
148
+
.avatar-wrapper:hover & {
149
+
filter: brightness(0.85);
150
+
151
+
&.is-blurred {
152
+
filter: brightness(0.85) blur(4px);
153
+
}
154
+
}
155
+
}
156
+
157
+
.follow-button {
158
+
display: flex;
159
+
align-items: center;
160
+
gap: 6px;
161
+
border-radius: 9999px;
162
+
background: var(--button);
163
+
padding: 9px 12px;
164
+
color: var(--button-text);
165
+
font-weight: 600;
166
+
font-size: calc(var(--font-size) * 0.8125);
167
+
line-height: calc(var(--font-size) * 1);
168
+
user-select: none;
169
+
170
+
.icon {
171
+
font-size: 16px;
172
+
}
173
+
174
+
&:hover {
175
+
background: var(--button-hover);
176
+
}
177
+
}
178
+
179
+
.display-name {
180
+
font-weight: 700;
181
+
font-size: calc(var(--font-size) * 1.25);
182
+
line-height: calc(var(--font-size) * 1.75);
183
+
overflow-wrap: break-word;
184
+
}
185
+
.handle {
186
+
color: var(--text-secondary);
187
+
overflow-wrap: break-word;
188
+
}
189
+
190
+
.stats {
191
+
display: flex;
192
+
flex-wrap: wrap;
193
+
gap: 20px;
194
+
195
+
min-width: 0;
196
+
}
197
+
.stat-entry {
198
+
color: var(--text-secondary);
199
+
}
200
+
.stat-count {
201
+
color: var(--text-primary);
202
+
font-weight: 700;
203
+
}
204
+
</style>
+54
packages/internal/components/profile-feed-header.svelte
+54
packages/internal/components/profile-feed-header.svelte
···
1
+
<script lang="ts">
2
+
import type { AppBskyActorDefs } from '@atcute/client/lexicons';
3
+
4
+
import { getProfileUrl } from '../utils/bsky-url';
5
+
6
+
interface Props {
7
+
profile: AppBskyActorDefs.ProfileViewDetailed;
8
+
}
9
+
10
+
const { profile }: Props = $props();
11
+
</script>
12
+
13
+
<div class="profile-feed-header">
14
+
<a target="_blank" href={getProfileUrl(profile.did)} class="title">Posts from @{profile.handle}</a>
15
+
16
+
<svg class="logo" fill="none" viewBox="0 0 320 286">
17
+
<path
18
+
fill="#0A7AFF"
19
+
d="M69.364 19.146c36.687 27.806 76.147 84.186 90.636 114.439 14.489-30.253 53.948-86.633 90.636-114.439C277.107-.917 320-16.44 320 32.957c0 9.865-5.603 82.875-8.889 94.729-11.423 41.208-53.045 51.719-90.071 45.357 64.719 11.12 81.182 47.953 45.627 84.785-80 82.874-106.667-44.333-106.667-44.333s-26.667 127.207-106.667 44.333c-35.555-36.832-19.092-73.665 45.627-84.785-37.026 6.362-78.648-4.149-90.071-45.357C5.603 115.832 0 42.822 0 32.957 0-16.44 42.893-.917 69.364 19.147Z"
20
+
/>
21
+
</svg>
22
+
</div>
23
+
24
+
<style>
25
+
.profile-feed-header {
26
+
display: flex;
27
+
justify-content: space-between;
28
+
align-items: center;
29
+
gap: 16px;
30
+
container-type: inline-size;
31
+
border-bottom: 1px solid var(--divider);
32
+
padding: 12px 16px;
33
+
}
34
+
35
+
.title {
36
+
padding: 4px 0;
37
+
min-width: 0;
38
+
overflow: hidden;
39
+
font-weight: 600;
40
+
font-size: calc(var(--font-size) * 1);
41
+
line-height: calc(var(--font-size) * 1.5);
42
+
text-overflow: ellipsis;
43
+
white-space: nowrap;
44
+
45
+
&:hover {
46
+
text-decoration: underline;
47
+
}
48
+
}
49
+
50
+
.logo {
51
+
width: 24px;
52
+
height: 24px;
53
+
}
54
+
</style>
+53
packages/internal/components/richtext-raw-renderer.svelte
+53
packages/internal/components/richtext-raw-renderer.svelte
···
1
+
<script lang="ts" module>
2
+
const HTTP_RE = /^https?:\/\//;
3
+
</script>
4
+
5
+
<script lang="ts">
6
+
import { tokenize } from '@atcute/bluesky-richtext-parser';
7
+
8
+
import { getHashtagUrl, getProfileUrl } from '../utils/bsky-url';
9
+
10
+
interface Props {
11
+
text: string;
12
+
}
13
+
14
+
const { text }: Props = $props();
15
+
</script>
16
+
17
+
<p class="rich-text is-small">
18
+
{#each tokenize(text) as token}
19
+
{#if token.type === 'autolink'}
20
+
<a target="_blank" href={token.url} rel="noopener nofollow" class="link">
21
+
{token.raw.replace(HTTP_RE, '')}
22
+
</a>
23
+
{:else if token.type === 'mention'}
24
+
<a target="_blank" href={getProfileUrl(token.handle)} class="mention">{token.raw}</a>
25
+
{:else if token.type === 'topic'}
26
+
<a target="_blank" href={getHashtagUrl(token.name)} class="hashtag">{token.raw}</a>
27
+
{:else}
28
+
{token.raw}
29
+
{/if}
30
+
{/each}
31
+
</p>
32
+
33
+
<style>
34
+
.rich-text {
35
+
overflow: hidden;
36
+
white-space: pre-wrap;
37
+
overflow-wrap: break-word;
38
+
39
+
&:empty {
40
+
display: none;
41
+
}
42
+
}
43
+
44
+
.link,
45
+
.mention,
46
+
.hashtag {
47
+
color: var(--text-link);
48
+
49
+
&:hover {
50
+
text-decoration: underline;
51
+
}
52
+
}
53
+
</style>
+66
packages/internal/components/richtext-renderer.svelte
+66
packages/internal/components/richtext-renderer.svelte
···
1
+
<script lang="ts" module>
2
+
import { segmentize, type Facet, type FacetFeature } from '@atcute/bluesky-richtext-segmenter';
3
+
4
+
import { getHashtagUrl, getProfileUrl } from '../utils/bsky-url';
5
+
6
+
const grabFirstSupported = (features: FacetFeature[] | undefined): FacetFeature | undefined => {
7
+
return features?.find(
8
+
(feature) =>
9
+
feature.$type === 'app.bsky.richtext.facet#link' ||
10
+
feature.$type === 'app.bsky.richtext.facet#mention' ||
11
+
feature.$type === 'app.bsky.richtext.facet#tag',
12
+
);
13
+
};
14
+
</script>
15
+
16
+
<script lang="ts">
17
+
interface Props {
18
+
text: string;
19
+
facets?: Facet[];
20
+
large?: boolean;
21
+
}
22
+
23
+
const { text, facets, large }: Props = $props();
24
+
</script>
25
+
26
+
<p class={`rich-text` + (large ? ` is-large` : ` is-small`)}>
27
+
{#each segmentize(text, facets) as segment}
28
+
{@const feature = grabFirstSupported(segment.features)}
29
+
30
+
{#if !feature}
31
+
{segment.text}
32
+
{:else if feature.$type === 'app.bsky.richtext.facet#link'}
33
+
<a target="_blank" href={feature.uri} rel="noopener nofollow" class="link">{segment.text}</a>
34
+
{:else if feature.$type === 'app.bsky.richtext.facet#mention'}
35
+
<a target="_blank" href={getProfileUrl(feature.did)} class="mention">{segment.text}</a>
36
+
{:else if feature.$type === 'app.bsky.richtext.facet#tag'}
37
+
<a target="_blank" href={getHashtagUrl(feature.tag)} class="hashtag">{segment.text}</a>
38
+
{/if}
39
+
{/each}
40
+
</p>
41
+
42
+
<style>
43
+
.rich-text {
44
+
overflow: hidden;
45
+
white-space: pre-wrap;
46
+
overflow-wrap: break-word;
47
+
48
+
&:empty {
49
+
display: none;
50
+
}
51
+
}
52
+
.is-large {
53
+
font-size: calc(var(--font-size) * 1);
54
+
line-height: calc(var(--font-size) * 1.5);
55
+
}
56
+
57
+
.link,
58
+
.mention,
59
+
.hashtag {
60
+
color: var(--text-link);
61
+
62
+
&:hover {
63
+
text-decoration: underline;
64
+
}
65
+
}
66
+
</style>
+34
packages/internal/package.json
+34
packages/internal/package.json
···
1
+
{
2
+
"private": true,
3
+
"type": "module",
4
+
"name": "internal",
5
+
"version": "0.1.0",
6
+
"exports": {
7
+
"./components/*": "./components/*",
8
+
"./types/*": "./types/*",
9
+
"./utils/*": "./utils/*"
10
+
},
11
+
"peerDependencies": {
12
+
"@atcute/bluesky": "^1.0.9",
13
+
"@atcute/bluesky-richtext-parser": "^1.0.7",
14
+
"@atcute/bluesky-richtext-segmenter": "^1.0.5",
15
+
"@atcute/client": "^2.0.6",
16
+
"svelte": "catalog:"
17
+
},
18
+
"peerDependenciesMeta": {
19
+
"@atcute/bluesky-richtext-parser": {
20
+
"optional": true
21
+
},
22
+
"@atcute/bluesky-richtext-segmenter": {
23
+
"optional": true
24
+
}
25
+
},
26
+
"devDependencies": {
27
+
"@atcute/bluesky": "^2.1.1",
28
+
"@atcute/bluesky-richtext-parser": "^1.0.7",
29
+
"@atcute/bluesky-richtext-segmenter": "^2.0.4",
30
+
"@atcute/client": "^3.1.0",
31
+
"@tsconfig/svelte": "^5.0.6",
32
+
"svelte": "catalog:"
33
+
}
34
+
}
+8
packages/internal/svelte.config.js
+8
packages/internal/svelte.config.js
+13
packages/internal/tsconfig.json
+13
packages/internal/tsconfig.json
···
1
+
{
2
+
"extends": "@tsconfig/svelte/tsconfig.json",
3
+
"compilerOptions": {
4
+
"types": ["@atcute/bluesky/lexicons"],
5
+
"target": "ESNext",
6
+
"useDefineForClassFields": true,
7
+
"module": "ESNext",
8
+
"resolveJsonModule": true,
9
+
"isolatedModules": true,
10
+
"moduleDetection": "force",
11
+
"noEmit": true,
12
+
},
13
+
}
+7
packages/internal/types/post.ts
+7
packages/internal/types/post.ts
+6
packages/internal/types/profile-card.ts
+6
packages/internal/types/profile-card.ts
+7
packages/internal/types/profile-feed.ts
+7
packages/internal/types/profile-feed.ts
+27
packages/internal/utils/bsky-url.ts
+27
packages/internal/utils/bsky-url.ts
···
1
+
export const getProfileUrl = (author: string): string => {
2
+
return `https://bsky.app/profile/${author}`;
3
+
};
4
+
5
+
export const getPostUrl = (author: string, rkey: string): string => {
6
+
return `https://bsky.app/profile/${author}/post/${rkey}`;
7
+
};
8
+
9
+
export const getHashtagUrl = (tag: string): string => {
10
+
return `https://bsky.app/hashtag/${tag}`;
11
+
};
12
+
13
+
export const getFeedUrl = (author: string, rkey: string): string => {
14
+
return `https://bsky.app/profile/${author}/feed/${rkey}`;
15
+
};
16
+
17
+
export const getListUrl = (author: string, rkey: string): string => {
18
+
return `https://bsky.app/profile/${author}/list/${rkey}`;
19
+
};
20
+
21
+
export const getStarterpackUrl = (author: string, rkey: string): string => {
22
+
return `https://bsky.app/starter-pack/${author}/${rkey}`;
23
+
};
24
+
25
+
export const getStarterpackImgUrl = (author: string, rkey: string): string => {
26
+
return `https://ogcard.cdn.bsky.app/start/${author}/${rkey}`;
27
+
};
+3
packages/internal/utils/constants.ts
+3
packages/internal/utils/constants.ts
+44
packages/internal/utils/date.ts
+44
packages/internal/utils/date.ts
···
1
+
let startOfYear = 0;
2
+
let endOfYear = 0;
3
+
4
+
const fmtAbsoluteLong = new Intl.DateTimeFormat('en-US', { dateStyle: 'long', timeStyle: 'short' });
5
+
const fmtAbsShortWithYear = new Intl.DateTimeFormat('en-US', { dateStyle: 'medium' });
6
+
const fmtAbsShort = new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric' });
7
+
8
+
export const formatShortDate = (date: string | number): string => {
9
+
const inst = new Date(date);
10
+
const time = inst.getTime();
11
+
12
+
if (isNaN(time)) {
13
+
return 'N/A';
14
+
}
15
+
16
+
const now = Date.now();
17
+
if (now > endOfYear) {
18
+
const date = new Date(now);
19
+
20
+
date.setMonth(0, 1);
21
+
date.setHours(0, 0, 0);
22
+
startOfYear = date.getTime();
23
+
24
+
date.setFullYear(date.getFullYear() + 1, 0, 0);
25
+
date.setHours(23, 59, 59, 999);
26
+
endOfYear = date.getTime();
27
+
}
28
+
29
+
if (time >= startOfYear && time <= endOfYear) {
30
+
return fmtAbsShort.format(inst);
31
+
}
32
+
33
+
return fmtAbsShortWithYear.format(inst);
34
+
};
35
+
36
+
export const formatLongDate = (date: string | number): string => {
37
+
const inst = new Date(date);
38
+
39
+
if (isNaN(inst.getTime())) {
40
+
return 'N/A';
41
+
}
42
+
43
+
return fmtAbsoluteLong.format(inst);
44
+
};
+60
packages/internal/utils/labels.ts
+60
packages/internal/utils/labels.ts
···
1
+
import type { At, ComAtprotoLabelDefs } from '@atcute/client/lexicons';
2
+
3
+
export const FlagsNone = 0;
4
+
export const FlagsNoSelf = 1 << 0;
5
+
6
+
type Label = ComAtprotoLabelDefs.Label;
7
+
8
+
export interface LabelDefinition {
9
+
name: string;
10
+
flags: number;
11
+
}
12
+
13
+
export const LABEL_MAPPING: Record<string, LabelDefinition> = {
14
+
'!hide': {
15
+
name: `Hidden by moderators`,
16
+
flags: FlagsNoSelf,
17
+
},
18
+
'!warn': {
19
+
name: `Content warning`,
20
+
flags: FlagsNoSelf,
21
+
},
22
+
23
+
porn: {
24
+
name: `Adult content`,
25
+
flags: FlagsNone,
26
+
},
27
+
sexual: {
28
+
name: `Sexually suggestive`,
29
+
flags: FlagsNone,
30
+
},
31
+
'graphic-media': {
32
+
name: `Graphic media`,
33
+
flags: FlagsNone,
34
+
},
35
+
nudity: {
36
+
name: `Nudity`,
37
+
flags: FlagsNone,
38
+
},
39
+
};
40
+
41
+
export const findLabel = (labels: Label[] | undefined, authorDid: At.Did): LabelDefinition | undefined => {
42
+
if (labels?.length) {
43
+
for (let idx = 0, len = labels.length; idx < len; idx++) {
44
+
const label = labels[idx];
45
+
const val = label.val;
46
+
47
+
if (!(val in LABEL_MAPPING)) {
48
+
continue;
49
+
}
50
+
51
+
const def = LABEL_MAPPING[val];
52
+
53
+
if (def.flags & FlagsNoSelf && label.src === authorDid) {
54
+
continue;
55
+
}
56
+
57
+
return def;
58
+
}
59
+
}
60
+
};
+18
packages/internal/utils/number.ts
+18
packages/internal/utils/number.ts
···
1
+
const long = new Intl.NumberFormat('en-US');
2
+
const compact = new Intl.NumberFormat('en-US', { notation: 'compact' });
3
+
4
+
export const formatCompactNumber = (value: number) => {
5
+
if (value < 1_000) {
6
+
return '' + value;
7
+
}
8
+
9
+
if (value < 100_000) {
10
+
return long.format(value);
11
+
}
12
+
13
+
return compact.format(value);
14
+
};
15
+
16
+
export const formatLongNumber = (value: number) => {
17
+
return long.format(value);
18
+
};
+27
packages/internal/utils/syntax/at-url.ts
+27
packages/internal/utils/syntax/at-url.ts
···
1
+
export const AT_URI_RE =
2
+
/^at:\/\/((?:did:[a-zA-Z0-9._:%-]+)|(?:[a-zA-Z0-9][a-zA-Z0-9-.]*))(?:\/([a-zA-Z0-9.-]+)(?:\/([a-zA-Z0-9_~.:-]{1,512}))?)?\/?(?:\?([^#\s]*))?(?:#([^\s]*))?$/;
3
+
4
+
export interface ParsedAtUri {
5
+
repo: string;
6
+
collection: string;
7
+
rkey: string;
8
+
query: string;
9
+
fragment: string;
10
+
}
11
+
12
+
export const parseAtUri = (str: string): ParsedAtUri => {
13
+
const match = AT_URI_RE.exec(str);
14
+
if (!match) {
15
+
throw new InvalidAtUriError(`invalid at-uri: ${str}`);
16
+
}
17
+
18
+
return {
19
+
repo: match[1],
20
+
collection: match[2] ?? '',
21
+
rkey: match[3] ?? '',
22
+
query: match[4] ?? '',
23
+
fragment: match[5] ?? '',
24
+
};
25
+
};
26
+
27
+
export class InvalidAtUriError extends Error {}
+1
packages/svelte-site/.gitignore
+1
packages/svelte-site/.gitignore
···
1
+
pages/
+66
packages/svelte-site/README.md
+66
packages/svelte-site/README.md
···
1
+
# Svelte + TS + Vite
2
+
3
+
This template should help get you started developing with Svelte and TypeScript in Vite.
4
+
5
+
## Recommended IDE Setup
6
+
7
+
[VS Code](https://code.visualstudio.com/) +
8
+
[Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode).
9
+
10
+
## Need an official Svelte framework?
11
+
12
+
Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy
13
+
anywhere with its serverless-first approach and adapt to various platforms, with out of the box
14
+
support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS,
15
+
Tailwind CSS, and more.
16
+
17
+
## Technical considerations
18
+
19
+
**Why use this over SvelteKit?**
20
+
21
+
- It brings its own routing solution which might not be preferable for some users.
22
+
- It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app.
23
+
24
+
This template contains as little as possible to get started with Vite + TypeScript + Svelte, while
25
+
taking into account the developer experience with regards to HMR and intellisense. It demonstrates
26
+
capabilities on par with the other `create-vite` templates and is a good starting point for
27
+
beginners dipping their toes into a Vite + Svelte project.
28
+
29
+
Should you later need the extended capabilities and extensibility provided by SvelteKit, the
30
+
template has been structured similarly to SvelteKit so that it is easy to migrate.
31
+
32
+
**Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?**
33
+
34
+
Setting `compilerOptions.types` shuts out all other types not explicitly listed in the
35
+
configuration. Using triple-slash references keeps the default TypeScript setting of accepting type
36
+
information from the entire workspace, while also adding `svelte` and `vite/client` type
37
+
information.
38
+
39
+
**Why include `.vscode/extensions.json`?**
40
+
41
+
Other templates indirectly recommend extensions via the README, but this file allows VS Code to
42
+
prompt the user to install the recommended extension upon opening the project.
43
+
44
+
**Why enable `allowJs` in the TS template?**
45
+
46
+
While `allowJs: false` would indeed prevent the use of `.js` files in the project, it does not
47
+
prevent the use of JavaScript syntax in `.svelte` files. In addition, it would force
48
+
`checkJs: false`, bringing the worst of both worlds: not being able to guarantee the entire codebase
49
+
is TypeScript, and also having worse typechecking for the existing JavaScript. In addition, there
50
+
are valid use cases in which a mixed codebase may be relevant.
51
+
52
+
**Why is HMR not preserving my local component state?**
53
+
54
+
HMR state preservation comes with a number of gotchas! It has been disabled by default in both
55
+
`svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read
56
+
the details [here](https://github.com/rixo/svelte-hmr#svelte-hmr).
57
+
58
+
If you have state that's important to retain within a component, consider creating an external store
59
+
which would not be replaced by HMR.
60
+
61
+
```ts
62
+
// store.ts
63
+
// An extremely simple external store
64
+
import { writable } from 'svelte/store';
65
+
export default writable(0);
66
+
```
+12
packages/svelte-site/index.html
+12
packages/svelte-site/index.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.0" />
6
+
<title>Bluesky embed</title>
7
+
</head>
8
+
<body>
9
+
<div id="app"></div>
10
+
<script type="module" src="./src/main.ts"></script>
11
+
</body>
12
+
</html>
+30
packages/svelte-site/package.json
+30
packages/svelte-site/package.json
···
1
+
{
2
+
"name": "svelte-site",
3
+
"private": true,
4
+
"version": "0.0.0",
5
+
"type": "module",
6
+
"scripts": {
7
+
"dev": "vite",
8
+
"build": "vite build",
9
+
"preview": "vite preview",
10
+
"check": "svelte-check --tsconfig ./tsconfig.json && tsc -p tsconfig.node.json",
11
+
"publish": "pnpm run build && ./scripts/publish.sh"
12
+
},
13
+
"dependencies": {
14
+
"@atcute/bluesky": "^2.1.1",
15
+
"@atcute/client": "^3.1.0",
16
+
"bluesky-post-embed": "workspace:^",
17
+
"bluesky-profile-card-embed": "workspace:^",
18
+
"bluesky-profile-feed-embed": "workspace:^",
19
+
"internal": "workspace:^"
20
+
},
21
+
"devDependencies": {
22
+
"@sveltejs/vite-plugin-svelte": "^6.2.1",
23
+
"@tsconfig/svelte": "^5.0.6",
24
+
"svelte": "^5.45.5",
25
+
"svelte-check": "^4.3.4",
26
+
"terser": "^5.44.1",
27
+
"tslib": "^2.8.1",
28
+
"vite": "^7.2.6"
29
+
}
30
+
}
+18
packages/svelte-site/scripts/publish.sh
+18
packages/svelte-site/scripts/publish.sh
···
1
+
#!/usr/bin/env bash
2
+
3
+
set -euo pipefail
4
+
5
+
if [[ -n $(git status --porcelain) ]]; then
6
+
echo 'Working directory is not clean'
7
+
git status --short
8
+
exit 1
9
+
fi
10
+
11
+
GIT_COMMIT=$(git rev-parse HEAD)
12
+
13
+
rsync -aHAX --delete --exclude=.git --exclude=.nojekyll dist/ pages/
14
+
touch pages/.nojekyll
15
+
16
+
git -C pages/ add .
17
+
git -C pages/ commit -m "deploy: ${GIT_COMMIT}"
18
+
git -C pages/ push
+159
packages/svelte-site/src/App.svelte
+159
packages/svelte-site/src/App.svelte
···
1
+
<script lang="ts" module>
2
+
const PostDisplay = () => import('./components/display/PostDisplay.svelte');
3
+
const ProfileCardDisplay = () => import('./components/display/ProfileCardDisplay.svelte');
4
+
const ProfileFeedDisplay = () => import('./components/display/ProfileFeedDisplay.svelte');
5
+
</script>
6
+
7
+
<script lang="ts">
8
+
import { extract_url } from './lib/matcher';
9
+
10
+
import Banner from './components/Banner.svelte';
11
+
import CircularSpinner from './components/CircularSpinner.svelte';
12
+
import Field from './components/Field.svelte';
13
+
import Lazy from './components/Lazy.svelte';
14
+
import TextInput from './components/TextInput.svelte';
15
+
16
+
const DEFAULT_URL = 'https://bsky.app/profile/did:plc:ragtjsm2j2vknwkz3zp4oxrd/post/3kj2umze7zj2n';
17
+
// const DEFAULT_URL = 'https://bsky.app/profile/did:plc:ragtjsm2j2vknwkz3zp4oxrd';
18
+
19
+
let url = $state('');
20
+
let profile_type = $state<'card' | 'feed'>('feed');
21
+
22
+
const matched = $derived(extract_url(url || DEFAULT_URL));
23
+
</script>
24
+
25
+
<div class="app">
26
+
<h1 class="header">
27
+
<code><bluesky-embed></code>
28
+
</h1>
29
+
30
+
<Field label="Bluesky post or profile URL">
31
+
<TextInput type="url" bind:value={url} placeholder={DEFAULT_URL} />
32
+
</Field>
33
+
34
+
{#if matched && matched.type === 'profile'}
35
+
<fieldset class="choices">
36
+
<label class="choice">
37
+
<input type="radio" name="profile-type" value="feed" bind:group={profile_type} />
38
+
<span>Profile feed</span>
39
+
</label>
40
+
41
+
<label class="choice">
42
+
<input type="radio" name="profile-type" value="card" bind:group={profile_type} />
43
+
<span>Profile card</span>
44
+
</label>
45
+
</fieldset>
46
+
{/if}
47
+
48
+
<main class="main">
49
+
{#if !matched}
50
+
<Banner type="alert">Invalid URL, did you type it correctly?</Banner>
51
+
{:else if matched.type === 'post'}
52
+
<Lazy loader={PostDisplay} fallback={LazyFallback} boundary={LazyBoundary}>
53
+
{#snippet children(Component)}
54
+
<Component {matched} />
55
+
{/snippet}
56
+
</Lazy>
57
+
{:else if matched.type === 'profile'}
58
+
<Lazy
59
+
loader={profile_type === 'card' ? ProfileCardDisplay : ProfileFeedDisplay}
60
+
fallback={LazyFallback}
61
+
boundary={LazyBoundary}
62
+
>
63
+
{#snippet children(Component)}
64
+
<Component {matched} />
65
+
{/snippet}
66
+
</Lazy>
67
+
{/if}
68
+
</main>
69
+
70
+
<footer class="footer">
71
+
<span>
72
+
made with โค๏ธ by <a href="https://bsky.app/profile/did:plc:ia76kvnndjutgedggx2ibrem">@mary.my.id</a>
73
+
</span>
74
+
<span aria-hidden="true"> ยท </span>
75
+
<span>
76
+
<a href="https://github.com/mary-ext/bluesky-embed">source code</a>
77
+
</span>
78
+
<span aria-hidden="true"> ยท </span>
79
+
<span>MIT License</span>
80
+
</footer>
81
+
</div>
82
+
83
+
{#snippet LazyFallback()}
84
+
<CircularSpinner />
85
+
{/snippet}
86
+
87
+
{#snippet LazyBoundary(err: unknown)}
88
+
<Banner type="alert">
89
+
{'' + err}
90
+
</Banner>
91
+
{/snippet}
92
+
93
+
<style>
94
+
.app {
95
+
margin: 0 auto;
96
+
padding: 36px 16px;
97
+
width: 100%;
98
+
max-width: calc(550px + (16 * 2px));
99
+
}
100
+
101
+
.header {
102
+
margin: 24px 0;
103
+
}
104
+
105
+
.main {
106
+
margin: 36px 0;
107
+
}
108
+
109
+
.choices {
110
+
display: flex;
111
+
flex-direction: column;
112
+
gap: 8px;
113
+
margin: 16px 0 36px 0;
114
+
border: 0;
115
+
padding: 0;
116
+
}
117
+
.choice {
118
+
display: flex;
119
+
align-items: center;
120
+
gap: 8px;
121
+
font-size: 0.875rem;
122
+
line-height: 1.25rem;
123
+
124
+
input {
125
+
appearance: none;
126
+
outline: 2px none #2563eb;
127
+
outline-offset: 2px;
128
+
border: 1px solid #9ca3af;
129
+
border-radius: 8px;
130
+
width: 16px;
131
+
height: 16px;
132
+
133
+
&:checked {
134
+
border: 0;
135
+
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
136
+
background-color: #2563eb;
137
+
}
138
+
&:focus {
139
+
outline-style: solid;
140
+
}
141
+
}
142
+
}
143
+
144
+
.footer {
145
+
display: flex;
146
+
flex-wrap: wrap;
147
+
gap: 0.5rem;
148
+
margin: 36px 0 0 0;
149
+
border-top: 1px solid #d1d5db;
150
+
padding: 36px 0 0 0;
151
+
color: #4b5563;
152
+
font-size: 0.875rem;
153
+
line-height: 1.25rem;
154
+
155
+
a {
156
+
color: #2563eb;
157
+
}
158
+
}
159
+
</style>
+42
packages/svelte-site/src/components/Banner.svelte
+42
packages/svelte-site/src/components/Banner.svelte
···
1
+
<script lang="ts">
2
+
import type { Snippet } from 'svelte';
3
+
4
+
interface Props {
5
+
type: 'alert' | 'inform';
6
+
children: Snippet<[]>;
7
+
}
8
+
9
+
let { type, children }: Props = $props();
10
+
</script>
11
+
12
+
<div class="banner" class:type-alert={type === 'alert'} class:type-inform={type === 'inform'}>
13
+
{@render children()}
14
+
</div>
15
+
16
+
<style>
17
+
.banner {
18
+
border: 1px solid;
19
+
border-radius: 4px;
20
+
padding: 10px 12px;
21
+
font-weight: 500;
22
+
font-size: 0.875rem;
23
+
line-height: 1.25rem;
24
+
25
+
:global(a) {
26
+
color: inherit;
27
+
font-weight: 600;
28
+
}
29
+
}
30
+
31
+
.type-alert {
32
+
border-color: #fca5a5;
33
+
background: #fee2e2;
34
+
color: #991b1b;
35
+
}
36
+
37
+
.type-inform {
38
+
border-color: #bfdbfe;
39
+
background: #dbeafe;
40
+
color: #1e40af;
41
+
}
42
+
</style>
+42
packages/svelte-site/src/components/CircularSpinner.svelte
+42
packages/svelte-site/src/components/CircularSpinner.svelte
···
1
+
<script lang="ts">
2
+
interface Props {}
3
+
4
+
let {}: Props = $props();
5
+
</script>
6
+
7
+
<svg viewBox="0 0 32 32" class="circular-spinner">
8
+
<circle cx="16" cy="16" fill="none" r="14" stroke-width="4" class="background" />
9
+
<circle
10
+
cx="16"
11
+
cy="16"
12
+
fill="none"
13
+
r="14"
14
+
stroke-width="4"
15
+
stroke-dasharray="80px"
16
+
stroke-dashoffset="60px"
17
+
class="accented"
18
+
/>
19
+
</svg>
20
+
21
+
<style>
22
+
.circular-spinner {
23
+
display: block;
24
+
animation: spin 1s linear infinite;
25
+
margin: 0 auto;
26
+
width: 24px;
27
+
height: 24px;
28
+
}
29
+
@keyframes spin {
30
+
to {
31
+
transform: rotate(360deg);
32
+
}
33
+
}
34
+
35
+
.accented {
36
+
stroke: #2563eb;
37
+
}
38
+
.background {
39
+
opacity: 20%;
40
+
stroke: #2563eb;
41
+
}
42
+
</style>
+100
packages/svelte-site/src/components/CodeBlock.svelte
+100
packages/svelte-site/src/components/CodeBlock.svelte
···
1
+
<script lang="ts">
2
+
interface Props {
3
+
code: string;
4
+
}
5
+
6
+
let { code }: Props = $props();
7
+
</script>
8
+
9
+
<div class="code-block">
10
+
<pre><code>{code}</code></pre>
11
+
12
+
<div class="actions">
13
+
<button
14
+
title="Copy"
15
+
aria-label="Copy"
16
+
class="action-button"
17
+
onclick={() => {
18
+
navigator.clipboard.writeText(code).catch(() => alert(`Failed to copy to clipboard`));
19
+
}}
20
+
>
21
+
<svg class="icon" fill="none" viewBox="0 0 24 24">
22
+
<path
23
+
stroke="currentColor"
24
+
stroke-linecap="square"
25
+
stroke-width="2"
26
+
d="M15 5h4v16H5V5h4m0-2h6v4H9V3Z"
27
+
/>
28
+
</svg>
29
+
</button>
30
+
</div>
31
+
</div>
32
+
33
+
<style>
34
+
.code-block {
35
+
display: flex;
36
+
gap: 12px;
37
+
border: 1px solid #d1d5db;
38
+
border-radius: 4px;
39
+
background: #f9fafb;
40
+
padding: 12px;
41
+
overflow: hidden;
42
+
overflow-x: auto;
43
+
44
+
pre {
45
+
flex-grow: 1;
46
+
margin: 0;
47
+
font-size: 0.75rem;
48
+
line-height: 1.25rem;
49
+
}
50
+
}
51
+
52
+
.actions {
53
+
position: sticky;
54
+
top: 0;
55
+
right: 0;
56
+
}
57
+
.action-button {
58
+
display: flex;
59
+
justify-content: center;
60
+
align-items: center;
61
+
cursor: pointer;
62
+
box-shadow:
63
+
0 1px 3px 0 rgb(0 0 0 / 0.1),
64
+
0 1px 2px -1px rgb(0 0 0 / 0.1);
65
+
border: 1px solid #d1d5db;
66
+
border-radius: 4px;
67
+
background: #ffffff;
68
+
padding: 0;
69
+
width: 32px;
70
+
height: 32px;
71
+
color: #4b5563;
72
+
73
+
@media (pointer: fine) {
74
+
opacity: 0;
75
+
transition: 75ms ease-in;
76
+
77
+
.code-block:hover &,
78
+
.code-block:focus-within & {
79
+
opacity: 1;
80
+
}
81
+
82
+
&:hover {
83
+
border-color: #9ca3af;
84
+
background: #e5e7eb;
85
+
color: #1f2937;
86
+
}
87
+
}
88
+
89
+
&:active {
90
+
border-color: #9ca3af;
91
+
background: #e5e7eb;
92
+
color: #1f2937;
93
+
}
94
+
}
95
+
96
+
.icon {
97
+
width: 16px;
98
+
height: 16px;
99
+
}
100
+
</style>
+36
packages/svelte-site/src/components/Field.svelte
+36
packages/svelte-site/src/components/Field.svelte
···
1
+
<script lang="ts">
2
+
import type { Snippet } from 'svelte';
3
+
4
+
interface Props {
5
+
label: string;
6
+
children: Snippet;
7
+
}
8
+
9
+
let { label, children }: Props = $props();
10
+
</script>
11
+
12
+
<div class="field">
13
+
<label class="input-wrapper">
14
+
<span class="label">{label}</span>
15
+
{@render children()}
16
+
</label>
17
+
</div>
18
+
19
+
<style>
20
+
.field {
21
+
display: flex;
22
+
flex-direction: column;
23
+
gap: 8px;
24
+
}
25
+
26
+
.input-wrapper {
27
+
display: contents;
28
+
}
29
+
30
+
.label {
31
+
color: #4b5563;
32
+
font-weight: 600;
33
+
font-size: 0.875rem;
34
+
line-height: 1.25rem;
35
+
}
36
+
</style>
+37
packages/svelte-site/src/components/Lazy.svelte
+37
packages/svelte-site/src/components/Lazy.svelte
···
1
+
<script lang="ts" module>
2
+
import type { Snippet, Component } from 'svelte';
3
+
4
+
type SvelteComponentModule<C extends Component = Component> = { default: C };
5
+
type LoaderFunction<C extends Component = Component> = () => Promise<SvelteComponentModule<C>>;
6
+
7
+
const map = new WeakMap<LoaderFunction, Promise<Component>>();
8
+
const get_promise = <C extends Component>(fn: LoaderFunction<C>): Promise<C> => {
9
+
let promise = map.get(fn) satisfies Promise<Component> | undefined;
10
+
if (promise === undefined) {
11
+
map.set(fn, (promise = fn().then((mod) => mod.default)));
12
+
}
13
+
14
+
return promise as Promise<C>;
15
+
};
16
+
</script>
17
+
18
+
<script lang="ts" generics="C extends Component<any>">
19
+
interface Props {
20
+
loader: LoaderFunction<C>;
21
+
children: Snippet<[component: C]>;
22
+
fallback: Snippet<[]>;
23
+
boundary: Snippet<[error: unknown]>;
24
+
}
25
+
26
+
let { loader, children, fallback, boundary }: Props = $props();
27
+
28
+
const promise = $derived(get_promise(loader));
29
+
</script>
30
+
31
+
{#await promise}
32
+
{@render fallback()}
33
+
{:then component}
34
+
{@render children(component)}
35
+
{:catch err}
36
+
{@render boundary(err)}
37
+
{/await}
+30
packages/svelte-site/src/components/TextInput.svelte
+30
packages/svelte-site/src/components/TextInput.svelte
···
1
+
<script lang="ts">
2
+
interface Props {
3
+
type?: 'text' | 'url';
4
+
value?: string;
5
+
placeholder?: string;
6
+
}
7
+
8
+
let { type, value = $bindable(), placeholder }: Props = $props();
9
+
</script>
10
+
11
+
<input {type} {placeholder} bind:value class="text-input" />
12
+
13
+
<style>
14
+
.text-input {
15
+
outline: 2px none #2563eb;
16
+
outline-offset: -1px;
17
+
border: 1px solid #9ca3af;
18
+
border-radius: 4px;
19
+
padding: 8px 12px;
20
+
font-size: 0.875rem;
21
+
line-height: 1.25rem;
22
+
23
+
&::placeholder {
24
+
color: #9ca3af;
25
+
}
26
+
&:focus {
27
+
outline-style: solid;
28
+
}
29
+
}
30
+
</style>
+115
packages/svelte-site/src/components/display/PostDisplay.svelte
+115
packages/svelte-site/src/components/display/PostDisplay.svelte
···
1
+
<script lang="ts">
2
+
import { onDestroy } from 'svelte';
3
+
4
+
import type { AppBskyFeedDefs, AppBskyFeedPost } from '@atcute/client/lexicons';
5
+
import { fetchPost as fetch_post } from 'bluesky-post-embed/core';
6
+
7
+
import { getPostUrl } from 'internal/utils/bsky-url.ts';
8
+
import { formatLongDate } from 'internal/utils/date.ts';
9
+
import { parseAtUri } from 'internal/utils/syntax/at-url.ts';
10
+
11
+
import { escape_html } from '../../lib/html';
12
+
import type { ExtractedPostInfo } from '../../lib/matcher';
13
+
14
+
import Banner from '../Banner.svelte';
15
+
import CircularSpinner from '../CircularSpinner.svelte';
16
+
import CodeBlock from '../CodeBlock.svelte';
17
+
import BlueskyPost from '../embeds/BlueskyPost.svelte';
18
+
import Guide from '../guides/Guide.svelte';
19
+
import GuideInstructions from '../guides/GuideInstructions.svelte';
20
+
21
+
interface Props {
22
+
matched: ExtractedPostInfo;
23
+
}
24
+
25
+
let { matched }: Props = $props();
26
+
27
+
let controller: AbortController | undefined;
28
+
const promise = $derived.by(() => {
29
+
controller?.abort();
30
+
controller = new AbortController();
31
+
32
+
const signal = controller.signal;
33
+
const uri = `at://${matched.author}/app.bsky.feed.post/${matched.rkey}`;
34
+
35
+
return fetch_post({ uri, signal });
36
+
});
37
+
38
+
onDestroy(() => {
39
+
controller?.abort();
40
+
});
41
+
42
+
const JSDELIVR_URL = `https://cdn.jsdelivr.net/npm/bluesky-post-embed@^1.0.0`;
43
+
const get_prerequisite_markup = () => {
44
+
return `<!-- Core web component and styling -->
45
+
<script type="module" src="${JSDELIVR_URL}/+esm"></${'script'}>
46
+
<link rel="stylesheet" href="${JSDELIVR_URL}/dist/core.min.css">
47
+
48
+
<!-- Built-in themes -->
49
+
<link rel="stylesheet" href="${JSDELIVR_URL}/themes/light.min.css" media="(prefers-color-scheme: light)">
50
+
<link rel="stylesheet" href="${JSDELIVR_URL}/themes/dim.min.css" media="(prefers-color-scheme: dark)">
51
+
52
+
<!-- Fallback/placeholder elements if JS script is taking a while to load or is failing -->
53
+
<style>
54
+
.bluesky-post-fallback {
55
+
margin: 16px 0;
56
+
border-left: 3px solid var(--divider);
57
+
padding: 4px 8px;
58
+
white-space: pre-wrap;
59
+
overflow-wrap: break-word;
60
+
}
61
+
.bluesky-post-fallback p {
62
+
margin: 0 0 8px 0;
63
+
}
64
+
</${'style'}>
65
+
`;
66
+
};
67
+
68
+
const get_markup = (post: AppBskyFeedDefs.PostView) => {
69
+
const author = post.author;
70
+
const record = post.record as AppBskyFeedPost.Record;
71
+
72
+
return `<bluesky-post src="${escape_html(post.uri)}">
73
+
<blockquote class="bluesky-post-fallback">
74
+
<p dir="auto">${escape_html(record.text)}</p>
75
+
โ ${author.displayName?.trim() ? `${escape_html(author.displayName)} (@${escape_html(author.handle)})` : `@${escape_html(author.handle)}`}
76
+
<a href="${escape_html(getPostUrl(author.did, parseAtUri(post.uri).rkey))}">${formatLongDate(post.indexedAt)}</a>
77
+
</blockquote>
78
+
</bluesky-post>
79
+
`;
80
+
};
81
+
</script>
82
+
83
+
{#await promise}
84
+
<CircularSpinner />
85
+
{:then data}
86
+
<BlueskyPost {data} />
87
+
88
+
{#if data.thread}
89
+
<Guide title="How do I embed this to my website?">
90
+
<Banner type="inform">
91
+
Doing server-side rendering? Check out examples for
92
+
<a href="https://github.com/mary-ext/bluesky-embed-astro">Astro</a> and
93
+
<a href="https://github.com/mary-ext/bluesky-embed-sveltekit">SvelteKit</a>.
94
+
</Banner>
95
+
96
+
<GuideInstructions>
97
+
<li>
98
+
<p>
99
+
Insert the following scripts and stylesheets to the <code><head></code> of your website.
100
+
</p>
101
+
<CodeBlock code={get_prerequisite_markup()} />
102
+
</li>
103
+
104
+
<li>
105
+
<p>Insert the following markup in wherever you want the post to be.</p>
106
+
<CodeBlock code={get_markup(data.thread.post)} />
107
+
</li>
108
+
</GuideInstructions>
109
+
</Guide>
110
+
{/if}
111
+
{:catch err}
112
+
<Banner type="alert">
113
+
{'' + err}
114
+
</Banner>
115
+
{/await}
+93
packages/svelte-site/src/components/display/ProfileCardDisplay.svelte
+93
packages/svelte-site/src/components/display/ProfileCardDisplay.svelte
···
1
+
<script lang="ts">
2
+
import { onDestroy } from 'svelte';
3
+
4
+
import type { AppBskyActorDefs } from '@atcute/client/lexicons';
5
+
import { fetchProfileCard as fetch_profile_card } from 'bluesky-profile-card-embed/core';
6
+
7
+
import { escape_html } from '../../lib/html';
8
+
import type { ExtractedProfileInfo } from '../../lib/matcher';
9
+
10
+
import Banner from '../Banner.svelte';
11
+
import CircularSpinner from '../CircularSpinner.svelte';
12
+
import CodeBlock from '../CodeBlock.svelte';
13
+
import BlueskyProfileCard from '../embeds/BlueskyProfileCard.svelte';
14
+
import Guide from '../guides/Guide.svelte';
15
+
import GuideInstructions from '../guides/GuideInstructions.svelte';
16
+
17
+
interface Props {
18
+
matched: ExtractedProfileInfo;
19
+
}
20
+
21
+
let { matched }: Props = $props();
22
+
23
+
let controller: AbortController | undefined;
24
+
const promise = $derived.by(() => {
25
+
controller?.abort();
26
+
controller = new AbortController();
27
+
28
+
const signal = controller.signal;
29
+
const actor = matched.actor;
30
+
31
+
return fetch_profile_card({ actor, signal });
32
+
});
33
+
34
+
onDestroy(() => {
35
+
controller?.abort();
36
+
});
37
+
38
+
const get_prerequisite_markup = () => {
39
+
const JSDELIVR_URL = `https://cdn.jsdelivr.net/npm/bluesky-profile-card-embed@^1.0.0`;
40
+
41
+
return `<!-- Core web component and styling -->
42
+
<script type="module" src="${JSDELIVR_URL}/+esm"></${'script'}>
43
+
<link rel="stylesheet" href="${JSDELIVR_URL}/dist/core.min.css">
44
+
45
+
<!-- Built-in themes -->
46
+
<link rel="stylesheet" href="${JSDELIVR_URL}/themes/light.min.css" media="(prefers-color-scheme: light)">
47
+
<link rel="stylesheet" href="${JSDELIVR_URL}/themes/dim.min.css" media="(prefers-color-scheme: dark)">
48
+
`;
49
+
};
50
+
51
+
const get_markup = (profile: AppBskyActorDefs.ProfileViewDetailed) => {
52
+
const url = `https://bsky.app/profile/${profile.did}`;
53
+
54
+
return `<bluesky-profile-card actor="${escape_html(profile.did)}">
55
+
<a target="_blank" href="${escape_html(url)}" class="bluesky-profile-card-fallback">
56
+
${
57
+
profile.displayName?.trim()
58
+
? `Follow ${escape_html(profile.displayName)} (@${escape_html(profile.handle)}) on Bluesky`
59
+
: `Follow @${escape_html(profile.handle)} on Bluesky`
60
+
}
61
+
</a>
62
+
</bluesky-profile-card>
63
+
`;
64
+
};
65
+
</script>
66
+
67
+
{#await promise}
68
+
<CircularSpinner />
69
+
{:then data}
70
+
<BlueskyProfileCard {data} />
71
+
72
+
{#if data.profile}
73
+
<Guide title="How do I embed this to my website?">
74
+
<GuideInstructions>
75
+
<li>
76
+
<p>
77
+
Insert the following scripts and stylesheets to the <code><head></code> of your website.
78
+
</p>
79
+
<CodeBlock code={get_prerequisite_markup()} />
80
+
</li>
81
+
82
+
<li>
83
+
<p>Insert the following markup in wherever you want the profile feed to be.</p>
84
+
<CodeBlock code={get_markup(data.profile)} />
85
+
</li>
86
+
</GuideInstructions>
87
+
</Guide>
88
+
{/if}
89
+
{:catch err}
90
+
<Banner type="alert">
91
+
{'' + err}
92
+
</Banner>
93
+
{/await}
+93
packages/svelte-site/src/components/display/ProfileFeedDisplay.svelte
+93
packages/svelte-site/src/components/display/ProfileFeedDisplay.svelte
···
1
+
<script lang="ts">
2
+
import { onDestroy } from 'svelte';
3
+
4
+
import type { AppBskyActorDefs } from '@atcute/client/lexicons';
5
+
import { fetchProfileFeed as fetch_profile_feed } from 'bluesky-profile-feed-embed/core';
6
+
7
+
import { escape_html } from '../../lib/html';
8
+
import type { ExtractedProfileInfo } from '../../lib/matcher';
9
+
10
+
import Banner from '../Banner.svelte';
11
+
import CircularSpinner from '../CircularSpinner.svelte';
12
+
import CodeBlock from '../CodeBlock.svelte';
13
+
import BlueskyProfileFeed from '../embeds/BlueskyProfileFeed.svelte';
14
+
import Guide from '../guides/Guide.svelte';
15
+
import GuideInstructions from '../guides/GuideInstructions.svelte';
16
+
17
+
interface Props {
18
+
matched: ExtractedProfileInfo;
19
+
}
20
+
21
+
let { matched }: Props = $props();
22
+
23
+
let controller: AbortController | undefined;
24
+
const promise = $derived.by(() => {
25
+
controller?.abort();
26
+
controller = new AbortController();
27
+
28
+
const signal = controller.signal;
29
+
const actor = matched.actor;
30
+
31
+
return fetch_profile_feed({ actor, signal });
32
+
});
33
+
34
+
onDestroy(() => {
35
+
controller?.abort();
36
+
});
37
+
38
+
const get_prerequisite_markup = () => {
39
+
const JSDELIVR_URL = `https://cdn.jsdelivr.net/npm/bluesky-profile-feed-embed@^1.0.0`;
40
+
41
+
return `<!-- Core web component and styling -->
42
+
<script type="module" src="${JSDELIVR_URL}/+esm"></${'script'}>
43
+
<link rel="stylesheet" href="${JSDELIVR_URL}/dist/core.min.css">
44
+
45
+
<!-- Built-in themes -->
46
+
<link rel="stylesheet" href="${JSDELIVR_URL}/themes/light.min.css" media="(prefers-color-scheme: light)">
47
+
<link rel="stylesheet" href="${JSDELIVR_URL}/themes/dim.min.css" media="(prefers-color-scheme: dark)">
48
+
`;
49
+
};
50
+
51
+
const get_markup = (profile: AppBskyActorDefs.ProfileViewDetailed) => {
52
+
const url = `https://bsky.app/profile/${profile.did}`;
53
+
54
+
return `<bluesky-profile-feed actor="${escape_html(profile.did)}" include-pins>
55
+
<a target="_blank" href="${escape_html(url)}" class="bluesky-profile-feed-fallback">
56
+
${
57
+
profile.displayName?.trim()
58
+
? `Posts by ${escape_html(profile.displayName)} (@${escape_html(profile.handle)})`
59
+
: `Posts by @${escape_html(profile.handle)}`
60
+
}
61
+
</a>
62
+
</bluesky-profile-feed>
63
+
`;
64
+
};
65
+
</script>
66
+
67
+
{#await promise}
68
+
<CircularSpinner />
69
+
{:then data}
70
+
<BlueskyProfileFeed {data} />
71
+
72
+
{#if data.profile}
73
+
<Guide title="How do I embed this to my website?">
74
+
<GuideInstructions>
75
+
<li>
76
+
<p>
77
+
Insert the following scripts and stylesheets to the <code><head></code> of your website.
78
+
</p>
79
+
<CodeBlock code={get_prerequisite_markup()} />
80
+
</li>
81
+
82
+
<li>
83
+
<p>Insert the following markup in wherever you want the profile feed to be.</p>
84
+
<CodeBlock code={get_markup(data.profile)} />
85
+
</li>
86
+
</GuideInstructions>
87
+
</Guide>
88
+
{/if}
89
+
{:catch err}
90
+
<Banner type="alert">
91
+
{'' + err}
92
+
</Banner>
93
+
{/await}
+12
packages/svelte-site/src/components/embeds/BlueskyPost.svelte
+12
packages/svelte-site/src/components/embeds/BlueskyPost.svelte
···
1
+
<script lang="ts">
2
+
import { type PostData, renderPost } from 'bluesky-post-embed/core';
3
+
import 'bluesky-post-embed/style.css';
4
+
5
+
interface Props {
6
+
data: PostData;
7
+
}
8
+
9
+
let { data }: Props = $props();
10
+
</script>
11
+
12
+
<bluesky-post src={data.thread?.post.uri}>{@html renderPost(data)}</bluesky-post>
+12
packages/svelte-site/src/components/embeds/BlueskyProfileCard.svelte
+12
packages/svelte-site/src/components/embeds/BlueskyProfileCard.svelte
···
1
+
<script lang="ts">
2
+
import { type ProfileCardData, renderProfileCard } from 'bluesky-profile-card-embed/core';
3
+
import 'bluesky-profile-card-embed/style.css';
4
+
5
+
interface Props {
6
+
data: ProfileCardData;
7
+
}
8
+
9
+
let { data }: Props = $props();
10
+
</script>
11
+
12
+
<bluesky-profile-card actor={data.profile?.did}>{@html renderProfileCard(data)}</bluesky-profile-card>
+12
packages/svelte-site/src/components/embeds/BlueskyProfileFeed.svelte
+12
packages/svelte-site/src/components/embeds/BlueskyProfileFeed.svelte
···
1
+
<script lang="ts">
2
+
import { type ProfileFeedData, renderProfileFeed } from 'bluesky-profile-feed-embed/core';
3
+
import 'bluesky-profile-feed-embed/style.css';
4
+
5
+
interface Props {
6
+
data: ProfileFeedData;
7
+
}
8
+
9
+
let { data }: Props = $props();
10
+
</script>
11
+
12
+
<bluesky-profile-feed actor={data.profile?.did}>{@html renderProfileFeed(data)}</bluesky-profile-feed>
+26
packages/svelte-site/src/components/guides/Guide.svelte
+26
packages/svelte-site/src/components/guides/Guide.svelte
···
1
+
<script lang="ts">
2
+
import type { Snippet } from 'svelte';
3
+
4
+
interface Props {
5
+
title: string;
6
+
children: Snippet<[]>;
7
+
}
8
+
9
+
let { title, children }: Props = $props();
10
+
</script>
11
+
12
+
<div class="guide">
13
+
<h4 class="guide-header">{title}</h4>
14
+
15
+
{@render children()}
16
+
</div>
17
+
18
+
<style>
19
+
.guide {
20
+
margin: 36px 0 0 0;
21
+
border-top: 1px solid #d1d5db;
22
+
}
23
+
.guide-header {
24
+
margin: 36px 0 16px 0;
25
+
}
26
+
</style>
+26
packages/svelte-site/src/components/guides/GuideInstructions.svelte
+26
packages/svelte-site/src/components/guides/GuideInstructions.svelte
···
1
+
<script lang="ts">
2
+
import type { Snippet } from 'svelte';
3
+
4
+
interface Props {
5
+
children: Snippet<[]>;
6
+
}
7
+
8
+
let { children }: Props = $props();
9
+
</script>
10
+
11
+
<ol class="guide-instructions">
12
+
{@render children()}
13
+
</ol>
14
+
15
+
<style>
16
+
.guide-instructions {
17
+
margin: 24px 0 0 0;
18
+
padding: 0 0 0 22px;
19
+
font-size: 0.875rem;
20
+
line-height: 1.25rem;
21
+
22
+
:global(li + li) {
23
+
margin: 24px 0 0 0;
24
+
}
25
+
}
26
+
</style>
+4
packages/svelte-site/src/lib/component.ts
+4
packages/svelte-site/src/lib/component.ts
+3
packages/svelte-site/src/lib/html.ts
+3
packages/svelte-site/src/lib/html.ts
+60
packages/svelte-site/src/lib/matcher.ts
+60
packages/svelte-site/src/lib/matcher.ts
···
1
+
import { is_at_identifier, is_tid } from './strings';
2
+
3
+
export interface ExtractedPostInfo {
4
+
type: 'post';
5
+
author: string;
6
+
rkey: string;
7
+
}
8
+
9
+
export interface ExtractedProfileInfo {
10
+
type: 'profile';
11
+
actor: string;
12
+
}
13
+
14
+
export type ExtractedInfo = ExtractedPostInfo | ExtractedProfileInfo;
15
+
16
+
export const extract_url = (str: string): ExtractedInfo | null => {
17
+
const url = safe_parse_url(str);
18
+
if (!url) {
19
+
return null;
20
+
}
21
+
22
+
let match: RegExpExecArray | null | undefined;
23
+
if (url.host === 'bsky.app' || url.host === 'staging.bsky.app' || url.host === 'main.bsky.dev') {
24
+
if ((match = /^\/profile\/([^/]+)\/post\/([^/]+)\/?$/.exec(url.pathname))) {
25
+
if (!is_at_identifier(match[1]) || !is_tid(match[2])) {
26
+
return null;
27
+
}
28
+
29
+
return { type: 'post', author: match[1], rkey: match[2] };
30
+
}
31
+
32
+
if ((match = /^\/profile\/([^/]+)\/?$/.exec(url.pathname))) {
33
+
if (!is_at_identifier(match[1])) {
34
+
return null;
35
+
}
36
+
37
+
return { type: 'profile', actor: match[1] };
38
+
}
39
+
}
40
+
41
+
return null;
42
+
};
43
+
44
+
const safe_parse_url = (str: string): URL | null => {
45
+
let url: URL | null | undefined;
46
+
if ('parse' in URL) {
47
+
url = URL.parse(str);
48
+
} else {
49
+
try {
50
+
// @ts-expect-error: `'parse' in URL` is giving truthy
51
+
url = new URL(str);
52
+
} catch {}
53
+
}
54
+
55
+
if (url && (url.protocol === 'https:' || url.protocol === 'http:')) {
56
+
return url;
57
+
}
58
+
59
+
return null;
60
+
};
+28
packages/svelte-site/src/lib/strings.ts
+28
packages/svelte-site/src/lib/strings.ts
···
1
+
export const RECORD_KEY_RE = /^(?!\.{1,2}$)[a-zA-Z0-9_~.:-]{1,512}$/;
2
+
3
+
export const is_record_key = (str: string): boolean => {
4
+
return str.length >= 1 && str.length <= 512 && RECORD_KEY_RE.test(str);
5
+
};
6
+
7
+
export const TID_RE = /^[234567abcdefghij][234567abcdefghijklmnopqrstuvwxyz]{12}$/;
8
+
9
+
export const is_tid = (str: string): boolean => {
10
+
return str.length === 13 && TID_RE.test(str);
11
+
};
12
+
13
+
export const DID_RE = /^did:([a-z]+):([a-zA-Z0-9._:%-]*[a-zA-Z0-9._-])$/;
14
+
15
+
export const is_did = (str: string): boolean => {
16
+
return str.length >= 7 && str.length <= 2048 && DID_RE.test(str);
17
+
};
18
+
19
+
export const HANDLE_RE =
20
+
/^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/;
21
+
22
+
export const is_handle = (str: string): boolean => {
23
+
return str.length >= 3 && str.length <= 253 && HANDLE_RE.test(str);
24
+
};
25
+
26
+
export const is_at_identifier = (str: string): boolean => {
27
+
return is_did(str) || is_handle(str);
28
+
};
+10
packages/svelte-site/src/main.ts
+10
packages/svelte-site/src/main.ts
+199
packages/svelte-site/src/styles/normalize.css
+199
packages/svelte-site/src/styles/normalize.css
···
1
+
/*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize */
2
+
3
+
/*
4
+
Document
5
+
========
6
+
*/
7
+
8
+
/**
9
+
Use a better box model (opinionated).
10
+
*/
11
+
12
+
*,
13
+
::before,
14
+
::after {
15
+
box-sizing: border-box;
16
+
}
17
+
18
+
html {
19
+
line-height: 1.15; /* 1. Correct the line height in all browsers. */
20
+
/* Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) */
21
+
font-family: 'Inter', 'Roboto', ui-sans-serif, sans-serif, 'Noto Color Emoji', 'Twemoji Mozilla';
22
+
-webkit-text-size-adjust: 100%; /* 2. Prevent adjustments of font size after orientation changes in iOS. */
23
+
tab-size: 4; /* 3. Use a more readable tab size (opinionated). */
24
+
}
25
+
26
+
/*
27
+
Sections
28
+
========
29
+
*/
30
+
31
+
body {
32
+
margin: 0; /* Remove the margin in all browsers. */
33
+
}
34
+
35
+
/*
36
+
Text-level semantics
37
+
====================
38
+
*/
39
+
40
+
/**
41
+
Add the correct font weight in Chrome and Safari.
42
+
*/
43
+
44
+
b,
45
+
strong {
46
+
font-weight: bolder;
47
+
}
48
+
49
+
/**
50
+
1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3)
51
+
2. Correct the odd 'em' font sizing in all browsers.
52
+
*/
53
+
54
+
code,
55
+
kbd,
56
+
samp,
57
+
pre {
58
+
font-size: 1em; /* 2 */
59
+
font-family: 'JetBrains Mono NL', ui-monospace, monospace; /* 1 */
60
+
}
61
+
62
+
/**
63
+
Add the correct font size in all browsers.
64
+
*/
65
+
66
+
small {
67
+
font-size: 80%;
68
+
}
69
+
70
+
/**
71
+
Prevent 'sub' and 'sup' elements from affecting the line height in all browsers.
72
+
*/
73
+
74
+
sub,
75
+
sup {
76
+
position: relative;
77
+
vertical-align: baseline;
78
+
font-size: 75%;
79
+
line-height: 0;
80
+
}
81
+
82
+
sub {
83
+
bottom: -0.25em;
84
+
}
85
+
86
+
sup {
87
+
top: -0.5em;
88
+
}
89
+
90
+
/*
91
+
Tabular data
92
+
============
93
+
*/
94
+
95
+
/**
96
+
Correct table border color inheritance in Chrome and Safari. (https://issues.chromium.org/issues/40615503, https://bugs.webkit.org/show_bug.cgi?id=195016)
97
+
*/
98
+
99
+
table {
100
+
border-color: currentcolor;
101
+
}
102
+
103
+
/*
104
+
Forms
105
+
=====
106
+
*/
107
+
108
+
/**
109
+
1. Change the font styles in all browsers.
110
+
2. Remove the margin in Firefox and Safari.
111
+
*/
112
+
113
+
button,
114
+
input,
115
+
optgroup,
116
+
select,
117
+
textarea {
118
+
margin: 0; /* 2 */
119
+
font-size: 100%; /* 1 */
120
+
line-height: 1.15; /* 1 */
121
+
font-family: inherit; /* 1 */
122
+
}
123
+
124
+
/**
125
+
Correct the inability to style clickable types in iOS and Safari.
126
+
*/
127
+
128
+
button,
129
+
[type='button'],
130
+
[type='reset'],
131
+
[type='submit'] {
132
+
-webkit-appearance: button;
133
+
}
134
+
135
+
/**
136
+
Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers.
137
+
*/
138
+
139
+
legend {
140
+
padding: 0;
141
+
}
142
+
143
+
/**
144
+
Add the correct vertical alignment in Chrome and Firefox.
145
+
*/
146
+
147
+
progress {
148
+
vertical-align: baseline;
149
+
}
150
+
151
+
/**
152
+
Correct the cursor style of increment and decrement buttons in Safari.
153
+
*/
154
+
155
+
::-webkit-inner-spin-button,
156
+
::-webkit-outer-spin-button {
157
+
height: auto;
158
+
}
159
+
160
+
/**
161
+
1. Correct the odd appearance in Chrome and Safari.
162
+
2. Correct the outline style in Safari.
163
+
*/
164
+
165
+
[type='search'] {
166
+
-webkit-appearance: textfield; /* 1 */
167
+
outline-offset: -2px; /* 2 */
168
+
}
169
+
170
+
/**
171
+
Remove the inner padding in Chrome and Safari on macOS.
172
+
*/
173
+
174
+
::-webkit-search-decoration {
175
+
-webkit-appearance: none;
176
+
}
177
+
178
+
/**
179
+
1. Correct the inability to style clickable types in iOS and Safari.
180
+
2. Change font properties to 'inherit' in Safari.
181
+
*/
182
+
183
+
::-webkit-file-upload-button {
184
+
-webkit-appearance: button; /* 1 */
185
+
font: inherit; /* 2 */
186
+
}
187
+
188
+
/*
189
+
Interactive
190
+
===========
191
+
*/
192
+
193
+
/*
194
+
Add the correct display in Chrome and Safari.
195
+
*/
196
+
197
+
summary {
198
+
display: list-item;
199
+
}
+2
packages/svelte-site/src/vite-env.d.ts
+2
packages/svelte-site/src/vite-env.d.ts
+7
packages/svelte-site/svelte.config.js
+7
packages/svelte-site/svelte.config.js
+21
packages/svelte-site/tsconfig.json
+21
packages/svelte-site/tsconfig.json
···
1
+
{
2
+
"extends": "@tsconfig/svelte/tsconfig.json",
3
+
"compilerOptions": {
4
+
"target": "ESNext",
5
+
"useDefineForClassFields": true,
6
+
"module": "ESNext",
7
+
"resolveJsonModule": true,
8
+
/**
9
+
* Typecheck JS in `.svelte` and `.js` files by default.
10
+
* Disable checkJs if you'd like to use dynamic types in JS.
11
+
* Note that setting allowJs false does not prevent the use
12
+
* of JS in `.svelte` files.
13
+
*/
14
+
"allowJs": true,
15
+
"checkJs": true,
16
+
"isolatedModules": true,
17
+
"moduleDetection": "force",
18
+
},
19
+
"include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"],
20
+
"references": [{ "path": "./tsconfig.node.json" }],
21
+
}
+13
packages/svelte-site/tsconfig.node.json
+13
packages/svelte-site/tsconfig.node.json
···
1
+
{
2
+
"compilerOptions": {
3
+
"composite": true,
4
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5
+
"skipLibCheck": true,
6
+
"module": "ESNext",
7
+
"moduleResolution": "bundler",
8
+
"strict": true,
9
+
"noEmit": true,
10
+
"noUncheckedSideEffectImports": true,
11
+
},
12
+
"include": ["vite.config.ts"],
13
+
}
+15
packages/svelte-site/vite.config.ts
+15
packages/svelte-site/vite.config.ts
···
1
+
import { defineConfig } from 'vite';
2
+
import { svelte } from '@sveltejs/vite-plugin-svelte';
3
+
4
+
// https://vite.dev/config/
5
+
export default defineConfig({
6
+
base: './',
7
+
build: {
8
+
target: 'esnext',
9
+
minify: 'terser',
10
+
},
11
+
esbuild: {
12
+
target: 'esnext',
13
+
},
14
+
plugins: [svelte()],
15
+
});
+1041
patches/svelte.patch
+1041
patches/svelte.patch
···
1
+
diff --git a/src/compiler/phases/3-transform/server/visitors/AwaitBlock.js b/src/compiler/phases/3-transform/server/visitors/AwaitBlock.js
2
+
index b8d2e421440a0f8e5b12c9cb55ed65ab0f5e7a8a..76bb16136e17cb010069eb7fd4d84cb600ad138c 100644
3
+
--- a/src/compiler/phases/3-transform/server/visitors/AwaitBlock.js
4
+
+++ b/src/compiler/phases/3-transform/server/visitors/AwaitBlock.js
5
+
@@ -33,5 +33,5 @@ export function AwaitBlock(node, context) {
6
+
);
7
+
}
8
+
9
+
- context.state.template.push(statement, block_close);
10
+
+ context.state.template.push(statement);
11
+
}
12
+
diff --git a/src/compiler/phases/3-transform/server/visitors/EachBlock.js b/src/compiler/phases/3-transform/server/visitors/EachBlock.js
13
+
index 3c0a8c167696960efa186179da60c47eca0db2e2..8e96d467f6f20af90226ddb16db383e4e3be70ed 100644
14
+
--- a/src/compiler/phases/3-transform/server/visitors/EachBlock.js
15
+
+++ b/src/compiler/phases/3-transform/server/visitors/EachBlock.js
16
+
@@ -47,21 +47,16 @@ export function EachBlock(node, context) {
17
+
);
18
+
19
+
if (node.fallback) {
20
+
- const open = b.stmt(b.call(b.id('$$renderer.push'), block_open));
21
+
-
22
+
const fallback = /** @type {BlockStatement} */ (context.visit(node.fallback));
23
+
24
+
- fallback.body.unshift(b.stmt(b.call(b.id('$$renderer.push'), block_open_else)));
25
+
-
26
+
block.body.push(
27
+
b.if(
28
+
b.binary('!==', b.member(array_id, 'length'), b.literal(0)),
29
+
- b.block([open, for_loop]),
30
+
+ b.block([for_loop]),
31
+
fallback
32
+
)
33
+
);
34
+
} else {
35
+
- state.template.push(block_open);
36
+
block.body.push(for_loop);
37
+
}
38
+
39
+
@@ -71,10 +66,9 @@ export function EachBlock(node, context) {
40
+
block,
41
+
node.metadata.expression.blockers(),
42
+
node.metadata.expression.has_await
43
+
- ),
44
+
- block_close
45
+
+ )
46
+
);
47
+
} else {
48
+
- state.template.push(...block.body, block_close);
49
+
+ state.template.push(...block.body);
50
+
}
51
+
}
52
+
diff --git a/src/compiler/phases/3-transform/server/visitors/Fragment.js b/src/compiler/phases/3-transform/server/visitors/Fragment.js
53
+
index ef5bd985ae5d6bcae838a58f68145fd070b73179..bdc556ee8547d4bfccdee0e6c10260a6f9769fba 100644
54
+
--- a/src/compiler/phases/3-transform/server/visitors/Fragment.js
55
+
+++ b/src/compiler/phases/3-transform/server/visitors/Fragment.js
56
+
@@ -36,11 +36,6 @@ export function Fragment(node, context) {
57
+
context.visit(node, state);
58
+
}
59
+
60
+
- if (is_text_first) {
61
+
- // insert `<!---->` to prevent this from being glued to the previous fragment
62
+
- state.template.push(empty_comment);
63
+
- }
64
+
-
65
+
process_children(trimmed, { ...context, state });
66
+
67
+
if (state.async_consts && state.async_consts.thunks.length > 0) {
68
+
diff --git a/src/compiler/phases/3-transform/server/visitors/IfBlock.js b/src/compiler/phases/3-transform/server/visitors/IfBlock.js
69
+
index e8418343be9b2fcba39add8762f5139e37cc7e11..3fccb04a180be2e499c2d71e3b6ac250bc7c30b6 100644
70
+
--- a/src/compiler/phases/3-transform/server/visitors/IfBlock.js
71
+
+++ b/src/compiler/phases/3-transform/server/visitors/IfBlock.js
72
+
@@ -16,10 +16,6 @@ export function IfBlock(node, context) {
73
+
? /** @type {BlockStatement} */ (context.visit(node.alternate))
74
+
: b.block([]);
75
+
76
+
- consequent.body.unshift(b.stmt(b.call(b.id('$$renderer.push'), block_open)));
77
+
-
78
+
- alternate.body.unshift(b.stmt(b.call(b.id('$$renderer.push'), block_open_else)));
79
+
-
80
+
/** @type {Statement} */
81
+
let statement = b.if(test, consequent, alternate);
82
+
83
+
@@ -35,5 +31,5 @@ export function IfBlock(node, context) {
84
+
);
85
+
}
86
+
87
+
- context.state.template.push(statement, block_close);
88
+
+ context.state.template.push(statement);
89
+
}
90
+
diff --git a/src/compiler/phases/3-transform/server/visitors/KeyBlock.js b/src/compiler/phases/3-transform/server/visitors/KeyBlock.js
91
+
index 1396aa8fada3c5ccbc469a9983af646765bd35e2..02906b5eda4b4a7c2d90243a9d2e28bca606f4d9 100644
92
+
--- a/src/compiler/phases/3-transform/server/visitors/KeyBlock.js
93
+
+++ b/src/compiler/phases/3-transform/server/visitors/KeyBlock.js
94
+
@@ -8,15 +8,7 @@ import { block_close, block_open, empty_comment } from './shared/utils.js';
95
+
* @param {ComponentContext} context
96
+
*/
97
+
export function KeyBlock(node, context) {
98
+
- const is_async = node.metadata.expression.is_async();
99
+
-
100
+
- if (is_async) context.state.template.push(block_open);
101
+
-
102
+
context.state.template.push(
103
+
- empty_comment,
104
+
/** @type {BlockStatement} */ (context.visit(node.fragment)),
105
+
- empty_comment
106
+
);
107
+
-
108
+
- if (is_async) context.state.template.push(block_close);
109
+
}
110
+
diff --git a/src/compiler/phases/3-transform/server/visitors/RenderTag.js b/src/compiler/phases/3-transform/server/visitors/RenderTag.js
111
+
index 6d7cef0d95a943d6251101289a821537b259162e..9b4576a8e571aed73046e84ebfa47c276eef7439 100644
112
+
--- a/src/compiler/phases/3-transform/server/visitors/RenderTag.js
113
+
+++ b/src/compiler/phases/3-transform/server/visitors/RenderTag.js
114
+
@@ -44,8 +44,4 @@ export function RenderTag(node, context) {
115
+
}
116
+
117
+
context.state.template.push(statement);
118
+
-
119
+
- if (!context.state.skip_hydration_boundaries) {
120
+
- context.state.template.push(empty_comment);
121
+
- }
122
+
}
123
+
diff --git a/src/compiler/phases/3-transform/server/visitors/SlotElement.js b/src/compiler/phases/3-transform/server/visitors/SlotElement.js
124
+
index d0f8e25d021a70589c6fd191139438a1a95bc45c..4455b964948500ab0f8e8468e7f7fd74139f1d1c 100644
125
+
--- a/src/compiler/phases/3-transform/server/visitors/SlotElement.js
126
+
+++ b/src/compiler/phases/3-transform/server/visitors/SlotElement.js
127
+
@@ -73,5 +73,5 @@ export function SlotElement(node, context) {
128
+
)
129
+
: b.stmt(slot);
130
+
131
+
- context.state.template.push(block_open, statement, block_close);
132
+
+ context.state.template.push(statement);
133
+
}
134
+
diff --git a/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js b/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js
135
+
index 8a30e765c23008f35ba7a5e43d3c0905f309f555..b5f66aea25087751a864ce515f5f021794ce3d01 100644
136
+
--- a/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js
137
+
+++ b/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js
138
+
@@ -45,8 +45,8 @@ export function SvelteBoundary(node, context) {
139
+
context.state.template.push(
140
+
b.if(
141
+
callee,
142
+
- b.block(build_template([block_open_else, b.stmt(pending), block_close])),
143
+
- b.block(build_template([block_open, block, block_close]))
144
+
+ b.block(build_template([b.stmt(pending)])),
145
+
+ b.block(build_template([block]))
146
+
)
147
+
);
148
+
} else {
149
+
@@ -62,10 +62,10 @@ export function SvelteBoundary(node, context) {
150
+
b.id('$$renderer')
151
+
)
152
+
: /** @type {BlockStatement} */ (context.visit(pending_snippet.body));
153
+
- context.state.template.push(block_open_else, pending, block_close);
154
+
+ context.state.template.push(pending);
155
+
}
156
+
} else {
157
+
const block = /** @type {BlockStatement} */ (context.visit(node.fragment));
158
+
- context.state.template.push(block_open, block, block_close);
159
+
+ context.state.template.push(block);
160
+
}
161
+
}
162
+
diff --git a/src/compiler/phases/3-transform/server/visitors/shared/component.js b/src/compiler/phases/3-transform/server/visitors/shared/component.js
163
+
index a90b5e41dfb2e746a12c2dbd5f149c9fbcac5d54..846edda2d005f1b1abbfa64029ae90e12f2a2acc 100644
164
+
--- a/src/compiler/phases/3-transform/server/visitors/shared/component.js
165
+
+++ b/src/compiler/phases/3-transform/server/visitors/shared/component.js
166
+
@@ -335,26 +335,13 @@ export function build_inline_component(node, expression, context) {
167
+
statement = create_async_block(
168
+
b.block([
169
+
optimiser.apply(),
170
+
- dynamic && custom_css_props.length === 0
171
+
- ? b.stmt(b.call('$$renderer.push', empty_comment))
172
+
- : b.empty,
173
+
+ b.empty,
174
+
statement
175
+
]),
176
+
optimiser.blockers(),
177
+
optimiser.has_await
178
+
);
179
+
- } else if (dynamic && custom_css_props.length === 0) {
180
+
- context.state.template.push(empty_comment);
181
+
}
182
+
183
+
context.state.template.push(statement);
184
+
-
185
+
- if (
186
+
- !is_async &&
187
+
- !context.state.skip_hydration_boundaries &&
188
+
- custom_css_props.length === 0 &&
189
+
- optimiser.expressions.length === 0
190
+
- ) {
191
+
- context.state.template.push(empty_comment);
192
+
- }
193
+
}
194
+
diff --git a/src/compiler/phases/3-transform/server/visitors/shared/utils.js b/src/compiler/phases/3-transform/server/visitors/shared/utils.js
195
+
index 4736a7c5daf27c6c2dd7c8f0e5136d668f178a95..b2e94a6f257614ed9e2e85f3ff77fa4ed9e3debd 100644
196
+
--- a/src/compiler/phases/3-transform/server/visitors/shared/utils.js
197
+
+++ b/src/compiler/phases/3-transform/server/visitors/shared/utils.js
198
+
@@ -116,6 +116,12 @@ export function build_template(template) {
199
+
const statements = [];
200
+
201
+
const flush = () => {
202
+
+ // Skip empty pushes
203
+
+ if (expressions.length === 0 && strings.every((s) => s === '')) {
204
+
+ strings = [];
205
+
+ return;
206
+
+ }
207
+
+
208
+
statements.push(
209
+
b.stmt(
210
+
b.call(
211
+
@@ -326,9 +332,11 @@ export function create_push(expression, metadata, needs_hydration_markers = fals
212
+
* @returns {Statement}
213
+
*/
214
+
export function call_component_renderer(body, component_fn_id) {
215
+
- return b.stmt(
216
+
- b.call('$$renderer.component', b.arrow([b.id('$$renderer')], body, false), component_fn_id)
217
+
- );
218
+
+ // Just emit the body directly - no component() wrapper needed for sync rendering
219
+
+ if (body.type === 'BlockStatement') {
220
+
+ return body;
221
+
+ }
222
+
+ return b.stmt(body);
223
+
}
224
+
225
+
/**
226
+
diff --git a/src/internal/server/index.js b/src/internal/server/index.js
227
+
index c0dbdbda14f6f6c98d47686e275f91034c1eaa6b..13b18b1625d48820f9960d473a8d0c1d07757f3f 100644
228
+
--- a/src/internal/server/index.js
229
+
+++ b/src/internal/server/index.js
230
+
@@ -17,7 +17,7 @@ import { DEV } from 'esm-env';
231
+
import { EMPTY_COMMENT, BLOCK_CLOSE, BLOCK_OPEN, BLOCK_OPEN_ELSE } from './hydration.js';
232
+
import { validate_store } from '../shared/validate.js';
233
+
import { is_boolean_attribute, is_raw_text_element, is_void } from '../../utils.js';
234
+
-import { Renderer } from './renderer.js';
235
+
+export { render } from './renderer.js';
236
+
237
+
// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
238
+
// https://infra.spec.whatwg.org/#noncharacter
239
+
@@ -51,17 +51,6 @@ export function element(renderer, tag, attributes_fn = noop, children_fn = noop)
240
+
renderer.push('<!---->');
241
+
}
242
+
243
+
-/**
244
+
- * Only available on the server and when compiling with the `server` option.
245
+
- * Takes a component and returns an object with `body` and `head` properties on it, which you can use to populate the HTML when server-rendering your app.
246
+
- * @template {Record<string, any>} Props
247
+
- * @param {Component<Props> | ComponentType<SvelteComponent<Props>>} component
248
+
- * @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string }} [options]
249
+
- * @returns {RenderOutput}
250
+
- */
251
+
-export function render(component, options = {}) {
252
+
- return Renderer.render(/** @type {Component<Props>} */ (component), options);
253
+
-}
254
+
255
+
/**
256
+
* @param {string} hash
257
+
@@ -411,12 +400,7 @@ export { await_block as await };
258
+
259
+
/** @param {any} array_like_or_iterator */
260
+
export function ensure_array_like(array_like_or_iterator) {
261
+
- if (array_like_or_iterator) {
262
+
- return array_like_or_iterator.length !== undefined
263
+
- ? array_like_or_iterator
264
+
- : Array.from(array_like_or_iterator);
265
+
- }
266
+
- return [];
267
+
+ return array_like_or_iterator;
268
+
}
269
+
270
+
/**
271
+
diff --git a/src/internal/server/renderer.js b/src/internal/server/renderer.js
272
+
index 0cfb1a7a93518729d04c26214140eaab29de4870..c36c956d1edff831a5d00f214184b882f96e0985 100644
273
+
--- a/src/internal/server/renderer.js
274
+
+++ b/src/internal/server/renderer.js
275
+
@@ -1,741 +1,18 @@
276
+
/** @import { Component } from 'svelte' */
277
+
-/** @import { HydratableContext, RenderOutput, SSRContext, SyncRenderOutput } from './types.js' */
278
+
-/** @import { MaybePromise } from '#shared' */
279
+
-import { async_mode_flag } from '../flags/index.js';
280
+
-import { abort } from './abort-signal.js';
281
+
-import { pop, push, set_ssr_context, ssr_context, save } from './context.js';
282
+
-import * as e from './errors.js';
283
+
-import * as w from './warnings.js';
284
+
-import { BLOCK_CLOSE, BLOCK_OPEN } from './hydration.js';
285
+
-import { attributes } from './index.js';
286
+
-import { get_render_context, with_render_context, init_render_context } from './render-context.js';
287
+
-import { DEV } from 'esm-env';
288
+
-
289
+
-/** @typedef {'head' | 'body'} RendererType */
290
+
-/** @typedef {{ [key in RendererType]: string }} AccumulatedContent */
291
+
-
292
+
-/**
293
+
- * @typedef {string | Renderer} RendererItem
294
+
- */
295
+
+/** @import { RenderOutput } from './types.js' */
296
+
297
+
/**
298
+
- * Renderers are basically a tree of `string | Renderer`s, where each `Renderer` in the tree represents
299
+
- * work that may or may not have completed. A renderer can be {@link collect}ed to aggregate the
300
+
- * content from itself and all of its children, but this will throw if any of the children are
301
+
- * performing asynchronous work. To asynchronously collect a renderer, just `await` it.
302
+
- *
303
+
- * The `string` values within a renderer are always associated with the {@link type} of that renderer. To switch types,
304
+
- * call {@link child} with a different `type` argument.
305
+
+ * @template {Record<string, any>} Props
306
+
+ * @param {Component<Props>} component
307
+
+ * @param {{ props?: Omit<Props, '$$slots' | '$$events'> }} [options]
308
+
+ * @returns {RenderOutput}
309
+
*/
310
+
-export class Renderer {
311
+
- /**
312
+
- * The contents of the renderer.
313
+
- * @type {RendererItem[]}
314
+
- */
315
+
- #out = [];
316
+
-
317
+
- /**
318
+
- * Any `onDestroy` callbacks registered during execution of this renderer.
319
+
- * @type {(() => void)[] | undefined}
320
+
- */
321
+
- #on_destroy = undefined;
322
+
-
323
+
- /**
324
+
- * Whether this renderer is a component body.
325
+
- * @type {boolean}
326
+
- */
327
+
- #is_component_body = false;
328
+
-
329
+
- /**
330
+
- * The type of string content that this renderer is accumulating.
331
+
- * @type {RendererType}
332
+
- */
333
+
- type;
334
+
-
335
+
- /** @type {Renderer | undefined} */
336
+
- #parent;
337
+
-
338
+
- /**
339
+
- * Asynchronous work associated with this renderer
340
+
- * @type {Promise<void> | undefined}
341
+
- */
342
+
- promise = undefined;
343
+
-
344
+
- /**
345
+
- * State which is associated with the content tree as a whole.
346
+
- * It will be re-exposed, uncopied, on all children.
347
+
- * @type {SSRState}
348
+
- * @readonly
349
+
- */
350
+
- global;
351
+
-
352
+
- /**
353
+
- * State that is local to the branch it is declared in.
354
+
- * It will be shallow-copied to all children.
355
+
- *
356
+
- * @type {{ select_value: string | undefined }}
357
+
- */
358
+
- local;
359
+
-
360
+
- /**
361
+
- * @param {SSRState} global
362
+
- * @param {Renderer | undefined} [parent]
363
+
- */
364
+
- constructor(global, parent) {
365
+
- this.#parent = parent;
366
+
-
367
+
- this.global = global;
368
+
- this.local = parent ? { ...parent.local } : { select_value: undefined };
369
+
- this.type = parent ? parent.type : 'body';
370
+
- }
371
+
-
372
+
- /**
373
+
- * @param {(renderer: Renderer) => void} fn
374
+
- */
375
+
- head(fn) {
376
+
- const head = new Renderer(this.global, this);
377
+
- head.type = 'head';
378
+
-
379
+
- this.#out.push(head);
380
+
- head.child(fn);
381
+
- }
382
+
-
383
+
- /**
384
+
- * @param {Array<Promise<void>>} blockers
385
+
- * @param {(renderer: Renderer) => void} fn
386
+
- */
387
+
- async_block(blockers, fn) {
388
+
- this.#out.push(BLOCK_OPEN);
389
+
- this.async(blockers, fn);
390
+
- this.#out.push(BLOCK_CLOSE);
391
+
- }
392
+
-
393
+
- /**
394
+
- * @param {Array<Promise<void>>} blockers
395
+
- * @param {(renderer: Renderer) => void} fn
396
+
- */
397
+
- async(blockers, fn) {
398
+
- let callback = fn;
399
+
-
400
+
- if (blockers.length > 0) {
401
+
- const context = ssr_context;
402
+
-
403
+
- callback = (renderer) => {
404
+
- return Promise.all(blockers).then(() => {
405
+
- const previous_context = ssr_context;
406
+
-
407
+
- try {
408
+
- set_ssr_context(context);
409
+
- return fn(renderer);
410
+
- } finally {
411
+
- set_ssr_context(previous_context);
412
+
- }
413
+
- });
414
+
- };
415
+
- }
416
+
-
417
+
- this.child(callback);
418
+
- }
419
+
-
420
+
- /**
421
+
- * @param {Array<() => void>} thunks
422
+
- */
423
+
- run(thunks) {
424
+
- const context = ssr_context;
425
+
-
426
+
- let promise = Promise.resolve(thunks[0]());
427
+
- const promises = [promise];
428
+
-
429
+
- for (const fn of thunks.slice(1)) {
430
+
- promise = promise.then(() => {
431
+
- const previous_context = ssr_context;
432
+
- set_ssr_context(context);
433
+
-
434
+
- try {
435
+
- return fn();
436
+
- } finally {
437
+
- set_ssr_context(previous_context);
438
+
- }
439
+
- });
440
+
-
441
+
- promises.push(promise);
442
+
- }
443
+
-
444
+
- return promises;
445
+
- }
446
+
-
447
+
- /**
448
+
- * Create a child renderer. The child renderer inherits the state from the parent,
449
+
- * but has its own content.
450
+
- * @param {(renderer: Renderer) => MaybePromise<void>} fn
451
+
- */
452
+
- child(fn) {
453
+
- const child = new Renderer(this.global, this);
454
+
- this.#out.push(child);
455
+
-
456
+
- const parent = ssr_context;
457
+
-
458
+
- set_ssr_context({
459
+
- ...ssr_context,
460
+
- p: parent,
461
+
- c: null,
462
+
- r: child
463
+
- });
464
+
-
465
+
- const result = fn(child);
466
+
-
467
+
- set_ssr_context(parent);
468
+
-
469
+
- if (result instanceof Promise) {
470
+
- if (child.global.mode === 'sync') {
471
+
- e.await_invalid();
472
+
- }
473
+
- // just to avoid unhandled promise rejections -- we'll end up throwing in `collect_async` if something fails
474
+
- result.catch(() => {});
475
+
- child.promise = result;
476
+
- }
477
+
-
478
+
- return child;
479
+
- }
480
+
-
481
+
- /**
482
+
- * Create a component renderer. The component renderer inherits the state from the parent,
483
+
- * but has its own content. It is treated as an ordering boundary for ondestroy callbacks.
484
+
- * @param {(renderer: Renderer) => MaybePromise<void>} fn
485
+
- * @param {Function} [component_fn]
486
+
- * @returns {void}
487
+
- */
488
+
- component(fn, component_fn) {
489
+
- push(component_fn);
490
+
- const child = this.child(fn);
491
+
- child.#is_component_body = true;
492
+
- pop();
493
+
- }
494
+
-
495
+
- /**
496
+
- * @param {Record<string, any>} attrs
497
+
- * @param {(renderer: Renderer) => void} fn
498
+
- * @param {string | undefined} [css_hash]
499
+
- * @param {Record<string, boolean> | undefined} [classes]
500
+
- * @param {Record<string, string> | undefined} [styles]
501
+
- * @param {number | undefined} [flags]
502
+
- * @returns {void}
503
+
- */
504
+
- select(attrs, fn, css_hash, classes, styles, flags) {
505
+
- const { value, ...select_attrs } = attrs;
506
+
-
507
+
- this.push(`<select${attributes(select_attrs, css_hash, classes, styles, flags)}>`);
508
+
- this.child((renderer) => {
509
+
- renderer.local.select_value = value;
510
+
- fn(renderer);
511
+
- });
512
+
- this.push('</select>');
513
+
- }
514
+
-
515
+
- /**
516
+
- * @param {Record<string, any>} attrs
517
+
- * @param {string | number | boolean | ((renderer: Renderer) => void)} body
518
+
- * @param {string | undefined} [css_hash]
519
+
- * @param {Record<string, boolean> | undefined} [classes]
520
+
- * @param {Record<string, string> | undefined} [styles]
521
+
- * @param {number | undefined} [flags]
522
+
- */
523
+
- option(attrs, body, css_hash, classes, styles, flags) {
524
+
- this.#out.push(`<option${attributes(attrs, css_hash, classes, styles, flags)}`);
525
+
-
526
+
- /**
527
+
- * @param {Renderer} renderer
528
+
- * @param {any} value
529
+
- * @param {{ head?: string, body: any }} content
530
+
- */
531
+
- const close = (renderer, value, { head, body }) => {
532
+
- if ('value' in attrs) {
533
+
- value = attrs.value;
534
+
- }
535
+
-
536
+
- if (value === this.local.select_value) {
537
+
- renderer.#out.push(' selected');
538
+
- }
539
+
-
540
+
- renderer.#out.push(`>${body}</option>`);
541
+
-
542
+
- // super edge case, but may as well handle it
543
+
- if (head) {
544
+
- renderer.head((child) => child.push(head));
545
+
- }
546
+
- };
547
+
-
548
+
- if (typeof body === 'function') {
549
+
- this.child((renderer) => {
550
+
- const r = new Renderer(this.global, this);
551
+
- body(r);
552
+
-
553
+
- if (this.global.mode === 'async') {
554
+
- return r.#collect_content_async().then((content) => {
555
+
- close(renderer, content.body.replaceAll('<!---->', ''), content);
556
+
- });
557
+
- } else {
558
+
- const content = r.#collect_content();
559
+
- close(renderer, content.body.replaceAll('<!---->', ''), content);
560
+
- }
561
+
- });
562
+
- } else {
563
+
- close(this, body, { body });
564
+
- }
565
+
- }
566
+
-
567
+
- /**
568
+
- * @param {(renderer: Renderer) => void} fn
569
+
- */
570
+
- title(fn) {
571
+
- const path = this.get_path();
572
+
-
573
+
- /** @param {string} head */
574
+
- const close = (head) => {
575
+
- this.global.set_title(head, path);
576
+
- };
577
+
-
578
+
- this.child((renderer) => {
579
+
- const r = new Renderer(renderer.global, renderer);
580
+
- fn(r);
581
+
-
582
+
- if (renderer.global.mode === 'async') {
583
+
- return r.#collect_content_async().then((content) => {
584
+
- close(content.head);
585
+
- });
586
+
- } else {
587
+
- const content = r.#collect_content();
588
+
- close(content.head);
589
+
- }
590
+
- });
591
+
- }
592
+
-
593
+
- /**
594
+
- * @param {string | (() => Promise<string>)} content
595
+
- */
596
+
- push(content) {
597
+
- if (typeof content === 'function') {
598
+
- this.child(async (renderer) => renderer.push(await content()));
599
+
- } else {
600
+
- this.#out.push(content);
601
+
- }
602
+
- }
603
+
-
604
+
- /**
605
+
- * @param {() => void} fn
606
+
- */
607
+
- on_destroy(fn) {
608
+
- (this.#on_destroy ??= []).push(fn);
609
+
- }
610
+
-
611
+
- /**
612
+
- * @returns {number[]}
613
+
- */
614
+
- get_path() {
615
+
- return this.#parent ? [...this.#parent.get_path(), this.#parent.#out.indexOf(this)] : [];
616
+
- }
617
+
-
618
+
- /**
619
+
- * @deprecated this is needed for legacy component bindings
620
+
- */
621
+
- copy() {
622
+
- const copy = new Renderer(this.global, this.#parent);
623
+
- copy.#out = this.#out.map((item) => (item instanceof Renderer ? item.copy() : item));
624
+
- copy.promise = this.promise;
625
+
- return copy;
626
+
- }
627
+
-
628
+
- /**
629
+
- * @param {Renderer} other
630
+
- * @deprecated this is needed for legacy component bindings
631
+
- */
632
+
- subsume(other) {
633
+
- if (this.global.mode !== other.global.mode) {
634
+
- throw new Error(
635
+
- "invariant: A renderer cannot switch modes. If you're seeing this, there's a compiler bug. File an issue!"
636
+
- );
637
+
- }
638
+
-
639
+
- this.local = other.local;
640
+
- this.#out = other.#out.map((item) => {
641
+
- if (item instanceof Renderer) {
642
+
- item.subsume(item);
643
+
- }
644
+
- return item;
645
+
- });
646
+
- this.promise = other.promise;
647
+
- this.type = other.type;
648
+
- }
649
+
-
650
+
- get length() {
651
+
- return this.#out.length;
652
+
- }
653
+
-
654
+
- /**
655
+
- * Only available on the server and when compiling with the `server` option.
656
+
- * Takes a component and returns an object with `body` and `head` properties on it, which you can use to populate the HTML when server-rendering your app.
657
+
- * @template {Record<string, any>} Props
658
+
- * @param {Component<Props>} component
659
+
- * @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string }} [options]
660
+
- * @returns {RenderOutput}
661
+
- */
662
+
- static render(component, options = {}) {
663
+
- /** @type {AccumulatedContent | undefined} */
664
+
- let sync;
665
+
- /** @type {Promise<AccumulatedContent> | undefined} */
666
+
- let async;
667
+
-
668
+
- const result = /** @type {RenderOutput} */ ({});
669
+
- // making these properties non-enumerable so that console.logging
670
+
- // doesn't trigger a sync render
671
+
- Object.defineProperties(result, {
672
+
- html: {
673
+
- get: () => {
674
+
- return (sync ??= Renderer.#render(component, options)).body;
675
+
- }
676
+
- },
677
+
- head: {
678
+
- get: () => {
679
+
- return (sync ??= Renderer.#render(component, options)).head;
680
+
- }
681
+
- },
682
+
- body: {
683
+
- get: () => {
684
+
- return (sync ??= Renderer.#render(component, options)).body;
685
+
- }
686
+
- },
687
+
- then: {
688
+
- value:
689
+
- /**
690
+
- * this is not type-safe, but honestly it's the best I can do right now, and it's a straightforward function.
691
+
- *
692
+
- * @template TResult1
693
+
- * @template [TResult2=never]
694
+
- * @param { (value: SyncRenderOutput) => TResult1 } onfulfilled
695
+
- * @param { (reason: unknown) => TResult2 } onrejected
696
+
- */
697
+
- (onfulfilled, onrejected) => {
698
+
- if (!async_mode_flag) {
699
+
- const result = (sync ??= Renderer.#render(component, options));
700
+
- const user_result = onfulfilled({
701
+
- head: result.head,
702
+
- body: result.body,
703
+
- html: result.body
704
+
- });
705
+
- return Promise.resolve(user_result);
706
+
- }
707
+
- async ??= init_render_context().then(() =>
708
+
- with_render_context(() => Renderer.#render_async(component, options))
709
+
- );
710
+
- return async.then((result) => {
711
+
- Object.defineProperty(result, 'html', {
712
+
- // eslint-disable-next-line getter-return
713
+
- get: () => {
714
+
- e.html_deprecated();
715
+
- }
716
+
- });
717
+
- return onfulfilled(/** @type {SyncRenderOutput} */ (result));
718
+
- }, onrejected);
719
+
- }
720
+
- }
721
+
- });
722
+
-
723
+
- return result;
724
+
- }
725
+
-
726
+
- /**
727
+
- * Collect all of the `onDestroy` callbacks registered during rendering. In an async context, this is only safe to call
728
+
- * after awaiting `collect_async`.
729
+
- *
730
+
- * Child renderers are "porous" and don't affect execution order, but component body renderers
731
+
- * create ordering boundaries. Within a renderer, callbacks run in order until hitting a component boundary.
732
+
- * @returns {Iterable<() => void>}
733
+
- */
734
+
- *#collect_on_destroy() {
735
+
- for (const component of this.#traverse_components()) {
736
+
- yield* component.#collect_ondestroy();
737
+
- }
738
+
- }
739
+
-
740
+
- /**
741
+
- * Performs a depth-first search of renderers, yielding the deepest components first, then additional components as we backtrack up the tree.
742
+
- * @returns {Iterable<Renderer>}
743
+
- */
744
+
- *#traverse_components() {
745
+
- for (const child of this.#out) {
746
+
- if (typeof child !== 'string') {
747
+
- yield* child.#traverse_components();
748
+
- }
749
+
- }
750
+
- if (this.#is_component_body) {
751
+
- yield this;
752
+
- }
753
+
- }
754
+
-
755
+
- /**
756
+
- * @returns {Iterable<() => void>}
757
+
- */
758
+
- *#collect_ondestroy() {
759
+
- if (this.#on_destroy) {
760
+
- for (const fn of this.#on_destroy) {
761
+
- yield fn;
762
+
- }
763
+
- }
764
+
- for (const child of this.#out) {
765
+
- if (child instanceof Renderer && !child.#is_component_body) {
766
+
- yield* child.#collect_ondestroy();
767
+
- }
768
+
- }
769
+
- }
770
+
-
771
+
- /**
772
+
- * Render a component. Throws if any of the children are performing asynchronous work.
773
+
- *
774
+
- * @template {Record<string, any>} Props
775
+
- * @param {Component<Props>} component
776
+
- * @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string }} options
777
+
- * @returns {AccumulatedContent}
778
+
- */
779
+
- static #render(component, options) {
780
+
- var previous_context = ssr_context;
781
+
- try {
782
+
- const renderer = Renderer.#open_render('sync', component, options);
783
+
-
784
+
- const content = renderer.#collect_content();
785
+
- return Renderer.#close_render(content, renderer);
786
+
- } finally {
787
+
- abort();
788
+
- set_ssr_context(previous_context);
789
+
- }
790
+
- }
791
+
-
792
+
- /**
793
+
- * Render a component.
794
+
- *
795
+
- * @template {Record<string, any>} Props
796
+
- * @param {Component<Props>} component
797
+
- * @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string }} options
798
+
- * @returns {Promise<AccumulatedContent>}
799
+
- */
800
+
- static async #render_async(component, options) {
801
+
- const previous_context = ssr_context;
802
+
-
803
+
- try {
804
+
- const renderer = Renderer.#open_render('async', component, options);
805
+
- const content = await renderer.#collect_content_async();
806
+
- const hydratables = await renderer.#collect_hydratables();
807
+
- if (hydratables !== null) {
808
+
- content.head = hydratables + content.head;
809
+
- }
810
+
- return Renderer.#close_render(content, renderer);
811
+
- } finally {
812
+
- set_ssr_context(previous_context);
813
+
- abort();
814
+
- }
815
+
- }
816
+
-
817
+
- /**
818
+
- * Collect all of the code from the `out` array and return it as a string, or a promise resolving to a string.
819
+
- * @param {AccumulatedContent} content
820
+
- * @returns {AccumulatedContent}
821
+
- */
822
+
- #collect_content(content = { head: '', body: '' }) {
823
+
- for (const item of this.#out) {
824
+
- if (typeof item === 'string') {
825
+
- content[this.type] += item;
826
+
- } else if (item instanceof Renderer) {
827
+
- item.#collect_content(content);
828
+
- }
829
+
- }
830
+
-
831
+
- return content;
832
+
- }
833
+
-
834
+
- /**
835
+
- * Collect all of the code from the `out` array and return it as a string.
836
+
- * @param {AccumulatedContent} content
837
+
- * @returns {Promise<AccumulatedContent>}
838
+
- */
839
+
- async #collect_content_async(content = { head: '', body: '' }) {
840
+
- await this.promise;
841
+
-
842
+
- // no danger to sequentially awaiting stuff in here; all of the work is already kicked off
843
+
- for (const item of this.#out) {
844
+
- if (typeof item === 'string') {
845
+
- content[this.type] += item;
846
+
- } else if (item instanceof Renderer) {
847
+
- await item.#collect_content_async(content);
848
+
- }
849
+
- }
850
+
-
851
+
- return content;
852
+
- }
853
+
-
854
+
- async #collect_hydratables() {
855
+
- const ctx = get_render_context().hydratable;
856
+
-
857
+
- for (const [_, key] of ctx.unresolved_promises) {
858
+
- // this is a problem -- it means we've finished the render but we're still waiting on a promise to resolve so we can
859
+
- // serialize it, so we're blocking the response on useless content.
860
+
- w.unresolved_hydratable(key, ctx.lookup.get(key)?.stack ?? '<missing stack trace>');
861
+
- }
862
+
-
863
+
- for (const comparison of ctx.comparisons) {
864
+
- // these reject if there's a mismatch
865
+
- await comparison;
866
+
- }
867
+
-
868
+
- return await Renderer.#hydratable_block(ctx);
869
+
- }
870
+
-
871
+
- /**
872
+
- * @template {Record<string, any>} Props
873
+
- * @param {'sync' | 'async'} mode
874
+
- * @param {import('svelte').Component<Props>} component
875
+
- * @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string }} options
876
+
- * @returns {Renderer}
877
+
- */
878
+
- static #open_render(mode, component, options) {
879
+
- const renderer = new Renderer(
880
+
- new SSRState(mode, options.idPrefix ? options.idPrefix + '-' : '')
881
+
- );
882
+
-
883
+
- renderer.push(BLOCK_OPEN);
884
+
-
885
+
- if (options.context) {
886
+
- push();
887
+
- /** @type {SSRContext} */ (ssr_context).c = options.context;
888
+
- /** @type {SSRContext} */ (ssr_context).r = renderer;
889
+
- }
890
+
-
891
+
- // @ts-expect-error
892
+
- component(renderer, options.props ?? {});
893
+
-
894
+
- if (options.context) {
895
+
- pop();
896
+
- }
897
+
-
898
+
- renderer.push(BLOCK_CLOSE);
899
+
-
900
+
- return renderer;
901
+
- }
902
+
-
903
+
- /**
904
+
- * @param {AccumulatedContent} content
905
+
- * @param {Renderer} renderer
906
+
- */
907
+
- static #close_render(content, renderer) {
908
+
- for (const cleanup of renderer.#collect_on_destroy()) {
909
+
- cleanup();
910
+
- }
911
+
-
912
+
- let head = content.head + renderer.global.get_title();
913
+
- let body = content.body;
914
+
-
915
+
- for (const { hash, code } of renderer.global.css) {
916
+
- head += `<style id="${hash}">${code}</style>`;
917
+
- }
918
+
-
919
+
- return {
920
+
- head,
921
+
- body
922
+
- };
923
+
- }
924
+
-
925
+
- /**
926
+
- * @param {HydratableContext} ctx
927
+
- */
928
+
- static async #hydratable_block(ctx) {
929
+
- if (ctx.lookup.size === 0) {
930
+
- return null;
931
+
- }
932
+
-
933
+
- let entries = [];
934
+
- let has_promises = false;
935
+
-
936
+
- for (const [k, v] of ctx.lookup) {
937
+
- if (v.promises) {
938
+
- has_promises = true;
939
+
- for (const p of v.promises) await p;
940
+
- }
941
+
-
942
+
- entries.push(`[${JSON.stringify(k)},${v.serialized}]`);
943
+
- }
944
+
-
945
+
- let prelude = `const h = (window.__svelte ??= {}).h ??= new Map();`;
946
+
-
947
+
- if (has_promises) {
948
+
- prelude = `const r = (v) => Promise.resolve(v);
949
+
- ${prelude}`;
950
+
- }
951
+
-
952
+
- // TODO csp -- have discussed but not implemented
953
+
- return `
954
+
- <script>
955
+
- {
956
+
- ${prelude}
957
+
-
958
+
- for (const [k, v] of [
959
+
- ${entries.join(',\n\t\t\t\t\t')}
960
+
- ]) {
961
+
- h.set(k, v);
962
+
- }
963
+
- }
964
+
- </script>`;
965
+
- }
966
+
+function render(component, options = {}) {
967
+
+ let out = '';
968
+
+ const renderer = { push(s) { out += s; } };
969
+
+ component(renderer, options.props ?? {});
970
+
+ return { body: out };
971
+
}
972
+
973
+
-export class SSRState {
974
+
- /** @readonly @type {'sync' | 'async'} */
975
+
- mode;
976
+
-
977
+
- /** @readonly @type {() => string} */
978
+
- uid;
979
+
-
980
+
- /** @readonly @type {Set<{ hash: string; code: string }>} */
981
+
- css = new Set();
982
+
-
983
+
- /** @type {{ path: number[], value: string }} */
984
+
- #title = { path: [], value: '' };
985
+
-
986
+
- /**
987
+
- * @param {'sync' | 'async'} mode
988
+
- * @param {string} [id_prefix]
989
+
- */
990
+
- constructor(mode, id_prefix = '') {
991
+
- this.mode = mode;
992
+
-
993
+
- let uid = 1;
994
+
- this.uid = () => `${id_prefix}s${uid++}`;
995
+
- }
996
+
-
997
+
- get_title() {
998
+
- return this.#title.value;
999
+
- }
1000
+
-
1001
+
- /**
1002
+
- * Performs a depth-first (lexicographic) comparison using the path. Rejects sets
1003
+
- * from earlier than or equal to the current value.
1004
+
- * @param {string} value
1005
+
- * @param {number[]} path
1006
+
- */
1007
+
- set_title(value, path) {
1008
+
- const current = this.#title.path;
1009
+
-
1010
+
- let i = 0;
1011
+
- let l = Math.min(path.length, current.length);
1012
+
-
1013
+
- // skip identical prefixes - [1, 2, 3, ...] === [1, 2, 3, ...]
1014
+
- while (i < l && path[i] === current[i]) i += 1;
1015
+
-
1016
+
- if (path[i] === undefined) return;
1017
+
-
1018
+
- // replace title if
1019
+
- // - incoming path is longer - [7, 8, 9] > [7, 8]
1020
+
- // - incoming path is later - [7, 8, 9] > [7, 8, 8]
1021
+
- if (current[i] === undefined || path[i] > current[i]) {
1022
+
- this.#title.path = path;
1023
+
- this.#title.value = value;
1024
+
- }
1025
+
- }
1026
+
-}
1027
+
+export { render };
1028
+
+export const Renderer = { render };
1029
+
diff --git a/src/internal/shared/attributes.js b/src/internal/shared/attributes.js
1030
+
index 4ad550e8d612e42d1e719dcbfcbce383bbbbb27f..a12fe17a0efd135bd9fd4f1b1594c059a6d12e54 100644
1031
+
--- a/src/internal/shared/attributes.js
1032
+
+++ b/src/internal/shared/attributes.js
1033
+
@@ -27,7 +27,7 @@ export function attr(name, value, is_boolean = false) {
1034
+
is_boolean = true;
1035
+
}
1036
+
if (value == null || (!value && is_boolean)) return '';
1037
+
- const normalized = (name in replacements && replacements[name].get(value)) || value;
1038
+
+ const normalized = value;
1039
+
const assignment = is_boolean ? '' : `="${escape_html(normalized, true)}"`;
1040
+
return ` ${name}${assignment}`;
1041
+
}
+2712
-1030
pnpm-lock.yaml
+2712
-1030
pnpm-lock.yaml
···
1
-
lockfileVersion: '6.0'
1
+
lockfileVersion: '9.0'
2
2
3
3
settings:
4
-
autoInstallPeers: false
4
+
autoInstallPeers: true
5
5
excludeLinksFromLockfile: false
6
6
7
-
dependencies:
8
-
'@externdefs/bluesky-client':
9
-
specifier: ^0.5.8
10
-
version: 0.5.8
7
+
catalogs:
8
+
default:
9
+
svelte:
10
+
specifier: ^5.45.5
11
+
version: 5.45.5
11
12
12
-
devDependencies:
13
-
'@babel/core':
14
-
specifier: ^7.24.4
15
-
version: 7.24.4
16
-
'@babel/plugin-syntax-typescript':
17
-
specifier: ^7.24.1
18
-
version: 7.24.1(@babel/core@7.24.4)
19
-
'@intrnl/jsx-to-string':
20
-
specifier: ^0.1.6
21
-
version: 0.1.6(@babel/core@7.24.4)
22
-
'@rollup/plugin-babel':
23
-
specifier: ^6.0.4
24
-
version: 6.0.4(@babel/core@7.24.4)
25
-
prettier:
26
-
specifier: ^3.2.5
27
-
version: 3.2.5
28
-
prettier-plugin-css-order:
29
-
specifier: ^2.1.2
30
-
version: 2.1.2(prettier@3.2.5)
31
-
typescript:
32
-
specifier: ~5.4.5
33
-
version: 5.4.5
34
-
vite:
35
-
specifier: ^5.2.8
36
-
version: 5.2.8
37
-
vite-plugin-dts:
38
-
specifier: ^3.8.1
39
-
version: 3.8.1(typescript@5.4.5)(vite@5.2.8)
13
+
patchedDependencies:
14
+
svelte:
15
+
hash: 0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd
16
+
path: patches/svelte.patch
40
17
41
-
packages:
18
+
importers:
42
19
43
-
/@ampproject/remapping@2.3.0:
44
-
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
45
-
engines: {node: '>=6.0.0'}
46
-
dependencies:
47
-
'@jridgewell/gen-mapping': 0.3.5
48
-
'@jridgewell/trace-mapping': 0.3.25
49
-
dev: true
20
+
.:
21
+
devDependencies:
22
+
'@changesets/cli':
23
+
specifier: ^2.29.8
24
+
version: 2.29.8(@types/node@24.10.1)
25
+
prettier:
26
+
specifier: ^3.7.4
27
+
version: 3.7.4
28
+
prettier-plugin-css-order:
29
+
specifier: ^2.1.2
30
+
version: 2.1.2(postcss@8.5.6)(prettier@3.7.4)
31
+
prettier-plugin-svelte:
32
+
specifier: ^3.4.0
33
+
version: 3.4.0(prettier@3.7.4)(svelte@5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd))
34
+
typescript:
35
+
specifier: ~5.8.3
36
+
version: 5.8.3
50
37
51
-
/@babel/code-frame@7.24.2:
52
-
resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==}
53
-
engines: {node: '>=6.9.0'}
38
+
packages/bluesky-post-embed:
54
39
dependencies:
55
-
'@babel/highlight': 7.24.2
56
-
picocolors: 1.0.0
57
-
dev: true
40
+
'@atcute/bluesky':
41
+
specifier: ^2.1.1
42
+
version: 2.1.1(@atcute/client@3.1.0)
43
+
'@atcute/bluesky-richtext-segmenter':
44
+
specifier: ^2.0.4
45
+
version: 2.0.4
46
+
'@atcute/client':
47
+
specifier: ^3.1.0
48
+
version: 3.1.0
49
+
devDependencies:
50
+
'@preact/preset-vite':
51
+
specifier: ^2.10.2
52
+
version: 2.10.2(@babel/core@7.28.5)(preact@10.28.0)(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1))
53
+
'@tsconfig/svelte':
54
+
specifier: ^5.0.6
55
+
version: 5.0.6
56
+
'@types/node':
57
+
specifier: ^24.10.1
58
+
version: 24.10.1
59
+
internal:
60
+
specifier: workspace:^
61
+
version: link:../internal
62
+
preact:
63
+
specifier: ^10.28.0
64
+
version: 10.28.0
65
+
svelte:
66
+
specifier: 'catalog:'
67
+
version: 5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd)
68
+
svelte-check:
69
+
specifier: ^4.3.4
70
+
version: 4.3.4(picomatch@4.0.3)(svelte@5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd))(typescript@5.8.3)
71
+
vite:
72
+
specifier: ^7.2.6
73
+
version: 7.2.6(@types/node@24.10.1)(terser@5.44.1)
74
+
vite-plugin-dts:
75
+
specifier: ^4.5.4
76
+
version: 4.5.4(@types/node@24.10.1)(rollup@4.53.3)(typescript@5.8.3)(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1))
58
77
59
-
/@babel/compat-data@7.24.4:
60
-
resolution: {integrity: sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==}
61
-
engines: {node: '>=6.9.0'}
62
-
dev: true
78
+
packages/bluesky-profile-card-embed:
79
+
dependencies:
80
+
'@atcute/bluesky':
81
+
specifier: ^2.1.1
82
+
version: 2.1.1(@atcute/client@3.1.0)
83
+
'@atcute/bluesky-richtext-parser':
84
+
specifier: ^1.0.7
85
+
version: 1.0.7
86
+
'@atcute/client':
87
+
specifier: ^3.1.0
88
+
version: 3.1.0
89
+
devDependencies:
90
+
'@preact/preset-vite':
91
+
specifier: ^2.10.2
92
+
version: 2.10.2(@babel/core@7.28.5)(preact@10.28.0)(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1))
93
+
'@tsconfig/svelte':
94
+
specifier: ^5.0.6
95
+
version: 5.0.6
96
+
'@types/node':
97
+
specifier: ^24.10.1
98
+
version: 24.10.1
99
+
internal:
100
+
specifier: workspace:^
101
+
version: link:../internal
102
+
preact:
103
+
specifier: ^10.28.0
104
+
version: 10.28.0
105
+
svelte:
106
+
specifier: 'catalog:'
107
+
version: 5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd)
108
+
svelte-check:
109
+
specifier: ^4.3.4
110
+
version: 4.3.4(picomatch@4.0.3)(svelte@5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd))(typescript@5.8.3)
111
+
vite:
112
+
specifier: ^7.2.6
113
+
version: 7.2.6(@types/node@24.10.1)(terser@5.44.1)
114
+
vite-plugin-dts:
115
+
specifier: ^4.5.4
116
+
version: 4.5.4(@types/node@24.10.1)(rollup@4.53.3)(typescript@5.8.3)(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1))
63
117
64
-
/@babel/core@7.24.4:
65
-
resolution: {integrity: sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==}
66
-
engines: {node: '>=6.9.0'}
118
+
packages/bluesky-profile-feed-embed:
67
119
dependencies:
68
-
'@ampproject/remapping': 2.3.0
69
-
'@babel/code-frame': 7.24.2
70
-
'@babel/generator': 7.24.4
71
-
'@babel/helper-compilation-targets': 7.23.6
72
-
'@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.4)
73
-
'@babel/helpers': 7.24.4
74
-
'@babel/parser': 7.24.4
75
-
'@babel/template': 7.24.0
76
-
'@babel/traverse': 7.24.1
77
-
'@babel/types': 7.24.0
78
-
convert-source-map: 2.0.0
79
-
debug: 4.3.4
80
-
gensync: 1.0.0-beta.2
81
-
json5: 2.2.3
82
-
semver: 6.3.1
83
-
transitivePeerDependencies:
84
-
- supports-color
85
-
dev: true
120
+
'@atcute/bluesky':
121
+
specifier: ^2.1.1
122
+
version: 2.1.1(@atcute/client@3.1.0)
123
+
'@atcute/bluesky-richtext-segmenter':
124
+
specifier: ^2.0.4
125
+
version: 2.0.4
126
+
'@atcute/client':
127
+
specifier: ^3.1.0
128
+
version: 3.1.0
129
+
devDependencies:
130
+
'@tsconfig/svelte':
131
+
specifier: ^5.0.6
132
+
version: 5.0.6
133
+
'@types/node':
134
+
specifier: ^24.10.1
135
+
version: 24.10.1
136
+
internal:
137
+
specifier: workspace:^
138
+
version: link:../internal
139
+
svelte:
140
+
specifier: 'catalog:'
141
+
version: 5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd)
142
+
svelte-check:
143
+
specifier: ^4.3.4
144
+
version: 4.3.4(picomatch@4.0.3)(svelte@5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd))(typescript@5.8.3)
145
+
vite:
146
+
specifier: ^7.2.6
147
+
version: 7.2.6(@types/node@24.10.1)(terser@5.44.1)
148
+
vite-plugin-dts:
149
+
specifier: ^4.5.4
150
+
version: 4.5.4(@types/node@24.10.1)(rollup@4.53.3)(typescript@5.8.3)(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1))
86
151
87
-
/@babel/generator@7.24.4:
88
-
resolution: {integrity: sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==}
89
-
engines: {node: '>=6.9.0'}
152
+
packages/internal:
153
+
devDependencies:
154
+
'@atcute/bluesky':
155
+
specifier: ^2.1.1
156
+
version: 2.1.1(@atcute/client@3.1.0)
157
+
'@atcute/bluesky-richtext-parser':
158
+
specifier: ^1.0.7
159
+
version: 1.0.7
160
+
'@atcute/bluesky-richtext-segmenter':
161
+
specifier: ^2.0.4
162
+
version: 2.0.4
163
+
'@atcute/client':
164
+
specifier: ^3.1.0
165
+
version: 3.1.0
166
+
'@tsconfig/svelte':
167
+
specifier: ^5.0.6
168
+
version: 5.0.6
169
+
svelte:
170
+
specifier: 'catalog:'
171
+
version: 5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd)
172
+
173
+
packages/svelte-site:
90
174
dependencies:
91
-
'@babel/types': 7.24.0
92
-
'@jridgewell/gen-mapping': 0.3.5
93
-
'@jridgewell/trace-mapping': 0.3.25
94
-
jsesc: 2.5.2
95
-
dev: true
175
+
'@atcute/bluesky':
176
+
specifier: ^2.1.1
177
+
version: 2.1.1(@atcute/client@3.1.0)
178
+
'@atcute/client':
179
+
specifier: ^3.1.0
180
+
version: 3.1.0
181
+
bluesky-post-embed:
182
+
specifier: workspace:^
183
+
version: link:../bluesky-post-embed
184
+
bluesky-profile-card-embed:
185
+
specifier: workspace:^
186
+
version: link:../bluesky-profile-card-embed
187
+
bluesky-profile-feed-embed:
188
+
specifier: workspace:^
189
+
version: link:../bluesky-profile-feed-embed
190
+
internal:
191
+
specifier: workspace:^
192
+
version: link:../internal
193
+
devDependencies:
194
+
'@sveltejs/vite-plugin-svelte':
195
+
specifier: ^6.2.1
196
+
version: 6.2.1(svelte@5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd))(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1))
197
+
'@tsconfig/svelte':
198
+
specifier: ^5.0.6
199
+
version: 5.0.6
200
+
svelte:
201
+
specifier: ^5.45.5
202
+
version: 5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd)
203
+
svelte-check:
204
+
specifier: ^4.3.4
205
+
version: 4.3.4(picomatch@4.0.3)(svelte@5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd))(typescript@5.8.3)
206
+
terser:
207
+
specifier: ^5.44.1
208
+
version: 5.44.1
209
+
tslib:
210
+
specifier: ^2.8.1
211
+
version: 2.8.1
212
+
vite:
213
+
specifier: ^7.2.6
214
+
version: 7.2.6(@types/node@24.10.1)(terser@5.44.1)
215
+
216
+
packages:
217
+
218
+
'@atcute/atproto@3.1.9':
219
+
resolution: {integrity: sha512-DyWwHCTdR4hY2BPNbLXgVmm7lI+fceOwWbE4LXbGvbvVtSn+ejSVFaAv01Ra3kWDha0whsOmbJL8JP0QPpf1+w==}
220
+
221
+
'@atcute/bluesky-richtext-parser@1.0.7':
222
+
resolution: {integrity: sha512-nOvU699OXiGMbyswao7JJnY0C9WkwE7PVC/m5WWt0UN9fsXSOor9IZWw+v9SATp+94BTJoG38XyUomUaJnoQRA==}
223
+
224
+
'@atcute/bluesky-richtext-segmenter@2.0.4':
225
+
resolution: {integrity: sha512-6m5QEAv4lU3qTy5MeJXJRRG33acipYJnMW1T7W/KrMyThGhQ7jSTTh8Z48quElgivgX7MDj6o/ow1oLUsjsCKw==}
226
+
227
+
'@atcute/bluesky@2.1.1':
228
+
resolution: {integrity: sha512-wEZfFW58J6yC1SqHcVJOn4qbHENTTzjeCEWthRT5HvKovADLqk54HSMSAuXDMBUbintSTBr0khQNZQ3ZdgzDdQ==}
229
+
peerDependencies:
230
+
'@atcute/client': ^3.0.0
231
+
232
+
'@atcute/bluesky@3.2.11':
233
+
resolution: {integrity: sha512-AboS6y4t+zaxIq7E4noue10csSpIuk/Uwo30/l6GgGBDPXrd7STw8Yb5nGZQP+TdG/uC8/c2mm7UnY65SDOh6A==}
234
+
235
+
'@atcute/client@3.1.0':
236
+
resolution: {integrity: sha512-+rQPsHXSf0DUm8XoHoaH7Y2E8tIpbsW84djyPj7dqAyrFIjvGuJ1X1DvMufwbTIcmLerdy+dzl34iZcz/h3Vhg==}
237
+
238
+
'@atcute/lexicons@1.2.5':
239
+
resolution: {integrity: sha512-9yO9WdgxW8jZ7SbzUycH710z+JmsQ9W9n5S6i6eghYju32kkluFmgBeS47r8e8p2+Dv4DemS7o/3SUGsX9FR5Q==}
96
240
97
-
/@babel/helper-compilation-targets@7.23.6:
98
-
resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==}
241
+
'@babel/code-frame@7.27.1':
242
+
resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
99
243
engines: {node: '>=6.9.0'}
100
-
dependencies:
101
-
'@babel/compat-data': 7.24.4
102
-
'@babel/helper-validator-option': 7.23.5
103
-
browserslist: 4.23.0
104
-
lru-cache: 5.1.1
105
-
semver: 6.3.1
106
-
dev: true
107
244
108
-
/@babel/helper-environment-visitor@7.22.20:
109
-
resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==}
245
+
'@babel/compat-data@7.28.5':
246
+
resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==}
110
247
engines: {node: '>=6.9.0'}
111
-
dev: true
112
248
113
-
/@babel/helper-function-name@7.23.0:
114
-
resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==}
249
+
'@babel/core@7.28.5':
250
+
resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==}
115
251
engines: {node: '>=6.9.0'}
116
-
dependencies:
117
-
'@babel/template': 7.24.0
118
-
'@babel/types': 7.24.0
119
-
dev: true
120
252
121
-
/@babel/helper-hoist-variables@7.22.5:
122
-
resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
253
+
'@babel/generator@7.28.5':
254
+
resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==}
123
255
engines: {node: '>=6.9.0'}
124
-
dependencies:
125
-
'@babel/types': 7.24.0
126
-
dev: true
127
256
128
-
/@babel/helper-module-imports@7.24.3:
129
-
resolution: {integrity: sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==}
257
+
'@babel/helper-annotate-as-pure@7.27.3':
258
+
resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==}
130
259
engines: {node: '>=6.9.0'}
131
-
dependencies:
132
-
'@babel/types': 7.24.0
133
-
dev: true
134
260
135
-
/@babel/helper-module-transforms@7.23.3(@babel/core@7.24.4):
136
-
resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
261
+
'@babel/helper-compilation-targets@7.27.2':
262
+
resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==}
137
263
engines: {node: '>=6.9.0'}
138
-
peerDependencies:
139
-
'@babel/core': ^7.0.0
140
-
dependencies:
141
-
'@babel/core': 7.24.4
142
-
'@babel/helper-environment-visitor': 7.22.20
143
-
'@babel/helper-module-imports': 7.24.3
144
-
'@babel/helper-simple-access': 7.22.5
145
-
'@babel/helper-split-export-declaration': 7.22.6
146
-
'@babel/helper-validator-identifier': 7.22.20
147
-
dev: true
148
264
149
-
/@babel/helper-plugin-utils@7.24.0:
150
-
resolution: {integrity: sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==}
265
+
'@babel/helper-globals@7.28.0':
266
+
resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
151
267
engines: {node: '>=6.9.0'}
152
-
dev: true
153
268
154
-
/@babel/helper-simple-access@7.22.5:
155
-
resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
269
+
'@babel/helper-module-imports@7.27.1':
270
+
resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==}
156
271
engines: {node: '>=6.9.0'}
157
-
dependencies:
158
-
'@babel/types': 7.24.0
159
-
dev: true
160
272
161
-
/@babel/helper-split-export-declaration@7.22.6:
162
-
resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
273
+
'@babel/helper-module-transforms@7.28.3':
274
+
resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==}
163
275
engines: {node: '>=6.9.0'}
164
-
dependencies:
165
-
'@babel/types': 7.24.0
166
-
dev: true
276
+
peerDependencies:
277
+
'@babel/core': ^7.0.0
167
278
168
-
/@babel/helper-string-parser@7.24.1:
169
-
resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==}
279
+
'@babel/helper-plugin-utils@7.27.1':
280
+
resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==}
170
281
engines: {node: '>=6.9.0'}
171
-
dev: true
172
282
173
-
/@babel/helper-validator-identifier@7.22.20:
174
-
resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
283
+
'@babel/helper-string-parser@7.27.1':
284
+
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
175
285
engines: {node: '>=6.9.0'}
176
-
dev: true
177
286
178
-
/@babel/helper-validator-option@7.23.5:
179
-
resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==}
287
+
'@babel/helper-validator-identifier@7.28.5':
288
+
resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
180
289
engines: {node: '>=6.9.0'}
181
-
dev: true
182
290
183
-
/@babel/helpers@7.24.4:
184
-
resolution: {integrity: sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==}
291
+
'@babel/helper-validator-option@7.27.1':
292
+
resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
185
293
engines: {node: '>=6.9.0'}
186
-
dependencies:
187
-
'@babel/template': 7.24.0
188
-
'@babel/traverse': 7.24.1
189
-
'@babel/types': 7.24.0
190
-
transitivePeerDependencies:
191
-
- supports-color
192
-
dev: true
193
294
194
-
/@babel/highlight@7.24.2:
195
-
resolution: {integrity: sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==}
295
+
'@babel/helpers@7.28.4':
296
+
resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==}
196
297
engines: {node: '>=6.9.0'}
197
-
dependencies:
198
-
'@babel/helper-validator-identifier': 7.22.20
199
-
chalk: 2.4.2
200
-
js-tokens: 4.0.0
201
-
picocolors: 1.0.0
202
-
dev: true
203
298
204
-
/@babel/parser@7.24.4:
205
-
resolution: {integrity: sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==}
299
+
'@babel/parser@7.28.5':
300
+
resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==}
206
301
engines: {node: '>=6.0.0'}
207
302
hasBin: true
208
-
dependencies:
209
-
'@babel/types': 7.24.0
210
-
dev: true
211
303
212
-
/@babel/plugin-syntax-jsx@7.24.1(@babel/core@7.24.4):
213
-
resolution: {integrity: sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==}
304
+
'@babel/plugin-syntax-jsx@7.27.1':
305
+
resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==}
214
306
engines: {node: '>=6.9.0'}
215
307
peerDependencies:
216
308
'@babel/core': ^7.0.0-0
217
-
dependencies:
218
-
'@babel/core': 7.24.4
219
-
'@babel/helper-plugin-utils': 7.24.0
220
-
dev: true
221
309
222
-
/@babel/plugin-syntax-typescript@7.24.1(@babel/core@7.24.4):
223
-
resolution: {integrity: sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==}
310
+
'@babel/plugin-transform-react-jsx-development@7.27.1':
311
+
resolution: {integrity: sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==}
224
312
engines: {node: '>=6.9.0'}
225
313
peerDependencies:
226
314
'@babel/core': ^7.0.0-0
227
-
dependencies:
228
-
'@babel/core': 7.24.4
229
-
'@babel/helper-plugin-utils': 7.24.0
230
-
dev: true
231
315
232
-
/@babel/template@7.24.0:
233
-
resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==}
316
+
'@babel/plugin-transform-react-jsx@7.27.1':
317
+
resolution: {integrity: sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==}
234
318
engines: {node: '>=6.9.0'}
235
-
dependencies:
236
-
'@babel/code-frame': 7.24.2
237
-
'@babel/parser': 7.24.4
238
-
'@babel/types': 7.24.0
239
-
dev: true
319
+
peerDependencies:
320
+
'@babel/core': ^7.0.0-0
240
321
241
-
/@babel/traverse@7.24.1:
242
-
resolution: {integrity: sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==}
322
+
'@babel/runtime@7.28.4':
323
+
resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==}
243
324
engines: {node: '>=6.9.0'}
244
-
dependencies:
245
-
'@babel/code-frame': 7.24.2
246
-
'@babel/generator': 7.24.4
247
-
'@babel/helper-environment-visitor': 7.22.20
248
-
'@babel/helper-function-name': 7.23.0
249
-
'@babel/helper-hoist-variables': 7.22.5
250
-
'@babel/helper-split-export-declaration': 7.22.6
251
-
'@babel/parser': 7.24.4
252
-
'@babel/types': 7.24.0
253
-
debug: 4.3.4
254
-
globals: 11.12.0
255
-
transitivePeerDependencies:
256
-
- supports-color
257
-
dev: true
258
325
259
-
/@babel/types@7.24.0:
260
-
resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==}
326
+
'@babel/template@7.27.2':
327
+
resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
328
+
engines: {node: '>=6.9.0'}
329
+
330
+
'@babel/traverse@7.28.5':
331
+
resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==}
332
+
engines: {node: '>=6.9.0'}
333
+
334
+
'@babel/types@7.28.5':
335
+
resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
261
336
engines: {node: '>=6.9.0'}
262
-
dependencies:
263
-
'@babel/helper-string-parser': 7.24.1
264
-
'@babel/helper-validator-identifier': 7.22.20
265
-
to-fast-properties: 2.0.0
266
-
dev: true
337
+
338
+
'@changesets/apply-release-plan@7.0.14':
339
+
resolution: {integrity: sha512-ddBvf9PHdy2YY0OUiEl3TV78mH9sckndJR14QAt87KLEbIov81XO0q0QAmvooBxXlqRRP8I9B7XOzZwQG7JkWA==}
340
+
341
+
'@changesets/assemble-release-plan@6.0.9':
342
+
resolution: {integrity: sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==}
343
+
344
+
'@changesets/changelog-git@0.2.1':
345
+
resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==}
346
+
347
+
'@changesets/cli@2.29.8':
348
+
resolution: {integrity: sha512-1weuGZpP63YWUYjay/E84qqwcnt5yJMM0tep10Up7Q5cS/DGe2IZ0Uj3HNMxGhCINZuR7aO9WBMdKnPit5ZDPA==}
349
+
hasBin: true
350
+
351
+
'@changesets/config@3.1.2':
352
+
resolution: {integrity: sha512-CYiRhA4bWKemdYi/uwImjPxqWNpqGPNbEBdX1BdONALFIDK7MCUj6FPkzD+z9gJcvDFUQJn9aDVf4UG7OT6Kog==}
353
+
354
+
'@changesets/errors@0.2.0':
355
+
resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==}
356
+
357
+
'@changesets/get-dependents-graph@2.1.3':
358
+
resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==}
359
+
360
+
'@changesets/get-release-plan@4.0.14':
361
+
resolution: {integrity: sha512-yjZMHpUHgl4Xl5gRlolVuxDkm4HgSJqT93Ri1Uz8kGrQb+5iJ8dkXJ20M2j/Y4iV5QzS2c5SeTxVSKX+2eMI0g==}
362
+
363
+
'@changesets/get-version-range-type@0.4.0':
364
+
resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==}
365
+
366
+
'@changesets/git@3.0.4':
367
+
resolution: {integrity: sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==}
368
+
369
+
'@changesets/logger@0.1.1':
370
+
resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==}
371
+
372
+
'@changesets/parse@0.4.2':
373
+
resolution: {integrity: sha512-Uo5MC5mfg4OM0jU3up66fmSn6/NE9INK+8/Vn/7sMVcdWg46zfbvvUSjD9EMonVqPi9fbrJH9SXHn48Tr1f2yA==}
374
+
375
+
'@changesets/pre@2.0.2':
376
+
resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==}
377
+
378
+
'@changesets/read@0.6.6':
379
+
resolution: {integrity: sha512-P5QaN9hJSQQKJShzzpBT13FzOSPyHbqdoIBUd2DJdgvnECCyO6LmAOWSV+O8se2TaZJVwSXjL+v9yhb+a9JeJg==}
380
+
381
+
'@changesets/should-skip-package@0.1.2':
382
+
resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==}
383
+
384
+
'@changesets/types@4.1.0':
385
+
resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==}
386
+
387
+
'@changesets/types@6.1.0':
388
+
resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==}
389
+
390
+
'@changesets/write@0.4.0':
391
+
resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==}
267
392
268
-
/@esbuild/aix-ppc64@0.20.2:
269
-
resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==}
270
-
engines: {node: '>=12'}
393
+
'@esbuild/aix-ppc64@0.25.12':
394
+
resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
395
+
engines: {node: '>=18'}
271
396
cpu: [ppc64]
272
397
os: [aix]
273
-
requiresBuild: true
274
-
dev: true
275
-
optional: true
276
398
277
-
/@esbuild/android-arm64@0.20.2:
278
-
resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==}
279
-
engines: {node: '>=12'}
399
+
'@esbuild/android-arm64@0.25.12':
400
+
resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==}
401
+
engines: {node: '>=18'}
280
402
cpu: [arm64]
281
403
os: [android]
282
-
requiresBuild: true
283
-
dev: true
284
-
optional: true
285
404
286
-
/@esbuild/android-arm@0.20.2:
287
-
resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==}
288
-
engines: {node: '>=12'}
405
+
'@esbuild/android-arm@0.25.12':
406
+
resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==}
407
+
engines: {node: '>=18'}
289
408
cpu: [arm]
290
409
os: [android]
291
-
requiresBuild: true
292
-
dev: true
293
-
optional: true
294
410
295
-
/@esbuild/android-x64@0.20.2:
296
-
resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==}
297
-
engines: {node: '>=12'}
411
+
'@esbuild/android-x64@0.25.12':
412
+
resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==}
413
+
engines: {node: '>=18'}
298
414
cpu: [x64]
299
415
os: [android]
300
-
requiresBuild: true
301
-
dev: true
302
-
optional: true
303
416
304
-
/@esbuild/darwin-arm64@0.20.2:
305
-
resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==}
306
-
engines: {node: '>=12'}
417
+
'@esbuild/darwin-arm64@0.25.12':
418
+
resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==}
419
+
engines: {node: '>=18'}
307
420
cpu: [arm64]
308
421
os: [darwin]
309
-
requiresBuild: true
310
-
dev: true
311
-
optional: true
312
422
313
-
/@esbuild/darwin-x64@0.20.2:
314
-
resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==}
315
-
engines: {node: '>=12'}
423
+
'@esbuild/darwin-x64@0.25.12':
424
+
resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==}
425
+
engines: {node: '>=18'}
316
426
cpu: [x64]
317
427
os: [darwin]
318
-
requiresBuild: true
319
-
dev: true
320
-
optional: true
321
428
322
-
/@esbuild/freebsd-arm64@0.20.2:
323
-
resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==}
324
-
engines: {node: '>=12'}
429
+
'@esbuild/freebsd-arm64@0.25.12':
430
+
resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==}
431
+
engines: {node: '>=18'}
325
432
cpu: [arm64]
326
433
os: [freebsd]
327
-
requiresBuild: true
328
-
dev: true
329
-
optional: true
330
434
331
-
/@esbuild/freebsd-x64@0.20.2:
332
-
resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==}
333
-
engines: {node: '>=12'}
435
+
'@esbuild/freebsd-x64@0.25.12':
436
+
resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==}
437
+
engines: {node: '>=18'}
334
438
cpu: [x64]
335
439
os: [freebsd]
336
-
requiresBuild: true
337
-
dev: true
338
-
optional: true
339
440
340
-
/@esbuild/linux-arm64@0.20.2:
341
-
resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==}
342
-
engines: {node: '>=12'}
441
+
'@esbuild/linux-arm64@0.25.12':
442
+
resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==}
443
+
engines: {node: '>=18'}
343
444
cpu: [arm64]
344
445
os: [linux]
345
-
requiresBuild: true
346
-
dev: true
347
-
optional: true
348
446
349
-
/@esbuild/linux-arm@0.20.2:
350
-
resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==}
351
-
engines: {node: '>=12'}
447
+
'@esbuild/linux-arm@0.25.12':
448
+
resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==}
449
+
engines: {node: '>=18'}
352
450
cpu: [arm]
353
451
os: [linux]
354
-
requiresBuild: true
355
-
dev: true
356
-
optional: true
357
452
358
-
/@esbuild/linux-ia32@0.20.2:
359
-
resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==}
360
-
engines: {node: '>=12'}
453
+
'@esbuild/linux-ia32@0.25.12':
454
+
resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==}
455
+
engines: {node: '>=18'}
361
456
cpu: [ia32]
362
457
os: [linux]
363
-
requiresBuild: true
364
-
dev: true
365
-
optional: true
366
458
367
-
/@esbuild/linux-loong64@0.20.2:
368
-
resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==}
369
-
engines: {node: '>=12'}
459
+
'@esbuild/linux-loong64@0.25.12':
460
+
resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==}
461
+
engines: {node: '>=18'}
370
462
cpu: [loong64]
371
463
os: [linux]
372
-
requiresBuild: true
373
-
dev: true
374
-
optional: true
375
464
376
-
/@esbuild/linux-mips64el@0.20.2:
377
-
resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==}
378
-
engines: {node: '>=12'}
465
+
'@esbuild/linux-mips64el@0.25.12':
466
+
resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==}
467
+
engines: {node: '>=18'}
379
468
cpu: [mips64el]
380
469
os: [linux]
381
-
requiresBuild: true
382
-
dev: true
383
-
optional: true
384
470
385
-
/@esbuild/linux-ppc64@0.20.2:
386
-
resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==}
387
-
engines: {node: '>=12'}
471
+
'@esbuild/linux-ppc64@0.25.12':
472
+
resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==}
473
+
engines: {node: '>=18'}
388
474
cpu: [ppc64]
389
475
os: [linux]
390
-
requiresBuild: true
391
-
dev: true
392
-
optional: true
393
476
394
-
/@esbuild/linux-riscv64@0.20.2:
395
-
resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==}
396
-
engines: {node: '>=12'}
477
+
'@esbuild/linux-riscv64@0.25.12':
478
+
resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==}
479
+
engines: {node: '>=18'}
397
480
cpu: [riscv64]
398
481
os: [linux]
399
-
requiresBuild: true
400
-
dev: true
401
-
optional: true
402
482
403
-
/@esbuild/linux-s390x@0.20.2:
404
-
resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==}
405
-
engines: {node: '>=12'}
483
+
'@esbuild/linux-s390x@0.25.12':
484
+
resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==}
485
+
engines: {node: '>=18'}
406
486
cpu: [s390x]
407
487
os: [linux]
408
-
requiresBuild: true
409
-
dev: true
410
-
optional: true
411
488
412
-
/@esbuild/linux-x64@0.20.2:
413
-
resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==}
414
-
engines: {node: '>=12'}
489
+
'@esbuild/linux-x64@0.25.12':
490
+
resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==}
491
+
engines: {node: '>=18'}
415
492
cpu: [x64]
416
493
os: [linux]
417
-
requiresBuild: true
418
-
dev: true
419
-
optional: true
420
494
421
-
/@esbuild/netbsd-x64@0.20.2:
422
-
resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==}
423
-
engines: {node: '>=12'}
495
+
'@esbuild/netbsd-arm64@0.25.12':
496
+
resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==}
497
+
engines: {node: '>=18'}
498
+
cpu: [arm64]
499
+
os: [netbsd]
500
+
501
+
'@esbuild/netbsd-x64@0.25.12':
502
+
resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==}
503
+
engines: {node: '>=18'}
424
504
cpu: [x64]
425
505
os: [netbsd]
426
-
requiresBuild: true
427
-
dev: true
428
-
optional: true
506
+
507
+
'@esbuild/openbsd-arm64@0.25.12':
508
+
resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==}
509
+
engines: {node: '>=18'}
510
+
cpu: [arm64]
511
+
os: [openbsd]
429
512
430
-
/@esbuild/openbsd-x64@0.20.2:
431
-
resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==}
432
-
engines: {node: '>=12'}
513
+
'@esbuild/openbsd-x64@0.25.12':
514
+
resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==}
515
+
engines: {node: '>=18'}
433
516
cpu: [x64]
434
517
os: [openbsd]
435
-
requiresBuild: true
436
-
dev: true
437
-
optional: true
518
+
519
+
'@esbuild/openharmony-arm64@0.25.12':
520
+
resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==}
521
+
engines: {node: '>=18'}
522
+
cpu: [arm64]
523
+
os: [openharmony]
438
524
439
-
/@esbuild/sunos-x64@0.20.2:
440
-
resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==}
441
-
engines: {node: '>=12'}
525
+
'@esbuild/sunos-x64@0.25.12':
526
+
resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==}
527
+
engines: {node: '>=18'}
442
528
cpu: [x64]
443
529
os: [sunos]
444
-
requiresBuild: true
445
-
dev: true
446
-
optional: true
447
530
448
-
/@esbuild/win32-arm64@0.20.2:
449
-
resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==}
450
-
engines: {node: '>=12'}
531
+
'@esbuild/win32-arm64@0.25.12':
532
+
resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==}
533
+
engines: {node: '>=18'}
451
534
cpu: [arm64]
452
535
os: [win32]
453
-
requiresBuild: true
454
-
dev: true
455
-
optional: true
456
536
457
-
/@esbuild/win32-ia32@0.20.2:
458
-
resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==}
459
-
engines: {node: '>=12'}
537
+
'@esbuild/win32-ia32@0.25.12':
538
+
resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==}
539
+
engines: {node: '>=18'}
460
540
cpu: [ia32]
461
541
os: [win32]
462
-
requiresBuild: true
463
-
dev: true
464
-
optional: true
465
542
466
-
/@esbuild/win32-x64@0.20.2:
467
-
resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==}
468
-
engines: {node: '>=12'}
543
+
'@esbuild/win32-x64@0.25.12':
544
+
resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==}
545
+
engines: {node: '>=18'}
469
546
cpu: [x64]
470
547
os: [win32]
471
-
requiresBuild: true
472
-
dev: true
473
-
optional: true
474
548
475
-
/@externdefs/bluesky-client@0.5.8:
476
-
resolution: {integrity: sha512-RM7huV2fZlPscfv8TTsM7wDQZ7yw4doJvch5TSIkj4NCHXg+iTADmzv7FCuWNvrMwsAbqiWe+Uz2AsPuTWiMDA==}
477
-
dev: false
549
+
'@inquirer/external-editor@1.0.3':
550
+
resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==}
551
+
engines: {node: '>=18'}
552
+
peerDependencies:
553
+
'@types/node': '>=18'
554
+
peerDependenciesMeta:
555
+
'@types/node':
556
+
optional: true
478
557
479
-
/@intrnl/jsx-to-string@0.1.6(@babel/core@7.24.4):
480
-
resolution: {integrity: sha512-ib6FWU0GcJdzsBv8zvFj43IDNRakWoC8JlpRDYBsOgIl3aXCR2PKazE6CnRJxmSvSHF9FQ5tgaLGFGSBGTjy1Q==}
481
-
dependencies:
482
-
'@babel/helper-plugin-utils': 7.24.0
483
-
'@babel/plugin-syntax-jsx': 7.24.1(@babel/core@7.24.4)
484
-
'@babel/types': 7.24.0
485
-
csstype: 3.1.3
486
-
transitivePeerDependencies:
487
-
- '@babel/core'
488
-
dev: true
558
+
'@isaacs/balanced-match@4.0.1':
559
+
resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==}
560
+
engines: {node: 20 || >=22}
489
561
490
-
/@jridgewell/gen-mapping@0.3.5:
491
-
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
492
-
engines: {node: '>=6.0.0'}
493
-
dependencies:
494
-
'@jridgewell/set-array': 1.2.1
495
-
'@jridgewell/sourcemap-codec': 1.4.15
496
-
'@jridgewell/trace-mapping': 0.3.25
497
-
dev: true
562
+
'@isaacs/brace-expansion@5.0.0':
563
+
resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==}
564
+
engines: {node: 20 || >=22}
565
+
566
+
'@jridgewell/gen-mapping@0.3.13':
567
+
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
568
+
569
+
'@jridgewell/remapping@2.3.5':
570
+
resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
498
571
499
-
/@jridgewell/resolve-uri@3.1.2:
572
+
'@jridgewell/resolve-uri@3.1.2':
500
573
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
501
574
engines: {node: '>=6.0.0'}
502
-
dev: true
503
575
504
-
/@jridgewell/set-array@1.2.1:
505
-
resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
506
-
engines: {node: '>=6.0.0'}
507
-
dev: true
576
+
'@jridgewell/source-map@0.3.11':
577
+
resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==}
508
578
509
-
/@jridgewell/sourcemap-codec@1.4.15:
510
-
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
511
-
dev: true
579
+
'@jridgewell/sourcemap-codec@1.5.5':
580
+
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
512
581
513
-
/@jridgewell/trace-mapping@0.3.25:
514
-
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
515
-
dependencies:
516
-
'@jridgewell/resolve-uri': 3.1.2
517
-
'@jridgewell/sourcemap-codec': 1.4.15
518
-
dev: true
582
+
'@jridgewell/trace-mapping@0.3.31':
583
+
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
519
584
520
-
/@microsoft/api-extractor-model@7.28.13:
521
-
resolution: {integrity: sha512-39v/JyldX4MS9uzHcdfmjjfS6cYGAoXV+io8B5a338pkHiSt+gy2eXQ0Q7cGFJ7quSa1VqqlMdlPrB6sLR/cAw==}
522
-
dependencies:
523
-
'@microsoft/tsdoc': 0.14.2
524
-
'@microsoft/tsdoc-config': 0.16.2
525
-
'@rushstack/node-core-library': 4.0.2
526
-
transitivePeerDependencies:
527
-
- '@types/node'
528
-
dev: true
585
+
'@manypkg/find-root@1.1.0':
586
+
resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==}
587
+
588
+
'@manypkg/get-packages@1.1.3':
589
+
resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==}
590
+
591
+
'@microsoft/api-extractor-model@7.32.1':
592
+
resolution: {integrity: sha512-u4yJytMYiUAnhcNQcZDTh/tVtlrzKlyKrQnLOV+4Qr/5gV+cpufWzCYAB1Q23URFqD6z2RoL2UYncM9xJVGNKA==}
529
593
530
-
/@microsoft/api-extractor@7.43.0:
531
-
resolution: {integrity: sha512-GFhTcJpB+MI6FhvXEI9b2K0snulNLWHqC/BbcJtyNYcKUiw7l3Lgis5ApsYncJ0leALX7/of4XfmXk+maT111w==}
594
+
'@microsoft/api-extractor@7.55.1':
595
+
resolution: {integrity: sha512-l8Z+8qrLkZFM3HM95Dbpqs6G39fpCa7O5p8A7AkA6hSevxkgwsOlLrEuPv0ADOyj5dI1Af5WVDiwpKG/ya5G3w==}
532
596
hasBin: true
533
-
dependencies:
534
-
'@microsoft/api-extractor-model': 7.28.13
535
-
'@microsoft/tsdoc': 0.14.2
536
-
'@microsoft/tsdoc-config': 0.16.2
537
-
'@rushstack/node-core-library': 4.0.2
538
-
'@rushstack/rig-package': 0.5.2
539
-
'@rushstack/terminal': 0.10.0
540
-
'@rushstack/ts-command-line': 4.19.1
541
-
lodash: 4.17.21
542
-
minimatch: 3.0.8
543
-
resolve: 1.22.8
544
-
semver: 7.5.4
545
-
source-map: 0.6.1
546
-
typescript: 5.4.2
547
-
transitivePeerDependencies:
548
-
- '@types/node'
549
-
dev: true
597
+
598
+
'@microsoft/tsdoc-config@0.18.0':
599
+
resolution: {integrity: sha512-8N/vClYyfOH+l4fLkkr9+myAoR6M7akc8ntBJ4DJdWH2b09uVfr71+LTMpNyG19fNqWDg8KEDZhx5wxuqHyGjw==}
600
+
601
+
'@microsoft/tsdoc@0.16.0':
602
+
resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==}
603
+
604
+
'@nodelib/fs.scandir@2.1.5':
605
+
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
606
+
engines: {node: '>= 8'}
607
+
608
+
'@nodelib/fs.stat@2.0.5':
609
+
resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
610
+
engines: {node: '>= 8'}
611
+
612
+
'@nodelib/fs.walk@1.2.8':
613
+
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
614
+
engines: {node: '>= 8'}
615
+
616
+
'@preact/preset-vite@2.10.2':
617
+
resolution: {integrity: sha512-K9wHlJOtkE+cGqlyQ5v9kL3Ge0Ql4LlIZjkUTL+1zf3nNdF88F9UZN6VTV8jdzBX9Fl7WSzeNMSDG7qECPmSmg==}
618
+
peerDependencies:
619
+
'@babel/core': 7.x
620
+
vite: 2.x || 3.x || 4.x || 5.x || 6.x || 7.x
621
+
622
+
'@prefresh/babel-plugin@0.5.2':
623
+
resolution: {integrity: sha512-AOl4HG6dAxWkJ5ndPHBgBa49oo/9bOiJuRDKHLSTyH+Fd9x00shTXpdiTj1W41l6oQIwUOAgJeHMn4QwIDpHkA==}
550
624
551
-
/@microsoft/tsdoc-config@0.16.2:
552
-
resolution: {integrity: sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==}
553
-
dependencies:
554
-
'@microsoft/tsdoc': 0.14.2
555
-
ajv: 6.12.6
556
-
jju: 1.4.0
557
-
resolve: 1.19.0
558
-
dev: true
625
+
'@prefresh/core@1.5.9':
626
+
resolution: {integrity: sha512-IKBKCPaz34OFVC+adiQ2qaTF5qdztO2/4ZPf4KsRTgjKosWqxVXmEbxCiUydYZRY8GVie+DQlKzQr9gt6HQ+EQ==}
627
+
peerDependencies:
628
+
preact: ^10.0.0 || ^11.0.0-0
559
629
560
-
/@microsoft/tsdoc@0.14.2:
561
-
resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==}
562
-
dev: true
630
+
'@prefresh/utils@1.2.1':
631
+
resolution: {integrity: sha512-vq/sIuN5nYfYzvyayXI4C2QkprfNaHUQ9ZX+3xLD8nL3rWyzpxOm1+K7RtMbhd+66QcaISViK7amjnheQ/4WZw==}
563
632
564
-
/@rollup/plugin-babel@6.0.4(@babel/core@7.24.4):
565
-
resolution: {integrity: sha512-YF7Y52kFdFT/xVSuVdjkV5ZdX/3YtmX0QulG+x0taQOtJdHYzVU61aSSkAgVJ7NOv6qPkIYiJSgSWWN/DM5sGw==}
566
-
engines: {node: '>=14.0.0'}
633
+
'@prefresh/vite@2.4.11':
634
+
resolution: {integrity: sha512-/XjURQqdRiCG3NpMmWqE9kJwrg9IchIOWHzulCfqg2sRe/8oQ1g5De7xrk9lbqPIQLn7ntBkKdqWXIj4E9YXyg==}
567
635
peerDependencies:
568
-
'@babel/core': ^7.0.0
569
-
'@types/babel__core': ^7.1.9
570
-
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
571
-
peerDependenciesMeta:
572
-
'@types/babel__core':
573
-
optional: true
574
-
rollup:
575
-
optional: true
576
-
dependencies:
577
-
'@babel/core': 7.24.4
578
-
'@babel/helper-module-imports': 7.24.3
579
-
'@rollup/pluginutils': 5.1.0
580
-
dev: true
636
+
preact: ^10.4.0 || ^11.0.0-0
637
+
vite: '>=2.0.0'
638
+
639
+
'@rollup/pluginutils@4.2.1':
640
+
resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
641
+
engines: {node: '>= 8.0.0'}
581
642
582
-
/@rollup/pluginutils@5.1.0:
583
-
resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
643
+
'@rollup/pluginutils@5.3.0':
644
+
resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==}
584
645
engines: {node: '>=14.0.0'}
585
646
peerDependencies:
586
647
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
587
648
peerDependenciesMeta:
588
649
rollup:
589
650
optional: true
590
-
dependencies:
591
-
'@types/estree': 1.0.5
592
-
estree-walker: 2.0.2
593
-
picomatch: 2.3.1
594
-
dev: true
595
651
596
-
/@rollup/rollup-android-arm-eabi@4.14.2:
597
-
resolution: {integrity: sha512-ahxSgCkAEk+P/AVO0vYr7DxOD3CwAQrT0Go9BJyGQ9Ef0QxVOfjDZMiF4Y2s3mLyPrjonchIMH/tbWHucJMykQ==}
652
+
'@rollup/rollup-android-arm-eabi@4.53.3':
653
+
resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==}
598
654
cpu: [arm]
599
655
os: [android]
600
-
requiresBuild: true
601
-
dev: true
602
-
optional: true
603
656
604
-
/@rollup/rollup-android-arm64@4.14.2:
605
-
resolution: {integrity: sha512-lAarIdxZWbFSHFSDao9+I/F5jDaKyCqAPMq5HqnfpBw8dKDiCaaqM0lq5h1pQTLeIqueeay4PieGR5jGZMWprw==}
657
+
'@rollup/rollup-android-arm64@4.53.3':
658
+
resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==}
606
659
cpu: [arm64]
607
660
os: [android]
608
-
requiresBuild: true
609
-
dev: true
610
-
optional: true
611
661
612
-
/@rollup/rollup-darwin-arm64@4.14.2:
613
-
resolution: {integrity: sha512-SWsr8zEUk82KSqquIMgZEg2GE5mCSfr9sE/thDROkX6pb3QQWPp8Vw8zOq2GyxZ2t0XoSIUlvHDkrf5Gmf7x3Q==}
662
+
'@rollup/rollup-darwin-arm64@4.53.3':
663
+
resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==}
614
664
cpu: [arm64]
615
665
os: [darwin]
616
-
requiresBuild: true
617
-
dev: true
618
-
optional: true
619
666
620
-
/@rollup/rollup-darwin-x64@4.14.2:
621
-
resolution: {integrity: sha512-o/HAIrQq0jIxJAhgtIvV5FWviYK4WB0WwV91SLUnsliw1lSAoLsmgEEgRWzDguAFeUEUUoIWXiJrPqU7vGiVkA==}
667
+
'@rollup/rollup-darwin-x64@4.53.3':
668
+
resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==}
622
669
cpu: [x64]
623
670
os: [darwin]
624
-
requiresBuild: true
625
-
dev: true
626
-
optional: true
671
+
672
+
'@rollup/rollup-freebsd-arm64@4.53.3':
673
+
resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==}
674
+
cpu: [arm64]
675
+
os: [freebsd]
676
+
677
+
'@rollup/rollup-freebsd-x64@4.53.3':
678
+
resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==}
679
+
cpu: [x64]
680
+
os: [freebsd]
681
+
682
+
'@rollup/rollup-linux-arm-gnueabihf@4.53.3':
683
+
resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==}
684
+
cpu: [arm]
685
+
os: [linux]
627
686
628
-
/@rollup/rollup-linux-arm-gnueabihf@4.14.2:
629
-
resolution: {integrity: sha512-nwlJ65UY9eGq91cBi6VyDfArUJSKOYt5dJQBq8xyLhvS23qO+4Nr/RreibFHjP6t+5ap2ohZrUJcHv5zk5ju/g==}
687
+
'@rollup/rollup-linux-arm-musleabihf@4.53.3':
688
+
resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==}
630
689
cpu: [arm]
631
690
os: [linux]
632
-
requiresBuild: true
633
-
dev: true
634
-
optional: true
635
691
636
-
/@rollup/rollup-linux-arm64-gnu@4.14.2:
637
-
resolution: {integrity: sha512-Pg5TxxO2IVlMj79+c/9G0LREC9SY3HM+pfAwX7zj5/cAuwrbfj2Wv9JbMHIdPCfQpYsI4g9mE+2Bw/3aeSs2rQ==}
692
+
'@rollup/rollup-linux-arm64-gnu@4.53.3':
693
+
resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==}
638
694
cpu: [arm64]
639
695
os: [linux]
640
-
requiresBuild: true
641
-
dev: true
642
-
optional: true
643
696
644
-
/@rollup/rollup-linux-arm64-musl@4.14.2:
645
-
resolution: {integrity: sha512-cAOTjGNm84gc6tS02D1EXtG7tDRsVSDTBVXOLbj31DkwfZwgTPYZ6aafSU7rD/4R2a34JOwlF9fQayuTSkoclA==}
697
+
'@rollup/rollup-linux-arm64-musl@4.53.3':
698
+
resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==}
646
699
cpu: [arm64]
647
700
os: [linux]
648
-
requiresBuild: true
649
-
dev: true
650
-
optional: true
701
+
702
+
'@rollup/rollup-linux-loong64-gnu@4.53.3':
703
+
resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==}
704
+
cpu: [loong64]
705
+
os: [linux]
651
706
652
-
/@rollup/rollup-linux-powerpc64le-gnu@4.14.2:
653
-
resolution: {integrity: sha512-4RyT6v1kXb7C0fn6zV33rvaX05P0zHoNzaXI/5oFHklfKm602j+N4mn2YvoezQViRLPnxP8M1NaY4s/5kXO5cw==}
707
+
'@rollup/rollup-linux-ppc64-gnu@4.53.3':
708
+
resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==}
654
709
cpu: [ppc64]
655
710
os: [linux]
656
-
requiresBuild: true
657
-
dev: true
658
-
optional: true
659
711
660
-
/@rollup/rollup-linux-riscv64-gnu@4.14.2:
661
-
resolution: {integrity: sha512-KNUH6jC/vRGAKSorySTyc/yRYlCwN/5pnMjXylfBniwtJx5O7X17KG/0efj8XM3TZU7raYRXJFFReOzNmL1n1w==}
712
+
'@rollup/rollup-linux-riscv64-gnu@4.53.3':
713
+
resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==}
662
714
cpu: [riscv64]
663
715
os: [linux]
664
-
requiresBuild: true
665
-
dev: true
666
-
optional: true
667
716
668
-
/@rollup/rollup-linux-s390x-gnu@4.14.2:
669
-
resolution: {integrity: sha512-xPV4y73IBEXToNPa3h5lbgXOi/v0NcvKxU0xejiFw6DtIYQqOTMhZ2DN18/HrrP0PmiL3rGtRG9gz1QE8vFKXQ==}
717
+
'@rollup/rollup-linux-riscv64-musl@4.53.3':
718
+
resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==}
719
+
cpu: [riscv64]
720
+
os: [linux]
721
+
722
+
'@rollup/rollup-linux-s390x-gnu@4.53.3':
723
+
resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==}
670
724
cpu: [s390x]
671
725
os: [linux]
672
-
requiresBuild: true
673
-
dev: true
674
-
optional: true
675
726
676
-
/@rollup/rollup-linux-x64-gnu@4.14.2:
677
-
resolution: {integrity: sha512-QBhtr07iFGmF9egrPOWyO5wciwgtzKkYPNLVCFZTmr4TWmY0oY2Dm/bmhHjKRwZoGiaKdNcKhFtUMBKvlchH+Q==}
727
+
'@rollup/rollup-linux-x64-gnu@4.53.3':
728
+
resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==}
678
729
cpu: [x64]
679
730
os: [linux]
680
-
requiresBuild: true
681
-
dev: true
682
-
optional: true
683
731
684
-
/@rollup/rollup-linux-x64-musl@4.14.2:
685
-
resolution: {integrity: sha512-8zfsQRQGH23O6qazZSFY5jP5gt4cFvRuKTpuBsC1ZnSWxV8ZKQpPqOZIUtdfMOugCcBvFGRa1pDC/tkf19EgBw==}
732
+
'@rollup/rollup-linux-x64-musl@4.53.3':
733
+
resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==}
686
734
cpu: [x64]
687
735
os: [linux]
688
-
requiresBuild: true
689
-
dev: true
690
-
optional: true
691
736
692
-
/@rollup/rollup-win32-arm64-msvc@4.14.2:
693
-
resolution: {integrity: sha512-H4s8UjgkPnlChl6JF5empNvFHp77Jx+Wfy2EtmYPe9G22XV+PMuCinZVHurNe8ggtwoaohxARJZbaH/3xjB/FA==}
737
+
'@rollup/rollup-openharmony-arm64@4.53.3':
738
+
resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==}
739
+
cpu: [arm64]
740
+
os: [openharmony]
741
+
742
+
'@rollup/rollup-win32-arm64-msvc@4.53.3':
743
+
resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==}
694
744
cpu: [arm64]
695
745
os: [win32]
696
-
requiresBuild: true
697
-
dev: true
698
-
optional: true
699
746
700
-
/@rollup/rollup-win32-ia32-msvc@4.14.2:
701
-
resolution: {integrity: sha512-djqpAjm/i8erWYF0K6UY4kRO3X5+T4TypIqw60Q8MTqSBaQNpNXDhxdjpZ3ikgb+wn99svA7jxcXpiyg9MUsdw==}
747
+
'@rollup/rollup-win32-ia32-msvc@4.53.3':
748
+
resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==}
702
749
cpu: [ia32]
703
750
os: [win32]
704
-
requiresBuild: true
705
-
dev: true
706
-
optional: true
707
751
708
-
/@rollup/rollup-win32-x64-msvc@4.14.2:
709
-
resolution: {integrity: sha512-teAqzLT0yTYZa8ZP7zhFKEx4cotS8Tkk5XiqNMJhD4CpaWB1BHARE4Qy+RzwnXvSAYv+Q3jAqCVBS+PS+Yee8Q==}
752
+
'@rollup/rollup-win32-x64-gnu@4.53.3':
753
+
resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==}
754
+
cpu: [x64]
755
+
os: [win32]
756
+
757
+
'@rollup/rollup-win32-x64-msvc@4.53.3':
758
+
resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==}
710
759
cpu: [x64]
711
760
os: [win32]
712
-
requiresBuild: true
713
-
dev: true
714
-
optional: true
761
+
762
+
'@rushstack/node-core-library@5.19.0':
763
+
resolution: {integrity: sha512-BxAopbeWBvNJ6VGiUL+5lbJXywTdsnMeOS8j57Cn/xY10r6sV/gbsTlfYKjzVCUBZATX2eRzJHSMCchsMTGN6A==}
764
+
peerDependencies:
765
+
'@types/node': '*'
766
+
peerDependenciesMeta:
767
+
'@types/node':
768
+
optional: true
715
769
716
-
/@rushstack/node-core-library@4.0.2:
717
-
resolution: {integrity: sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==}
770
+
'@rushstack/problem-matcher@0.1.1':
771
+
resolution: {integrity: sha512-Fm5XtS7+G8HLcJHCWpES5VmeMyjAKaWeyZU5qPzZC+22mPlJzAsOxymHiWIfuirtPckX3aptWws+K2d0BzniJA==}
718
772
peerDependencies:
719
773
'@types/node': '*'
720
774
peerDependenciesMeta:
721
775
'@types/node':
722
776
optional: true
723
-
dependencies:
724
-
fs-extra: 7.0.1
725
-
import-lazy: 4.0.0
726
-
jju: 1.4.0
727
-
resolve: 1.22.8
728
-
semver: 7.5.4
729
-
z-schema: 5.0.5
730
-
dev: true
731
777
732
-
/@rushstack/rig-package@0.5.2:
733
-
resolution: {integrity: sha512-mUDecIJeH3yYGZs2a48k+pbhM6JYwWlgjs2Ca5f2n1G2/kgdgP9D/07oglEGf6mRyXEnazhEENeYTSNDRCwdqA==}
734
-
dependencies:
735
-
resolve: 1.22.8
736
-
strip-json-comments: 3.1.1
737
-
dev: true
778
+
'@rushstack/rig-package@0.6.0':
779
+
resolution: {integrity: sha512-ZQmfzsLE2+Y91GF15c65L/slMRVhF6Hycq04D4TwtdGaUAbIXXg9c5pKA5KFU7M4QMaihoobp9JJYpYcaY3zOw==}
738
780
739
-
/@rushstack/terminal@0.10.0:
740
-
resolution: {integrity: sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==}
781
+
'@rushstack/terminal@0.19.4':
782
+
resolution: {integrity: sha512-f4XQk02CrKfrMgyOfhYd3qWI944dLC21S4I/LUhrlAP23GTMDNG6EK5effQtFkISwUKCgD9vMBrJZaPSUquxWQ==}
741
783
peerDependencies:
742
784
'@types/node': '*'
743
785
peerDependenciesMeta:
744
786
'@types/node':
745
787
optional: true
746
-
dependencies:
747
-
'@rushstack/node-core-library': 4.0.2
748
-
supports-color: 8.1.1
749
-
dev: true
788
+
789
+
'@rushstack/ts-command-line@5.1.4':
790
+
resolution: {integrity: sha512-H0I6VdJ6sOUbktDFpP2VW5N29w8v4hRoNZOQz02vtEi6ZTYL1Ju8u+TcFiFawUDrUsx/5MQTUhd79uwZZVwVlA==}
791
+
792
+
'@standard-schema/spec@1.0.0':
793
+
resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
750
794
751
-
/@rushstack/ts-command-line@4.19.1:
752
-
resolution: {integrity: sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==}
753
-
dependencies:
754
-
'@rushstack/terminal': 0.10.0
755
-
'@types/argparse': 1.0.38
756
-
argparse: 1.0.10
757
-
string-argv: 0.3.2
758
-
transitivePeerDependencies:
759
-
- '@types/node'
760
-
dev: true
795
+
'@sveltejs/acorn-typescript@1.0.8':
796
+
resolution: {integrity: sha512-esgN+54+q0NjB0Y/4BomT9samII7jGwNy/2a3wNZbT2A2RpmXsXwUt24LvLhx6jUq2gVk4cWEvcRO6MFQbOfNA==}
797
+
peerDependencies:
798
+
acorn: ^8.9.0
761
799
762
-
/@types/argparse@1.0.38:
800
+
'@sveltejs/vite-plugin-svelte-inspector@5.0.1':
801
+
resolution: {integrity: sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA==}
802
+
engines: {node: ^20.19 || ^22.12 || >=24}
803
+
peerDependencies:
804
+
'@sveltejs/vite-plugin-svelte': ^6.0.0-next.0
805
+
svelte: ^5.0.0
806
+
vite: ^6.3.0 || ^7.0.0
807
+
808
+
'@sveltejs/vite-plugin-svelte@6.2.1':
809
+
resolution: {integrity: sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==}
810
+
engines: {node: ^20.19 || ^22.12 || >=24}
811
+
peerDependencies:
812
+
svelte: ^5.0.0
813
+
vite: ^6.3.0 || ^7.0.0
814
+
815
+
'@tsconfig/svelte@5.0.6':
816
+
resolution: {integrity: sha512-yGxYL0I9eETH1/DR9qVJey4DAsCdeau4a9wYPKuXfEhm8lFO8wg+LLYJjIpAm6Fw7HSlhepPhYPDop75485yWQ==}
817
+
818
+
'@types/argparse@1.0.38':
763
819
resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==}
764
-
dev: true
765
820
766
-
/@types/estree@1.0.5:
767
-
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
768
-
dev: true
821
+
'@types/estree@1.0.8':
822
+
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
769
823
770
-
/@volar/language-core@1.11.1:
771
-
resolution: {integrity: sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==}
772
-
dependencies:
773
-
'@volar/source-map': 1.11.1
774
-
dev: true
824
+
'@types/node@12.20.55':
825
+
resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==}
775
826
776
-
/@volar/source-map@1.11.1:
777
-
resolution: {integrity: sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==}
778
-
dependencies:
779
-
muggle-string: 0.3.1
780
-
dev: true
827
+
'@types/node@24.10.1':
828
+
resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==}
781
829
782
-
/@volar/typescript@1.11.1:
783
-
resolution: {integrity: sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==}
784
-
dependencies:
785
-
'@volar/language-core': 1.11.1
786
-
path-browserify: 1.0.1
787
-
dev: true
830
+
'@volar/language-core@2.4.26':
831
+
resolution: {integrity: sha512-hH0SMitMxnB43OZpyF1IFPS9bgb2I3bpCh76m2WEK7BE0A0EzpYsRp0CCH2xNKshr7kacU5TQBLYn4zj7CG60A==}
788
832
789
-
/@vue/compiler-core@3.4.21:
790
-
resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==}
791
-
dependencies:
792
-
'@babel/parser': 7.24.4
793
-
'@vue/shared': 3.4.21
794
-
entities: 4.5.0
795
-
estree-walker: 2.0.2
796
-
source-map-js: 1.2.0
797
-
dev: true
833
+
'@volar/source-map@2.4.26':
834
+
resolution: {integrity: sha512-JJw0Tt/kSFsIRmgTQF4JSt81AUSI1aEye5Zl65EeZ8H35JHnTvFGmpDOBn5iOxd48fyGE+ZvZBp5FcgAy/1Qhw==}
835
+
836
+
'@volar/typescript@2.4.26':
837
+
resolution: {integrity: sha512-N87ecLD48Sp6zV9zID/5yuS1+5foj0DfuYGdQ6KHj/IbKvyKv1zNX6VCmnKYwtmHadEO6mFc2EKISiu3RDPAvA==}
838
+
839
+
'@vue/compiler-core@3.5.25':
840
+
resolution: {integrity: sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==}
841
+
842
+
'@vue/compiler-dom@3.5.25':
843
+
resolution: {integrity: sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==}
798
844
799
-
/@vue/compiler-dom@3.4.21:
800
-
resolution: {integrity: sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==}
801
-
dependencies:
802
-
'@vue/compiler-core': 3.4.21
803
-
'@vue/shared': 3.4.21
804
-
dev: true
845
+
'@vue/compiler-vue2@2.7.16':
846
+
resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
805
847
806
-
/@vue/language-core@1.8.27(typescript@5.4.5):
807
-
resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==}
848
+
'@vue/language-core@2.2.0':
849
+
resolution: {integrity: sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==}
808
850
peerDependencies:
809
851
typescript: '*'
810
852
peerDependenciesMeta:
811
853
typescript:
812
854
optional: true
813
-
dependencies:
814
-
'@volar/language-core': 1.11.1
815
-
'@volar/source-map': 1.11.1
816
-
'@vue/compiler-dom': 3.4.21
817
-
'@vue/shared': 3.4.21
818
-
computeds: 0.0.1
819
-
minimatch: 9.0.4
820
-
muggle-string: 0.3.1
821
-
path-browserify: 1.0.1
822
-
typescript: 5.4.5
823
-
vue-template-compiler: 2.7.16
824
-
dev: true
855
+
856
+
'@vue/shared@3.5.25':
857
+
resolution: {integrity: sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==}
858
+
859
+
acorn@8.15.0:
860
+
resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
861
+
engines: {node: '>=0.4.0'}
862
+
hasBin: true
863
+
864
+
ajv-draft-04@1.0.0:
865
+
resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==}
866
+
peerDependencies:
867
+
ajv: ^8.5.0
868
+
peerDependenciesMeta:
869
+
ajv:
870
+
optional: true
825
871
826
-
/@vue/shared@3.4.21:
827
-
resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==}
828
-
dev: true
872
+
ajv-formats@3.0.1:
873
+
resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==}
874
+
peerDependencies:
875
+
ajv: ^8.0.0
876
+
peerDependenciesMeta:
877
+
ajv:
878
+
optional: true
829
879
830
-
/ajv@6.12.6:
831
-
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
832
-
dependencies:
833
-
fast-deep-equal: 3.1.3
834
-
fast-json-stable-stringify: 2.1.0
835
-
json-schema-traverse: 0.4.1
836
-
uri-js: 4.4.1
837
-
dev: true
880
+
ajv@8.12.0:
881
+
resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==}
882
+
883
+
ajv@8.13.0:
884
+
resolution: {integrity: sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==}
885
+
886
+
alien-signals@0.4.14:
887
+
resolution: {integrity: sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==}
888
+
889
+
ansi-colors@4.1.3:
890
+
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
891
+
engines: {node: '>=6'}
838
892
839
-
/ansi-styles@3.2.1:
840
-
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
841
-
engines: {node: '>=4'}
842
-
dependencies:
843
-
color-convert: 1.9.3
844
-
dev: true
893
+
ansi-regex@5.0.1:
894
+
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
895
+
engines: {node: '>=8'}
845
896
846
-
/argparse@1.0.10:
897
+
argparse@1.0.10:
847
898
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
848
-
dependencies:
849
-
sprintf-js: 1.0.3
850
-
dev: true
899
+
900
+
argparse@2.0.1:
901
+
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
902
+
903
+
aria-query@5.3.2:
904
+
resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
905
+
engines: {node: '>= 0.4'}
906
+
907
+
array-union@2.1.0:
908
+
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
909
+
engines: {node: '>=8'}
851
910
852
-
/balanced-match@1.0.2:
911
+
axobject-query@4.1.0:
912
+
resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
913
+
engines: {node: '>= 0.4'}
914
+
915
+
babel-plugin-transform-hook-names@1.0.2:
916
+
resolution: {integrity: sha512-5gafyjyyBTTdX/tQQ0hRgu4AhNHG/hqWi0ZZmg2xvs2FgRkJXzDNKBZCyoYqgFkovfDrgM8OoKg8karoUvWeCw==}
917
+
peerDependencies:
918
+
'@babel/core': ^7.12.10
919
+
920
+
balanced-match@1.0.2:
853
921
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
854
-
dev: true
922
+
923
+
baseline-browser-mapping@2.9.0:
924
+
resolution: {integrity: sha512-Mh++g+2LPfzZToywfE1BUzvZbfOY52Nil0rn9H1CPC5DJ7fX+Vir7nToBeoiSbB1zTNeGYbELEvJESujgGrzXw==}
925
+
hasBin: true
926
+
927
+
better-path-resolve@1.0.0:
928
+
resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==}
929
+
engines: {node: '>=4'}
855
930
856
-
/brace-expansion@1.1.11:
857
-
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
858
-
dependencies:
859
-
balanced-match: 1.0.2
860
-
concat-map: 0.0.1
861
-
dev: true
931
+
boolbase@1.0.0:
932
+
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
933
+
934
+
brace-expansion@2.0.2:
935
+
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
862
936
863
-
/brace-expansion@2.0.1:
864
-
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
865
-
dependencies:
866
-
balanced-match: 1.0.2
867
-
dev: true
937
+
braces@3.0.3:
938
+
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
939
+
engines: {node: '>=8'}
868
940
869
-
/browserslist@4.23.0:
870
-
resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==}
941
+
browserslist@4.28.1:
942
+
resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==}
871
943
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
872
944
hasBin: true
873
-
dependencies:
874
-
caniuse-lite: 1.0.30001609
875
-
electron-to-chromium: 1.4.735
876
-
node-releases: 2.0.14
877
-
update-browserslist-db: 1.0.13(browserslist@4.23.0)
878
-
dev: true
945
+
946
+
buffer-from@1.1.2:
947
+
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
879
948
880
-
/caniuse-lite@1.0.30001609:
881
-
resolution: {integrity: sha512-JFPQs34lHKx1B5t1EpQpWH4c+29zIyn/haGsbpfq3suuV9v56enjFt23zqijxGTMwy1p/4H2tjnQMY+p1WoAyA==}
882
-
dev: true
949
+
caniuse-lite@1.0.30001759:
950
+
resolution: {integrity: sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==}
883
951
884
-
/chalk@2.4.2:
885
-
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
886
-
engines: {node: '>=4'}
887
-
dependencies:
888
-
ansi-styles: 3.2.1
889
-
escape-string-regexp: 1.0.5
890
-
supports-color: 5.5.0
891
-
dev: true
952
+
chardet@2.1.1:
953
+
resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==}
892
954
893
-
/color-convert@1.9.3:
894
-
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
895
-
dependencies:
896
-
color-name: 1.1.3
897
-
dev: true
955
+
chokidar@4.0.3:
956
+
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
957
+
engines: {node: '>= 14.16.0'}
898
958
899
-
/color-name@1.1.3:
900
-
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
901
-
dev: true
959
+
ci-info@3.9.0:
960
+
resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==}
961
+
engines: {node: '>=8'}
962
+
963
+
clsx@2.1.1:
964
+
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
965
+
engines: {node: '>=6'}
966
+
967
+
commander@2.20.3:
968
+
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
902
969
903
-
/commander@9.5.0:
904
-
resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
905
-
engines: {node: ^12.20.0 || >=14}
906
-
requiresBuild: true
907
-
dev: true
908
-
optional: true
970
+
compare-versions@6.1.1:
971
+
resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==}
909
972
910
-
/computeds@0.0.1:
911
-
resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==}
912
-
dev: true
973
+
confbox@0.1.8:
974
+
resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
913
975
914
-
/concat-map@0.0.1:
915
-
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
916
-
dev: true
976
+
confbox@0.2.2:
977
+
resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==}
917
978
918
-
/convert-source-map@2.0.0:
979
+
convert-source-map@2.0.0:
919
980
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
920
-
dev: true
981
+
982
+
cross-spawn@7.0.6:
983
+
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
984
+
engines: {node: '>= 8'}
921
985
922
-
/css-declaration-sorter@7.2.0:
923
-
resolution: {integrity: sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==}
986
+
css-declaration-sorter@7.3.0:
987
+
resolution: {integrity: sha512-LQF6N/3vkAMYF4xoHLJfG718HRJh34Z8BnNhd6bosOMIVjMlhuZK5++oZa3uYAgrI5+7x2o27gUqTR2U/KjUOQ==}
924
988
engines: {node: ^14 || ^16 || >=18}
925
989
peerDependencies:
926
990
postcss: ^8.0.9
927
-
dev: true
928
991
929
-
/csstype@3.1.3:
930
-
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
931
-
dev: true
992
+
css-select@5.2.2:
993
+
resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==}
994
+
995
+
css-what@6.2.2:
996
+
resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==}
997
+
engines: {node: '>= 6'}
932
998
933
-
/de-indent@1.0.2:
999
+
de-indent@1.0.2:
934
1000
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
935
-
dev: true
936
1001
937
-
/debug@4.3.4:
938
-
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
1002
+
debug@4.4.3:
1003
+
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
939
1004
engines: {node: '>=6.0'}
940
1005
peerDependencies:
941
1006
supports-color: '*'
942
1007
peerDependenciesMeta:
943
1008
supports-color:
944
1009
optional: true
945
-
dependencies:
946
-
ms: 2.1.2
947
-
dev: true
1010
+
1011
+
deepmerge@4.3.1:
1012
+
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
1013
+
engines: {node: '>=0.10.0'}
1014
+
1015
+
detect-indent@6.1.0:
1016
+
resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
1017
+
engines: {node: '>=8'}
1018
+
1019
+
devalue@5.5.0:
1020
+
resolution: {integrity: sha512-69sM5yrHfFLJt0AZ9QqZXGCPfJ7fQjvpln3Rq5+PS03LD32Ost1Q9N+eEnaQwGRIriKkMImXD56ocjQmfjbV3w==}
1021
+
1022
+
diff@8.0.2:
1023
+
resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==}
1024
+
engines: {node: '>=0.3.1'}
1025
+
1026
+
dir-glob@3.0.1:
1027
+
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
1028
+
engines: {node: '>=8'}
1029
+
1030
+
dom-serializer@2.0.0:
1031
+
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
1032
+
1033
+
domelementtype@2.3.0:
1034
+
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
1035
+
1036
+
domhandler@5.0.3:
1037
+
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
1038
+
engines: {node: '>= 4'}
1039
+
1040
+
domutils@3.2.2:
1041
+
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
1042
+
1043
+
electron-to-chromium@1.5.264:
1044
+
resolution: {integrity: sha512-1tEf0nLgltC3iy9wtlYDlQDc5Rg9lEKVjEmIHJ21rI9OcqkvD45K1oyNIRA4rR1z3LgJ7KeGzEBojVcV6m4qjA==}
948
1045
949
-
/electron-to-chromium@1.4.735:
950
-
resolution: {integrity: sha512-pkYpvwg8VyOTQAeBqZ7jsmpCjko1Qc6We1ZtZCjRyYbT5v4AIUKDy5cQTRotQlSSZmMr8jqpEt6JtOj5k7lR7A==}
951
-
dev: true
1046
+
enquirer@2.4.1:
1047
+
resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==}
1048
+
engines: {node: '>=8.6'}
952
1049
953
-
/entities@4.5.0:
1050
+
entities@4.5.0:
954
1051
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
955
1052
engines: {node: '>=0.12'}
956
-
dev: true
957
1053
958
-
/esbuild@0.20.2:
959
-
resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==}
960
-
engines: {node: '>=12'}
1054
+
esbuild@0.25.12:
1055
+
resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
1056
+
engines: {node: '>=18'}
961
1057
hasBin: true
962
-
requiresBuild: true
963
-
optionalDependencies:
964
-
'@esbuild/aix-ppc64': 0.20.2
965
-
'@esbuild/android-arm': 0.20.2
966
-
'@esbuild/android-arm64': 0.20.2
967
-
'@esbuild/android-x64': 0.20.2
968
-
'@esbuild/darwin-arm64': 0.20.2
969
-
'@esbuild/darwin-x64': 0.20.2
970
-
'@esbuild/freebsd-arm64': 0.20.2
971
-
'@esbuild/freebsd-x64': 0.20.2
972
-
'@esbuild/linux-arm': 0.20.2
973
-
'@esbuild/linux-arm64': 0.20.2
974
-
'@esbuild/linux-ia32': 0.20.2
975
-
'@esbuild/linux-loong64': 0.20.2
976
-
'@esbuild/linux-mips64el': 0.20.2
977
-
'@esbuild/linux-ppc64': 0.20.2
978
-
'@esbuild/linux-riscv64': 0.20.2
979
-
'@esbuild/linux-s390x': 0.20.2
980
-
'@esbuild/linux-x64': 0.20.2
981
-
'@esbuild/netbsd-x64': 0.20.2
982
-
'@esbuild/openbsd-x64': 0.20.2
983
-
'@esbuild/sunos-x64': 0.20.2
984
-
'@esbuild/win32-arm64': 0.20.2
985
-
'@esbuild/win32-ia32': 0.20.2
986
-
'@esbuild/win32-x64': 0.20.2
987
-
dev: true
988
1058
989
-
/escalade@3.1.2:
990
-
resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
1059
+
escalade@3.2.0:
1060
+
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
991
1061
engines: {node: '>=6'}
992
-
dev: true
993
1062
994
-
/escape-string-regexp@1.0.5:
995
-
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
996
-
engines: {node: '>=0.8.0'}
997
-
dev: true
1063
+
esm-env@1.2.2:
1064
+
resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==}
1065
+
1066
+
esprima@4.0.1:
1067
+
resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
1068
+
engines: {node: '>=4'}
1069
+
hasBin: true
1070
+
1071
+
esrap@2.2.1:
1072
+
resolution: {integrity: sha512-GiYWG34AN/4CUyaWAgunGt0Rxvr1PTMlGC0vvEov/uOQYWne2bpN03Um+k8jT+q3op33mKouP2zeJ6OlM+qeUg==}
998
1073
999
-
/estree-walker@2.0.2:
1074
+
estree-walker@2.0.2:
1000
1075
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
1001
-
dev: true
1002
1076
1003
-
/fast-deep-equal@3.1.3:
1077
+
exsolve@1.0.8:
1078
+
resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==}
1079
+
1080
+
extendable-error@0.1.7:
1081
+
resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==}
1082
+
1083
+
fast-deep-equal@3.1.3:
1004
1084
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
1005
-
dev: true
1085
+
1086
+
fast-glob@3.3.3:
1087
+
resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
1088
+
engines: {node: '>=8.6.0'}
1089
+
1090
+
fastq@1.19.1:
1091
+
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
1092
+
1093
+
fdir@6.5.0:
1094
+
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
1095
+
engines: {node: '>=12.0.0'}
1096
+
peerDependencies:
1097
+
picomatch: ^3 || ^4
1098
+
peerDependenciesMeta:
1099
+
picomatch:
1100
+
optional: true
1101
+
1102
+
fill-range@7.1.1:
1103
+
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
1104
+
engines: {node: '>=8'}
1105
+
1106
+
find-up@4.1.0:
1107
+
resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
1108
+
engines: {node: '>=8'}
1006
1109
1007
-
/fast-json-stable-stringify@2.1.0:
1008
-
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
1009
-
dev: true
1110
+
fs-extra@11.3.2:
1111
+
resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==}
1112
+
engines: {node: '>=14.14'}
1010
1113
1011
-
/fs-extra@7.0.1:
1114
+
fs-extra@7.0.1:
1012
1115
resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
1013
1116
engines: {node: '>=6 <7 || >=8'}
1014
-
dependencies:
1015
-
graceful-fs: 4.2.11
1016
-
jsonfile: 4.0.0
1017
-
universalify: 0.1.2
1018
-
dev: true
1117
+
1118
+
fs-extra@8.1.0:
1119
+
resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==}
1120
+
engines: {node: '>=6 <7 || >=8'}
1019
1121
1020
-
/fsevents@2.3.3:
1122
+
fsevents@2.3.3:
1021
1123
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
1022
1124
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
1023
1125
os: [darwin]
1024
-
requiresBuild: true
1025
-
dev: true
1026
-
optional: true
1027
1126
1028
-
/function-bind@1.1.2:
1127
+
function-bind@1.1.2:
1029
1128
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
1030
-
dev: true
1031
1129
1032
-
/gensync@1.0.0-beta.2:
1130
+
gensync@1.0.0-beta.2:
1033
1131
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
1034
1132
engines: {node: '>=6.9.0'}
1035
-
dev: true
1036
1133
1037
-
/globals@11.12.0:
1038
-
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
1039
-
engines: {node: '>=4'}
1040
-
dev: true
1134
+
glob-parent@5.1.2:
1135
+
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
1136
+
engines: {node: '>= 6'}
1041
1137
1042
-
/graceful-fs@4.2.11:
1043
-
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
1044
-
dev: true
1138
+
globby@11.1.0:
1139
+
resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
1140
+
engines: {node: '>=10'}
1045
1141
1046
-
/has-flag@3.0.0:
1047
-
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
1048
-
engines: {node: '>=4'}
1049
-
dev: true
1142
+
graceful-fs@4.2.11:
1143
+
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
1050
1144
1051
-
/has-flag@4.0.0:
1145
+
has-flag@4.0.0:
1052
1146
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
1053
1147
engines: {node: '>=8'}
1054
-
dev: true
1055
1148
1056
-
/hasown@2.0.2:
1149
+
hasown@2.0.2:
1057
1150
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
1058
1151
engines: {node: '>= 0.4'}
1059
-
dependencies:
1060
-
function-bind: 1.1.2
1061
-
dev: true
1062
1152
1063
-
/he@1.2.0:
1153
+
he@1.2.0:
1064
1154
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
1065
1155
hasBin: true
1066
-
dev: true
1156
+
1157
+
human-id@4.1.3:
1158
+
resolution: {integrity: sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==}
1159
+
hasBin: true
1160
+
1161
+
iconv-lite@0.7.0:
1162
+
resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==}
1163
+
engines: {node: '>=0.10.0'}
1164
+
1165
+
ignore@5.3.2:
1166
+
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
1167
+
engines: {node: '>= 4'}
1067
1168
1068
-
/import-lazy@4.0.0:
1169
+
import-lazy@4.0.0:
1069
1170
resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==}
1070
1171
engines: {node: '>=8'}
1071
-
dev: true
1172
+
1173
+
is-core-module@2.16.1:
1174
+
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
1175
+
engines: {node: '>= 0.4'}
1176
+
1177
+
is-extglob@2.1.1:
1178
+
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
1179
+
engines: {node: '>=0.10.0'}
1180
+
1181
+
is-glob@4.0.3:
1182
+
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
1183
+
engines: {node: '>=0.10.0'}
1184
+
1185
+
is-number@7.0.0:
1186
+
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
1187
+
engines: {node: '>=0.12.0'}
1188
+
1189
+
is-reference@3.0.3:
1190
+
resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==}
1191
+
1192
+
is-subdir@1.2.0:
1193
+
resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==}
1194
+
engines: {node: '>=4'}
1195
+
1196
+
is-windows@1.0.2:
1197
+
resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==}
1198
+
engines: {node: '>=0.10.0'}
1072
1199
1073
-
/is-core-module@2.13.1:
1074
-
resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
1075
-
dependencies:
1076
-
hasown: 2.0.2
1077
-
dev: true
1200
+
isexe@2.0.0:
1201
+
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
1078
1202
1079
-
/jju@1.4.0:
1203
+
jju@1.4.0:
1080
1204
resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==}
1081
-
dev: true
1082
1205
1083
-
/js-tokens@4.0.0:
1206
+
js-tokens@4.0.0:
1084
1207
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
1085
-
dev: true
1208
+
1209
+
js-yaml@3.14.2:
1210
+
resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==}
1211
+
hasBin: true
1086
1212
1087
-
/jsesc@2.5.2:
1088
-
resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
1089
-
engines: {node: '>=4'}
1213
+
js-yaml@4.1.1:
1214
+
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
1090
1215
hasBin: true
1091
-
dev: true
1216
+
1217
+
jsesc@3.1.0:
1218
+
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
1219
+
engines: {node: '>=6'}
1220
+
hasBin: true
1092
1221
1093
-
/json-schema-traverse@0.4.1:
1094
-
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
1095
-
dev: true
1222
+
json-schema-traverse@1.0.0:
1223
+
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
1096
1224
1097
-
/json5@2.2.3:
1225
+
json5@2.2.3:
1098
1226
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
1099
1227
engines: {node: '>=6'}
1100
1228
hasBin: true
1101
-
dev: true
1102
1229
1103
-
/jsonfile@4.0.0:
1230
+
jsonfile@4.0.0:
1104
1231
resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
1105
-
optionalDependencies:
1106
-
graceful-fs: 4.2.11
1107
-
dev: true
1232
+
1233
+
jsonfile@6.2.0:
1234
+
resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==}
1108
1235
1109
-
/kolorist@1.8.0:
1236
+
kolorist@1.8.0:
1110
1237
resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
1111
-
dev: true
1112
1238
1113
-
/lodash.get@4.4.2:
1114
-
resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
1115
-
dev: true
1239
+
local-pkg@1.1.2:
1240
+
resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==}
1241
+
engines: {node: '>=14'}
1116
1242
1117
-
/lodash.isequal@4.5.0:
1118
-
resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==}
1119
-
dev: true
1243
+
locate-character@3.0.0:
1244
+
resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==}
1245
+
1246
+
locate-path@5.0.0:
1247
+
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
1248
+
engines: {node: '>=8'}
1249
+
1250
+
lodash.startcase@4.4.0:
1251
+
resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==}
1120
1252
1121
-
/lodash@4.17.21:
1253
+
lodash@4.17.21:
1122
1254
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
1123
-
dev: true
1124
1255
1125
-
/lru-cache@5.1.1:
1256
+
lru-cache@5.1.1:
1126
1257
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
1127
-
dependencies:
1128
-
yallist: 3.1.1
1129
-
dev: true
1130
1258
1131
-
/lru-cache@6.0.0:
1259
+
lru-cache@6.0.0:
1132
1260
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
1133
1261
engines: {node: '>=10'}
1134
-
dependencies:
1135
-
yallist: 4.0.0
1136
-
dev: true
1137
1262
1138
-
/magic-string@0.30.9:
1139
-
resolution: {integrity: sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==}
1140
-
engines: {node: '>=12'}
1141
-
dependencies:
1142
-
'@jridgewell/sourcemap-codec': 1.4.15
1143
-
dev: true
1263
+
magic-string@0.30.21:
1264
+
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
1265
+
1266
+
merge2@1.4.1:
1267
+
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
1268
+
engines: {node: '>= 8'}
1269
+
1270
+
micromatch@4.0.8:
1271
+
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
1272
+
engines: {node: '>=8.6'}
1144
1273
1145
-
/minimatch@3.0.8:
1146
-
resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==}
1147
-
dependencies:
1148
-
brace-expansion: 1.1.11
1149
-
dev: true
1274
+
minimatch@10.0.3:
1275
+
resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==}
1276
+
engines: {node: 20 || >=22}
1150
1277
1151
-
/minimatch@9.0.4:
1152
-
resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==}
1278
+
minimatch@9.0.5:
1279
+
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
1153
1280
engines: {node: '>=16 || 14 >=14.17'}
1154
-
dependencies:
1155
-
brace-expansion: 2.0.1
1156
-
dev: true
1157
1281
1158
-
/ms@2.1.2:
1159
-
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
1160
-
dev: true
1282
+
mlly@1.8.0:
1283
+
resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==}
1161
1284
1162
-
/muggle-string@0.3.1:
1163
-
resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==}
1164
-
dev: true
1285
+
mri@1.2.0:
1286
+
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
1287
+
engines: {node: '>=4'}
1288
+
1289
+
ms@2.1.3:
1290
+
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
1291
+
1292
+
muggle-string@0.4.1:
1293
+
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
1165
1294
1166
-
/nanoid@3.3.7:
1167
-
resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
1295
+
nanoid@3.3.11:
1296
+
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
1168
1297
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
1169
1298
hasBin: true
1170
-
dev: true
1171
1299
1172
-
/node-releases@2.0.14:
1173
-
resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
1174
-
dev: true
1300
+
node-html-parser@6.1.13:
1301
+
resolution: {integrity: sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==}
1175
1302
1176
-
/path-browserify@1.0.1:
1303
+
node-releases@2.0.27:
1304
+
resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
1305
+
1306
+
nth-check@2.1.1:
1307
+
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
1308
+
1309
+
outdent@0.5.0:
1310
+
resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==}
1311
+
1312
+
p-filter@2.1.0:
1313
+
resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==}
1314
+
engines: {node: '>=8'}
1315
+
1316
+
p-limit@2.3.0:
1317
+
resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
1318
+
engines: {node: '>=6'}
1319
+
1320
+
p-locate@4.1.0:
1321
+
resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
1322
+
engines: {node: '>=8'}
1323
+
1324
+
p-map@2.1.0:
1325
+
resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==}
1326
+
engines: {node: '>=6'}
1327
+
1328
+
p-try@2.2.0:
1329
+
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
1330
+
engines: {node: '>=6'}
1331
+
1332
+
package-manager-detector@0.2.11:
1333
+
resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==}
1334
+
1335
+
path-browserify@1.0.1:
1177
1336
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
1178
-
dev: true
1179
1337
1180
-
/path-parse@1.0.7:
1338
+
path-exists@4.0.0:
1339
+
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
1340
+
engines: {node: '>=8'}
1341
+
1342
+
path-key@3.1.1:
1343
+
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
1344
+
engines: {node: '>=8'}
1345
+
1346
+
path-parse@1.0.7:
1181
1347
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
1182
-
dev: true
1348
+
1349
+
path-type@4.0.0:
1350
+
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
1351
+
engines: {node: '>=8'}
1352
+
1353
+
pathe@2.0.3:
1354
+
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
1183
1355
1184
-
/picocolors@1.0.0:
1185
-
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
1186
-
dev: true
1356
+
picocolors@1.1.1:
1357
+
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
1187
1358
1188
-
/picomatch@2.3.1:
1359
+
picomatch@2.3.1:
1189
1360
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
1190
1361
engines: {node: '>=8.6'}
1191
-
dev: true
1362
+
1363
+
picomatch@4.0.3:
1364
+
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
1365
+
engines: {node: '>=12'}
1192
1366
1193
-
/postcss-less@6.0.0:
1367
+
pify@4.0.1:
1368
+
resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
1369
+
engines: {node: '>=6'}
1370
+
1371
+
pkg-types@1.3.1:
1372
+
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
1373
+
1374
+
pkg-types@2.3.0:
1375
+
resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
1376
+
1377
+
postcss-less@6.0.0:
1194
1378
resolution: {integrity: sha512-FPX16mQLyEjLzEuuJtxA8X3ejDLNGGEG503d2YGZR5Ask1SpDN8KmZUMpzCvyalWRywAn1n1VOA5dcqfCLo5rg==}
1195
1379
engines: {node: '>=12'}
1196
1380
peerDependencies:
1197
1381
postcss: ^8.3.5
1198
-
dev: true
1199
1382
1200
-
/postcss-scss@4.0.9:
1383
+
postcss-scss@4.0.9:
1201
1384
resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==}
1202
1385
engines: {node: '>=12.0'}
1203
1386
peerDependencies:
1204
1387
postcss: ^8.4.29
1205
-
dev: true
1206
1388
1207
-
/postcss@8.4.38:
1208
-
resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
1389
+
postcss@8.5.6:
1390
+
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
1209
1391
engines: {node: ^10 || ^12 || >=14}
1210
-
dependencies:
1211
-
nanoid: 3.3.7
1212
-
picocolors: 1.0.0
1213
-
source-map-js: 1.2.0
1214
-
dev: true
1392
+
1393
+
preact@10.28.0:
1394
+
resolution: {integrity: sha512-rytDAoiXr3+t6OIP3WGlDd0ouCUG1iCWzkcY3++Nreuoi17y6T5i/zRhe6uYfoVcxq6YU+sBtJouuRDsq8vvqA==}
1215
1395
1216
-
/prettier-plugin-css-order@2.1.2(prettier@3.2.5):
1396
+
prettier-plugin-css-order@2.1.2:
1217
1397
resolution: {integrity: sha512-vomxPjHI6pOMYcBuouSJHxxQClJXaUpU9rsV9IAO2wrSTZILRRlrxAAR8t9UF6wtczLkLfNRFUwM+ZbGXOONUA==}
1218
1398
engines: {node: '>=16'}
1219
1399
peerDependencies:
1220
1400
prettier: 3.x
1221
-
dependencies:
1222
-
css-declaration-sorter: 7.2.0
1223
-
postcss-less: 6.0.0
1224
-
postcss-scss: 4.0.9
1225
-
prettier: 3.2.5
1226
-
transitivePeerDependencies:
1227
-
- postcss
1228
-
dev: true
1401
+
1402
+
prettier-plugin-svelte@3.4.0:
1403
+
resolution: {integrity: sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==}
1404
+
peerDependencies:
1405
+
prettier: ^3.0.0
1406
+
svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0
1407
+
1408
+
prettier@2.8.8:
1409
+
resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
1410
+
engines: {node: '>=10.13.0'}
1411
+
hasBin: true
1229
1412
1230
-
/prettier@3.2.5:
1231
-
resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==}
1413
+
prettier@3.7.4:
1414
+
resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==}
1232
1415
engines: {node: '>=14'}
1233
1416
hasBin: true
1234
-
dev: true
1235
1417
1236
-
/punycode@2.3.1:
1418
+
punycode@2.3.1:
1237
1419
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
1238
1420
engines: {node: '>=6'}
1239
-
dev: true
1421
+
1422
+
quansync@0.2.11:
1423
+
resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==}
1424
+
1425
+
queue-microtask@1.2.3:
1426
+
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
1427
+
1428
+
read-yaml-file@1.1.0:
1429
+
resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==}
1430
+
engines: {node: '>=6'}
1431
+
1432
+
readdirp@4.1.2:
1433
+
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
1434
+
engines: {node: '>= 14.18.0'}
1435
+
1436
+
require-from-string@2.0.2:
1437
+
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
1438
+
engines: {node: '>=0.10.0'}
1240
1439
1241
-
/resolve@1.19.0:
1242
-
resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==}
1243
-
dependencies:
1244
-
is-core-module: 2.13.1
1245
-
path-parse: 1.0.7
1246
-
dev: true
1440
+
resolve-from@5.0.0:
1441
+
resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
1442
+
engines: {node: '>=8'}
1247
1443
1248
-
/resolve@1.22.8:
1249
-
resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
1444
+
resolve@1.22.11:
1445
+
resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==}
1446
+
engines: {node: '>= 0.4'}
1250
1447
hasBin: true
1251
-
dependencies:
1252
-
is-core-module: 2.13.1
1253
-
path-parse: 1.0.7
1254
-
supports-preserve-symlinks-flag: 1.0.0
1255
-
dev: true
1448
+
1449
+
reusify@1.1.0:
1450
+
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
1451
+
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
1256
1452
1257
-
/rollup@4.14.2:
1258
-
resolution: {integrity: sha512-WkeoTWvuBoFjFAhsEOHKRoZ3r9GfTyhh7Vff1zwebEFLEFjT1lG3784xEgKiTa7E+e70vsC81roVL2MP4tgEEQ==}
1453
+
rollup@4.53.3:
1454
+
resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==}
1259
1455
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
1260
1456
hasBin: true
1261
-
dependencies:
1262
-
'@types/estree': 1.0.5
1263
-
optionalDependencies:
1264
-
'@rollup/rollup-android-arm-eabi': 4.14.2
1265
-
'@rollup/rollup-android-arm64': 4.14.2
1266
-
'@rollup/rollup-darwin-arm64': 4.14.2
1267
-
'@rollup/rollup-darwin-x64': 4.14.2
1268
-
'@rollup/rollup-linux-arm-gnueabihf': 4.14.2
1269
-
'@rollup/rollup-linux-arm64-gnu': 4.14.2
1270
-
'@rollup/rollup-linux-arm64-musl': 4.14.2
1271
-
'@rollup/rollup-linux-powerpc64le-gnu': 4.14.2
1272
-
'@rollup/rollup-linux-riscv64-gnu': 4.14.2
1273
-
'@rollup/rollup-linux-s390x-gnu': 4.14.2
1274
-
'@rollup/rollup-linux-x64-gnu': 4.14.2
1275
-
'@rollup/rollup-linux-x64-musl': 4.14.2
1276
-
'@rollup/rollup-win32-arm64-msvc': 4.14.2
1277
-
'@rollup/rollup-win32-ia32-msvc': 4.14.2
1278
-
'@rollup/rollup-win32-x64-msvc': 4.14.2
1279
-
fsevents: 2.3.3
1280
-
dev: true
1457
+
1458
+
run-parallel@1.2.0:
1459
+
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
1460
+
1461
+
sade@1.8.1:
1462
+
resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
1463
+
engines: {node: '>=6'}
1464
+
1465
+
safer-buffer@2.1.2:
1466
+
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
1281
1467
1282
-
/semver@6.3.1:
1468
+
semver@6.3.1:
1283
1469
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
1284
1470
hasBin: true
1285
-
dev: true
1286
1471
1287
-
/semver@7.5.4:
1472
+
semver@7.5.4:
1288
1473
resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
1289
1474
engines: {node: '>=10'}
1290
1475
hasBin: true
1291
-
dependencies:
1292
-
lru-cache: 6.0.0
1293
-
dev: true
1294
1476
1295
-
/semver@7.6.0:
1296
-
resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==}
1477
+
semver@7.7.3:
1478
+
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
1297
1479
engines: {node: '>=10'}
1298
1480
hasBin: true
1299
-
dependencies:
1300
-
lru-cache: 6.0.0
1301
-
dev: true
1481
+
1482
+
shebang-command@2.0.0:
1483
+
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
1484
+
engines: {node: '>=8'}
1485
+
1486
+
shebang-regex@3.0.0:
1487
+
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
1488
+
engines: {node: '>=8'}
1489
+
1490
+
signal-exit@4.1.0:
1491
+
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
1492
+
engines: {node: '>=14'}
1493
+
1494
+
simple-code-frame@1.3.0:
1495
+
resolution: {integrity: sha512-MB4pQmETUBlNs62BBeRjIFGeuy/x6gGKh7+eRUemn1rCFhqo7K+4slPqsyizCbcbYLnaYqaoZ2FWsZ/jN06D8w==}
1496
+
1497
+
slash@3.0.0:
1498
+
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
1499
+
engines: {node: '>=8'}
1302
1500
1303
-
/source-map-js@1.2.0:
1304
-
resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
1501
+
source-map-js@1.2.1:
1502
+
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
1305
1503
engines: {node: '>=0.10.0'}
1306
-
dev: true
1504
+
1505
+
source-map-support@0.5.21:
1506
+
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
1307
1507
1308
-
/source-map@0.6.1:
1508
+
source-map@0.6.1:
1309
1509
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
1310
1510
engines: {node: '>=0.10.0'}
1311
-
dev: true
1511
+
1512
+
source-map@0.7.6:
1513
+
resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==}
1514
+
engines: {node: '>= 12'}
1312
1515
1313
-
/sprintf-js@1.0.3:
1516
+
spawndamnit@3.0.1:
1517
+
resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==}
1518
+
1519
+
sprintf-js@1.0.3:
1314
1520
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
1315
-
dev: true
1521
+
1522
+
stack-trace@1.0.0-pre2:
1523
+
resolution: {integrity: sha512-2ztBJRek8IVofG9DBJqdy2N5kulaacX30Nz7xmkYF6ale9WBVmIy6mFBchvGX7Vx/MyjBhx+Rcxqrj+dbOnQ6A==}
1524
+
engines: {node: '>=16'}
1316
1525
1317
-
/string-argv@0.3.2:
1526
+
string-argv@0.3.2:
1318
1527
resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
1319
1528
engines: {node: '>=0.6.19'}
1320
-
dev: true
1321
1529
1322
-
/strip-json-comments@3.1.1:
1323
-
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
1530
+
strip-ansi@6.0.1:
1531
+
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
1324
1532
engines: {node: '>=8'}
1325
-
dev: true
1326
1533
1327
-
/supports-color@5.5.0:
1328
-
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
1534
+
strip-bom@3.0.0:
1535
+
resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
1329
1536
engines: {node: '>=4'}
1330
-
dependencies:
1331
-
has-flag: 3.0.0
1332
-
dev: true
1333
1537
1334
-
/supports-color@8.1.1:
1538
+
strip-json-comments@3.1.1:
1539
+
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
1540
+
engines: {node: '>=8'}
1541
+
1542
+
supports-color@8.1.1:
1335
1543
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
1336
1544
engines: {node: '>=10'}
1337
-
dependencies:
1338
-
has-flag: 4.0.0
1339
-
dev: true
1340
1545
1341
-
/supports-preserve-symlinks-flag@1.0.0:
1546
+
supports-preserve-symlinks-flag@1.0.0:
1342
1547
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
1343
1548
engines: {node: '>= 0.4'}
1344
-
dev: true
1549
+
1550
+
svelte-check@4.3.4:
1551
+
resolution: {integrity: sha512-DVWvxhBrDsd+0hHWKfjP99lsSXASeOhHJYyuKOFYJcP7ThfSCKgjVarE8XfuMWpS5JV3AlDf+iK1YGGo2TACdw==}
1552
+
engines: {node: '>= 18.0.0'}
1553
+
hasBin: true
1554
+
peerDependencies:
1555
+
svelte: ^4.0.0 || ^5.0.0-next.0
1556
+
typescript: '>=5.0.0'
1345
1557
1346
-
/to-fast-properties@2.0.0:
1347
-
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
1348
-
engines: {node: '>=4'}
1349
-
dev: true
1558
+
svelte@5.45.5:
1559
+
resolution: {integrity: sha512-2074U+vObO5Zs8/qhxtBwdi6ZXNIhEBTzNmUFjiZexLxTdt9vq96D/0pnQELl6YcpLMD7pZ2dhXKByfGS8SAdg==}
1560
+
engines: {node: '>=18'}
1350
1561
1351
-
/typescript@5.4.2:
1352
-
resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==}
1562
+
term-size@2.2.1:
1563
+
resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==}
1564
+
engines: {node: '>=8'}
1565
+
1566
+
terser@5.44.1:
1567
+
resolution: {integrity: sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==}
1568
+
engines: {node: '>=10'}
1569
+
hasBin: true
1570
+
1571
+
tinyglobby@0.2.15:
1572
+
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
1573
+
engines: {node: '>=12.0.0'}
1574
+
1575
+
to-regex-range@5.0.1:
1576
+
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
1577
+
engines: {node: '>=8.0'}
1578
+
1579
+
tslib@2.8.1:
1580
+
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
1581
+
1582
+
typescript@5.8.2:
1583
+
resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==}
1353
1584
engines: {node: '>=14.17'}
1354
1585
hasBin: true
1355
-
dev: true
1356
1586
1357
-
/typescript@5.4.5:
1358
-
resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==}
1587
+
typescript@5.8.3:
1588
+
resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
1359
1589
engines: {node: '>=14.17'}
1360
1590
hasBin: true
1361
-
dev: true
1591
+
1592
+
ufo@1.6.1:
1593
+
resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==}
1362
1594
1363
-
/universalify@0.1.2:
1595
+
undici-types@7.16.0:
1596
+
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
1597
+
1598
+
universalify@0.1.2:
1364
1599
resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
1365
1600
engines: {node: '>= 4.0.0'}
1366
-
dev: true
1601
+
1602
+
universalify@2.0.1:
1603
+
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
1604
+
engines: {node: '>= 10.0.0'}
1367
1605
1368
-
/update-browserslist-db@1.0.13(browserslist@4.23.0):
1369
-
resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==}
1606
+
update-browserslist-db@1.2.1:
1607
+
resolution: {integrity: sha512-R9NcHbbZ45RoWfTdhn1J9SS7zxNvlddv4YRrHTUaFdtjbmfncfedB45EC9IaqJQ97iAR1GZgOfyRQO+ExIF6EQ==}
1370
1608
hasBin: true
1371
1609
peerDependencies:
1372
1610
browserslist: '>= 4.21.0'
1373
-
dependencies:
1374
-
browserslist: 4.23.0
1375
-
escalade: 3.1.2
1376
-
picocolors: 1.0.0
1377
-
dev: true
1378
1611
1379
-
/uri-js@4.4.1:
1612
+
uri-js@4.4.1:
1380
1613
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
1381
-
dependencies:
1382
-
punycode: 2.3.1
1383
-
dev: true
1384
1614
1385
-
/validator@13.11.0:
1386
-
resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==}
1387
-
engines: {node: '>= 0.10'}
1388
-
dev: true
1389
-
1390
-
/vite-plugin-dts@3.8.1(typescript@5.4.5)(vite@5.2.8):
1391
-
resolution: {integrity: sha512-zEYyQxH7lKto1VTKZHF3ZZeOPkkJgnMrePY4VxDHfDSvDjmYMMfWjZxYmNwW8QxbaItWJQhhXY+geAbyNphI7g==}
1392
-
engines: {node: ^14.18.0 || >=16.0.0}
1615
+
vite-plugin-dts@4.5.4:
1616
+
resolution: {integrity: sha512-d4sOM8M/8z7vRXHHq/ebbblfaxENjogAAekcfcDCCwAyvGqnPrc7f4NZbvItS+g4WTgerW0xDwSz5qz11JT3vg==}
1393
1617
peerDependencies:
1394
1618
typescript: '*'
1395
1619
vite: '*'
1396
1620
peerDependenciesMeta:
1397
1621
vite:
1398
1622
optional: true
1399
-
dependencies:
1400
-
'@microsoft/api-extractor': 7.43.0
1401
-
'@rollup/pluginutils': 5.1.0
1402
-
'@vue/language-core': 1.8.27(typescript@5.4.5)
1403
-
debug: 4.3.4
1404
-
kolorist: 1.8.0
1405
-
magic-string: 0.30.9
1406
-
typescript: 5.4.5
1407
-
vite: 5.2.8
1408
-
vue-tsc: 1.8.27(typescript@5.4.5)
1409
-
transitivePeerDependencies:
1410
-
- '@types/node'
1411
-
- rollup
1412
-
- supports-color
1413
-
dev: true
1623
+
1624
+
vite-prerender-plugin@0.5.12:
1625
+
resolution: {integrity: sha512-EiwhbMn+flg14EysbLTmZSzq8NGTxhytgK3bf4aGRF1evWLGwZiHiUJ1KZDvbxgKbMf2pG6fJWGEa3UZXOnR1g==}
1626
+
peerDependencies:
1627
+
vite: 5.x || 6.x || 7.x
1414
1628
1415
-
/vite@5.2.8:
1416
-
resolution: {integrity: sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==}
1417
-
engines: {node: ^18.0.0 || >=20.0.0}
1629
+
vite@7.2.6:
1630
+
resolution: {integrity: sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==}
1631
+
engines: {node: ^20.19.0 || >=22.12.0}
1418
1632
hasBin: true
1419
1633
peerDependencies:
1420
-
'@types/node': ^18.0.0 || >=20.0.0
1421
-
less: '*'
1634
+
'@types/node': ^20.19.0 || >=22.12.0
1635
+
jiti: '>=1.21.0'
1636
+
less: ^4.0.0
1422
1637
lightningcss: ^1.21.0
1423
-
sass: '*'
1424
-
stylus: '*'
1425
-
sugarss: '*'
1426
-
terser: ^5.4.0
1638
+
sass: ^1.70.0
1639
+
sass-embedded: ^1.70.0
1640
+
stylus: '>=0.54.8'
1641
+
sugarss: ^5.0.0
1642
+
terser: ^5.16.0
1643
+
tsx: ^4.8.1
1644
+
yaml: ^2.4.2
1427
1645
peerDependenciesMeta:
1428
1646
'@types/node':
1647
+
optional: true
1648
+
jiti:
1429
1649
optional: true
1430
1650
less:
1431
1651
optional: true
···
1433
1653
optional: true
1434
1654
sass:
1435
1655
optional: true
1656
+
sass-embedded:
1657
+
optional: true
1436
1658
stylus:
1437
1659
optional: true
1438
1660
sugarss:
1439
1661
optional: true
1440
1662
terser:
1441
1663
optional: true
1664
+
tsx:
1665
+
optional: true
1666
+
yaml:
1667
+
optional: true
1668
+
1669
+
vitefu@1.1.1:
1670
+
resolution: {integrity: sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==}
1671
+
peerDependencies:
1672
+
vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0
1673
+
peerDependenciesMeta:
1674
+
vite:
1675
+
optional: true
1676
+
1677
+
vscode-uri@3.1.0:
1678
+
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
1679
+
1680
+
which@2.0.2:
1681
+
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
1682
+
engines: {node: '>= 8'}
1683
+
hasBin: true
1684
+
1685
+
yallist@3.1.1:
1686
+
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
1687
+
1688
+
yallist@4.0.0:
1689
+
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
1690
+
1691
+
zimmerframe@1.1.4:
1692
+
resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==}
1693
+
1694
+
snapshots:
1695
+
1696
+
'@atcute/atproto@3.1.9':
1442
1697
dependencies:
1443
-
esbuild: 0.20.2
1444
-
postcss: 8.4.38
1445
-
rollup: 4.14.2
1698
+
'@atcute/lexicons': 1.2.5
1699
+
1700
+
'@atcute/bluesky-richtext-parser@1.0.7': {}
1701
+
1702
+
'@atcute/bluesky-richtext-segmenter@2.0.4':
1703
+
dependencies:
1704
+
'@atcute/bluesky': 3.2.11
1705
+
'@atcute/lexicons': 1.2.5
1706
+
1707
+
'@atcute/bluesky@2.1.1(@atcute/client@3.1.0)':
1708
+
dependencies:
1709
+
'@atcute/client': 3.1.0
1710
+
1711
+
'@atcute/bluesky@3.2.11':
1712
+
dependencies:
1713
+
'@atcute/atproto': 3.1.9
1714
+
'@atcute/lexicons': 1.2.5
1715
+
1716
+
'@atcute/client@3.1.0': {}
1717
+
1718
+
'@atcute/lexicons@1.2.5':
1719
+
dependencies:
1720
+
'@standard-schema/spec': 1.0.0
1721
+
esm-env: 1.2.2
1722
+
1723
+
'@babel/code-frame@7.27.1':
1724
+
dependencies:
1725
+
'@babel/helper-validator-identifier': 7.28.5
1726
+
js-tokens: 4.0.0
1727
+
picocolors: 1.1.1
1728
+
1729
+
'@babel/compat-data@7.28.5': {}
1730
+
1731
+
'@babel/core@7.28.5':
1732
+
dependencies:
1733
+
'@babel/code-frame': 7.27.1
1734
+
'@babel/generator': 7.28.5
1735
+
'@babel/helper-compilation-targets': 7.27.2
1736
+
'@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5)
1737
+
'@babel/helpers': 7.28.4
1738
+
'@babel/parser': 7.28.5
1739
+
'@babel/template': 7.27.2
1740
+
'@babel/traverse': 7.28.5
1741
+
'@babel/types': 7.28.5
1742
+
'@jridgewell/remapping': 2.3.5
1743
+
convert-source-map: 2.0.0
1744
+
debug: 4.4.3
1745
+
gensync: 1.0.0-beta.2
1746
+
json5: 2.2.3
1747
+
semver: 6.3.1
1748
+
transitivePeerDependencies:
1749
+
- supports-color
1750
+
1751
+
'@babel/generator@7.28.5':
1752
+
dependencies:
1753
+
'@babel/parser': 7.28.5
1754
+
'@babel/types': 7.28.5
1755
+
'@jridgewell/gen-mapping': 0.3.13
1756
+
'@jridgewell/trace-mapping': 0.3.31
1757
+
jsesc: 3.1.0
1758
+
1759
+
'@babel/helper-annotate-as-pure@7.27.3':
1760
+
dependencies:
1761
+
'@babel/types': 7.28.5
1762
+
1763
+
'@babel/helper-compilation-targets@7.27.2':
1764
+
dependencies:
1765
+
'@babel/compat-data': 7.28.5
1766
+
'@babel/helper-validator-option': 7.27.1
1767
+
browserslist: 4.28.1
1768
+
lru-cache: 5.1.1
1769
+
semver: 6.3.1
1770
+
1771
+
'@babel/helper-globals@7.28.0': {}
1772
+
1773
+
'@babel/helper-module-imports@7.27.1':
1774
+
dependencies:
1775
+
'@babel/traverse': 7.28.5
1776
+
'@babel/types': 7.28.5
1777
+
transitivePeerDependencies:
1778
+
- supports-color
1779
+
1780
+
'@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)':
1781
+
dependencies:
1782
+
'@babel/core': 7.28.5
1783
+
'@babel/helper-module-imports': 7.27.1
1784
+
'@babel/helper-validator-identifier': 7.28.5
1785
+
'@babel/traverse': 7.28.5
1786
+
transitivePeerDependencies:
1787
+
- supports-color
1788
+
1789
+
'@babel/helper-plugin-utils@7.27.1': {}
1790
+
1791
+
'@babel/helper-string-parser@7.27.1': {}
1792
+
1793
+
'@babel/helper-validator-identifier@7.28.5': {}
1794
+
1795
+
'@babel/helper-validator-option@7.27.1': {}
1796
+
1797
+
'@babel/helpers@7.28.4':
1798
+
dependencies:
1799
+
'@babel/template': 7.27.2
1800
+
'@babel/types': 7.28.5
1801
+
1802
+
'@babel/parser@7.28.5':
1803
+
dependencies:
1804
+
'@babel/types': 7.28.5
1805
+
1806
+
'@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)':
1807
+
dependencies:
1808
+
'@babel/core': 7.28.5
1809
+
'@babel/helper-plugin-utils': 7.27.1
1810
+
1811
+
'@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.28.5)':
1812
+
dependencies:
1813
+
'@babel/core': 7.28.5
1814
+
'@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.5)
1815
+
transitivePeerDependencies:
1816
+
- supports-color
1817
+
1818
+
'@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.5)':
1819
+
dependencies:
1820
+
'@babel/core': 7.28.5
1821
+
'@babel/helper-annotate-as-pure': 7.27.3
1822
+
'@babel/helper-module-imports': 7.27.1
1823
+
'@babel/helper-plugin-utils': 7.27.1
1824
+
'@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5)
1825
+
'@babel/types': 7.28.5
1826
+
transitivePeerDependencies:
1827
+
- supports-color
1828
+
1829
+
'@babel/runtime@7.28.4': {}
1830
+
1831
+
'@babel/template@7.27.2':
1832
+
dependencies:
1833
+
'@babel/code-frame': 7.27.1
1834
+
'@babel/parser': 7.28.5
1835
+
'@babel/types': 7.28.5
1836
+
1837
+
'@babel/traverse@7.28.5':
1838
+
dependencies:
1839
+
'@babel/code-frame': 7.27.1
1840
+
'@babel/generator': 7.28.5
1841
+
'@babel/helper-globals': 7.28.0
1842
+
'@babel/parser': 7.28.5
1843
+
'@babel/template': 7.27.2
1844
+
'@babel/types': 7.28.5
1845
+
debug: 4.4.3
1846
+
transitivePeerDependencies:
1847
+
- supports-color
1848
+
1849
+
'@babel/types@7.28.5':
1850
+
dependencies:
1851
+
'@babel/helper-string-parser': 7.27.1
1852
+
'@babel/helper-validator-identifier': 7.28.5
1853
+
1854
+
'@changesets/apply-release-plan@7.0.14':
1855
+
dependencies:
1856
+
'@changesets/config': 3.1.2
1857
+
'@changesets/get-version-range-type': 0.4.0
1858
+
'@changesets/git': 3.0.4
1859
+
'@changesets/should-skip-package': 0.1.2
1860
+
'@changesets/types': 6.1.0
1861
+
'@manypkg/get-packages': 1.1.3
1862
+
detect-indent: 6.1.0
1863
+
fs-extra: 7.0.1
1864
+
lodash.startcase: 4.4.0
1865
+
outdent: 0.5.0
1866
+
prettier: 2.8.8
1867
+
resolve-from: 5.0.0
1868
+
semver: 7.7.3
1869
+
1870
+
'@changesets/assemble-release-plan@6.0.9':
1871
+
dependencies:
1872
+
'@changesets/errors': 0.2.0
1873
+
'@changesets/get-dependents-graph': 2.1.3
1874
+
'@changesets/should-skip-package': 0.1.2
1875
+
'@changesets/types': 6.1.0
1876
+
'@manypkg/get-packages': 1.1.3
1877
+
semver: 7.7.3
1878
+
1879
+
'@changesets/changelog-git@0.2.1':
1880
+
dependencies:
1881
+
'@changesets/types': 6.1.0
1882
+
1883
+
'@changesets/cli@2.29.8(@types/node@24.10.1)':
1884
+
dependencies:
1885
+
'@changesets/apply-release-plan': 7.0.14
1886
+
'@changesets/assemble-release-plan': 6.0.9
1887
+
'@changesets/changelog-git': 0.2.1
1888
+
'@changesets/config': 3.1.2
1889
+
'@changesets/errors': 0.2.0
1890
+
'@changesets/get-dependents-graph': 2.1.3
1891
+
'@changesets/get-release-plan': 4.0.14
1892
+
'@changesets/git': 3.0.4
1893
+
'@changesets/logger': 0.1.1
1894
+
'@changesets/pre': 2.0.2
1895
+
'@changesets/read': 0.6.6
1896
+
'@changesets/should-skip-package': 0.1.2
1897
+
'@changesets/types': 6.1.0
1898
+
'@changesets/write': 0.4.0
1899
+
'@inquirer/external-editor': 1.0.3(@types/node@24.10.1)
1900
+
'@manypkg/get-packages': 1.1.3
1901
+
ansi-colors: 4.1.3
1902
+
ci-info: 3.9.0
1903
+
enquirer: 2.4.1
1904
+
fs-extra: 7.0.1
1905
+
mri: 1.2.0
1906
+
p-limit: 2.3.0
1907
+
package-manager-detector: 0.2.11
1908
+
picocolors: 1.1.1
1909
+
resolve-from: 5.0.0
1910
+
semver: 7.7.3
1911
+
spawndamnit: 3.0.1
1912
+
term-size: 2.2.1
1913
+
transitivePeerDependencies:
1914
+
- '@types/node'
1915
+
1916
+
'@changesets/config@3.1.2':
1917
+
dependencies:
1918
+
'@changesets/errors': 0.2.0
1919
+
'@changesets/get-dependents-graph': 2.1.3
1920
+
'@changesets/logger': 0.1.1
1921
+
'@changesets/types': 6.1.0
1922
+
'@manypkg/get-packages': 1.1.3
1923
+
fs-extra: 7.0.1
1924
+
micromatch: 4.0.8
1925
+
1926
+
'@changesets/errors@0.2.0':
1927
+
dependencies:
1928
+
extendable-error: 0.1.7
1929
+
1930
+
'@changesets/get-dependents-graph@2.1.3':
1931
+
dependencies:
1932
+
'@changesets/types': 6.1.0
1933
+
'@manypkg/get-packages': 1.1.3
1934
+
picocolors: 1.1.1
1935
+
semver: 7.7.3
1936
+
1937
+
'@changesets/get-release-plan@4.0.14':
1938
+
dependencies:
1939
+
'@changesets/assemble-release-plan': 6.0.9
1940
+
'@changesets/config': 3.1.2
1941
+
'@changesets/pre': 2.0.2
1942
+
'@changesets/read': 0.6.6
1943
+
'@changesets/types': 6.1.0
1944
+
'@manypkg/get-packages': 1.1.3
1945
+
1946
+
'@changesets/get-version-range-type@0.4.0': {}
1947
+
1948
+
'@changesets/git@3.0.4':
1949
+
dependencies:
1950
+
'@changesets/errors': 0.2.0
1951
+
'@manypkg/get-packages': 1.1.3
1952
+
is-subdir: 1.2.0
1953
+
micromatch: 4.0.8
1954
+
spawndamnit: 3.0.1
1955
+
1956
+
'@changesets/logger@0.1.1':
1957
+
dependencies:
1958
+
picocolors: 1.1.1
1959
+
1960
+
'@changesets/parse@0.4.2':
1961
+
dependencies:
1962
+
'@changesets/types': 6.1.0
1963
+
js-yaml: 4.1.1
1964
+
1965
+
'@changesets/pre@2.0.2':
1966
+
dependencies:
1967
+
'@changesets/errors': 0.2.0
1968
+
'@changesets/types': 6.1.0
1969
+
'@manypkg/get-packages': 1.1.3
1970
+
fs-extra: 7.0.1
1971
+
1972
+
'@changesets/read@0.6.6':
1973
+
dependencies:
1974
+
'@changesets/git': 3.0.4
1975
+
'@changesets/logger': 0.1.1
1976
+
'@changesets/parse': 0.4.2
1977
+
'@changesets/types': 6.1.0
1978
+
fs-extra: 7.0.1
1979
+
p-filter: 2.1.0
1980
+
picocolors: 1.1.1
1981
+
1982
+
'@changesets/should-skip-package@0.1.2':
1983
+
dependencies:
1984
+
'@changesets/types': 6.1.0
1985
+
'@manypkg/get-packages': 1.1.3
1986
+
1987
+
'@changesets/types@4.1.0': {}
1988
+
1989
+
'@changesets/types@6.1.0': {}
1990
+
1991
+
'@changesets/write@0.4.0':
1992
+
dependencies:
1993
+
'@changesets/types': 6.1.0
1994
+
fs-extra: 7.0.1
1995
+
human-id: 4.1.3
1996
+
prettier: 2.8.8
1997
+
1998
+
'@esbuild/aix-ppc64@0.25.12':
1999
+
optional: true
2000
+
2001
+
'@esbuild/android-arm64@0.25.12':
2002
+
optional: true
2003
+
2004
+
'@esbuild/android-arm@0.25.12':
2005
+
optional: true
2006
+
2007
+
'@esbuild/android-x64@0.25.12':
2008
+
optional: true
2009
+
2010
+
'@esbuild/darwin-arm64@0.25.12':
2011
+
optional: true
2012
+
2013
+
'@esbuild/darwin-x64@0.25.12':
2014
+
optional: true
2015
+
2016
+
'@esbuild/freebsd-arm64@0.25.12':
2017
+
optional: true
2018
+
2019
+
'@esbuild/freebsd-x64@0.25.12':
2020
+
optional: true
2021
+
2022
+
'@esbuild/linux-arm64@0.25.12':
2023
+
optional: true
2024
+
2025
+
'@esbuild/linux-arm@0.25.12':
2026
+
optional: true
2027
+
2028
+
'@esbuild/linux-ia32@0.25.12':
2029
+
optional: true
2030
+
2031
+
'@esbuild/linux-loong64@0.25.12':
2032
+
optional: true
2033
+
2034
+
'@esbuild/linux-mips64el@0.25.12':
2035
+
optional: true
2036
+
2037
+
'@esbuild/linux-ppc64@0.25.12':
2038
+
optional: true
2039
+
2040
+
'@esbuild/linux-riscv64@0.25.12':
2041
+
optional: true
2042
+
2043
+
'@esbuild/linux-s390x@0.25.12':
2044
+
optional: true
2045
+
2046
+
'@esbuild/linux-x64@0.25.12':
2047
+
optional: true
2048
+
2049
+
'@esbuild/netbsd-arm64@0.25.12':
2050
+
optional: true
2051
+
2052
+
'@esbuild/netbsd-x64@0.25.12':
2053
+
optional: true
2054
+
2055
+
'@esbuild/openbsd-arm64@0.25.12':
2056
+
optional: true
2057
+
2058
+
'@esbuild/openbsd-x64@0.25.12':
2059
+
optional: true
2060
+
2061
+
'@esbuild/openharmony-arm64@0.25.12':
2062
+
optional: true
2063
+
2064
+
'@esbuild/sunos-x64@0.25.12':
2065
+
optional: true
2066
+
2067
+
'@esbuild/win32-arm64@0.25.12':
2068
+
optional: true
2069
+
2070
+
'@esbuild/win32-ia32@0.25.12':
2071
+
optional: true
2072
+
2073
+
'@esbuild/win32-x64@0.25.12':
2074
+
optional: true
2075
+
2076
+
'@inquirer/external-editor@1.0.3(@types/node@24.10.1)':
2077
+
dependencies:
2078
+
chardet: 2.1.1
2079
+
iconv-lite: 0.7.0
2080
+
optionalDependencies:
2081
+
'@types/node': 24.10.1
2082
+
2083
+
'@isaacs/balanced-match@4.0.1': {}
2084
+
2085
+
'@isaacs/brace-expansion@5.0.0':
2086
+
dependencies:
2087
+
'@isaacs/balanced-match': 4.0.1
2088
+
2089
+
'@jridgewell/gen-mapping@0.3.13':
2090
+
dependencies:
2091
+
'@jridgewell/sourcemap-codec': 1.5.5
2092
+
'@jridgewell/trace-mapping': 0.3.31
2093
+
2094
+
'@jridgewell/remapping@2.3.5':
2095
+
dependencies:
2096
+
'@jridgewell/gen-mapping': 0.3.13
2097
+
'@jridgewell/trace-mapping': 0.3.31
2098
+
2099
+
'@jridgewell/resolve-uri@3.1.2': {}
2100
+
2101
+
'@jridgewell/source-map@0.3.11':
2102
+
dependencies:
2103
+
'@jridgewell/gen-mapping': 0.3.13
2104
+
'@jridgewell/trace-mapping': 0.3.31
2105
+
2106
+
'@jridgewell/sourcemap-codec@1.5.5': {}
2107
+
2108
+
'@jridgewell/trace-mapping@0.3.31':
2109
+
dependencies:
2110
+
'@jridgewell/resolve-uri': 3.1.2
2111
+
'@jridgewell/sourcemap-codec': 1.5.5
2112
+
2113
+
'@manypkg/find-root@1.1.0':
2114
+
dependencies:
2115
+
'@babel/runtime': 7.28.4
2116
+
'@types/node': 12.20.55
2117
+
find-up: 4.1.0
2118
+
fs-extra: 8.1.0
2119
+
2120
+
'@manypkg/get-packages@1.1.3':
2121
+
dependencies:
2122
+
'@babel/runtime': 7.28.4
2123
+
'@changesets/types': 4.1.0
2124
+
'@manypkg/find-root': 1.1.0
2125
+
fs-extra: 8.1.0
2126
+
globby: 11.1.0
2127
+
read-yaml-file: 1.1.0
2128
+
2129
+
'@microsoft/api-extractor-model@7.32.1(@types/node@24.10.1)':
2130
+
dependencies:
2131
+
'@microsoft/tsdoc': 0.16.0
2132
+
'@microsoft/tsdoc-config': 0.18.0
2133
+
'@rushstack/node-core-library': 5.19.0(@types/node@24.10.1)
2134
+
transitivePeerDependencies:
2135
+
- '@types/node'
2136
+
2137
+
'@microsoft/api-extractor@7.55.1(@types/node@24.10.1)':
2138
+
dependencies:
2139
+
'@microsoft/api-extractor-model': 7.32.1(@types/node@24.10.1)
2140
+
'@microsoft/tsdoc': 0.16.0
2141
+
'@microsoft/tsdoc-config': 0.18.0
2142
+
'@rushstack/node-core-library': 5.19.0(@types/node@24.10.1)
2143
+
'@rushstack/rig-package': 0.6.0
2144
+
'@rushstack/terminal': 0.19.4(@types/node@24.10.1)
2145
+
'@rushstack/ts-command-line': 5.1.4(@types/node@24.10.1)
2146
+
diff: 8.0.2
2147
+
lodash: 4.17.21
2148
+
minimatch: 10.0.3
2149
+
resolve: 1.22.11
2150
+
semver: 7.5.4
2151
+
source-map: 0.6.1
2152
+
typescript: 5.8.2
2153
+
transitivePeerDependencies:
2154
+
- '@types/node'
2155
+
2156
+
'@microsoft/tsdoc-config@0.18.0':
2157
+
dependencies:
2158
+
'@microsoft/tsdoc': 0.16.0
2159
+
ajv: 8.12.0
2160
+
jju: 1.4.0
2161
+
resolve: 1.22.11
2162
+
2163
+
'@microsoft/tsdoc@0.16.0': {}
2164
+
2165
+
'@nodelib/fs.scandir@2.1.5':
2166
+
dependencies:
2167
+
'@nodelib/fs.stat': 2.0.5
2168
+
run-parallel: 1.2.0
2169
+
2170
+
'@nodelib/fs.stat@2.0.5': {}
2171
+
2172
+
'@nodelib/fs.walk@1.2.8':
2173
+
dependencies:
2174
+
'@nodelib/fs.scandir': 2.1.5
2175
+
fastq: 1.19.1
2176
+
2177
+
'@preact/preset-vite@2.10.2(@babel/core@7.28.5)(preact@10.28.0)(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1))':
2178
+
dependencies:
2179
+
'@babel/core': 7.28.5
2180
+
'@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.5)
2181
+
'@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.5)
2182
+
'@prefresh/vite': 2.4.11(preact@10.28.0)(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1))
2183
+
'@rollup/pluginutils': 4.2.1
2184
+
babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.28.5)
2185
+
debug: 4.4.3
2186
+
picocolors: 1.1.1
2187
+
vite: 7.2.6(@types/node@24.10.1)(terser@5.44.1)
2188
+
vite-prerender-plugin: 0.5.12(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1))
2189
+
transitivePeerDependencies:
2190
+
- preact
2191
+
- supports-color
2192
+
2193
+
'@prefresh/babel-plugin@0.5.2': {}
2194
+
2195
+
'@prefresh/core@1.5.9(preact@10.28.0)':
2196
+
dependencies:
2197
+
preact: 10.28.0
2198
+
2199
+
'@prefresh/utils@1.2.1': {}
2200
+
2201
+
'@prefresh/vite@2.4.11(preact@10.28.0)(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1))':
2202
+
dependencies:
2203
+
'@babel/core': 7.28.5
2204
+
'@prefresh/babel-plugin': 0.5.2
2205
+
'@prefresh/core': 1.5.9(preact@10.28.0)
2206
+
'@prefresh/utils': 1.2.1
2207
+
'@rollup/pluginutils': 4.2.1
2208
+
preact: 10.28.0
2209
+
vite: 7.2.6(@types/node@24.10.1)(terser@5.44.1)
2210
+
transitivePeerDependencies:
2211
+
- supports-color
2212
+
2213
+
'@rollup/pluginutils@4.2.1':
2214
+
dependencies:
2215
+
estree-walker: 2.0.2
2216
+
picomatch: 2.3.1
2217
+
2218
+
'@rollup/pluginutils@5.3.0(rollup@4.53.3)':
2219
+
dependencies:
2220
+
'@types/estree': 1.0.8
2221
+
estree-walker: 2.0.2
2222
+
picomatch: 4.0.3
2223
+
optionalDependencies:
2224
+
rollup: 4.53.3
2225
+
2226
+
'@rollup/rollup-android-arm-eabi@4.53.3':
2227
+
optional: true
2228
+
2229
+
'@rollup/rollup-android-arm64@4.53.3':
2230
+
optional: true
2231
+
2232
+
'@rollup/rollup-darwin-arm64@4.53.3':
2233
+
optional: true
2234
+
2235
+
'@rollup/rollup-darwin-x64@4.53.3':
2236
+
optional: true
2237
+
2238
+
'@rollup/rollup-freebsd-arm64@4.53.3':
2239
+
optional: true
2240
+
2241
+
'@rollup/rollup-freebsd-x64@4.53.3':
2242
+
optional: true
2243
+
2244
+
'@rollup/rollup-linux-arm-gnueabihf@4.53.3':
2245
+
optional: true
2246
+
2247
+
'@rollup/rollup-linux-arm-musleabihf@4.53.3':
2248
+
optional: true
2249
+
2250
+
'@rollup/rollup-linux-arm64-gnu@4.53.3':
2251
+
optional: true
2252
+
2253
+
'@rollup/rollup-linux-arm64-musl@4.53.3':
2254
+
optional: true
2255
+
2256
+
'@rollup/rollup-linux-loong64-gnu@4.53.3':
2257
+
optional: true
2258
+
2259
+
'@rollup/rollup-linux-ppc64-gnu@4.53.3':
2260
+
optional: true
2261
+
2262
+
'@rollup/rollup-linux-riscv64-gnu@4.53.3':
2263
+
optional: true
2264
+
2265
+
'@rollup/rollup-linux-riscv64-musl@4.53.3':
2266
+
optional: true
2267
+
2268
+
'@rollup/rollup-linux-s390x-gnu@4.53.3':
2269
+
optional: true
2270
+
2271
+
'@rollup/rollup-linux-x64-gnu@4.53.3':
2272
+
optional: true
2273
+
2274
+
'@rollup/rollup-linux-x64-musl@4.53.3':
2275
+
optional: true
2276
+
2277
+
'@rollup/rollup-openharmony-arm64@4.53.3':
2278
+
optional: true
2279
+
2280
+
'@rollup/rollup-win32-arm64-msvc@4.53.3':
2281
+
optional: true
2282
+
2283
+
'@rollup/rollup-win32-ia32-msvc@4.53.3':
2284
+
optional: true
2285
+
2286
+
'@rollup/rollup-win32-x64-gnu@4.53.3':
2287
+
optional: true
2288
+
2289
+
'@rollup/rollup-win32-x64-msvc@4.53.3':
2290
+
optional: true
2291
+
2292
+
'@rushstack/node-core-library@5.19.0(@types/node@24.10.1)':
2293
+
dependencies:
2294
+
ajv: 8.13.0
2295
+
ajv-draft-04: 1.0.0(ajv@8.13.0)
2296
+
ajv-formats: 3.0.1(ajv@8.13.0)
2297
+
fs-extra: 11.3.2
2298
+
import-lazy: 4.0.0
2299
+
jju: 1.4.0
2300
+
resolve: 1.22.11
2301
+
semver: 7.5.4
2302
+
optionalDependencies:
2303
+
'@types/node': 24.10.1
2304
+
2305
+
'@rushstack/problem-matcher@0.1.1(@types/node@24.10.1)':
2306
+
optionalDependencies:
2307
+
'@types/node': 24.10.1
2308
+
2309
+
'@rushstack/rig-package@0.6.0':
2310
+
dependencies:
2311
+
resolve: 1.22.11
2312
+
strip-json-comments: 3.1.1
2313
+
2314
+
'@rushstack/terminal@0.19.4(@types/node@24.10.1)':
2315
+
dependencies:
2316
+
'@rushstack/node-core-library': 5.19.0(@types/node@24.10.1)
2317
+
'@rushstack/problem-matcher': 0.1.1(@types/node@24.10.1)
2318
+
supports-color: 8.1.1
1446
2319
optionalDependencies:
1447
-
fsevents: 2.3.3
1448
-
dev: true
2320
+
'@types/node': 24.10.1
2321
+
2322
+
'@rushstack/ts-command-line@5.1.4(@types/node@24.10.1)':
2323
+
dependencies:
2324
+
'@rushstack/terminal': 0.19.4(@types/node@24.10.1)
2325
+
'@types/argparse': 1.0.38
2326
+
argparse: 1.0.10
2327
+
string-argv: 0.3.2
2328
+
transitivePeerDependencies:
2329
+
- '@types/node'
1449
2330
1450
-
/vue-template-compiler@2.7.16:
1451
-
resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==}
2331
+
'@standard-schema/spec@1.0.0': {}
2332
+
2333
+
'@sveltejs/acorn-typescript@1.0.8(acorn@8.15.0)':
2334
+
dependencies:
2335
+
acorn: 8.15.0
2336
+
2337
+
'@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd))(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1)))(svelte@5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd))(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1))':
2338
+
dependencies:
2339
+
'@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd))(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1))
2340
+
debug: 4.4.3
2341
+
svelte: 5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd)
2342
+
vite: 7.2.6(@types/node@24.10.1)(terser@5.44.1)
2343
+
transitivePeerDependencies:
2344
+
- supports-color
2345
+
2346
+
'@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd))(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1))':
2347
+
dependencies:
2348
+
'@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd))(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1)))(svelte@5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd))(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1))
2349
+
debug: 4.4.3
2350
+
deepmerge: 4.3.1
2351
+
magic-string: 0.30.21
2352
+
svelte: 5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd)
2353
+
vite: 7.2.6(@types/node@24.10.1)(terser@5.44.1)
2354
+
vitefu: 1.1.1(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1))
2355
+
transitivePeerDependencies:
2356
+
- supports-color
2357
+
2358
+
'@tsconfig/svelte@5.0.6': {}
2359
+
2360
+
'@types/argparse@1.0.38': {}
2361
+
2362
+
'@types/estree@1.0.8': {}
2363
+
2364
+
'@types/node@12.20.55': {}
2365
+
2366
+
'@types/node@24.10.1':
2367
+
dependencies:
2368
+
undici-types: 7.16.0
2369
+
2370
+
'@volar/language-core@2.4.26':
2371
+
dependencies:
2372
+
'@volar/source-map': 2.4.26
2373
+
2374
+
'@volar/source-map@2.4.26': {}
2375
+
2376
+
'@volar/typescript@2.4.26':
2377
+
dependencies:
2378
+
'@volar/language-core': 2.4.26
2379
+
path-browserify: 1.0.1
2380
+
vscode-uri: 3.1.0
2381
+
2382
+
'@vue/compiler-core@3.5.25':
2383
+
dependencies:
2384
+
'@babel/parser': 7.28.5
2385
+
'@vue/shared': 3.5.25
2386
+
entities: 4.5.0
2387
+
estree-walker: 2.0.2
2388
+
source-map-js: 1.2.1
2389
+
2390
+
'@vue/compiler-dom@3.5.25':
2391
+
dependencies:
2392
+
'@vue/compiler-core': 3.5.25
2393
+
'@vue/shared': 3.5.25
2394
+
2395
+
'@vue/compiler-vue2@2.7.16':
1452
2396
dependencies:
1453
2397
de-indent: 1.0.2
1454
2398
he: 1.2.0
1455
-
dev: true
2399
+
2400
+
'@vue/language-core@2.2.0(typescript@5.8.3)':
2401
+
dependencies:
2402
+
'@volar/language-core': 2.4.26
2403
+
'@vue/compiler-dom': 3.5.25
2404
+
'@vue/compiler-vue2': 2.7.16
2405
+
'@vue/shared': 3.5.25
2406
+
alien-signals: 0.4.14
2407
+
minimatch: 9.0.5
2408
+
muggle-string: 0.4.1
2409
+
path-browserify: 1.0.1
2410
+
optionalDependencies:
2411
+
typescript: 5.8.3
2412
+
2413
+
'@vue/shared@3.5.25': {}
2414
+
2415
+
acorn@8.15.0: {}
2416
+
2417
+
ajv-draft-04@1.0.0(ajv@8.13.0):
2418
+
optionalDependencies:
2419
+
ajv: 8.13.0
2420
+
2421
+
ajv-formats@3.0.1(ajv@8.13.0):
2422
+
optionalDependencies:
2423
+
ajv: 8.13.0
2424
+
2425
+
ajv@8.12.0:
2426
+
dependencies:
2427
+
fast-deep-equal: 3.1.3
2428
+
json-schema-traverse: 1.0.0
2429
+
require-from-string: 2.0.2
2430
+
uri-js: 4.4.1
2431
+
2432
+
ajv@8.13.0:
2433
+
dependencies:
2434
+
fast-deep-equal: 3.1.3
2435
+
json-schema-traverse: 1.0.0
2436
+
require-from-string: 2.0.2
2437
+
uri-js: 4.4.1
2438
+
2439
+
alien-signals@0.4.14: {}
2440
+
2441
+
ansi-colors@4.1.3: {}
2442
+
2443
+
ansi-regex@5.0.1: {}
2444
+
2445
+
argparse@1.0.10:
2446
+
dependencies:
2447
+
sprintf-js: 1.0.3
2448
+
2449
+
argparse@2.0.1: {}
2450
+
2451
+
aria-query@5.3.2: {}
2452
+
2453
+
array-union@2.1.0: {}
2454
+
2455
+
axobject-query@4.1.0: {}
2456
+
2457
+
babel-plugin-transform-hook-names@1.0.2(@babel/core@7.28.5):
2458
+
dependencies:
2459
+
'@babel/core': 7.28.5
2460
+
2461
+
balanced-match@1.0.2: {}
2462
+
2463
+
baseline-browser-mapping@2.9.0: {}
2464
+
2465
+
better-path-resolve@1.0.0:
2466
+
dependencies:
2467
+
is-windows: 1.0.2
2468
+
2469
+
boolbase@1.0.0: {}
2470
+
2471
+
brace-expansion@2.0.2:
2472
+
dependencies:
2473
+
balanced-match: 1.0.2
2474
+
2475
+
braces@3.0.3:
2476
+
dependencies:
2477
+
fill-range: 7.1.1
2478
+
2479
+
browserslist@4.28.1:
2480
+
dependencies:
2481
+
baseline-browser-mapping: 2.9.0
2482
+
caniuse-lite: 1.0.30001759
2483
+
electron-to-chromium: 1.5.264
2484
+
node-releases: 2.0.27
2485
+
update-browserslist-db: 1.2.1(browserslist@4.28.1)
2486
+
2487
+
buffer-from@1.1.2: {}
2488
+
2489
+
caniuse-lite@1.0.30001759: {}
2490
+
2491
+
chardet@2.1.1: {}
2492
+
2493
+
chokidar@4.0.3:
2494
+
dependencies:
2495
+
readdirp: 4.1.2
2496
+
2497
+
ci-info@3.9.0: {}
2498
+
2499
+
clsx@2.1.1: {}
2500
+
2501
+
commander@2.20.3: {}
2502
+
2503
+
compare-versions@6.1.1: {}
2504
+
2505
+
confbox@0.1.8: {}
2506
+
2507
+
confbox@0.2.2: {}
2508
+
2509
+
convert-source-map@2.0.0: {}
2510
+
2511
+
cross-spawn@7.0.6:
2512
+
dependencies:
2513
+
path-key: 3.1.1
2514
+
shebang-command: 2.0.0
2515
+
which: 2.0.2
2516
+
2517
+
css-declaration-sorter@7.3.0(postcss@8.5.6):
2518
+
dependencies:
2519
+
postcss: 8.5.6
2520
+
2521
+
css-select@5.2.2:
2522
+
dependencies:
2523
+
boolbase: 1.0.0
2524
+
css-what: 6.2.2
2525
+
domhandler: 5.0.3
2526
+
domutils: 3.2.2
2527
+
nth-check: 2.1.1
2528
+
2529
+
css-what@6.2.2: {}
2530
+
2531
+
de-indent@1.0.2: {}
2532
+
2533
+
debug@4.4.3:
2534
+
dependencies:
2535
+
ms: 2.1.3
2536
+
2537
+
deepmerge@4.3.1: {}
2538
+
2539
+
detect-indent@6.1.0: {}
2540
+
2541
+
devalue@5.5.0: {}
2542
+
2543
+
diff@8.0.2: {}
2544
+
2545
+
dir-glob@3.0.1:
2546
+
dependencies:
2547
+
path-type: 4.0.0
2548
+
2549
+
dom-serializer@2.0.0:
2550
+
dependencies:
2551
+
domelementtype: 2.3.0
2552
+
domhandler: 5.0.3
2553
+
entities: 4.5.0
2554
+
2555
+
domelementtype@2.3.0: {}
2556
+
2557
+
domhandler@5.0.3:
2558
+
dependencies:
2559
+
domelementtype: 2.3.0
2560
+
2561
+
domutils@3.2.2:
2562
+
dependencies:
2563
+
dom-serializer: 2.0.0
2564
+
domelementtype: 2.3.0
2565
+
domhandler: 5.0.3
2566
+
2567
+
electron-to-chromium@1.5.264: {}
2568
+
2569
+
enquirer@2.4.1:
2570
+
dependencies:
2571
+
ansi-colors: 4.1.3
2572
+
strip-ansi: 6.0.1
2573
+
2574
+
entities@4.5.0: {}
2575
+
2576
+
esbuild@0.25.12:
2577
+
optionalDependencies:
2578
+
'@esbuild/aix-ppc64': 0.25.12
2579
+
'@esbuild/android-arm': 0.25.12
2580
+
'@esbuild/android-arm64': 0.25.12
2581
+
'@esbuild/android-x64': 0.25.12
2582
+
'@esbuild/darwin-arm64': 0.25.12
2583
+
'@esbuild/darwin-x64': 0.25.12
2584
+
'@esbuild/freebsd-arm64': 0.25.12
2585
+
'@esbuild/freebsd-x64': 0.25.12
2586
+
'@esbuild/linux-arm': 0.25.12
2587
+
'@esbuild/linux-arm64': 0.25.12
2588
+
'@esbuild/linux-ia32': 0.25.12
2589
+
'@esbuild/linux-loong64': 0.25.12
2590
+
'@esbuild/linux-mips64el': 0.25.12
2591
+
'@esbuild/linux-ppc64': 0.25.12
2592
+
'@esbuild/linux-riscv64': 0.25.12
2593
+
'@esbuild/linux-s390x': 0.25.12
2594
+
'@esbuild/linux-x64': 0.25.12
2595
+
'@esbuild/netbsd-arm64': 0.25.12
2596
+
'@esbuild/netbsd-x64': 0.25.12
2597
+
'@esbuild/openbsd-arm64': 0.25.12
2598
+
'@esbuild/openbsd-x64': 0.25.12
2599
+
'@esbuild/openharmony-arm64': 0.25.12
2600
+
'@esbuild/sunos-x64': 0.25.12
2601
+
'@esbuild/win32-arm64': 0.25.12
2602
+
'@esbuild/win32-ia32': 0.25.12
2603
+
'@esbuild/win32-x64': 0.25.12
2604
+
2605
+
escalade@3.2.0: {}
2606
+
2607
+
esm-env@1.2.2: {}
2608
+
2609
+
esprima@4.0.1: {}
2610
+
2611
+
esrap@2.2.1:
2612
+
dependencies:
2613
+
'@jridgewell/sourcemap-codec': 1.5.5
2614
+
2615
+
estree-walker@2.0.2: {}
2616
+
2617
+
exsolve@1.0.8: {}
2618
+
2619
+
extendable-error@0.1.7: {}
2620
+
2621
+
fast-deep-equal@3.1.3: {}
2622
+
2623
+
fast-glob@3.3.3:
2624
+
dependencies:
2625
+
'@nodelib/fs.stat': 2.0.5
2626
+
'@nodelib/fs.walk': 1.2.8
2627
+
glob-parent: 5.1.2
2628
+
merge2: 1.4.1
2629
+
micromatch: 4.0.8
2630
+
2631
+
fastq@1.19.1:
2632
+
dependencies:
2633
+
reusify: 1.1.0
2634
+
2635
+
fdir@6.5.0(picomatch@4.0.3):
2636
+
optionalDependencies:
2637
+
picomatch: 4.0.3
2638
+
2639
+
fill-range@7.1.1:
2640
+
dependencies:
2641
+
to-regex-range: 5.0.1
2642
+
2643
+
find-up@4.1.0:
2644
+
dependencies:
2645
+
locate-path: 5.0.0
2646
+
path-exists: 4.0.0
2647
+
2648
+
fs-extra@11.3.2:
2649
+
dependencies:
2650
+
graceful-fs: 4.2.11
2651
+
jsonfile: 6.2.0
2652
+
universalify: 2.0.1
2653
+
2654
+
fs-extra@7.0.1:
2655
+
dependencies:
2656
+
graceful-fs: 4.2.11
2657
+
jsonfile: 4.0.0
2658
+
universalify: 0.1.2
2659
+
2660
+
fs-extra@8.1.0:
2661
+
dependencies:
2662
+
graceful-fs: 4.2.11
2663
+
jsonfile: 4.0.0
2664
+
universalify: 0.1.2
2665
+
2666
+
fsevents@2.3.3:
2667
+
optional: true
2668
+
2669
+
function-bind@1.1.2: {}
2670
+
2671
+
gensync@1.0.0-beta.2: {}
2672
+
2673
+
glob-parent@5.1.2:
2674
+
dependencies:
2675
+
is-glob: 4.0.3
2676
+
2677
+
globby@11.1.0:
2678
+
dependencies:
2679
+
array-union: 2.1.0
2680
+
dir-glob: 3.0.1
2681
+
fast-glob: 3.3.3
2682
+
ignore: 5.3.2
2683
+
merge2: 1.4.1
2684
+
slash: 3.0.0
2685
+
2686
+
graceful-fs@4.2.11: {}
2687
+
2688
+
has-flag@4.0.0: {}
2689
+
2690
+
hasown@2.0.2:
2691
+
dependencies:
2692
+
function-bind: 1.1.2
2693
+
2694
+
he@1.2.0: {}
2695
+
2696
+
human-id@4.1.3: {}
2697
+
2698
+
iconv-lite@0.7.0:
2699
+
dependencies:
2700
+
safer-buffer: 2.1.2
2701
+
2702
+
ignore@5.3.2: {}
2703
+
2704
+
import-lazy@4.0.0: {}
2705
+
2706
+
is-core-module@2.16.1:
2707
+
dependencies:
2708
+
hasown: 2.0.2
2709
+
2710
+
is-extglob@2.1.1: {}
2711
+
2712
+
is-glob@4.0.3:
2713
+
dependencies:
2714
+
is-extglob: 2.1.1
2715
+
2716
+
is-number@7.0.0: {}
2717
+
2718
+
is-reference@3.0.3:
2719
+
dependencies:
2720
+
'@types/estree': 1.0.8
2721
+
2722
+
is-subdir@1.2.0:
2723
+
dependencies:
2724
+
better-path-resolve: 1.0.0
2725
+
2726
+
is-windows@1.0.2: {}
2727
+
2728
+
isexe@2.0.0: {}
2729
+
2730
+
jju@1.4.0: {}
2731
+
2732
+
js-tokens@4.0.0: {}
2733
+
2734
+
js-yaml@3.14.2:
2735
+
dependencies:
2736
+
argparse: 1.0.10
2737
+
esprima: 4.0.1
2738
+
2739
+
js-yaml@4.1.1:
2740
+
dependencies:
2741
+
argparse: 2.0.1
2742
+
2743
+
jsesc@3.1.0: {}
2744
+
2745
+
json-schema-traverse@1.0.0: {}
2746
+
2747
+
json5@2.2.3: {}
2748
+
2749
+
jsonfile@4.0.0:
2750
+
optionalDependencies:
2751
+
graceful-fs: 4.2.11
2752
+
2753
+
jsonfile@6.2.0:
2754
+
dependencies:
2755
+
universalify: 2.0.1
2756
+
optionalDependencies:
2757
+
graceful-fs: 4.2.11
2758
+
2759
+
kolorist@1.8.0: {}
2760
+
2761
+
local-pkg@1.1.2:
2762
+
dependencies:
2763
+
mlly: 1.8.0
2764
+
pkg-types: 2.3.0
2765
+
quansync: 0.2.11
2766
+
2767
+
locate-character@3.0.0: {}
2768
+
2769
+
locate-path@5.0.0:
2770
+
dependencies:
2771
+
p-locate: 4.1.0
2772
+
2773
+
lodash.startcase@4.4.0: {}
2774
+
2775
+
lodash@4.17.21: {}
2776
+
2777
+
lru-cache@5.1.1:
2778
+
dependencies:
2779
+
yallist: 3.1.1
2780
+
2781
+
lru-cache@6.0.0:
2782
+
dependencies:
2783
+
yallist: 4.0.0
2784
+
2785
+
magic-string@0.30.21:
2786
+
dependencies:
2787
+
'@jridgewell/sourcemap-codec': 1.5.5
2788
+
2789
+
merge2@1.4.1: {}
2790
+
2791
+
micromatch@4.0.8:
2792
+
dependencies:
2793
+
braces: 3.0.3
2794
+
picomatch: 2.3.1
2795
+
2796
+
minimatch@10.0.3:
2797
+
dependencies:
2798
+
'@isaacs/brace-expansion': 5.0.0
2799
+
2800
+
minimatch@9.0.5:
2801
+
dependencies:
2802
+
brace-expansion: 2.0.2
2803
+
2804
+
mlly@1.8.0:
2805
+
dependencies:
2806
+
acorn: 8.15.0
2807
+
pathe: 2.0.3
2808
+
pkg-types: 1.3.1
2809
+
ufo: 1.6.1
2810
+
2811
+
mri@1.2.0: {}
2812
+
2813
+
ms@2.1.3: {}
2814
+
2815
+
muggle-string@0.4.1: {}
2816
+
2817
+
nanoid@3.3.11: {}
2818
+
2819
+
node-html-parser@6.1.13:
2820
+
dependencies:
2821
+
css-select: 5.2.2
2822
+
he: 1.2.0
2823
+
2824
+
node-releases@2.0.27: {}
2825
+
2826
+
nth-check@2.1.1:
2827
+
dependencies:
2828
+
boolbase: 1.0.0
2829
+
2830
+
outdent@0.5.0: {}
2831
+
2832
+
p-filter@2.1.0:
2833
+
dependencies:
2834
+
p-map: 2.1.0
2835
+
2836
+
p-limit@2.3.0:
2837
+
dependencies:
2838
+
p-try: 2.2.0
2839
+
2840
+
p-locate@4.1.0:
2841
+
dependencies:
2842
+
p-limit: 2.3.0
2843
+
2844
+
p-map@2.1.0: {}
2845
+
2846
+
p-try@2.2.0: {}
2847
+
2848
+
package-manager-detector@0.2.11:
2849
+
dependencies:
2850
+
quansync: 0.2.11
2851
+
2852
+
path-browserify@1.0.1: {}
2853
+
2854
+
path-exists@4.0.0: {}
2855
+
2856
+
path-key@3.1.1: {}
2857
+
2858
+
path-parse@1.0.7: {}
2859
+
2860
+
path-type@4.0.0: {}
2861
+
2862
+
pathe@2.0.3: {}
2863
+
2864
+
picocolors@1.1.1: {}
2865
+
2866
+
picomatch@2.3.1: {}
2867
+
2868
+
picomatch@4.0.3: {}
2869
+
2870
+
pify@4.0.1: {}
2871
+
2872
+
pkg-types@1.3.1:
2873
+
dependencies:
2874
+
confbox: 0.1.8
2875
+
mlly: 1.8.0
2876
+
pathe: 2.0.3
2877
+
2878
+
pkg-types@2.3.0:
2879
+
dependencies:
2880
+
confbox: 0.2.2
2881
+
exsolve: 1.0.8
2882
+
pathe: 2.0.3
2883
+
2884
+
postcss-less@6.0.0(postcss@8.5.6):
2885
+
dependencies:
2886
+
postcss: 8.5.6
2887
+
2888
+
postcss-scss@4.0.9(postcss@8.5.6):
2889
+
dependencies:
2890
+
postcss: 8.5.6
2891
+
2892
+
postcss@8.5.6:
2893
+
dependencies:
2894
+
nanoid: 3.3.11
2895
+
picocolors: 1.1.1
2896
+
source-map-js: 1.2.1
2897
+
2898
+
preact@10.28.0: {}
2899
+
2900
+
prettier-plugin-css-order@2.1.2(postcss@8.5.6)(prettier@3.7.4):
2901
+
dependencies:
2902
+
css-declaration-sorter: 7.3.0(postcss@8.5.6)
2903
+
postcss-less: 6.0.0(postcss@8.5.6)
2904
+
postcss-scss: 4.0.9(postcss@8.5.6)
2905
+
prettier: 3.7.4
2906
+
transitivePeerDependencies:
2907
+
- postcss
2908
+
2909
+
prettier-plugin-svelte@3.4.0(prettier@3.7.4)(svelte@5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd)):
2910
+
dependencies:
2911
+
prettier: 3.7.4
2912
+
svelte: 5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd)
2913
+
2914
+
prettier@2.8.8: {}
2915
+
2916
+
prettier@3.7.4: {}
2917
+
2918
+
punycode@2.3.1: {}
2919
+
2920
+
quansync@0.2.11: {}
2921
+
2922
+
queue-microtask@1.2.3: {}
2923
+
2924
+
read-yaml-file@1.1.0:
2925
+
dependencies:
2926
+
graceful-fs: 4.2.11
2927
+
js-yaml: 3.14.2
2928
+
pify: 4.0.1
2929
+
strip-bom: 3.0.0
2930
+
2931
+
readdirp@4.1.2: {}
2932
+
2933
+
require-from-string@2.0.2: {}
2934
+
2935
+
resolve-from@5.0.0: {}
2936
+
2937
+
resolve@1.22.11:
2938
+
dependencies:
2939
+
is-core-module: 2.16.1
2940
+
path-parse: 1.0.7
2941
+
supports-preserve-symlinks-flag: 1.0.0
2942
+
2943
+
reusify@1.1.0: {}
2944
+
2945
+
rollup@4.53.3:
2946
+
dependencies:
2947
+
'@types/estree': 1.0.8
2948
+
optionalDependencies:
2949
+
'@rollup/rollup-android-arm-eabi': 4.53.3
2950
+
'@rollup/rollup-android-arm64': 4.53.3
2951
+
'@rollup/rollup-darwin-arm64': 4.53.3
2952
+
'@rollup/rollup-darwin-x64': 4.53.3
2953
+
'@rollup/rollup-freebsd-arm64': 4.53.3
2954
+
'@rollup/rollup-freebsd-x64': 4.53.3
2955
+
'@rollup/rollup-linux-arm-gnueabihf': 4.53.3
2956
+
'@rollup/rollup-linux-arm-musleabihf': 4.53.3
2957
+
'@rollup/rollup-linux-arm64-gnu': 4.53.3
2958
+
'@rollup/rollup-linux-arm64-musl': 4.53.3
2959
+
'@rollup/rollup-linux-loong64-gnu': 4.53.3
2960
+
'@rollup/rollup-linux-ppc64-gnu': 4.53.3
2961
+
'@rollup/rollup-linux-riscv64-gnu': 4.53.3
2962
+
'@rollup/rollup-linux-riscv64-musl': 4.53.3
2963
+
'@rollup/rollup-linux-s390x-gnu': 4.53.3
2964
+
'@rollup/rollup-linux-x64-gnu': 4.53.3
2965
+
'@rollup/rollup-linux-x64-musl': 4.53.3
2966
+
'@rollup/rollup-openharmony-arm64': 4.53.3
2967
+
'@rollup/rollup-win32-arm64-msvc': 4.53.3
2968
+
'@rollup/rollup-win32-ia32-msvc': 4.53.3
2969
+
'@rollup/rollup-win32-x64-gnu': 4.53.3
2970
+
'@rollup/rollup-win32-x64-msvc': 4.53.3
2971
+
fsevents: 2.3.3
2972
+
2973
+
run-parallel@1.2.0:
2974
+
dependencies:
2975
+
queue-microtask: 1.2.3
2976
+
2977
+
sade@1.8.1:
2978
+
dependencies:
2979
+
mri: 1.2.0
2980
+
2981
+
safer-buffer@2.1.2: {}
2982
+
2983
+
semver@6.3.1: {}
2984
+
2985
+
semver@7.5.4:
2986
+
dependencies:
2987
+
lru-cache: 6.0.0
2988
+
2989
+
semver@7.7.3: {}
2990
+
2991
+
shebang-command@2.0.0:
2992
+
dependencies:
2993
+
shebang-regex: 3.0.0
2994
+
2995
+
shebang-regex@3.0.0: {}
2996
+
2997
+
signal-exit@4.1.0: {}
2998
+
2999
+
simple-code-frame@1.3.0:
3000
+
dependencies:
3001
+
kolorist: 1.8.0
3002
+
3003
+
slash@3.0.0: {}
3004
+
3005
+
source-map-js@1.2.1: {}
3006
+
3007
+
source-map-support@0.5.21:
3008
+
dependencies:
3009
+
buffer-from: 1.1.2
3010
+
source-map: 0.6.1
3011
+
3012
+
source-map@0.6.1: {}
3013
+
3014
+
source-map@0.7.6: {}
3015
+
3016
+
spawndamnit@3.0.1:
3017
+
dependencies:
3018
+
cross-spawn: 7.0.6
3019
+
signal-exit: 4.1.0
3020
+
3021
+
sprintf-js@1.0.3: {}
3022
+
3023
+
stack-trace@1.0.0-pre2: {}
3024
+
3025
+
string-argv@0.3.2: {}
3026
+
3027
+
strip-ansi@6.0.1:
3028
+
dependencies:
3029
+
ansi-regex: 5.0.1
3030
+
3031
+
strip-bom@3.0.0: {}
3032
+
3033
+
strip-json-comments@3.1.1: {}
3034
+
3035
+
supports-color@8.1.1:
3036
+
dependencies:
3037
+
has-flag: 4.0.0
3038
+
3039
+
supports-preserve-symlinks-flag@1.0.0: {}
3040
+
3041
+
svelte-check@4.3.4(picomatch@4.0.3)(svelte@5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd))(typescript@5.8.3):
3042
+
dependencies:
3043
+
'@jridgewell/trace-mapping': 0.3.31
3044
+
chokidar: 4.0.3
3045
+
fdir: 6.5.0(picomatch@4.0.3)
3046
+
picocolors: 1.1.1
3047
+
sade: 1.8.1
3048
+
svelte: 5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd)
3049
+
typescript: 5.8.3
3050
+
transitivePeerDependencies:
3051
+
- picomatch
3052
+
3053
+
svelte@5.45.5(patch_hash=0f942ae2231640ac09d0a1e9913071000fffa9bdfe69630d767130d415a78ecd):
3054
+
dependencies:
3055
+
'@jridgewell/remapping': 2.3.5
3056
+
'@jridgewell/sourcemap-codec': 1.5.5
3057
+
'@sveltejs/acorn-typescript': 1.0.8(acorn@8.15.0)
3058
+
'@types/estree': 1.0.8
3059
+
acorn: 8.15.0
3060
+
aria-query: 5.3.2
3061
+
axobject-query: 4.1.0
3062
+
clsx: 2.1.1
3063
+
devalue: 5.5.0
3064
+
esm-env: 1.2.2
3065
+
esrap: 2.2.1
3066
+
is-reference: 3.0.3
3067
+
locate-character: 3.0.0
3068
+
magic-string: 0.30.21
3069
+
zimmerframe: 1.1.4
3070
+
3071
+
term-size@2.2.1: {}
3072
+
3073
+
terser@5.44.1:
3074
+
dependencies:
3075
+
'@jridgewell/source-map': 0.3.11
3076
+
acorn: 8.15.0
3077
+
commander: 2.20.3
3078
+
source-map-support: 0.5.21
1456
3079
1457
-
/vue-tsc@1.8.27(typescript@5.4.5):
1458
-
resolution: {integrity: sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==}
1459
-
hasBin: true
1460
-
peerDependencies:
1461
-
typescript: '*'
3080
+
tinyglobby@0.2.15:
1462
3081
dependencies:
1463
-
'@volar/typescript': 1.11.1
1464
-
'@vue/language-core': 1.8.27(typescript@5.4.5)
1465
-
semver: 7.6.0
1466
-
typescript: 5.4.5
1467
-
dev: true
3082
+
fdir: 6.5.0(picomatch@4.0.3)
3083
+
picomatch: 4.0.3
1468
3084
1469
-
/yallist@3.1.1:
1470
-
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
1471
-
dev: true
3085
+
to-regex-range@5.0.1:
3086
+
dependencies:
3087
+
is-number: 7.0.0
1472
3088
1473
-
/yallist@4.0.0:
1474
-
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
1475
-
dev: true
3089
+
tslib@2.8.1: {}
1476
3090
1477
-
/z-schema@5.0.5:
1478
-
resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==}
1479
-
engines: {node: '>=8.0.0'}
1480
-
hasBin: true
3091
+
typescript@5.8.2: {}
3092
+
3093
+
typescript@5.8.3: {}
3094
+
3095
+
ufo@1.6.1: {}
3096
+
3097
+
undici-types@7.16.0: {}
3098
+
3099
+
universalify@0.1.2: {}
3100
+
3101
+
universalify@2.0.1: {}
3102
+
3103
+
update-browserslist-db@1.2.1(browserslist@4.28.1):
3104
+
dependencies:
3105
+
browserslist: 4.28.1
3106
+
escalade: 3.2.0
3107
+
picocolors: 1.1.1
3108
+
3109
+
uri-js@4.4.1:
1481
3110
dependencies:
1482
-
lodash.get: 4.4.2
1483
-
lodash.isequal: 4.5.0
1484
-
validator: 13.11.0
3111
+
punycode: 2.3.1
3112
+
3113
+
vite-plugin-dts@4.5.4(@types/node@24.10.1)(rollup@4.53.3)(typescript@5.8.3)(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1)):
3114
+
dependencies:
3115
+
'@microsoft/api-extractor': 7.55.1(@types/node@24.10.1)
3116
+
'@rollup/pluginutils': 5.3.0(rollup@4.53.3)
3117
+
'@volar/typescript': 2.4.26
3118
+
'@vue/language-core': 2.2.0(typescript@5.8.3)
3119
+
compare-versions: 6.1.1
3120
+
debug: 4.4.3
3121
+
kolorist: 1.8.0
3122
+
local-pkg: 1.1.2
3123
+
magic-string: 0.30.21
3124
+
typescript: 5.8.3
1485
3125
optionalDependencies:
1486
-
commander: 9.5.0
1487
-
dev: true
3126
+
vite: 7.2.6(@types/node@24.10.1)(terser@5.44.1)
3127
+
transitivePeerDependencies:
3128
+
- '@types/node'
3129
+
- rollup
3130
+
- supports-color
3131
+
3132
+
vite-prerender-plugin@0.5.12(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1)):
3133
+
dependencies:
3134
+
kolorist: 1.8.0
3135
+
magic-string: 0.30.21
3136
+
node-html-parser: 6.1.13
3137
+
simple-code-frame: 1.3.0
3138
+
source-map: 0.7.6
3139
+
stack-trace: 1.0.0-pre2
3140
+
vite: 7.2.6(@types/node@24.10.1)(terser@5.44.1)
3141
+
3142
+
vite@7.2.6(@types/node@24.10.1)(terser@5.44.1):
3143
+
dependencies:
3144
+
esbuild: 0.25.12
3145
+
fdir: 6.5.0(picomatch@4.0.3)
3146
+
picomatch: 4.0.3
3147
+
postcss: 8.5.6
3148
+
rollup: 4.53.3
3149
+
tinyglobby: 0.2.15
3150
+
optionalDependencies:
3151
+
'@types/node': 24.10.1
3152
+
fsevents: 2.3.3
3153
+
terser: 5.44.1
3154
+
3155
+
vitefu@1.1.1(vite@7.2.6(@types/node@24.10.1)(terser@5.44.1)):
3156
+
optionalDependencies:
3157
+
vite: 7.2.6(@types/node@24.10.1)(terser@5.44.1)
3158
+
3159
+
vscode-uri@3.1.0: {}
3160
+
3161
+
which@2.0.2:
3162
+
dependencies:
3163
+
isexe: 2.0.0
3164
+
3165
+
yallist@3.1.1: {}
3166
+
3167
+
yallist@4.0.0: {}
3168
+
3169
+
zimmerframe@1.1.4: {}
+9
pnpm-workspace.yaml
+9
pnpm-workspace.yaml
-17
src/index.ts
-17
src/index.ts
···
1
-
import '../lib/index.ts';
2
-
import type { BlueskyPost } from '../lib/index.ts';
3
-
4
-
import './style.css';
5
-
6
-
const dark = matchMedia('(prefers-color-scheme: dark)');
7
-
8
-
const update_theme = () => {
9
-
const is_dark = dark.matches;
10
-
11
-
for (const node of document.querySelectorAll<BlueskyPost>('bluesky-post')) {
12
-
node.setAttribute('theme', !is_dark ? 'light' : 'dark');
13
-
}
14
-
};
15
-
16
-
update_theme();
17
-
dark.addEventListener('change', update_theme);
-75
src/style.css
-75
src/style.css
···
1
-
:root {
2
-
--background-primary: #fafafa;
3
-
--background-secondary: #e5e5e5;
4
-
--text-primary: #000000;
5
-
--text-link: #1d4ed8;
6
-
--divider: #c8c8c8;
7
-
}
8
-
9
-
@media (prefers-color-scheme: dark) {
10
-
:root {
11
-
--background-primary: #0a0a0a;
12
-
--background-secondary: #171717;
13
-
--text-primary: #ffffff;
14
-
--text-link: #60a5fa;
15
-
--divider: #404040;
16
-
}
17
-
}
18
-
19
-
html {
20
-
background: var(--background-primary);
21
-
color: var(--text-primary);
22
-
color-scheme: light dark;
23
-
font-size: 14px;
24
-
line-height: 1.25rem;
25
-
}
26
-
27
-
body {
28
-
margin: 24px auto;
29
-
padding: 0 16px;
30
-
max-width: 680px;
31
-
}
32
-
33
-
h1,
34
-
h2,
35
-
h3,
36
-
h4,
37
-
h5,
38
-
h6,
39
-
p {
40
-
margin-block-start: 1.1rem;
41
-
margin-block-end: 1.1rem;
42
-
}
43
-
44
-
h1 {
45
-
font-size: 1.25rem;
46
-
}
47
-
h2 {
48
-
font-size: 1.125rem;
49
-
}
50
-
51
-
a {
52
-
color: var(--text-link);
53
-
}
54
-
55
-
pre {
56
-
border-radius: 4px;
57
-
background: var(--background-secondary);
58
-
padding: 8px;
59
-
overflow-x: auto;
60
-
font-size: 12px;
61
-
}
62
-
63
-
bluesky-post {
64
-
--font-size: 16px;
65
-
margin: 16px auto;
66
-
}
67
-
68
-
.bluesky-post-fallback {
69
-
margin: 16px 0;
70
-
border-left: 3px solid var(--divider);
71
-
padding: 4px 8px;
72
-
}
73
-
.bluesky-post-fallback p {
74
-
margin: 0 0 8px 0;
75
-
}
+19
themes/dark.css
+19
themes/dark.css
···
1
+
.bluesky-embed {
2
+
--font-size: 16px;
3
+
--font-family:
4
+
system-ui, 'Segoe UI', 'Roboto', 'Helvetica', 'Arial', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji';
5
+
--max-feed-height: 600px;
6
+
}
7
+
8
+
.bluesky-embed {
9
+
--text-primary: #f1f3f5;
10
+
--text-secondary: #8c9eb2;
11
+
--text-link: #1083fe;
12
+
--background-primary: #000000;
13
+
--background-secondary: #212d3b;
14
+
--divider: rgb(37, 51, 66);
15
+
--divider-hover: rgb(66, 87, 108);
16
+
--button: #208bfe;
17
+
--button-text: #ffffff;
18
+
--button-hover: #4ca2fe;
19
+
}
+19
themes/dim.css
+19
themes/dim.css
···
1
+
.bluesky-embed {
2
+
--font-size: 16px;
3
+
--font-family:
4
+
system-ui, 'Segoe UI', 'Roboto', 'Helvetica', 'Arial', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji';
5
+
--max-feed-height: 600px;
6
+
}
7
+
8
+
.bluesky-embed {
9
+
--text-primary: #f1f3f5;
10
+
--text-secondary: #aebbc9;
11
+
--text-link: #1083fe;
12
+
--background-primary: #161e27;
13
+
--background-secondary: #212d3b;
14
+
--divider: #2e4052;
15
+
--divider-hover: #4a6179;
16
+
--button: #208bfe;
17
+
--button-text: #ffffff;
18
+
--button-hover: #4ca2fe;
19
+
}
+19
themes/light.css
+19
themes/light.css
···
1
+
.bluesky-embed {
2
+
--font-size: 16px;
3
+
--font-family:
4
+
system-ui, 'Segoe UI', 'Roboto', 'Helvetica', 'Arial', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji';
5
+
--max-feed-height: 600px;
6
+
}
7
+
8
+
.bluesky-embed {
9
+
--text-primary: #000000;
10
+
--text-secondary: #455668;
11
+
--text-link: #1083fe;
12
+
--background-primary: #ffffff;
13
+
--background-secondary: #455668;
14
+
--divider-hover: #a9b7c5;
15
+
--divider: #d4dbe2;
16
+
--button: #1083fe;
17
+
--button-text: #ffffff;
18
+
--button-hover: #0168d5;
19
+
}
-25
tsconfig.json
-25
tsconfig.json
···
1
-
{
2
-
"compilerOptions": {
3
-
"target": "ESNext",
4
-
"lib": ["DOM", "DOM.Iterable", "ESNext"],
5
-
"types": [],
6
-
"skipLibCheck": true,
7
-
8
-
"module": "ESNext",
9
-
"moduleResolution": "Bundler",
10
-
"allowImportingTsExtensions": true,
11
-
"resolveJsonModule": true,
12
-
"noEmit": true,
13
-
"jsx": "preserve",
14
-
"jsxImportSource": "@intrnl/jsx-to-string",
15
-
16
-
"incremental": true,
17
-
"strict": true,
18
-
"noUnusedLocals": true,
19
-
"noUnusedParameters": true,
20
-
"noFallthroughCasesInSwitch": true,
21
-
"verbatimModuleSyntax": true,
22
-
},
23
-
"include": ["src", "lib"],
24
-
"references": [{ "path": "./tsconfig.node.json" }],
25
-
}
-10
tsconfig.node.json
-10
tsconfig.node.json
-46
vite.config.ts
-46
vite.config.ts
···
1
-
import { defineConfig } from 'vite';
2
-
3
-
import babel from '@rollup/plugin-babel';
4
-
import dts from 'vite-plugin-dts';
5
-
6
-
export default defineConfig({
7
-
base: '/',
8
-
optimizeDeps: {
9
-
include: ['@intrnl/jsx-to-string/runtime'],
10
-
},
11
-
build: {
12
-
sourcemap: true,
13
-
target: 'esnext',
14
-
minify: false,
15
-
cssMinify: true,
16
-
lib: {
17
-
entry: {
18
-
core: 'lib/core.tsx',
19
-
element: 'lib/index.ts',
20
-
},
21
-
formats: ['es'],
22
-
},
23
-
rollupOptions: {
24
-
external: [
25
-
'@externdefs/bluesky-client',
26
-
'@externdefs/bluesky-client/lexicons',
27
-
'@externdefs/bluesky-client/xrpc',
28
-
],
29
-
},
30
-
},
31
-
esbuild: {
32
-
target: 'es2022',
33
-
},
34
-
plugins: [
35
-
{
36
-
enforce: 'pre',
37
-
...babel({
38
-
babelrc: false,
39
-
babelHelpers: 'bundled',
40
-
extensions: ['.tsx'],
41
-
plugins: [['@babel/plugin-syntax-typescript', { isTSX: true }], ['@intrnl/jsx-to-string/babel']],
42
-
}),
43
-
},
44
-
dts({ rollupTypes: true }),
45
-
],
46
-
});