@atproto-ui/core 0.13.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 (51) hide show
  1. package/dist/backlinks.d.ts +18 -0
  2. package/dist/backlinks.js +15 -0
  3. package/dist/blob-fetch.d.ts +42 -0
  4. package/dist/blob-fetch.js +53 -0
  5. package/dist/cache.d.ts +155 -0
  6. package/dist/cache.js +345 -0
  7. package/dist/client.d.ts +45 -0
  8. package/dist/client.js +118 -0
  9. package/dist/fetch-and-cache-handler.d.ts +84 -0
  10. package/dist/fetch-and-cache-handler.js +63 -0
  11. package/dist/grain.d.ts +22 -0
  12. package/dist/grain.js +30 -0
  13. package/dist/icons.d.ts +11 -0
  14. package/dist/icons.js +11 -0
  15. package/dist/identity.d.ts +28 -0
  16. package/dist/identity.js +55 -0
  17. package/dist/index.d.ts +27 -0
  18. package/dist/index.js +22 -0
  19. package/dist/leaflet.d.ts +22 -0
  20. package/dist/leaflet.js +85 -0
  21. package/dist/music.d.ts +35 -0
  22. package/dist/music.js +88 -0
  23. package/dist/paginated.d.ts +61 -0
  24. package/dist/paginated.js +111 -0
  25. package/dist/records.d.ts +171 -0
  26. package/dist/records.js +321 -0
  27. package/dist/rpc.d.ts +21 -0
  28. package/dist/rpc.js +24 -0
  29. package/dist/styles.d.ts +40 -0
  30. package/dist/styles.js +1728 -0
  31. package/dist/tangled-fetch.d.ts +9 -0
  32. package/dist/tangled-fetch.js +15 -0
  33. package/dist/types/blob.d.ts +14 -0
  34. package/dist/types/bluesky.d.ts +4 -0
  35. package/dist/types/grain.d.ts +31 -0
  36. package/dist/types/leaflet.d.ts +173 -0
  37. package/dist/types/record.d.ts +28 -0
  38. package/dist/types/record.js +7 -0
  39. package/dist/types/tangled.d.ts +19 -0
  40. package/dist/types/teal.d.ts +36 -0
  41. package/dist/utils/at-uri.d.ts +10 -0
  42. package/dist/utils/at-uri.js +33 -0
  43. package/dist/utils/blob.d.ts +5 -0
  44. package/dist/utils/blob.js +26 -0
  45. package/dist/utils/profile.d.ts +2 -0
  46. package/dist/utils/profile.js +5 -0
  47. package/dist/utils/richtext.d.ts +28 -0
  48. package/dist/utils/richtext.js +95 -0
  49. package/dist/utils/time.d.ts +2 -0
  50. package/dist/utils/time.js +41 -0
  51. package/package.json +41 -0
package/dist/client.js ADDED
@@ -0,0 +1,118 @@
1
+ import { Client, simpleFetchHandler } from "@atcute/client";
2
+ import { CompositeDidDocumentResolver, PlcDidDocumentResolver, WebDidDocumentResolver, XrpcHandleResolver } from "@atcute/identity-resolver";
3
+ var DEFAULT_PLC = "https://plc.directory", DEFAULT_IDENTITY_SERVICE = "https://public.api.bsky.app", DEFAULT_SLINGSHOT = "https://slingshot.microcosm.blue", DEFAULT_APPVIEW = "https://public.api.bsky.app", DEFAULT_BLUESKY_APP = "https://bsky.app", DEFAULT_TANGLED = "https://tangled.org", DEFAULT_CONSTELLATION = "https://constellation.microcosm.blue", ABSOLUTE_URL_RE = /^[a-zA-Z][a-zA-Z\d+\-.]*:/, SUPPORTED_DID_METHODS = ["plc", "web"];
4
+ const DEFAULT_CONFIG = {
5
+ plcDirectory: DEFAULT_PLC,
6
+ identityService: DEFAULT_IDENTITY_SERVICE,
7
+ slingshotBaseUrl: DEFAULT_SLINGSHOT,
8
+ blueskyAppviewService: DEFAULT_APPVIEW,
9
+ blueskyAppBaseUrl: DEFAULT_BLUESKY_APP,
10
+ tangledBaseUrl: DEFAULT_TANGLED,
11
+ constellationBaseUrl: DEFAULT_CONSTELLATION
12
+ }, SLINGSHOT_BASE_URL = DEFAULT_SLINGSHOT, normalizeBaseUrl = (e) => {
13
+ let d = e.trim();
14
+ if (!d) throw Error("Service URL cannot be empty");
15
+ let f = ABSOLUTE_URL_RE.test(d) ? d : `https://${d.replace(/^\/+/, "")}`, p = new URL(f), m = p.pathname.replace(/\/+$/, "");
16
+ return m ? `${p.origin}${m}` : p.origin;
17
+ };
18
+ var ServiceResolver = class {
19
+ constructor(e = {}) {
20
+ let d = e.plcDirectory && e.plcDirectory.trim() ? e.plcDirectory : DEFAULT_PLC, v = e.identityService && e.identityService.trim() ? e.identityService : DEFAULT_IDENTITY_SERVICE, y = e.slingshotBaseUrl && e.slingshotBaseUrl.trim() ? e.slingshotBaseUrl : DEFAULT_SLINGSHOT;
21
+ this.plc = normalizeBaseUrl(d);
22
+ let b = normalizeBaseUrl(v);
23
+ this.slingshot = normalizeBaseUrl(y), this.fetchImpl = bindFetch(e.fetch);
24
+ let x = new PlcDidDocumentResolver({
25
+ apiUrl: this.plc,
26
+ fetch: this.fetchImpl
27
+ }), S = new WebDidDocumentResolver({ fetch: this.fetchImpl });
28
+ this.didResolver = new CompositeDidDocumentResolver({ methods: {
29
+ plc: x,
30
+ web: S
31
+ } }), this.handleResolver = new XrpcHandleResolver({
32
+ serviceUrl: b,
33
+ fetch: this.fetchImpl
34
+ });
35
+ }
36
+ async resolveDidDoc(e) {
37
+ let d = e.trim();
38
+ if (!d.startsWith("did:")) throw Error(`Invalid DID ${e}`);
39
+ let f = d.indexOf(":", 4), p = f === -1 ? d.slice(4) : d.slice(4, f);
40
+ if (!SUPPORTED_DID_METHODS.includes(p)) throw Error(`Unsupported DID method ${p ?? "<unknown>"}`);
41
+ return this.didResolver.resolve(d);
42
+ }
43
+ async pdsEndpointForDid(e) {
44
+ let d = (await this.resolveDidDoc(e)).service?.find((e) => e.type === "AtprotoPersonalDataServer");
45
+ if (!d || !d.serviceEndpoint || typeof d.serviceEndpoint != "string") throw Error(`No PDS endpoint in DID doc for ${e}`);
46
+ return d.serviceEndpoint.replace(/\/$/, "");
47
+ }
48
+ getSlingshotUrl() {
49
+ return this.slingshot;
50
+ }
51
+ async resolveHandle(e) {
52
+ let d = e.trim().toLowerCase();
53
+ if (!d) throw Error("Handle cannot be empty");
54
+ let f;
55
+ try {
56
+ let e = new URL("/xrpc/com.atproto.identity.resolveHandle", this.slingshot);
57
+ e.searchParams.set("handle", d);
58
+ let p = await this.fetchImpl(e);
59
+ if (p.ok) {
60
+ let e = await p.json();
61
+ if (e?.did) return e.did;
62
+ f = /* @__PURE__ */ Error("Slingshot resolveHandle response missing DID");
63
+ } else {
64
+ f = /* @__PURE__ */ Error(`Slingshot resolveHandle failed with status ${p.status}`);
65
+ let e = p.body;
66
+ e && e.cancel().catch(() => {});
67
+ }
68
+ } catch (e) {
69
+ if (e instanceof DOMException && e.name === "AbortError") throw e;
70
+ f = e instanceof Error ? e : Error(String(e));
71
+ }
72
+ try {
73
+ return await this.handleResolver.resolve(d);
74
+ } catch (e) {
75
+ throw f && e instanceof Error && (e.message = `${e.message}; Slingshot resolveHandle failed: ${f.message}`), e;
76
+ }
77
+ }
78
+ };
79
+ async function createAtprotoClient(d = {}) {
80
+ let f = bindFetch(d.fetch), p = d.service, m = new ServiceResolver({
81
+ ...d,
82
+ fetch: f
83
+ });
84
+ if (!p && d.did && (p = await m.pdsEndpointForDid(d.did)), !p) throw Error("service or did required");
85
+ let h = normalizeBaseUrl(p), g = m.getSlingshotUrl(), _ = createSlingshotAwareHandler(h, g, f);
86
+ return {
87
+ rpc: new Client({ handler: _ }),
88
+ service: h,
89
+ resolver: m
90
+ };
91
+ }
92
+ var SLINGSHOT_RETRY_PATHS = ["/xrpc/com.atproto.repo.getRecord", "/xrpc/com.atproto.identity.resolveHandle"];
93
+ function createSlingshotAwareHandler(e, f, p) {
94
+ let m = simpleFetchHandler({
95
+ service: e,
96
+ fetch: p
97
+ }), h = simpleFetchHandler({
98
+ service: f,
99
+ fetch: p
100
+ });
101
+ return async (e, d) => {
102
+ if (SLINGSHOT_RETRY_PATHS.find((d) => e === d || e.startsWith(`${d}?`))) try {
103
+ let f = await h(e, d);
104
+ if (f.ok) return f;
105
+ let p = f.body;
106
+ p && p.cancel().catch(() => {});
107
+ } catch (e) {
108
+ if (e instanceof DOMException && e.name === "AbortError") throw e;
109
+ }
110
+ return m(e, d);
111
+ };
112
+ }
113
+ function bindFetch(e) {
114
+ let d = e ?? globalThis.fetch;
115
+ if (typeof d != "function") throw Error("fetch implementation not available");
116
+ return d.bind(globalThis);
117
+ }
118
+ export { DEFAULT_CONFIG, SLINGSHOT_BASE_URL, ServiceResolver, createAtprotoClient, normalizeBaseUrl };
@@ -0,0 +1,84 @@
1
+ import { type FetchHandler } from "@atcute/client";
2
+ import type { AtprotoCaches } from "./cache";
3
+ import { primeBlueskyFeedCache } from "./records";
4
+ export interface FetchAndCacheHandlerOptions {
5
+ /**
6
+ * Service URL passed through to atcute's `simpleFetchHandler`.
7
+ */
8
+ service: string | URL;
9
+ /**
10
+ * Optional fetch implementation. Useful for tests, benchmarks, and request
11
+ * counters.
12
+ */
13
+ fetch?: typeof fetch;
14
+ /**
15
+ * Cache instances to prime. Defaults to atproto-ui's shared caches.
16
+ */
17
+ caches?: AtprotoCaches;
18
+ }
19
+ export interface FetchAndCacheResult {
20
+ dids: number;
21
+ profiles: number;
22
+ records: number;
23
+ }
24
+ type FeedItemLike = Parameters<typeof primeBlueskyFeedCache>[0]["feed"][number];
25
+ interface PostViewLike {
26
+ uri?: string;
27
+ record?: unknown;
28
+ author?: {
29
+ did?: string;
30
+ handle?: string;
31
+ displayName?: string;
32
+ description?: string;
33
+ avatar?: string;
34
+ createdAt?: string;
35
+ pronouns?: string;
36
+ website?: string;
37
+ };
38
+ reply?: {
39
+ parent?: {
40
+ uri?: string;
41
+ author?: {
42
+ did?: string;
43
+ handle?: string;
44
+ };
45
+ };
46
+ };
47
+ }
48
+ interface AppviewPayloadLike {
49
+ feed?: FeedItemLike[];
50
+ posts?: PostViewLike[];
51
+ thread?: ThreadViewLike;
52
+ did?: string;
53
+ handle?: string;
54
+ displayName?: string;
55
+ description?: string;
56
+ avatar?: string;
57
+ banner?: string;
58
+ createdAt?: string;
59
+ pronouns?: string;
60
+ website?: string;
61
+ profiles?: Array<AppviewPayloadLike>;
62
+ }
63
+ interface ThreadViewLike {
64
+ post?: PostViewLike;
65
+ parent?: ThreadViewLike | {
66
+ post?: PostViewLike;
67
+ };
68
+ replies?: Array<ThreadViewLike | {
69
+ post?: PostViewLike;
70
+ }>;
71
+ }
72
+ /**
73
+ * Creates an atcute-compatible fetch handler that automatically primes
74
+ * atproto-ui's shared caches from Bluesky appview responses.
75
+ *
76
+ * Use this in an atcute `Client` when an app fetches feed/profile data itself
77
+ * and then passes records into atproto-ui components. The response still flows
78
+ * through atcute unchanged; the handler only clones successful JSON responses
79
+ * and stores the DID/profile/post data appview already returned.
80
+ */
81
+ export declare function createFetchAndCacheHandler(options: FetchAndCacheHandlerOptions): FetchHandler;
82
+ export declare function primeResponseFromAppview(pathname: string, response: Response, caches?: AtprotoCaches): Promise<FetchAndCacheResult | undefined>;
83
+ export declare function primeBlueskyAppviewPayload(payload: AppviewPayloadLike, caches?: AtprotoCaches): FetchAndCacheResult;
84
+ export {};
@@ -0,0 +1,63 @@
1
+ import { getSharedCaches } from "./cache.js";
2
+ import { primeBlueskyFeedCache, primeBlueskyProfileCache } from "./records.js";
3
+ import { simpleFetchHandler } from "@atcute/client";
4
+ function createFetchAndCacheHandler(a) {
5
+ let o = simpleFetchHandler({
6
+ service: a.service,
7
+ fetch: a.fetch
8
+ }), c = a.caches ?? getSharedCaches();
9
+ return async (e, a) => {
10
+ let s = await o(e, a);
11
+ return await primeResponseFromAppview(e, s, c), s;
12
+ };
13
+ }
14
+ async function primeResponseFromAppview(a, o, s = getSharedCaches()) {
15
+ if (!(!o.ok || !isBlueskyAppviewPath(a))) try {
16
+ let e = await o.clone().json();
17
+ return primeBlueskyAppviewPayload(e, s);
18
+ } catch {
19
+ return;
20
+ }
21
+ }
22
+ function primeBlueskyAppviewPayload(s, c = getSharedCaches()) {
23
+ let l = {
24
+ dids: 0,
25
+ profiles: 0,
26
+ records: 0
27
+ };
28
+ Array.isArray(s.feed) && addPrimeResult(l, primeBlueskyFeedCache({
29
+ feed: s.feed,
30
+ ...c
31
+ })), Array.isArray(s.posts) && addPrimeResult(l, primeBlueskyFeedCache({
32
+ feed: s.posts.map((e) => ({ post: e })),
33
+ ...c
34
+ }));
35
+ let u = collectThreadPosts(s.thread);
36
+ u.length > 0 && addPrimeResult(l, primeBlueskyFeedCache({
37
+ feed: u.map((e) => ({ post: e })),
38
+ ...c
39
+ }));
40
+ for (let e of [s, ...Array.isArray(s.profiles) ? s.profiles : []]) e.did && addPrimeResult(l, primeBlueskyProfileCache({
41
+ profile: e,
42
+ ...c
43
+ }));
44
+ return l;
45
+ }
46
+ function isBlueskyAppviewPath(e) {
47
+ return xrpcNsid(e)?.startsWith("app.bsky.") ?? !1;
48
+ }
49
+ function xrpcNsid(e) {
50
+ let a = e.split("?")[0] ?? e;
51
+ if (a.startsWith("/xrpc/")) return decodeURIComponent(a.slice(6));
52
+ }
53
+ function collectThreadPosts(e) {
54
+ if (!e) return [];
55
+ let a = [], o = (e) => {
56
+ if (e && (e.post && a.push(e.post), "parent" in e && o(e.parent), "replies" in e && Array.isArray(e.replies))) for (let a of e.replies) o(a);
57
+ };
58
+ return o(e), a;
59
+ }
60
+ function addPrimeResult(e, a) {
61
+ e.dids += a.dids, e.profiles += a.profiles, e.records += a.records;
62
+ }
63
+ export { createFetchAndCacheHandler, primeBlueskyAppviewPayload, primeResponseFromAppview };
@@ -0,0 +1,22 @@
1
+ import type { ServiceResolver } from "./client";
2
+ import type { GrainPhotoRecord } from "./types/grain";
3
+ export declare const GRAIN_GALLERY_COLLECTION = "social.grain.gallery";
4
+ export declare const GRAIN_GALLERY_ITEM_COLLECTION = "social.grain.gallery.item";
5
+ export declare const GRAIN_PHOTO_COLLECTION = "social.grain.photo";
6
+ export interface GrainGalleryPhoto {
7
+ record: GrainPhotoRecord;
8
+ did: string;
9
+ rkey: string;
10
+ position?: number;
11
+ }
12
+ export interface GalleryBacklink {
13
+ did: string;
14
+ rkey: string;
15
+ }
16
+ /**
17
+ * Resolves grain.social gallery photos from gallery-item backlinks: for each
18
+ * backlink it loads the gallery-item record, follows its `item` URI to the photo
19
+ * record, and returns the photos sorted by position. Network/resolution only —
20
+ * framework hooks just feed in the backlinks and render the result.
21
+ */
22
+ export declare function fetchGrainGalleryPhotos(backlinks: GalleryBacklink[], resolver: ServiceResolver): Promise<GrainGalleryPhoto[]>;
package/dist/grain.js ADDED
@@ -0,0 +1,30 @@
1
+ import { callGetRecord } from "./rpc.js";
2
+ import { parseAtUri } from "./utils/at-uri.js";
3
+ const GRAIN_GALLERY_COLLECTION = "social.grain.gallery", GRAIN_GALLERY_ITEM_COLLECTION = "social.grain.gallery.item", GRAIN_PHOTO_COLLECTION = "social.grain.photo";
4
+ async function getRecordValue(t, n, r, i) {
5
+ try {
6
+ let a = await callGetRecord(i.getSlingshotUrl(), t, n, r);
7
+ if (a.ok) return a.data.value;
8
+ } catch {}
9
+ try {
10
+ let a = await i.pdsEndpointForDid(t), o = await callGetRecord(a, t, n, r);
11
+ if (o.ok) return o.data.value;
12
+ } catch {}
13
+ return null;
14
+ }
15
+ async function fetchGrainGalleryPhotos(e, n) {
16
+ return !e || e.length === 0 ? [] : (await Promise.all(e.map(async (e) => {
17
+ let i = await getRecordValue(e.did, GRAIN_GALLERY_ITEM_COLLECTION, e.rkey, n);
18
+ if (!i) return null;
19
+ let o = parseAtUri(i.item);
20
+ if (!o) return null;
21
+ let s = await getRecordValue(o.did, o.collection, o.rkey, n);
22
+ return s ? {
23
+ record: s,
24
+ did: o.did,
25
+ rkey: o.rkey,
26
+ position: i.position
27
+ } : null;
28
+ }))).filter((e) => e !== null).sort((e, t) => (e.position ?? 0) - (t.position ?? 0));
29
+ }
30
+ export { GRAIN_GALLERY_COLLECTION, GRAIN_GALLERY_ITEM_COLLECTION, GRAIN_PHOTO_COLLECTION, fetchGrainGalleryPhotos };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Shared SVG icon data so every framework renders identical icons.
3
+ * Each entry is the inner `<path>` data plus the SVG viewBox.
4
+ */
5
+ export interface IconData {
6
+ viewBox: string;
7
+ path: string;
8
+ }
9
+ export declare const tangledIcon: IconData;
10
+ export declare const githubIcon: IconData;
11
+ export declare const starIcon: IconData;
package/dist/icons.js ADDED
@@ -0,0 +1,11 @@
1
+ const tangledIcon = {
2
+ viewBox: "0 0 25 25",
3
+ path: "m 16.208435,23.914069 c -0.06147,-0.02273 -0.147027,-0.03034 -0.190158,-0.01691 -0.197279,0.06145 -1.31068,-0.230493 -1.388819,-0.364153 -0.01956,-0.03344 -0.163274,-0.134049 -0.319377,-0.223561 -0.550395,-0.315603 -1.010951,-0.696643 -1.428383,-1.181771 -0.264598,-0.307509 -0.597257,-0.785384 -0.597257,-0.857979 0,-0.0216 -0.02841,-0.06243 -0.06313,-0.0907 -0.04977,-0.04053 -0.160873,0.0436 -0.52488,0.397463 -0.479803,0.466432 -0.78924,0.689475 -1.355603,0.977118 -0.183693,0.0933 -0.323426,0.179989 -0.310516,0.192658 0.02801,0.02748 -0.7656391,0.270031 -1.209129,0.369517 -0.5378332,0.120647 -1.6341809,0.08626 -1.9721503,-0.06186 C 6.7977157,23.031391 6.56735,22.957551 6.3371134,22.889782 4.9717169,22.487902 3.7511914,21.481518 3.1172396,20.234838 2.6890391,19.392772 2.5582276,18.827446 2.5610489,17.831154 2.5639589,16.802192 2.7366641,16.125844 3.2142117,15.273187 3.3040457,15.112788 3.3713143,14.976533 3.3636956,14.9704 3.3560756,14.9643 3.2459634,14.90305 3.1189994,14.834381 1.7582586,14.098312 0.77760984,12.777439 0.44909837,11.23818 0.33531456,10.705039 0.33670119,9.7067968 0.45195381,9.1778795 0.72259241,7.9359287 1.3827188,6.8888436 2.4297498,6.0407205 2.6856126,5.8334648 3.2975489,5.4910878 3.6885849,5.3364049 L 4.0584319,5.190106 4.2333984,4.860432 C 4.8393906,3.7186139 5.8908314,2.7968028 7.1056396,2.3423025 7.7690673,2.0940921 8.2290216,2.0150935 9.01853,2.0137575 c 0.9625627,-0.00163 1.629181,0.1532762 2.485864,0.5776514 l 0.271744,0.1346134 0.42911,-0.3607688 c 1.082666,-0.9102346 2.185531,-1.3136811 3.578383,-1.3090327 0.916696,0.00306 1.573918,0.1517893 2.356121,0.5331927 1.465948,0.7148 2.54506,2.0625628 2.865177,3.57848 l 0.07653,0.362429 0.515095,0.2556611 c 1.022872,0.5076874 1.756122,1.1690944 2.288361,2.0641468 0.401896,0.6758594 0.537303,1.0442682 0.675505,1.8378683 0.288575,1.6570823 -0.266229,3.3548023 -1.490464,4.5608743 -0.371074,0.36557 -0.840205,0.718265 -1.203442,0.904754 -0.144112,0.07398 -0.271303,0.15826 -0.282647,0.187269 -0.01134,0.02901 0.02121,0.142764 0.07234,0.25279 0.184248,0.396467 0.451371,1.331823 0.619371,2.168779 0.463493,2.30908 -0.754646,4.693707 -2.92278,5.721632 -0.479538,0.227352 -0.717629,0.309322 -1.144194,0.39393 -0.321869,0.06383 -1.850573,0.09139 -2.000174,0.03604 z M 12.25443,18.636956 c 0.739923,-0.24652 1.382521,-0.718922 1.874623,-1.37812 0.0752,-0.100718 0.213883,-0.275851 0.308198,-0.389167 0.09432,-0.113318 0.210136,-0.271056 0.257381,-0.350531 0.416347,-0.700389 0.680936,-1.176102 0.766454,-1.378041 0.05594,-0.132087 0.114653,-0.239607 0.130477,-0.238929 0.01583,6.79e-4 0.08126,0.08531 0.145412,0.188069 0.178029,0.285173 0.614305,0.658998 0.868158,0.743878 0.259802,0.08686 0.656158,0.09598 0.911369,0.02095 0.213812,-0.06285 0.507296,-0.298016 0.645179,-0.516947 0.155165,-0.246374 0.327989,-0.989595 0.327989,-1.410501 0,-1.26718 -0.610975,-3.143405 -1.237774,-3.801045 -0.198483,-0.2082486 -0.208557,-0.2319396 -0.208557,-0.4904655 0,-0.2517771 -0.08774,-0.5704927 -0.258476,-0.938956 C 16.694963,8.50313 16.375697,8.1377479 16.135846,7.9543702 L 15.932296,7.7987471 15.683004,7.9356529 C 15.131767,8.2383821 14.435638,8.1945733 13.943459,7.8261812 L 13.782862,7.7059758 13.686773,7.8908012 C 13.338849,8.5600578 12.487087,8.8811064 11.743178,8.6233891 11.487199,8.5347109 11.358897,8.4505994 11.063189,8.1776138 L 10.69871,7.8411436 10.453484,8.0579255 C 10.318608,8.1771557 10.113778,8.3156283 9.9983037,8.3656417 9.7041488,8.4930449 9.1808299,8.5227884 8.8979004,8.4281886 8.7754792,8.3872574 8.6687415,8.3537661 8.6607053,8.3537661 c -0.03426,0 -0.3092864,0.3066098 -0.3791974,0.42275 -0.041935,0.069664 -0.1040482,0.1266636 -0.1380294,0.1266636 -0.1316419,0 -0.4197402,0.1843928 -0.6257041,0.4004735 -0.1923125,0.2017571 -0.6853701,0.9036038 -0.8926582,1.2706578 -0.042662,0.07554 -0.1803555,0.353687 -0.3059848,0.618091 -0.1256293,0.264406 -0.3270073,0.686768 -0.4475067,0.938581 -0.1204992,0.251816 -0.2469926,0.519654 -0.2810961,0.595199 -0.2592829,0.574347 -0.285919,1.391094 -0.057822,1.77304 0.1690683,0.283105 0.4224039,0.480895 0.7285507,0.568809 0.487122,0.139885 0.9109638,-0.004 1.6013422,-0.543768 l 0.4560939,-0.356568 0.0036,0.172041 c 0.01635,0.781837 0.1831084,1.813183 0.4016641,2.484154 0.1160449,0.356262 0.3781448,0.83968 0.5614081,1.035462 0.2171883,0.232025 0.7140951,0.577268 1.0100284,0.701749 0.121485,0.0511 0.351032,0.110795 0.510105,0.132647 0.396966,0.05452 1.2105,0.02265 1.448934,-0.05679 z"
4
+ }, githubIcon = {
5
+ viewBox: "0 0 16 16",
6
+ path: "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"
7
+ }, starIcon = {
8
+ viewBox: "0 0 16 16",
9
+ path: "M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Z"
10
+ };
11
+ export { githubIcon, starIcon, tangledIcon };
@@ -0,0 +1,28 @@
1
+ import type { DidCache } from "./cache";
2
+ import type { ServiceResolver } from "./client";
3
+ export interface DidResolutionSnapshot {
4
+ did?: string;
5
+ handle?: string;
6
+ loading: boolean;
7
+ promise?: Promise<{
8
+ did?: string;
9
+ handle?: string;
10
+ }>;
11
+ }
12
+ export interface PdsEndpointSnapshot {
13
+ endpoint?: string;
14
+ loading: boolean;
15
+ promise?: Promise<string | undefined>;
16
+ }
17
+ export declare function normalizeAtIdentifier(value: string | null | undefined): string | undefined;
18
+ export declare function normalizeHandle(value: string | null | undefined): string | undefined;
19
+ export declare function resolveDidInput(options: {
20
+ input: string | null | undefined;
21
+ resolver: ServiceResolver;
22
+ didCache: DidCache;
23
+ }): DidResolutionSnapshot;
24
+ export declare function resolvePdsEndpoint(options: {
25
+ did: string | null | undefined;
26
+ resolver: ServiceResolver;
27
+ didCache: DidCache;
28
+ }): PdsEndpointSnapshot;
@@ -0,0 +1,55 @@
1
+ function normalizeAtIdentifier(e) {
2
+ if (typeof e != "string") return;
3
+ let t = e.trim();
4
+ if (!t) return;
5
+ let n = t.toLowerCase();
6
+ if (!(n === "null" || n === "undefined" || n.startsWith("null.") || n.startsWith("undefined."))) return t;
7
+ }
8
+ function normalizeHandle(t) {
9
+ let n = normalizeAtIdentifier(t);
10
+ if (!(!n || n.startsWith("did:"))) return n.toLowerCase();
11
+ }
12
+ function resolveDidInput(n) {
13
+ let r = normalizeAtIdentifier(n.input);
14
+ if (!r) return {
15
+ did: void 0,
16
+ handle: void 0,
17
+ loading: !1
18
+ };
19
+ let i = r.startsWith("did:"), a = i ? void 0 : normalizeHandle(r), o = i ? n.didCache.getByDid(r) : n.didCache.getByHandle(a), s = o?.did ?? (i ? r : void 0), c = o?.handle ?? (i ? void 0 : a), l = !i && !!a && !o?.did, u = i && (!o?.doc || o.handle === void 0);
20
+ if (!l && !u) return {
21
+ did: s,
22
+ handle: c,
23
+ loading: !1
24
+ };
25
+ let d = (async () => {
26
+ let e = o;
27
+ return !i && a && l && (e = await n.didCache.ensureHandle(n.resolver, a)), i && (e = await n.didCache.ensureDidDoc(n.resolver, r)), {
28
+ did: e?.did ?? (i ? r : void 0),
29
+ handle: e?.handle ?? (i ? void 0 : a)
30
+ };
31
+ })();
32
+ return {
33
+ did: s,
34
+ handle: c,
35
+ loading: !0,
36
+ promise: d
37
+ };
38
+ }
39
+ function resolvePdsEndpoint(t) {
40
+ let n = normalizeAtIdentifier(t.did);
41
+ if (!n) return {
42
+ endpoint: void 0,
43
+ loading: !1
44
+ };
45
+ let r = t.didCache.getByDid(n);
46
+ return r?.pdsEndpoint ? {
47
+ endpoint: r.pdsEndpoint,
48
+ loading: !1
49
+ } : {
50
+ endpoint: void 0,
51
+ loading: !0,
52
+ promise: t.didCache.ensurePdsEndpoint(t.resolver, n).then((e) => e.pdsEndpoint)
53
+ };
54
+ }
55
+ export { normalizeAtIdentifier, normalizeHandle, resolveDidInput, resolvePdsEndpoint };
@@ -0,0 +1,27 @@
1
+ export * from "./cache";
2
+ export * from "./client";
3
+ export * from "./fetch-and-cache-handler";
4
+ export * from "./identity";
5
+ export * from "./rpc";
6
+ export * from "./records";
7
+ export * from "./blob-fetch";
8
+ export * from "./backlinks";
9
+ export * from "./tangled-fetch";
10
+ export * from "./types/blob";
11
+ export * from "./types/bluesky";
12
+ export * from "./types/grain";
13
+ export * from "./types/leaflet";
14
+ export * from "./types/record";
15
+ export * from "./types/tangled";
16
+ export * from "./types/teal";
17
+ export * from "./utils/at-uri";
18
+ export * from "./utils/blob";
19
+ export * from "./utils/profile";
20
+ export * from "./utils/richtext";
21
+ export * from "./styles";
22
+ export * from "./paginated";
23
+ export * from "./icons";
24
+ export * from "./music";
25
+ export * from "./utils/time";
26
+ export * from "./grain";
27
+ export * from "./leaflet";
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ import { BlobCache, DidCache, PaginatedRecordsCache, RecordCache, clearSharedCaches, getSharedCacheStats, getSharedCaches, resetSharedCacheStats, sharedBlobCache, sharedDidCache, sharedPaginatedRecordsCache, sharedRecordCache } from "./cache.js";
2
+ import { DEFAULT_CONFIG, SLINGSHOT_BASE_URL, ServiceResolver, createAtprotoClient, normalizeBaseUrl } from "./client.js";
3
+ import { callAppviewRpc, callGetRecord, callListRecords } from "./rpc.js";
4
+ import { extractCidFromBlob, extractCidFromCdnUrl, getBlobImageUrl, isBlobWithCdn } from "./utils/blob.js";
5
+ import { ensureCachedRecord, extractRkey, fetchBlueskyRecord, fetchBlueskyRecordUncached, fetchLatestRecord, fetchPaginatedRecordsPage, fetchRepositoryRecord, getBlueskyRecord, isValidTimestamp, primeBlueskyFeedCache, primeBlueskyProfileCache } from "./records.js";
6
+ import { createFetchAndCacheHandler, primeBlueskyAppviewPayload, primeResponseFromAppview } from "./fetch-and-cache-handler.js";
7
+ import { normalizeAtIdentifier, normalizeHandle, resolveDidInput, resolvePdsEndpoint } from "./identity.js";
8
+ import { fetchBlob, getBlob } from "./blob-fetch.js";
9
+ import { fetchBacklinks } from "./backlinks.js";
10
+ import { fetchRepoLanguages } from "./tangled-fetch.js";
11
+ import { DEFAULT_LATEST_RECORD_COLLECTION, DEFAULT_RECORD_COLLECTION, resolveRecordCollection } from "./types/record.js";
12
+ import { formatDidForLabel, leafletRkeyUrl, normalizeLeafletBasePath, parseAtUri, toBlueskyPostUrl } from "./utils/at-uri.js";
13
+ import { getAvatarCid } from "./utils/profile.js";
14
+ import { createRichTextSegments, createTextSegments } from "./utils/richtext.js";
15
+ import { blueskyPostListStyles, cardStyles, grainGalleryStyles, grainStyles, leafletDocStyles, leafletStyles, mergeStyles, postStyles, profileStyles, songHistoryStyles, styleString, tangledStringStyles, tangledStyles, tealStatusStyles, tealStyles } from "./styles.js";
16
+ import { PaginatedRecordsController, paginatedControllerKey } from "./paginated.js";
17
+ import { githubIcon, starIcon, tangledIcon } from "./icons.js";
18
+ import { availableMusicPlatforms, fetchTrackArtwork, musicPlatforms } from "./music.js";
19
+ import { formatRelativeTime } from "./utils/time.js";
20
+ import { GRAIN_GALLERY_COLLECTION, GRAIN_GALLERY_ITEM_COLLECTION, GRAIN_PHOTO_COLLECTION, fetchGrainGalleryPhotos } from "./grain.js";
21
+ import { createLeafletSegments, leafletAlignment } from "./leaflet.js";
22
+ export { BlobCache, DEFAULT_CONFIG, DEFAULT_LATEST_RECORD_COLLECTION, DEFAULT_RECORD_COLLECTION, DidCache, GRAIN_GALLERY_COLLECTION, GRAIN_GALLERY_ITEM_COLLECTION, GRAIN_PHOTO_COLLECTION, PaginatedRecordsCache, PaginatedRecordsController, RecordCache, SLINGSHOT_BASE_URL, ServiceResolver, availableMusicPlatforms, blueskyPostListStyles, callAppviewRpc, callGetRecord, callListRecords, cardStyles, clearSharedCaches, createAtprotoClient, createFetchAndCacheHandler, createLeafletSegments, createRichTextSegments, createTextSegments, ensureCachedRecord, extractCidFromBlob, extractCidFromCdnUrl, extractRkey, fetchBacklinks, fetchBlob, fetchBlueskyRecord, fetchBlueskyRecordUncached, fetchGrainGalleryPhotos, fetchLatestRecord, fetchPaginatedRecordsPage, fetchRepoLanguages, fetchRepositoryRecord, fetchTrackArtwork, formatDidForLabel, formatRelativeTime, getAvatarCid, getBlob, getBlobImageUrl, getBlueskyRecord, getSharedCacheStats, getSharedCaches, githubIcon, grainGalleryStyles, grainStyles, isBlobWithCdn, isValidTimestamp, leafletAlignment, leafletDocStyles, leafletRkeyUrl, leafletStyles, mergeStyles, musicPlatforms, normalizeAtIdentifier, normalizeBaseUrl, normalizeHandle, normalizeLeafletBasePath, paginatedControllerKey, parseAtUri, postStyles, primeBlueskyAppviewPayload, primeBlueskyFeedCache, primeBlueskyProfileCache, primeResponseFromAppview, profileStyles, resetSharedCacheStats, resolveDidInput, resolvePdsEndpoint, resolveRecordCollection, sharedBlobCache, sharedDidCache, sharedPaginatedRecordsCache, sharedRecordCache, songHistoryStyles, starIcon, styleString, tangledIcon, tangledStringStyles, tangledStyles, tealStatusStyles, tealStyles, toBlueskyPostUrl };
@@ -0,0 +1,22 @@
1
+ import type { LeafletRichTextFacet, LeafletAlignmentValue } from "./types/leaflet";
2
+ /** A run of text with resolved rich-text styling flags. */
3
+ export interface LeafletSegment {
4
+ text: string;
5
+ bold?: boolean;
6
+ italic?: boolean;
7
+ underline?: boolean;
8
+ strike?: boolean;
9
+ code?: boolean;
10
+ highlight?: boolean;
11
+ link?: string;
12
+ id?: string;
13
+ }
14
+ export type LeafletTextAlign = "left" | "center" | "right" | "justify" | undefined;
15
+ /** Resolves a leaflet alignment lex value to a CSS text-align. */
16
+ export declare function leafletAlignment(value?: LeafletAlignmentValue): LeafletTextAlign;
17
+ /**
18
+ * Splits leaflet block plaintext into styled segments using its rich-text facets.
19
+ * Facet byte ranges are converted to char ranges; overlapping facets are merged
20
+ * into flat style flags per run, so any framework can render them simply.
21
+ */
22
+ export declare function createLeafletSegments(plaintext: string, facets?: LeafletRichTextFacet[]): LeafletSegment[];
@@ -0,0 +1,85 @@
1
+ function leafletAlignment(e) {
2
+ if (!e) return;
3
+ let t = e.startsWith("#") ? e.slice(1) : e;
4
+ switch (t.includes("#") && (t = t.split("#").pop() ?? t), t.startsWith("lex:") && (t = t.split(":").pop() ?? t), t) {
5
+ case "textAlignLeft": return "left";
6
+ case "textAlignCenter": return "center";
7
+ case "textAlignRight": return "right";
8
+ case "textAlignJustify": return "justify";
9
+ default: return;
10
+ }
11
+ }
12
+ function buildBytePrefix(e) {
13
+ let t = new TextEncoder(), n = [0], r = 0;
14
+ for (let i = 0; i < e.length;) {
15
+ let a = e.codePointAt(i), o = t.encode(String.fromCodePoint(a));
16
+ r += o.length, n.push(r), i += a > 65535 ? 2 : 1;
17
+ }
18
+ return n;
19
+ }
20
+ function byteOffsetToCharIndex(e, t) {
21
+ for (let n = 0; n < e.length; n++) {
22
+ if (e[n] === t) return n;
23
+ if (e[n] > t) return Math.max(0, n - 1);
24
+ }
25
+ return e.length - 1;
26
+ }
27
+ function sliceByCharRange(e, t, n) {
28
+ if (t <= 0 && n >= e.length) return e;
29
+ let r = "", i = 0;
30
+ for (let a = 0; a < e.length && i < n;) {
31
+ let o = e.codePointAt(a);
32
+ i >= t && i < n && (r += String.fromCodePoint(o)), a += o > 65535 ? 2 : 1, i++;
33
+ }
34
+ return r;
35
+ }
36
+ function applyFeatures(e, t) {
37
+ for (let n of t) switch (n.$type) {
38
+ case "pub.leaflet.richtext.facet#link":
39
+ e.link = n.uri;
40
+ break;
41
+ case "pub.leaflet.richtext.facet#code":
42
+ e.code = !0;
43
+ break;
44
+ case "pub.leaflet.richtext.facet#highlight":
45
+ e.highlight = !0;
46
+ break;
47
+ case "pub.leaflet.richtext.facet#underline":
48
+ e.underline = !0;
49
+ break;
50
+ case "pub.leaflet.richtext.facet#strikethrough":
51
+ e.strike = !0;
52
+ break;
53
+ case "pub.leaflet.richtext.facet#bold":
54
+ e.bold = !0;
55
+ break;
56
+ case "pub.leaflet.richtext.facet#italic":
57
+ e.italic = !0;
58
+ break;
59
+ case "pub.leaflet.richtext.facet#id":
60
+ e.id = n.id;
61
+ break;
62
+ }
63
+ return e;
64
+ }
65
+ function createLeafletSegments(e, a) {
66
+ if (!a?.length) return [{ text: e }];
67
+ let o = buildBytePrefix(e), s = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Map(), l = new Set([0, o.length - 1]);
68
+ for (let e of a) {
69
+ let { byteStart: t, byteEnd: r } = e.index ?? {};
70
+ if (typeof t != "number" || typeof r != "number" || t >= r) continue;
71
+ let i = byteOffsetToCharIndex(o, t), a = byteOffsetToCharIndex(o, r);
72
+ i >= a || (l.add(i), l.add(a), e.features?.length && (s.set(i, [...s.get(i) ?? [], ...e.features]), c.set(a, [...c.get(a) ?? [], ...e.features])));
73
+ }
74
+ let u = Array.from(l).sort((e, t) => e - t), d = [], f = [];
75
+ for (let t = 0; t < u.length - 1; t++) {
76
+ let n = u[t], a = u[t + 1], o = c.get(n);
77
+ o?.length && (f = f.filter((e) => !o.includes(e)));
78
+ let l = s.get(n);
79
+ if (l?.length && (f = [...f, ...l]), n === a) continue;
80
+ let p = sliceByCharRange(e, n, a);
81
+ d.push(applyFeatures({ text: p }, f));
82
+ }
83
+ return d;
84
+ }
85
+ export { createLeafletSegments, leafletAlignment };
@@ -0,0 +1,35 @@
1
+ import type { TealPlayItem } from "./types/teal";
2
+ export interface SonglinkPlatform {
3
+ url: string;
4
+ entityUniqueId: string;
5
+ nativeAppUriMobile?: string;
6
+ nativeAppUriDesktop?: string;
7
+ }
8
+ export interface SonglinkResponse {
9
+ entityUniqueId?: string;
10
+ linksByPlatform: Record<string, SonglinkPlatform>;
11
+ entitiesByUniqueId: Record<string, {
12
+ thumbnailUrl?: string;
13
+ title?: string;
14
+ artistName?: string;
15
+ }>;
16
+ }
17
+ export interface TrackArtwork {
18
+ albumArt?: string;
19
+ songlink?: SonglinkResponse;
20
+ }
21
+ /** Music streaming platforms recognised in Songlink results, with brand icon + colour. */
22
+ export interface MusicPlatform {
23
+ name: string;
24
+ svg: string;
25
+ color: string;
26
+ }
27
+ export declare const musicPlatforms: Record<string, MusicPlatform>;
28
+ /** Platforms (in display order) that have a link in the given Songlink response. */
29
+ export declare function availableMusicPlatforms(songlink?: SonglinkResponse): string[];
30
+ /**
31
+ * Resolves album artwork + cross-platform streaming links for a teal.fm play item.
32
+ * Tries Songlink-by-ISRC, then iTunes search (+ Songlink by iTunes id), then
33
+ * Songlink-by-originUrl. Network-only; safe to call from any framework.
34
+ */
35
+ export declare function fetchTrackArtwork(item: TealPlayItem, signal?: AbortSignal): Promise<TrackArtwork>;