@postrun/react 0.1.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.
package/dist/index.js ADDED
@@ -0,0 +1,1284 @@
1
+ "use client";
2
+ import { createContext, memo, useMemo, useState, useEffect, Fragment as Fragment$1, useRef, createElement, useContext, useCallback } from 'react';
3
+ import { useInfiniteQuery, useQuery, useMutation, QueryClient } from '@tanstack/react-query';
4
+ import { createPostrunClient, profilesList, profilesGet, profilesCreate, profilesUpdate, profilesDelete, connectionsConnect, connectionsListByProfile, connectionsGet, connectionsListAccounts, connectionsSelect, connectionsDelete, mediaCreate, mediaGet, mediaUpdate, mediaDelete, postsList, postsGet, postsCreate, buildCreatePost, isPostPlatform, postsUpdate, postsDelete } from '@postrun/js';
5
+ import pRetry, { AbortError } from 'p-retry';
6
+ import pWaitFor from 'p-wait-for';
7
+ import axios, { isAxiosError } from 'axios';
8
+ import { enrichTweet, TweetContainer, TweetHeader, TweetInReplyTo, TweetBody, TweetMedia, QuotedTweet } from 'react-tweet';
9
+ import { FiMessageCircle, FiRepeat, FiHeart, FiBarChart2, FiShare, FiGlobe, FiUsers, FiThumbsUp, FiMessageSquare, FiSend } from 'react-icons/fi';
10
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
11
+ import twitterText from 'twitter-text';
12
+ import { LuBadgeCheck } from 'react-icons/lu';
13
+
14
+ // src/context.tsx
15
+ function createDefaultQueryClient() {
16
+ return new QueryClient({
17
+ defaultOptions: {
18
+ queries: { staleTime: 3e4 }
19
+ }
20
+ });
21
+ }
22
+ var PostrunContext = createContext(null);
23
+ PostrunContext.displayName = "PostrunContext";
24
+ function PostrunProvider({
25
+ getToken,
26
+ baseUrl,
27
+ queryClient,
28
+ children
29
+ }) {
30
+ const getTokenRef = useRef(getToken);
31
+ useEffect(() => {
32
+ getTokenRef.current = getToken;
33
+ }, [getToken]);
34
+ const client = useMemo(
35
+ () => createPostrunClient({ getToken: () => getTokenRef.current(), baseUrl }),
36
+ [baseUrl]
37
+ );
38
+ const resolvedQueryClient = useMemo(
39
+ () => queryClient ?? createDefaultQueryClient(),
40
+ [queryClient]
41
+ );
42
+ const value = useMemo(
43
+ () => ({ client, queryClient: resolvedQueryClient }),
44
+ [client, resolvedQueryClient]
45
+ );
46
+ return createElement(PostrunContext.Provider, { value }, children);
47
+ }
48
+ function usePostrun() {
49
+ const value = useContext(PostrunContext);
50
+ if (value === null) {
51
+ throw new Error("usePostrun must be used within a <PostrunProvider>.");
52
+ }
53
+ return value;
54
+ }
55
+ function useInfiniteList(args) {
56
+ const { queryClient } = usePostrun();
57
+ const limit = args.limit ?? 20;
58
+ const query = useInfiniteQuery(
59
+ {
60
+ queryKey: args.queryKey,
61
+ queryFn: ({ pageParam }) => args.fetchPage({ limit, offset: pageParam }),
62
+ initialPageParam: 0,
63
+ getNextPageParam: (last) => last.has_more ? last.offset + last.data.length : void 0,
64
+ enabled: args.enabled
65
+ },
66
+ queryClient
67
+ );
68
+ return {
69
+ items: query.data?.pages.flatMap((page) => page.data) ?? [],
70
+ total: query.data?.pages[0]?.total ?? 0,
71
+ loadMore: () => {
72
+ void query.fetchNextPage();
73
+ },
74
+ hasMore: query.hasNextPage,
75
+ isLoading: query.isLoading,
76
+ isLoadingMore: query.isFetchingNextPage,
77
+ error: query.error,
78
+ refetch: () => {
79
+ void query.refetch();
80
+ }
81
+ };
82
+ }
83
+
84
+ // src/keys.ts
85
+ var ROOT = "postrun";
86
+ var profileKeys = {
87
+ all: [ROOT, "profiles"],
88
+ lists: () => [...profileKeys.all, "list"],
89
+ list: (query) => [...profileKeys.lists(), query ?? {}],
90
+ // Nested under lists() so a create/update/delete invalidating lists() also
91
+ // refreshes the infinite cache; distinct tail so the two cache shapes (a
92
+ // single Page vs accumulated pages) never collide on one key.
93
+ infinite: (query) => [...profileKeys.lists(), "infinite", query ?? {}],
94
+ details: () => [...profileKeys.all, "detail"],
95
+ detail: (id) => [...profileKeys.details(), id]
96
+ };
97
+ var postKeys = {
98
+ all: [ROOT, "posts"],
99
+ lists: () => [...postKeys.all, "list"],
100
+ list: (query) => [...postKeys.lists(), query ?? {}],
101
+ // Nested under lists() so a create/update/delete invalidating lists() also
102
+ // refreshes the infinite cache; distinct tail so the two cache shapes (a
103
+ // single Page vs accumulated pages) never collide on one key.
104
+ infinite: (query) => [...postKeys.lists(), "infinite", query ?? {}],
105
+ details: () => [...postKeys.all, "detail"],
106
+ detail: (id) => [...postKeys.details(), id]
107
+ };
108
+ var mediaKeys = {
109
+ all: [ROOT, "media"],
110
+ details: () => [...mediaKeys.all, "detail"],
111
+ detail: (id) => [...mediaKeys.details(), id]
112
+ };
113
+ var connectionKeys = {
114
+ all: [ROOT, "connections"],
115
+ lists: () => [...connectionKeys.all, "list"],
116
+ list: (profileId) => [...connectionKeys.lists(), profileId],
117
+ details: () => [...connectionKeys.all, "detail"],
118
+ detail: (id) => [...connectionKeys.details(), id],
119
+ accounts: (id) => [...connectionKeys.all, "accounts", id]
120
+ };
121
+
122
+ // src/profiles.ts
123
+ function useProfiles(query) {
124
+ const { client, queryClient } = usePostrun();
125
+ return useQuery(
126
+ {
127
+ queryKey: profileKeys.list(query),
128
+ queryFn: async () => (await profilesList({ client, query })).data
129
+ },
130
+ queryClient
131
+ );
132
+ }
133
+ function useProfilesInfinite(filters, options) {
134
+ const { client } = usePostrun();
135
+ return useInfiniteList({
136
+ queryKey: profileKeys.infinite(filters),
137
+ limit: options?.pageSize,
138
+ fetchPage: async ({ limit, offset }) => (await profilesList({ client, query: { ...filters, limit, offset } })).data
139
+ });
140
+ }
141
+ function useProfile(id) {
142
+ const { client, queryClient } = usePostrun();
143
+ return useQuery(
144
+ {
145
+ queryKey: profileKeys.detail(id),
146
+ queryFn: async () => (await profilesGet({ client, path: { id } })).data,
147
+ enabled: Boolean(id)
148
+ },
149
+ queryClient
150
+ );
151
+ }
152
+ function useCreateProfile() {
153
+ const { client, queryClient } = usePostrun();
154
+ return useMutation(
155
+ {
156
+ mutationFn: async (body) => (await profilesCreate({ client, body })).data,
157
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: profileKeys.lists() })
158
+ },
159
+ queryClient
160
+ );
161
+ }
162
+ function useUpdateProfile() {
163
+ const { client, queryClient } = usePostrun();
164
+ return useMutation(
165
+ {
166
+ mutationFn: async ({ id, ...body }) => (await profilesUpdate({ client, path: { id }, body })).data,
167
+ onSuccess: (_result, { id }) => {
168
+ queryClient.invalidateQueries({ queryKey: profileKeys.lists() });
169
+ queryClient.invalidateQueries({ queryKey: profileKeys.detail(id) });
170
+ }
171
+ },
172
+ queryClient
173
+ );
174
+ }
175
+ function useDeleteProfile() {
176
+ const { client, queryClient } = usePostrun();
177
+ return useMutation(
178
+ {
179
+ mutationFn: async (id) => (await profilesDelete({ client, path: { id } })).data,
180
+ onSuccess: (_result, id) => {
181
+ queryClient.invalidateQueries({ queryKey: profileKeys.lists() });
182
+ queryClient.removeQueries({ queryKey: profileKeys.detail(id) });
183
+ }
184
+ },
185
+ queryClient
186
+ );
187
+ }
188
+
189
+ // src/navigate.ts
190
+ function navigate(url) {
191
+ window.location.assign(url);
192
+ }
193
+
194
+ // src/connections.ts
195
+ function useConnect() {
196
+ const { client, queryClient } = usePostrun();
197
+ return useMutation(
198
+ {
199
+ mutationFn: async ({ profileId, platform }) => {
200
+ const session = (await connectionsConnect({
201
+ client,
202
+ path: { id: profileId },
203
+ body: { platform }
204
+ })).data;
205
+ navigate(session.connect_url);
206
+ return session;
207
+ }
208
+ },
209
+ queryClient
210
+ );
211
+ }
212
+ function useConnections(profileId) {
213
+ const { client, queryClient } = usePostrun();
214
+ return useQuery(
215
+ {
216
+ queryKey: connectionKeys.list(profileId),
217
+ queryFn: async () => (await connectionsListByProfile({ client, path: { id: profileId } })).data,
218
+ enabled: Boolean(profileId)
219
+ },
220
+ queryClient
221
+ );
222
+ }
223
+ function useConnection(id) {
224
+ const { client, queryClient } = usePostrun();
225
+ return useQuery(
226
+ {
227
+ queryKey: connectionKeys.detail(id),
228
+ queryFn: async () => (await connectionsGet({ client, path: { id } })).data,
229
+ enabled: Boolean(id)
230
+ },
231
+ queryClient
232
+ );
233
+ }
234
+ function useDiscoverableAccounts(id) {
235
+ const { client, queryClient } = usePostrun();
236
+ return useQuery(
237
+ {
238
+ queryKey: connectionKeys.accounts(id),
239
+ queryFn: async () => (await connectionsListAccounts({ client, path: { id } })).data,
240
+ enabled: Boolean(id)
241
+ },
242
+ queryClient
243
+ );
244
+ }
245
+ function useSelectAccount() {
246
+ const { client, queryClient } = usePostrun();
247
+ return useMutation(
248
+ {
249
+ mutationFn: async ({ id, ...body }) => (await connectionsSelect({ client, path: { id }, body })).data,
250
+ onSuccess: (_result, { id }) => {
251
+ queryClient.invalidateQueries({ queryKey: connectionKeys.lists() });
252
+ queryClient.invalidateQueries({ queryKey: connectionKeys.detail(id) });
253
+ queryClient.invalidateQueries({ queryKey: connectionKeys.accounts(id) });
254
+ }
255
+ },
256
+ queryClient
257
+ );
258
+ }
259
+ function useDisconnect() {
260
+ const { client, queryClient } = usePostrun();
261
+ return useMutation(
262
+ {
263
+ mutationFn: async (id) => (await connectionsDelete({ client, path: { id } })).data,
264
+ onSuccess: (_result, id) => {
265
+ queryClient.invalidateQueries({ queryKey: connectionKeys.lists() });
266
+ queryClient.removeQueries({ queryKey: connectionKeys.detail(id) });
267
+ }
268
+ },
269
+ queryClient
270
+ );
271
+ }
272
+ var UploadError = class extends Error {
273
+ status;
274
+ constructor(status, message) {
275
+ super(message);
276
+ this.name = "UploadError";
277
+ this.status = status;
278
+ }
279
+ };
280
+ async function uploadBytes(target, file, options = {}) {
281
+ const { onProgress, signal } = options;
282
+ try {
283
+ await axios.request({
284
+ method: target.method,
285
+ url: target.url,
286
+ data: file,
287
+ headers: target.headers,
288
+ signal,
289
+ onUploadProgress: (event) => {
290
+ if (event.total) {
291
+ onProgress?.(event.loaded / event.total);
292
+ }
293
+ }
294
+ });
295
+ onProgress?.(1);
296
+ } catch (error) {
297
+ if (signal?.aborted) {
298
+ throw error;
299
+ }
300
+ if (isAxiosError(error)) {
301
+ const status = error.response?.status ?? 0;
302
+ throw new UploadError(
303
+ status,
304
+ status ? `Upload failed (HTTP ${status}).` : "Upload failed: network error."
305
+ );
306
+ }
307
+ throw error;
308
+ }
309
+ }
310
+
311
+ // src/media.ts
312
+ var DOCUMENT_MIME = /^application\/(pdf|msword|vnd\.(openxmlformats-officedocument\.(wordprocessingml\.document|presentationml\.presentation)|ms-powerpoint))$/;
313
+ function inferKind(contentType) {
314
+ if (contentType === "image/gif") return "gif";
315
+ if (contentType.startsWith("image/")) return "image";
316
+ if (contentType.startsWith("video/")) return "video";
317
+ if (DOCUMENT_MIME.test(contentType)) return "document";
318
+ throw new Error(
319
+ `Could not infer media kind from "${contentType}". Pass { kind } explicitly.`
320
+ );
321
+ }
322
+ async function pollUntilSettled(client, id, signal) {
323
+ let latest;
324
+ await pWaitFor(
325
+ async () => {
326
+ if (signal.aborted) {
327
+ throw new DOMException("Upload aborted", "AbortError");
328
+ }
329
+ latest = (await mediaGet({ client, path: { id } })).data;
330
+ return latest.status === "ready" || latest.status === "failed";
331
+ },
332
+ { interval: 1500, timeout: 3e5 }
333
+ );
334
+ if (!latest) {
335
+ throw new Error("Media polling returned no result.");
336
+ }
337
+ return latest;
338
+ }
339
+ function useMediaUpload() {
340
+ const { client, queryClient } = usePostrun();
341
+ const [status, setStatus] = useState("idle");
342
+ const [progress, setProgress] = useState(0);
343
+ const [media, setMedia] = useState(null);
344
+ const [error, setError] = useState(null);
345
+ const abortRef = useRef(null);
346
+ const upload = useCallback(
347
+ async (file, options) => {
348
+ const contentType = options.contentType || file.type;
349
+ if (!contentType) {
350
+ throw new Error(
351
+ "Could not determine the file's content type. Pass { contentType } explicitly."
352
+ );
353
+ }
354
+ const kind = options.kind ?? inferKind(contentType);
355
+ abortRef.current?.abort();
356
+ const controller = new AbortController();
357
+ abortRef.current = controller;
358
+ setStatus("uploading");
359
+ setProgress(0);
360
+ setMedia(null);
361
+ setError(null);
362
+ try {
363
+ const created = (await mediaCreate({
364
+ client,
365
+ body: {
366
+ profile_id: options.profileId,
367
+ kind,
368
+ content_type: contentType,
369
+ targets: options.targets,
370
+ raw: options.raw,
371
+ alt_text: options.altText,
372
+ external_id: options.externalId,
373
+ metadata: options.metadata
374
+ }
375
+ })).data;
376
+ if (created.upload) {
377
+ const target = created.upload;
378
+ await pRetry(
379
+ async () => {
380
+ try {
381
+ await uploadBytes(target, file, {
382
+ onProgress: setProgress,
383
+ signal: controller.signal
384
+ });
385
+ } catch (uploadError) {
386
+ if (uploadError instanceof UploadError && uploadError.status >= 400 && uploadError.status < 500) {
387
+ throw new AbortError(uploadError);
388
+ }
389
+ throw uploadError;
390
+ }
391
+ },
392
+ { retries: 3, signal: controller.signal }
393
+ );
394
+ }
395
+ setStatus("processing");
396
+ const settled = await pollUntilSettled(
397
+ client,
398
+ created.id,
399
+ controller.signal
400
+ );
401
+ queryClient.setQueryData(mediaKeys.detail(created.id), settled);
402
+ setMedia(settled);
403
+ setStatus(settled.status === "failed" ? "failed" : "ready");
404
+ return settled;
405
+ } catch (caught) {
406
+ setError(caught);
407
+ setStatus("failed");
408
+ throw caught;
409
+ } finally {
410
+ if (abortRef.current === controller) {
411
+ abortRef.current = null;
412
+ }
413
+ }
414
+ },
415
+ [client, queryClient]
416
+ );
417
+ const cancel = useCallback(() => abortRef.current?.abort(), []);
418
+ const reset = useCallback(() => {
419
+ setStatus("idle");
420
+ setProgress(0);
421
+ setMedia(null);
422
+ setError(null);
423
+ }, []);
424
+ return { upload, cancel, reset, status, progress, media, error };
425
+ }
426
+ function useMedia(id) {
427
+ const { client, queryClient } = usePostrun();
428
+ return useQuery(
429
+ {
430
+ queryKey: mediaKeys.detail(id),
431
+ queryFn: async () => (await mediaGet({ client, path: { id } })).data,
432
+ enabled: Boolean(id),
433
+ refetchInterval: (query) => {
434
+ const current = query.state.data;
435
+ return current?.status === "uploading" || current?.status === "processing" ? 2e3 : false;
436
+ }
437
+ },
438
+ queryClient
439
+ );
440
+ }
441
+ function useUpdateMedia() {
442
+ const { client, queryClient } = usePostrun();
443
+ return useMutation(
444
+ {
445
+ mutationFn: async ({ id, ...body }) => (await mediaUpdate({ client, path: { id }, body })).data,
446
+ onSuccess: (result, { id }) => queryClient.setQueryData(mediaKeys.detail(id), result)
447
+ },
448
+ queryClient
449
+ );
450
+ }
451
+ function useDeleteMedia() {
452
+ const { client, queryClient } = usePostrun();
453
+ return useMutation(
454
+ {
455
+ mutationFn: async (id) => (await mediaDelete({ client, path: { id } })).data,
456
+ onSuccess: (_result, id) => queryClient.removeQueries({ queryKey: mediaKeys.detail(id) })
457
+ },
458
+ queryClient
459
+ );
460
+ }
461
+ var IN_FLIGHT = /* @__PURE__ */ new Set([
462
+ "scheduled",
463
+ "publishing"
464
+ ]);
465
+ var isLivePostStatus = (status) => IN_FLIGHT.has(status);
466
+ function usePosts(query) {
467
+ const { client, queryClient } = usePostrun();
468
+ return useQuery(
469
+ {
470
+ queryKey: postKeys.list(query),
471
+ queryFn: async () => (await postsList({ client, query })).data
472
+ },
473
+ queryClient
474
+ );
475
+ }
476
+ function usePostsInfinite(filters, options) {
477
+ const { client } = usePostrun();
478
+ return useInfiniteList({
479
+ queryKey: postKeys.infinite(filters),
480
+ limit: options?.pageSize,
481
+ fetchPage: async ({ limit, offset }) => (await postsList({ client, query: { ...filters, limit, offset } })).data
482
+ });
483
+ }
484
+ function useCalendar(filters, options) {
485
+ const { client, queryClient } = usePostrun();
486
+ return useQuery(
487
+ {
488
+ queryKey: postKeys.list(filters),
489
+ queryFn: async () => (await postsList({ client, query: filters })).data,
490
+ refetchInterval: (query) => {
491
+ if (options?.live === false) return false;
492
+ const posts = query.state.data?.data ?? [];
493
+ return posts.some((post) => isLivePostStatus(post.status)) ? 5e3 : false;
494
+ }
495
+ },
496
+ queryClient
497
+ );
498
+ }
499
+ function usePost(id, options) {
500
+ const { client, queryClient } = usePostrun();
501
+ return useQuery(
502
+ {
503
+ queryKey: postKeys.detail(id),
504
+ queryFn: async () => (await postsGet({ client, path: { id } })).data,
505
+ enabled: Boolean(id),
506
+ refetchInterval: (query) => {
507
+ if (options?.live === false) return false;
508
+ const status = query.state.data?.status;
509
+ return status && isLivePostStatus(status) ? 2e3 : false;
510
+ }
511
+ },
512
+ queryClient
513
+ );
514
+ }
515
+ function useCreatePost(profileId) {
516
+ const { client, queryClient } = usePostrun();
517
+ const connections = useConnections(profileId);
518
+ const connected = connections.data?.data ?? [];
519
+ const mutation = useMutation(
520
+ {
521
+ mutationFn: async (input) => (await postsCreate({
522
+ client,
523
+ body: buildCreatePost({ ...input, profileId }, connected)
524
+ })).data,
525
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: postKeys.lists() })
526
+ },
527
+ queryClient
528
+ );
529
+ return {
530
+ create: mutation.mutateAsync,
531
+ isPending: mutation.isPending,
532
+ error: mutation.error,
533
+ data: mutation.data,
534
+ reset: mutation.reset,
535
+ // The profile's connections must load before `create` can resolve a channel;
536
+ // gate on this so a call during loading isn't mislabeled "not connected".
537
+ isReady: connections.isSuccess,
538
+ connectedChannels: connected.map((connection) => connection.platform).filter(isPostPlatform)
539
+ };
540
+ }
541
+ function useUpdatePost(postId) {
542
+ const { client, queryClient } = usePostrun();
543
+ return useMutation(
544
+ {
545
+ mutationFn: async (body) => (await postsUpdate({ client, path: { id: postId }, body })).data,
546
+ onSuccess: (result) => {
547
+ queryClient.invalidateQueries({ queryKey: postKeys.lists() });
548
+ queryClient.setQueryData(postKeys.detail(postId), result);
549
+ }
550
+ },
551
+ queryClient
552
+ );
553
+ }
554
+ function useDeletePost() {
555
+ const { client, queryClient } = usePostrun();
556
+ return useMutation(
557
+ {
558
+ mutationFn: async (id) => (await postsDelete({ client, path: { id } })).data,
559
+ onSuccess: (_result, id) => {
560
+ queryClient.invalidateQueries({ queryKey: postKeys.lists() });
561
+ queryClient.removeQueries({ queryKey: postKeys.detail(id) });
562
+ }
563
+ },
564
+ queryClient
565
+ );
566
+ }
567
+ function fileKey(file) {
568
+ return file ? `${file.name}:${file.size}:${file.lastModified}` : "";
569
+ }
570
+ function mediaSignature(media) {
571
+ return (media ?? []).map((item) => {
572
+ const source = item.url ?? fileKey(item.file);
573
+ const size = `${item.width ?? ""}x${item.height ?? ""}`;
574
+ return `${item.kind}|${source}|${item.posterUrl ?? ""}|${size}|${item.alt ?? ""}`;
575
+ }).join("\xA7");
576
+ }
577
+ function altSignatureOf(media) {
578
+ return (media ?? []).map((m) => m?.alt_text_override ?? "").join("\xA7");
579
+ }
580
+ function useResolvedMedia(media, altFallbacks, altSignature) {
581
+ const signature = mediaSignature(media);
582
+ const [objectUrls, setObjectUrls] = useState({});
583
+ useEffect(() => {
584
+ const created = [];
585
+ const next = {};
586
+ (media ?? []).forEach((item, index) => {
587
+ if (!item.url && item.file) {
588
+ const url = URL.createObjectURL(item.file);
589
+ created.push(url);
590
+ next[index] = url;
591
+ }
592
+ });
593
+ setObjectUrls(next);
594
+ return () => {
595
+ for (const url of created) {
596
+ URL.revokeObjectURL(url);
597
+ }
598
+ };
599
+ }, [signature]);
600
+ return useMemo(
601
+ () => (media ?? []).flatMap((item, index) => {
602
+ const src = item.url ?? objectUrls[index];
603
+ if (!src) {
604
+ return [];
605
+ }
606
+ return [
607
+ {
608
+ kind: item.kind,
609
+ src,
610
+ width: item.width,
611
+ height: item.height,
612
+ alt: item.alt ?? altFallbacks?.[index]?.alt_text_override ?? void 0,
613
+ posterSrc: item.posterUrl
614
+ }
615
+ ];
616
+ }),
617
+ // `signature` + `altSignature` capture the content; `objectUrls` flips once
618
+ // File blobs resolve. Referencing `media`/`altFallbacks` directly is safe.
619
+ // eslint-disable-next-line react-hooks/exhaustive-deps
620
+ [signature, altSignature, objectUrls]
621
+ );
622
+ }
623
+ var ROW = {
624
+ display: "flex",
625
+ justifyContent: "space-between",
626
+ maxWidth: 425,
627
+ marginTop: 12,
628
+ color: "var(--tweet-color-gray-secondary, #536471)"
629
+ };
630
+ var ACTIONS = [
631
+ { label: "Reply", Icon: FiMessageCircle },
632
+ { label: "Repost", Icon: FiRepeat },
633
+ { label: "Like", Icon: FiHeart },
634
+ { label: "Views", Icon: FiBarChart2 },
635
+ { label: "Share", Icon: FiShare }
636
+ ];
637
+ function XPreviewActions() {
638
+ return /* @__PURE__ */ jsx("div", { style: ROW, children: ACTIONS.map(({ label, Icon }) => /* @__PURE__ */ jsx(Icon, { size: 18, "aria-label": label, role: "img" }, label)) });
639
+ }
640
+ var {
641
+ extractCashtagsWithIndices,
642
+ extractHashtagsWithIndices,
643
+ extractMentionsWithIndices,
644
+ extractUrlsWithIndices,
645
+ modifyIndicesFromUTF16ToUnicode
646
+ } = twitterText;
647
+ function extractEntities(text) {
648
+ const hashtags = extractHashtagsWithIndices(text);
649
+ const mentions = extractMentionsWithIndices(text);
650
+ const urls = extractUrlsWithIndices(text);
651
+ const cashtags = extractCashtagsWithIndices(text);
652
+ modifyIndicesFromUTF16ToUnicode(text, [
653
+ ...hashtags,
654
+ ...mentions,
655
+ ...urls,
656
+ ...cashtags
657
+ ]);
658
+ return {
659
+ hashtags: hashtags.map((h) => ({ text: h.hashtag, indices: h.indices })),
660
+ user_mentions: mentions.map((m) => ({
661
+ id_str: "",
662
+ name: m.screenName,
663
+ screen_name: m.screenName,
664
+ indices: m.indices
665
+ })),
666
+ urls: urls.map((u) => ({
667
+ display_url: u.url,
668
+ expanded_url: u.url,
669
+ url: u.url,
670
+ indices: u.indices
671
+ })),
672
+ symbols: cashtags.map((c) => ({ text: c.cashtag, indices: c.indices }))
673
+ };
674
+ }
675
+
676
+ // src/preview/x/to-tweet.ts
677
+ var PLACEHOLDER_AVATAR = "data:image/svg+xml;utf8," + encodeURIComponent(
678
+ '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48"><circle cx="24" cy="24" r="24" fill="#cfd9de"/></svg>'
679
+ );
680
+ var FALLBACK_WIDTH = 1200;
681
+ var FALLBACK_HEIGHT = 800;
682
+ function codepointLength(text) {
683
+ return Array.from(text).length;
684
+ }
685
+ function buildUser(author) {
686
+ const verified = author.verified ?? false;
687
+ return {
688
+ id_str: "",
689
+ name: author.name,
690
+ profile_image_url_https: author.avatarUrl ?? PLACEHOLDER_AVATAR,
691
+ profile_image_shape: "Circle",
692
+ screen_name: author.handle,
693
+ verified: false,
694
+ is_blue_verified: verified
695
+ };
696
+ }
697
+ function mediaBase(src, width, height) {
698
+ const w = width ?? FALLBACK_WIDTH;
699
+ const h = height ?? FALLBACK_HEIGHT;
700
+ const size = { h, w, resize: "fit" };
701
+ return {
702
+ display_url: "",
703
+ expanded_url: "",
704
+ ext_media_availability: { status: "available" },
705
+ ext_media_color: { palette: [] },
706
+ indices: [0, 0],
707
+ media_url_https: src,
708
+ original_info: { height: h, width: w, focus_rects: [] },
709
+ sizes: { large: size, medium: size, small: size, thumb: size },
710
+ url: src
711
+ };
712
+ }
713
+ function buildPhoto(media) {
714
+ return {
715
+ ...mediaBase(media.src, media.width, media.height),
716
+ type: "photo",
717
+ ext_alt_text: media.alt
718
+ };
719
+ }
720
+ function buildVideo(media) {
721
+ const poster = media.posterSrc ?? media.src;
722
+ const videoInfo = {
723
+ aspect_ratio: [media.width ?? 16, media.height ?? 9],
724
+ variants: [{ content_type: "video/mp4", url: media.src }]
725
+ };
726
+ const base = mediaBase(poster, media.width, media.height);
727
+ return media.kind === "gif" ? { ...base, type: "animated_gif", video_info: videoInfo } : { ...base, type: "video", video_info: videoInfo };
728
+ }
729
+ function buildMediaDetails(media) {
730
+ return media.map(
731
+ (item) => item.kind === "image" ? buildPhoto(item) : buildVideo(item)
732
+ );
733
+ }
734
+ function tweetScaffold() {
735
+ return {
736
+ lang: "en",
737
+ created_at: "",
738
+ edit_control: {
739
+ edit_tweet_ids: [],
740
+ editable_until_msecs: "0",
741
+ is_edit_eligible: false,
742
+ edits_remaining: "0"
743
+ },
744
+ isEdited: false,
745
+ isStaleEdit: false,
746
+ favorite_count: 0,
747
+ conversation_count: 0,
748
+ news_action_type: "conversation"
749
+ };
750
+ }
751
+ function buildQuoted(quote, hasQuoteId) {
752
+ if (!quote && !hasQuoteId) {
753
+ return void 0;
754
+ }
755
+ const author = quote?.author ?? { name: "Quoted post", handle: "" };
756
+ const body = quote?.body ?? "";
757
+ const media = quote?.media ?? [];
758
+ return {
759
+ lang: "en",
760
+ created_at: "",
761
+ display_text_range: [0, codepointLength(body)],
762
+ entities: extractEntities(body),
763
+ id_str: "",
764
+ text: body,
765
+ user: buildUser(author),
766
+ edit_control: {
767
+ edit_tweet_ids: [],
768
+ editable_until_msecs: "0",
769
+ is_edit_eligible: false,
770
+ edits_remaining: "0"
771
+ },
772
+ isEdited: false,
773
+ isStaleEdit: false,
774
+ reply_count: 0,
775
+ retweet_count: 0,
776
+ favorite_count: 0,
777
+ self_thread: { id_str: "" },
778
+ ...media.length > 0 ? { mediaDetails: buildMediaDetails(media) } : {}
779
+ };
780
+ }
781
+ function toTweet(input) {
782
+ const { variant, author, media = [], quotedTweet, replyToHandle } = input;
783
+ const text = variant.body ?? "";
784
+ const mediaDetails = buildMediaDetails(media);
785
+ const quoted = buildQuoted(
786
+ quotedTweet,
787
+ variant.settings?.quote_tweet_id !== void 0
788
+ );
789
+ const reply = variant.settings?.reply;
790
+ return {
791
+ __typename: "Tweet",
792
+ ...tweetScaffold(),
793
+ // Codepoint length, not `text.length` (UTF-16) — react-tweet renders the
794
+ // body off `Array.from(text)`, so emoji must not shift the range.
795
+ display_text_range: [0, codepointLength(text)],
796
+ entities: extractEntities(text),
797
+ id_str: "",
798
+ text,
799
+ user: buildUser(author),
800
+ ...mediaDetails.length > 0 ? { mediaDetails } : {},
801
+ ...quoted ? { quoted_tweet: quoted } : {},
802
+ // Both the handle AND the parent id are needed: enrichTweet builds the
803
+ // reply link as `…/${screen_name}/status/${status_id_str}`, so omitting the
804
+ // id yields a `/status/undefined` href.
805
+ ...reply && replyToHandle ? {
806
+ in_reply_to_screen_name: replyToHandle,
807
+ in_reply_to_status_id_str: reply.in_reply_to_tweet_id
808
+ } : {}
809
+ };
810
+ }
811
+ function XPostPreviewImpl({
812
+ variant,
813
+ author,
814
+ media,
815
+ quotedTweet,
816
+ replyToHandle,
817
+ theme = "auto",
818
+ showActions = true,
819
+ className,
820
+ style,
821
+ components
822
+ }) {
823
+ const resolvedMedia = useResolvedMedia(
824
+ media,
825
+ variant.media,
826
+ altSignatureOf(variant.media)
827
+ );
828
+ const resolvedQuotedMedia = useResolvedMedia(quotedTweet?.media, void 0, "");
829
+ const tweet = useMemo(() => {
830
+ const resolvedQuoted = quotedTweet ? {
831
+ author: quotedTweet.author,
832
+ body: quotedTweet.body,
833
+ media: resolvedQuotedMedia
834
+ } : void 0;
835
+ return enrichTweet(
836
+ toTweet({
837
+ variant,
838
+ author,
839
+ media: resolvedMedia,
840
+ quotedTweet: resolvedQuoted,
841
+ replyToHandle
842
+ })
843
+ );
844
+ }, [
845
+ variant,
846
+ author,
847
+ resolvedMedia,
848
+ quotedTweet,
849
+ resolvedQuotedMedia,
850
+ replyToHandle
851
+ ]);
852
+ return /* @__PURE__ */ jsx(
853
+ "div",
854
+ {
855
+ "data-theme": theme === "auto" ? void 0 : theme,
856
+ className,
857
+ style,
858
+ children: /* @__PURE__ */ jsxs(TweetContainer, { children: [
859
+ /* @__PURE__ */ jsx(TweetHeader, { tweet, components }),
860
+ tweet.in_reply_to_screen_name ? /* @__PURE__ */ jsx(TweetInReplyTo, { tweet }) : null,
861
+ /* @__PURE__ */ jsx(TweetBody, { tweet }),
862
+ tweet.mediaDetails?.length ? /* @__PURE__ */ jsx(TweetMedia, { tweet, components }) : null,
863
+ tweet.quoted_tweet ? /* @__PURE__ */ jsx(QuotedTweet, { tweet: tweet.quoted_tweet }) : null,
864
+ showActions ? /* @__PURE__ */ jsx(XPreviewActions, {}) : null
865
+ ] })
866
+ }
867
+ );
868
+ }
869
+ var XPostPreview = memo(XPostPreviewImpl);
870
+ var LI_VAR = {
871
+ bg: "--pr-li-bg",
872
+ text: "--pr-li-text",
873
+ muted: "--pr-li-muted",
874
+ border: "--pr-li-border",
875
+ accent: "--pr-li-accent"
876
+ };
877
+ var LIGHT = {
878
+ bg: "#ffffff",
879
+ text: "rgba(0,0,0,0.9)",
880
+ muted: "rgba(0,0,0,0.6)",
881
+ border: "rgba(0,0,0,0.08)",
882
+ accent: "rgb(10,102,194)"
883
+ };
884
+ var DARK = {
885
+ bg: "#1b1f23",
886
+ text: "rgba(255,255,255,0.9)",
887
+ muted: "rgba(255,255,255,0.6)",
888
+ border: "rgba(255,255,255,0.15)",
889
+ accent: "rgb(112,181,249)"
890
+ };
891
+ function varRef(name) {
892
+ return `var(${name})`;
893
+ }
894
+ function paletteVars(dark) {
895
+ const p = dark ? DARK : LIGHT;
896
+ return {
897
+ "--pr-li-bg": p.bg,
898
+ "--pr-li-text": p.text,
899
+ "--pr-li-muted": p.muted,
900
+ "--pr-li-border": p.border,
901
+ "--pr-li-accent": p.accent
902
+ };
903
+ }
904
+ function usePrefersDark() {
905
+ const [dark, setDark] = useState(false);
906
+ useEffect(() => {
907
+ if (typeof window === "undefined" || !window.matchMedia) {
908
+ return;
909
+ }
910
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
911
+ setDark(mq.matches);
912
+ const onChange = (event) => setDark(event.matches);
913
+ mq.addEventListener("change", onChange);
914
+ return () => mq.removeEventListener("change", onChange);
915
+ }, []);
916
+ return dark;
917
+ }
918
+ function useIsDark(theme) {
919
+ const prefersDark = usePrefersDark();
920
+ if (theme === "auto") {
921
+ return prefersDark;
922
+ }
923
+ return theme === "dark";
924
+ }
925
+ var ROW2 = {
926
+ display: "flex",
927
+ justifyContent: "space-around",
928
+ borderTop: `1px solid ${varRef(LI_VAR.border)}`,
929
+ marginTop: 8,
930
+ padding: "4px 8px"
931
+ };
932
+ var ITEM = {
933
+ display: "flex",
934
+ alignItems: "center",
935
+ gap: 6,
936
+ padding: "8px",
937
+ color: varRef(LI_VAR.muted),
938
+ fontSize: 14,
939
+ fontWeight: 600
940
+ };
941
+ var ACTIONS2 = [
942
+ { label: "Like", Icon: FiThumbsUp },
943
+ { label: "Comment", Icon: FiMessageSquare },
944
+ { label: "Repost", Icon: FiRepeat },
945
+ { label: "Send", Icon: FiSend }
946
+ ];
947
+ function EngagementBar() {
948
+ return /* @__PURE__ */ jsx("div", { style: ROW2, children: ACTIONS2.map(({ label, Icon }) => /* @__PURE__ */ jsxs("span", { style: ITEM, children: [
949
+ /* @__PURE__ */ jsx(Icon, { size: 20, "aria-hidden": true }),
950
+ label
951
+ ] }, label)) });
952
+ }
953
+ var PLACEHOLDER_AVATAR2 = "data:image/svg+xml;utf8," + encodeURIComponent(
954
+ '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48"><circle cx="24" cy="24" r="24" fill="#9aa6b2"/></svg>'
955
+ );
956
+ function Header({ author, visibility, time = "Now" }) {
957
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, padding: "12px 16px 0" }, children: [
958
+ /* @__PURE__ */ jsx(
959
+ "img",
960
+ {
961
+ src: author.avatarUrl ?? PLACEHOLDER_AVATAR2,
962
+ alt: "",
963
+ width: 48,
964
+ height: 48,
965
+ style: { width: 48, height: 48, borderRadius: "50%", objectFit: "cover", flex: "0 0 auto" }
966
+ }
967
+ ),
968
+ /* @__PURE__ */ jsxs("div", { style: { minWidth: 0, display: "flex", flexDirection: "column" }, children: [
969
+ /* @__PURE__ */ jsxs(
970
+ "span",
971
+ {
972
+ style: {
973
+ display: "flex",
974
+ alignItems: "center",
975
+ fontSize: 14,
976
+ fontWeight: 600,
977
+ color: varRef(LI_VAR.text),
978
+ lineHeight: 1.3
979
+ },
980
+ children: [
981
+ author.name,
982
+ author.verified ? /* @__PURE__ */ jsx(
983
+ LuBadgeCheck,
984
+ {
985
+ size: 16,
986
+ "aria-label": "Verified",
987
+ role: "img",
988
+ style: { color: varRef(LI_VAR.accent), marginLeft: 3, flex: "0 0 auto" }
989
+ }
990
+ ) : null
991
+ ]
992
+ }
993
+ ),
994
+ author.headline ? /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: varRef(LI_VAR.muted), lineHeight: 1.3 }, children: author.headline }) : null,
995
+ /* @__PURE__ */ jsxs(
996
+ "span",
997
+ {
998
+ style: {
999
+ display: "flex",
1000
+ alignItems: "center",
1001
+ gap: 4,
1002
+ fontSize: 12,
1003
+ color: varRef(LI_VAR.muted),
1004
+ lineHeight: 1.3
1005
+ },
1006
+ children: [
1007
+ time,
1008
+ " \u2022",
1009
+ " ",
1010
+ visibility === "PUBLIC" ? /* @__PURE__ */ jsx(FiGlobe, { size: 13, "aria-label": "Public", role: "img" }) : /* @__PURE__ */ jsx(FiUsers, { size: 13, "aria-label": "Connections", role: "img" })
1011
+ ]
1012
+ }
1013
+ )
1014
+ ] })
1015
+ ] });
1016
+ }
1017
+ var MAX_TILES = 4;
1018
+ var MOSAIC_HEIGHT = 272;
1019
+ var GRID_BASE = {
1020
+ display: "grid",
1021
+ gap: 2,
1022
+ height: MOSAIC_HEIGHT,
1023
+ overflow: "hidden"
1024
+ };
1025
+ function gridStyle(tiles) {
1026
+ if (tiles === 2) {
1027
+ return { ...GRID_BASE, gridTemplateColumns: "1fr 1fr" };
1028
+ }
1029
+ if (tiles === 3) {
1030
+ return {
1031
+ ...GRID_BASE,
1032
+ gridTemplateColumns: "1fr 1fr",
1033
+ gridTemplateRows: "1fr 1fr",
1034
+ gridTemplateAreas: '"a b" "a c"'
1035
+ };
1036
+ }
1037
+ return {
1038
+ ...GRID_BASE,
1039
+ gridTemplateColumns: "1fr 1fr",
1040
+ gridTemplateRows: "1fr 1fr"
1041
+ };
1042
+ }
1043
+ var IMG_STYLE = {
1044
+ width: "100%",
1045
+ height: "100%",
1046
+ objectFit: "cover",
1047
+ display: "block"
1048
+ };
1049
+ var AREAS = ["a", "b", "c"];
1050
+ function Tile({
1051
+ item,
1052
+ area,
1053
+ overlay
1054
+ }) {
1055
+ return /* @__PURE__ */ jsxs("div", { style: { position: "relative", gridArea: area, overflow: "hidden" }, children: [
1056
+ /* @__PURE__ */ jsx("img", { src: item.src, alt: item.alt ?? "", style: IMG_STYLE }),
1057
+ overlay ? /* @__PURE__ */ jsx(
1058
+ "div",
1059
+ {
1060
+ "aria-hidden": false,
1061
+ style: {
1062
+ position: "absolute",
1063
+ inset: 0,
1064
+ display: "flex",
1065
+ alignItems: "center",
1066
+ justifyContent: "center",
1067
+ background: "rgba(0,0,0,0.5)",
1068
+ color: "#fff",
1069
+ fontSize: 28,
1070
+ fontWeight: 600
1071
+ },
1072
+ children: `+${overlay}`
1073
+ }
1074
+ ) : null
1075
+ ] });
1076
+ }
1077
+ function ImageMosaic({ media }) {
1078
+ if (media.length === 0) {
1079
+ return null;
1080
+ }
1081
+ if (media.length === 1) {
1082
+ const only = media[0];
1083
+ return /* @__PURE__ */ jsx(
1084
+ "img",
1085
+ {
1086
+ src: only.src,
1087
+ alt: only.alt ?? "",
1088
+ style: { width: "100%", height: "auto", display: "block" }
1089
+ }
1090
+ );
1091
+ }
1092
+ const tiles = media.slice(0, MAX_TILES);
1093
+ const hidden = media.length - MAX_TILES;
1094
+ return /* @__PURE__ */ jsx("div", { style: gridStyle(tiles.length), children: tiles.map((item, index) => /* @__PURE__ */ jsx(
1095
+ Tile,
1096
+ {
1097
+ item,
1098
+ area: tiles.length === 3 ? AREAS[index] : void 0,
1099
+ overlay: hidden > 0 && index === MAX_TILES - 1 ? hidden : void 0
1100
+ },
1101
+ item.src + index
1102
+ )) });
1103
+ }
1104
+ function VideoTile({ item }) {
1105
+ return /* @__PURE__ */ jsx(
1106
+ "video",
1107
+ {
1108
+ src: item.src,
1109
+ poster: item.posterSrc,
1110
+ controls: true,
1111
+ style: { width: "100%", display: "block", background: "#000" }
1112
+ }
1113
+ );
1114
+ }
1115
+ function Media({ media }) {
1116
+ if (media.length === 0) {
1117
+ return null;
1118
+ }
1119
+ const video = media.find(
1120
+ (item) => item.kind === "video" || item.kind === "gif"
1121
+ );
1122
+ if (video) {
1123
+ return /* @__PURE__ */ jsx(VideoTile, { item: video });
1124
+ }
1125
+ const images = media.filter((item) => item.kind === "image");
1126
+ return /* @__PURE__ */ jsx(ImageMosaic, { media: images });
1127
+ }
1128
+ var FOLD_CHARS = 200;
1129
+ var BODY_STYLE = {
1130
+ whiteSpace: "pre-wrap",
1131
+ wordBreak: "break-word",
1132
+ fontSize: 14,
1133
+ lineHeight: 1.43
1134
+ };
1135
+ function truncateAtWord(text, max) {
1136
+ if (text.length <= max) {
1137
+ return text;
1138
+ }
1139
+ const slice = text.slice(0, max);
1140
+ const lastSpace = slice.lastIndexOf(" ");
1141
+ return slice.slice(0, lastSpace > 0 ? lastSpace : max);
1142
+ }
1143
+ function escapeRegExp(value) {
1144
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1145
+ }
1146
+ function buildPattern(mentionNames) {
1147
+ const mentionAlt = mentionNames.filter((name) => name.trim().length > 0).map(escapeRegExp).join("|");
1148
+ const parts = [
1149
+ "https?:\\/\\/[^\\s]+",
1150
+ // urls
1151
+ "#[\\p{L}\\p{N}_]+",
1152
+ // hashtags (unicode-aware)
1153
+ ...mentionAlt ? [mentionAlt] : []
1154
+ ];
1155
+ return new RegExp(`(${parts.join("|")})`, "gu");
1156
+ }
1157
+ function linkStyle(color) {
1158
+ return { color, textDecoration: "none", fontWeight: 500 };
1159
+ }
1160
+ function linkify(text, colors, mentionNames) {
1161
+ const pattern = buildPattern(mentionNames);
1162
+ const nodes = [];
1163
+ let lastIndex = 0;
1164
+ let key = 0;
1165
+ for (const match of text.matchAll(pattern)) {
1166
+ const token = match[0];
1167
+ const start = match.index;
1168
+ if (start > lastIndex) {
1169
+ nodes.push(
1170
+ /* @__PURE__ */ jsx(Fragment$1, { children: text.slice(lastIndex, start) }, key++)
1171
+ );
1172
+ }
1173
+ if (token.startsWith("http")) {
1174
+ nodes.push(
1175
+ /* @__PURE__ */ jsx(
1176
+ "a",
1177
+ {
1178
+ href: token,
1179
+ style: linkStyle(colors.accent),
1180
+ target: "_blank",
1181
+ rel: "noreferrer",
1182
+ children: token
1183
+ },
1184
+ key++
1185
+ )
1186
+ );
1187
+ } else if (token.startsWith("#")) {
1188
+ nodes.push(
1189
+ /* @__PURE__ */ jsx("a", { href: "#", style: linkStyle(colors.accent), children: token }, key++)
1190
+ );
1191
+ } else {
1192
+ nodes.push(
1193
+ /* @__PURE__ */ jsx("span", { style: linkStyle(colors.accent), children: token }, key++)
1194
+ );
1195
+ }
1196
+ lastIndex = start + token.length;
1197
+ }
1198
+ if (lastIndex < text.length) {
1199
+ nodes.push(/* @__PURE__ */ jsx(Fragment$1, { children: text.slice(lastIndex) }, key++));
1200
+ }
1201
+ return nodes;
1202
+ }
1203
+ function PostBody({ text, mentionNames = [], colors }) {
1204
+ const [expanded, setExpanded] = useState(false);
1205
+ const isLong = text.length > FOLD_CHARS;
1206
+ const shown = isLong && !expanded ? truncateAtWord(text, FOLD_CHARS) : text;
1207
+ return /* @__PURE__ */ jsxs("div", { style: BODY_STYLE, children: [
1208
+ linkify(shown, colors, mentionNames),
1209
+ isLong && !expanded ? /* @__PURE__ */ jsxs(Fragment, { children: [
1210
+ "\u2026",
1211
+ " ",
1212
+ /* @__PURE__ */ jsx(
1213
+ "button",
1214
+ {
1215
+ type: "button",
1216
+ onClick: () => setExpanded(true),
1217
+ style: {
1218
+ background: "none",
1219
+ border: "none",
1220
+ padding: 0,
1221
+ cursor: "pointer",
1222
+ color: colors.muted,
1223
+ fontWeight: 600,
1224
+ fontSize: 14
1225
+ },
1226
+ children: "more"
1227
+ }
1228
+ )
1229
+ ] }) : null
1230
+ ] });
1231
+ }
1232
+ var FONT_STACK = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif';
1233
+ function LinkedInPostPreviewImpl({
1234
+ variant,
1235
+ author,
1236
+ media,
1237
+ theme = "auto",
1238
+ time,
1239
+ showActions = true,
1240
+ className,
1241
+ style
1242
+ }) {
1243
+ const dark = useIsDark(theme);
1244
+ const resolvedMedia = useResolvedMedia(
1245
+ media,
1246
+ variant.media,
1247
+ altSignatureOf(variant.media)
1248
+ );
1249
+ const visibility = variant.settings?.visibility ?? "PUBLIC";
1250
+ const mentionNames = (variant.settings?.mentions ?? []).map((m) => m.name);
1251
+ const bodyColors = {
1252
+ accent: varRef(LI_VAR.accent),
1253
+ muted: varRef(LI_VAR.muted)
1254
+ };
1255
+ const cardStyle = {
1256
+ ...paletteVars(dark),
1257
+ background: varRef(LI_VAR.bg),
1258
+ color: varRef(LI_VAR.text),
1259
+ border: `1px solid ${varRef(LI_VAR.border)}`,
1260
+ borderRadius: 10,
1261
+ maxWidth: 552,
1262
+ overflow: "hidden",
1263
+ fontFamily: FONT_STACK,
1264
+ ...style
1265
+ };
1266
+ return /* @__PURE__ */ jsxs("div", { className, style: cardStyle, children: [
1267
+ /* @__PURE__ */ jsx(Header, { author, visibility, time }),
1268
+ variant.body ? /* @__PURE__ */ jsx("div", { style: { padding: "8px 16px 0" }, children: /* @__PURE__ */ jsx(
1269
+ PostBody,
1270
+ {
1271
+ text: variant.body,
1272
+ mentionNames,
1273
+ colors: bodyColors
1274
+ }
1275
+ ) }) : null,
1276
+ resolvedMedia.length > 0 ? /* @__PURE__ */ jsx("div", { style: { marginTop: 12 }, children: /* @__PURE__ */ jsx(Media, { media: resolvedMedia }) }) : null,
1277
+ showActions ? /* @__PURE__ */ jsx(EngagementBar, {}) : null
1278
+ ] });
1279
+ }
1280
+ var LinkedInPostPreview = memo(LinkedInPostPreviewImpl);
1281
+
1282
+ export { LinkedInPostPreview, PostrunProvider, UploadError, XPostPreview, connectionKeys, mediaKeys, postKeys, profileKeys, useCalendar, useConnect, useConnection, useConnections, useCreatePost, useCreateProfile, useDeleteMedia, useDeletePost, useDeleteProfile, useDisconnect, useDiscoverableAccounts, useInfiniteList, useMedia, useMediaUpload, usePost, usePostrun, usePosts, usePostsInfinite, useProfile, useProfiles, useProfilesInfinite, useSelectAccount, useUpdateMedia, useUpdatePost, useUpdateProfile };
1283
+ //# sourceMappingURL=index.js.map
1284
+ //# sourceMappingURL=index.js.map