@atmo-dev/kit 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +59 -1
  2. package/dist/components/ExternalEmbed.svelte +174 -0
  3. package/dist/components/ExternalEmbed.svelte.d.ts +8 -0
  4. package/dist/components/GrainAlbumPicker.svelte +218 -0
  5. package/dist/components/GrainAlbumPicker.svelte.d.ts +14 -0
  6. package/dist/components/GrainAlbumView.svelte +94 -0
  7. package/dist/components/GrainAlbumView.svelte.d.ts +10 -0
  8. package/dist/components/NestedComments.svelte +20 -10
  9. package/dist/components/NestedComments.svelte.d.ts +2 -0
  10. package/dist/components/PhotoGrid.svelte +53 -0
  11. package/dist/components/PhotoGrid.svelte.d.ts +9 -0
  12. package/dist/components/PhotoLightbox.svelte +332 -0
  13. package/dist/components/PhotoLightbox.svelte.d.ts +12 -0
  14. package/dist/components/PostContent.svelte +78 -20
  15. package/dist/components/PostContent.svelte.d.ts +5 -0
  16. package/dist/components/StreamplacePicker.svelte +94 -0
  17. package/dist/components/StreamplacePicker.svelte.d.ts +12 -0
  18. package/dist/grain/types.d.ts +41 -0
  19. package/dist/grain/types.js +1 -0
  20. package/dist/grain/urls.d.ts +7 -0
  21. package/dist/grain/urls.js +74 -0
  22. package/dist/grain/xrpc.d.ts +3 -0
  23. package/dist/grain/xrpc.js +129 -0
  24. package/dist/grain.d.ts +3 -0
  25. package/dist/grain.js +2 -0
  26. package/dist/index.d.ts +16 -0
  27. package/dist/index.js +9 -0
  28. package/dist/media.d.ts +10 -0
  29. package/dist/media.js +1 -0
  30. package/dist/social/external-embeds.d.ts +39 -0
  31. package/dist/social/external-embeds.js +107 -0
  32. package/dist/social/posts.d.ts +59 -17
  33. package/dist/social/posts.js +99 -12
  34. package/dist/social.d.ts +2 -0
  35. package/dist/social.js +1 -0
  36. package/dist/streamplace.d.ts +6 -0
  37. package/dist/streamplace.js +1 -0
  38. package/package.json +13 -1
@@ -1,4 +1,69 @@
1
+ import { isActorIdentifier, isRecordKey } from '@atcute/lexicons/syntax';
1
2
  import { blueskyProfileUrl } from './urls.js';
3
+ /** Convert record-view variants without mistaking blocked authors or starter packs for posts. */
4
+ function recordEmbed(record, depth) {
5
+ const type = record.$type;
6
+ if (record.blocked || type === 'app.bsky.embed.record#viewBlocked')
7
+ return { type: 'quote', unavailable: true, reason: 'blocked' };
8
+ if (record.detached || type === 'app.bsky.embed.record#viewDetached')
9
+ return { type: 'quote', unavailable: true, reason: 'detached' };
10
+ if (record.notFound || type === 'app.bsky.embed.record#viewNotFound')
11
+ return { type: 'quote', unavailable: true };
12
+ const kind = {
13
+ 'app.bsky.feed.defs#generatorView': 'feed',
14
+ 'app.bsky.graph.defs#listView': 'list',
15
+ 'app.bsky.graph.defs#starterPackViewBasic': 'starter-pack',
16
+ 'app.bsky.labeler.defs#labelerView': 'labeler'
17
+ };
18
+ const recordKind = Object.hasOwn(kind, type ?? '') ? kind[type] : undefined;
19
+ if (recordKind) {
20
+ const match = /^at:\/\/([^/]+)\/([^/]+)\/([^/]+)$/.exec(record.uri ?? '');
21
+ if (!match)
22
+ return { type: 'unsupported' };
23
+ const [, actor, , rkey] = match;
24
+ if (!isActorIdentifier(actor) || !isRecordKey(rkey))
25
+ return { type: 'unsupported' };
26
+ const details = record.record;
27
+ const profile = blueskyProfileUrl(actor);
28
+ const href = recordKind === 'starter-pack'
29
+ ? `https://bsky.app/starter-pack/${actor}/${encodeURIComponent(rkey)}`
30
+ : recordKind === 'labeler'
31
+ ? profile
32
+ : `${profile}/${recordKind === 'feed' ? 'feed' : 'lists'}/${encodeURIComponent(rkey)}`;
33
+ return {
34
+ type: 'record',
35
+ kind: recordKind,
36
+ href,
37
+ title: record.displayName ||
38
+ record.name ||
39
+ (typeof details?.name === 'string' && details.name) ||
40
+ record.creator?.displayName ||
41
+ record.creator?.handle ||
42
+ 'Untitled',
43
+ description: record.description ||
44
+ (typeof details?.description === 'string' ? details.description : undefined),
45
+ image: record.avatar || (recordKind === 'labeler' ? record.creator?.avatar : undefined),
46
+ handle: record.creator?.handle
47
+ };
48
+ }
49
+ if (record.uri &&
50
+ record.author?.handle &&
51
+ depth < 3 &&
52
+ (!type || type === 'app.bsky.embed.record#viewRecord')) {
53
+ return {
54
+ type: 'quote',
55
+ post: blueskyPostToPostData({
56
+ ...record,
57
+ uri: record.uri,
58
+ author: { ...record.author, handle: record.author.handle },
59
+ record: record.record
60
+ }, depth + 1)
61
+ };
62
+ }
63
+ return type && type !== 'app.bsky.embed.record#viewRecord'
64
+ ? { type: 'unsupported' }
65
+ : { type: 'quote', unavailable: true };
66
+ }
2
67
  export function blueskyPostToPostData(post, depth = 0) {
3
68
  const record = post.record ?? post.value;
4
69
  const result = {
@@ -14,11 +79,14 @@ export function blueskyPostToPostData(post, depth = 0) {
14
79
  facets: record?.facets,
15
80
  createdAt: record?.createdAt,
16
81
  replyCount: post.replyCount,
82
+ repostCount: post.repostCount,
17
83
  likeCount: post.likeCount,
18
84
  embeds: []
19
85
  };
20
86
  function add(embed) {
21
- if (embed.images)
87
+ if (embed.media)
88
+ add(embed.media);
89
+ if (embed.images) {
22
90
  result.embeds.push({
23
91
  type: 'images',
24
92
  images: embed.images.map((image) => ({
@@ -28,7 +96,23 @@ export function blueskyPostToPostData(post, depth = 0) {
28
96
  ...image.aspectRatio
29
97
  }))
30
98
  });
31
- if (embed.external)
99
+ }
100
+ else if (embed.items) {
101
+ const images = embed.items.filter((item) => item.thumbnail && (!item.$type || item.$type === 'app.bsky.embed.gallery#viewImage'));
102
+ if (images.length)
103
+ result.embeds.push({
104
+ type: 'images',
105
+ images: images.map((image) => ({
106
+ src: image.thumbnail,
107
+ fullsize: image.fullsize,
108
+ alt: image.alt ?? '',
109
+ ...image.aspectRatio
110
+ }))
111
+ });
112
+ if (images.length !== embed.items.length)
113
+ result.embeds.push({ type: 'unsupported' });
114
+ }
115
+ else if (embed.external) {
32
116
  result.embeds.push({
33
117
  type: 'external',
34
118
  uri: embed.external.uri,
@@ -36,22 +120,25 @@ export function blueskyPostToPostData(post, depth = 0) {
36
120
  description: embed.external.description,
37
121
  image: embed.external.thumb
38
122
  });
39
- if (embed.playlist)
123
+ }
124
+ else if (embed.playlist) {
40
125
  result.embeds.push({
41
126
  type: 'video',
42
127
  playlist: embed.playlist,
43
128
  poster: embed.thumbnail,
44
129
  alt: embed.alt
45
130
  });
46
- if (embed.media)
47
- add(embed.media);
48
- if (embed.record && depth < 3) {
49
- const quote = embed.record.record ?? embed.record;
50
- result.embeds.push({
51
- type: 'quote',
52
- post: quote.author && quote.uri ? blueskyPostToPostData(quote, depth + 1) : undefined,
53
- unavailable: !quote.author
54
- });
131
+ }
132
+ else if (!embed.record && !embed.media) {
133
+ result.embeds.push({ type: 'unsupported' });
134
+ }
135
+ if (embed.record) {
136
+ // recordWithMedia wraps an embed.record#view; a plain record embed does not.
137
+ const quote = embed.record.$type === 'app.bsky.embed.record#view' ||
138
+ (!embed.record.uri && embed.record.record)
139
+ ? embed.record.record
140
+ : embed.record;
141
+ result.embeds.push(quote ? recordEmbed(quote, depth) : { type: 'quote', unavailable: true });
55
142
  }
56
143
  }
57
144
  for (const embed of post.embeds ?? (post.embed ? [post.embed] : []))
package/dist/social.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export { PREFERRED_CLIENT_COLLECTION, PREFERRED_CLIENT_RKEY, PREFERRED_CLIENT_SCOPE, socialClients, normalizeSocialClientUrl, parsePreferredSocialClient, preferredSocialClientRecord, socialClientInfo } from './social/preferred-client.js';
2
2
  export type { SocialClientId, PreferredSocialClient } from './social/preferred-client.js';
3
+ export { defaultExternalEmbedProviders, youtubeEmbedProvider, klipyEmbedProvider, resolveExternalEmbed } from './social/external-embeds.js';
4
+ export type { ExternalPostEmbed, ExternalEmbedProvider, ExternalEmbedPlayer } from './social/external-embeds.js';
3
5
  export { blueskyProfileUrl, socialProfileUrl, socialPostUrl, socialHashtagUrl } from './social/urls.js';
package/dist/social.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export { PREFERRED_CLIENT_COLLECTION, PREFERRED_CLIENT_RKEY, PREFERRED_CLIENT_SCOPE, socialClients, normalizeSocialClientUrl, parsePreferredSocialClient, preferredSocialClientRecord, socialClientInfo } from './social/preferred-client.js';
2
+ export { defaultExternalEmbedProviders, youtubeEmbedProvider, klipyEmbedProvider, resolveExternalEmbed } from './social/external-embeds.js';
2
3
  export { blueskyProfileUrl, socialProfileUrl, socialPostUrl, socialHashtagUrl } from './social/urls.js';
@@ -0,0 +1,6 @@
1
+ export { parseStreamplaceLink } from './grain/urls.js';
2
+ export type StreamplaceLink = {
3
+ actor: string;
4
+ url: string;
5
+ };
6
+ export type StreamplaceSelection = (stream: StreamplaceLink) => void | string | Promise<void | string>;
@@ -0,0 +1 @@
1
+ export { parseStreamplaceLink } from './grain/urls.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atmo-dev/kit",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "The shared UI foundation for Atmo apps.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -27,6 +27,18 @@
27
27
  "svelte": "./dist/index.js",
28
28
  "default": "./dist/index.js"
29
29
  },
30
+ "./grain": {
31
+ "types": "./dist/grain.d.ts",
32
+ "default": "./dist/grain.js"
33
+ },
34
+ "./streamplace": {
35
+ "types": "./dist/streamplace.d.ts",
36
+ "default": "./dist/streamplace.js"
37
+ },
38
+ "./media": {
39
+ "types": "./dist/media.d.ts",
40
+ "default": "./dist/media.js"
41
+ },
30
42
  "./social": {
31
43
  "types": "./dist/social.d.ts",
32
44
  "default": "./dist/social.js"