@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.
- package/dist/backlinks.d.ts +18 -0
- package/dist/backlinks.js +15 -0
- package/dist/blob-fetch.d.ts +42 -0
- package/dist/blob-fetch.js +53 -0
- package/dist/cache.d.ts +155 -0
- package/dist/cache.js +345 -0
- package/dist/client.d.ts +45 -0
- package/dist/client.js +118 -0
- package/dist/fetch-and-cache-handler.d.ts +84 -0
- package/dist/fetch-and-cache-handler.js +63 -0
- package/dist/grain.d.ts +22 -0
- package/dist/grain.js +30 -0
- package/dist/icons.d.ts +11 -0
- package/dist/icons.js +11 -0
- package/dist/identity.d.ts +28 -0
- package/dist/identity.js +55 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +22 -0
- package/dist/leaflet.d.ts +22 -0
- package/dist/leaflet.js +85 -0
- package/dist/music.d.ts +35 -0
- package/dist/music.js +88 -0
- package/dist/paginated.d.ts +61 -0
- package/dist/paginated.js +111 -0
- package/dist/records.d.ts +171 -0
- package/dist/records.js +321 -0
- package/dist/rpc.d.ts +21 -0
- package/dist/rpc.js +24 -0
- package/dist/styles.d.ts +40 -0
- package/dist/styles.js +1728 -0
- package/dist/tangled-fetch.d.ts +9 -0
- package/dist/tangled-fetch.js +15 -0
- package/dist/types/blob.d.ts +14 -0
- package/dist/types/bluesky.d.ts +4 -0
- package/dist/types/grain.d.ts +31 -0
- package/dist/types/leaflet.d.ts +173 -0
- package/dist/types/record.d.ts +28 -0
- package/dist/types/record.js +7 -0
- package/dist/types/tangled.d.ts +19 -0
- package/dist/types/teal.d.ts +36 -0
- package/dist/utils/at-uri.d.ts +10 -0
- package/dist/utils/at-uri.js +33 -0
- package/dist/utils/blob.d.ts +5 -0
- package/dist/utils/blob.js +26 -0
- package/dist/utils/profile.d.ts +2 -0
- package/dist/utils/profile.js +5 -0
- package/dist/utils/richtext.d.ts +28 -0
- package/dist/utils/richtext.js +95 -0
- package/dist/utils/time.d.ts +2 -0
- package/dist/utils/time.js +41 -0
- package/package.json +41 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RepoLanguagesResponse } from "./types/tangled";
|
|
2
|
+
export interface FetchRepoLanguagesOptions {
|
|
3
|
+
knot: string;
|
|
4
|
+
did: string;
|
|
5
|
+
repoName: string;
|
|
6
|
+
branch?: string;
|
|
7
|
+
fetch?: typeof fetch;
|
|
8
|
+
}
|
|
9
|
+
export declare function fetchRepoLanguages({ knot, did, repoName, branch, fetch: fetchImpl, }: FetchRepoLanguagesOptions): Promise<RepoLanguagesResponse>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { normalizeAtIdentifier } from "./identity.js";
|
|
2
|
+
async function fetchRepoLanguages({ knot: t, did: n, repoName: r, branch: i, fetch: a = fetch }) {
|
|
3
|
+
let o = i ? [i] : ["main", "master"];
|
|
4
|
+
for (let i of o) {
|
|
5
|
+
let o = normalizeAtIdentifier(t);
|
|
6
|
+
if (!o) break;
|
|
7
|
+
let s = `${(o.match(/^[a-zA-Z][a-zA-Z\d+\-.]*:/) ? o : `https://${o}`).replace(/\/+$/, "")}/xrpc/sh.tangled.repo.languages?repo=${encodeURIComponent(`${n}/${r}`)}&ref=${encodeURIComponent(i)}`;
|
|
8
|
+
try {
|
|
9
|
+
let e = await a(s);
|
|
10
|
+
if (e.ok) return await e.json();
|
|
11
|
+
} catch {}
|
|
12
|
+
}
|
|
13
|
+
throw Error(i ? `Failed to fetch languages for branch: ${i}` : "Failed to fetch languages for main or master branch");
|
|
14
|
+
}
|
|
15
|
+
export { fetchRepoLanguages };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extended blob reference that includes a CDN URL from appview responses.
|
|
3
|
+
* Used when fetching from Bluesky appview to avoid extra blob fetches.
|
|
4
|
+
*/
|
|
5
|
+
export interface BlobWithCdn {
|
|
6
|
+
$type: "blob";
|
|
7
|
+
ref: {
|
|
8
|
+
$link: string;
|
|
9
|
+
};
|
|
10
|
+
mimeType: string;
|
|
11
|
+
size: number;
|
|
12
|
+
/** CDN URL from Bluesky appview (e.g., https://cdn.bsky.app/img/avatar/plain/did:plc:xxx/bafkreixxx@jpeg) */
|
|
13
|
+
cdnUrl?: string;
|
|
14
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Blob } from "@atcute/lexicons/interfaces";
|
|
2
|
+
import type { BlobWithCdn } from "./blob";
|
|
3
|
+
export interface GrainGalleryRecord {
|
|
4
|
+
$type: "social.grain.gallery";
|
|
5
|
+
title: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
labels?: {
|
|
8
|
+
$type: "com.atproto.label.defs#selfLabels";
|
|
9
|
+
values: Array<{
|
|
10
|
+
val: string;
|
|
11
|
+
}>;
|
|
12
|
+
};
|
|
13
|
+
createdAt: string;
|
|
14
|
+
}
|
|
15
|
+
export interface GrainGalleryItemRecord {
|
|
16
|
+
$type: "social.grain.gallery.item";
|
|
17
|
+
item: string;
|
|
18
|
+
gallery: string;
|
|
19
|
+
position?: number;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
}
|
|
22
|
+
export interface GrainPhotoRecord {
|
|
23
|
+
$type: "social.grain.photo";
|
|
24
|
+
alt: string;
|
|
25
|
+
photo: Blob<`image/${string}`> | BlobWithCdn;
|
|
26
|
+
createdAt?: string;
|
|
27
|
+
aspectRatio?: {
|
|
28
|
+
width: number;
|
|
29
|
+
height: number;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
export interface StrongRef {
|
|
2
|
+
uri: string;
|
|
3
|
+
cid: string;
|
|
4
|
+
}
|
|
5
|
+
export interface LeafletDocumentRecord {
|
|
6
|
+
$type?: "pub.leaflet.document";
|
|
7
|
+
title: string;
|
|
8
|
+
postRef?: StrongRef;
|
|
9
|
+
description?: string;
|
|
10
|
+
publishedAt?: string;
|
|
11
|
+
publication: string;
|
|
12
|
+
author: string;
|
|
13
|
+
pages: LeafletDocumentPage[];
|
|
14
|
+
}
|
|
15
|
+
export type LeafletDocumentPage = LeafletLinearDocumentPage;
|
|
16
|
+
export interface LeafletLinearDocumentPage {
|
|
17
|
+
$type?: "pub.leaflet.pages.linearDocument";
|
|
18
|
+
blocks?: LeafletLinearDocumentBlock[];
|
|
19
|
+
}
|
|
20
|
+
export type LeafletAlignmentValue = "#textAlignLeft" | "#textAlignCenter" | "#textAlignRight" | "#textAlignJustify" | "textAlignLeft" | "textAlignCenter" | "textAlignRight" | "textAlignJustify";
|
|
21
|
+
export interface LeafletLinearDocumentBlock {
|
|
22
|
+
block: LeafletBlock;
|
|
23
|
+
alignment?: LeafletAlignmentValue;
|
|
24
|
+
}
|
|
25
|
+
export type LeafletBlock = LeafletTextBlock | LeafletHeaderBlock | LeafletBlockquoteBlock | LeafletImageBlock | LeafletUnorderedListBlock | LeafletWebsiteBlock | LeafletIFrameBlock | LeafletMathBlock | LeafletCodeBlock | LeafletHorizontalRuleBlock | LeafletBskyPostBlock;
|
|
26
|
+
export interface LeafletBaseTextBlock {
|
|
27
|
+
plaintext: string;
|
|
28
|
+
facets?: LeafletRichTextFacet[];
|
|
29
|
+
}
|
|
30
|
+
export interface LeafletTextBlock extends LeafletBaseTextBlock {
|
|
31
|
+
$type?: "pub.leaflet.blocks.text";
|
|
32
|
+
}
|
|
33
|
+
export interface LeafletHeaderBlock extends LeafletBaseTextBlock {
|
|
34
|
+
$type?: "pub.leaflet.blocks.header";
|
|
35
|
+
level?: number;
|
|
36
|
+
}
|
|
37
|
+
export interface LeafletBlockquoteBlock extends LeafletBaseTextBlock {
|
|
38
|
+
$type?: "pub.leaflet.blocks.blockquote";
|
|
39
|
+
}
|
|
40
|
+
export interface LeafletImageBlock {
|
|
41
|
+
$type?: "pub.leaflet.blocks.image";
|
|
42
|
+
image: LeafletBlobRef;
|
|
43
|
+
alt?: string;
|
|
44
|
+
aspectRatio: {
|
|
45
|
+
width: number;
|
|
46
|
+
height: number;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export interface LeafletUnorderedListBlock {
|
|
50
|
+
$type?: "pub.leaflet.blocks.unorderedList";
|
|
51
|
+
children: LeafletListItem[];
|
|
52
|
+
}
|
|
53
|
+
export interface LeafletListItem {
|
|
54
|
+
content: LeafletListContent;
|
|
55
|
+
children?: LeafletListItem[];
|
|
56
|
+
}
|
|
57
|
+
export type LeafletListContent = LeafletTextBlock | LeafletHeaderBlock | LeafletImageBlock;
|
|
58
|
+
export interface LeafletWebsiteBlock {
|
|
59
|
+
$type?: "pub.leaflet.blocks.website";
|
|
60
|
+
src: string;
|
|
61
|
+
title?: string;
|
|
62
|
+
description?: string;
|
|
63
|
+
previewImage?: LeafletBlobRef;
|
|
64
|
+
}
|
|
65
|
+
export interface LeafletIFrameBlock {
|
|
66
|
+
$type?: "pub.leaflet.blocks.iframe";
|
|
67
|
+
url: string;
|
|
68
|
+
height?: number;
|
|
69
|
+
}
|
|
70
|
+
export interface LeafletMathBlock {
|
|
71
|
+
$type?: "pub.leaflet.blocks.math";
|
|
72
|
+
tex: string;
|
|
73
|
+
}
|
|
74
|
+
export interface LeafletCodeBlock {
|
|
75
|
+
$type?: "pub.leaflet.blocks.code";
|
|
76
|
+
plaintext: string;
|
|
77
|
+
language?: string;
|
|
78
|
+
syntaxHighlightingTheme?: string;
|
|
79
|
+
}
|
|
80
|
+
export interface LeafletHorizontalRuleBlock {
|
|
81
|
+
$type?: "pub.leaflet.blocks.horizontalRule";
|
|
82
|
+
}
|
|
83
|
+
export interface LeafletBskyPostBlock {
|
|
84
|
+
$type?: "pub.leaflet.blocks.bskyPost";
|
|
85
|
+
postRef: StrongRef;
|
|
86
|
+
}
|
|
87
|
+
export interface LeafletRichTextFacet {
|
|
88
|
+
index: LeafletByteSlice;
|
|
89
|
+
features: LeafletRichTextFeature[];
|
|
90
|
+
}
|
|
91
|
+
export interface LeafletByteSlice {
|
|
92
|
+
byteStart: number;
|
|
93
|
+
byteEnd: number;
|
|
94
|
+
}
|
|
95
|
+
export type LeafletRichTextFeature = LeafletRichTextLinkFeature | LeafletRichTextCodeFeature | LeafletRichTextHighlightFeature | LeafletRichTextUnderlineFeature | LeafletRichTextStrikethroughFeature | LeafletRichTextIdFeature | LeafletRichTextBoldFeature | LeafletRichTextItalicFeature;
|
|
96
|
+
export interface LeafletRichTextLinkFeature {
|
|
97
|
+
$type: "pub.leaflet.richtext.facet#link";
|
|
98
|
+
uri: string;
|
|
99
|
+
}
|
|
100
|
+
export interface LeafletRichTextCodeFeature {
|
|
101
|
+
$type: "pub.leaflet.richtext.facet#code";
|
|
102
|
+
}
|
|
103
|
+
export interface LeafletRichTextHighlightFeature {
|
|
104
|
+
$type: "pub.leaflet.richtext.facet#highlight";
|
|
105
|
+
}
|
|
106
|
+
export interface LeafletRichTextUnderlineFeature {
|
|
107
|
+
$type: "pub.leaflet.richtext.facet#underline";
|
|
108
|
+
}
|
|
109
|
+
export interface LeafletRichTextStrikethroughFeature {
|
|
110
|
+
$type: "pub.leaflet.richtext.facet#strikethrough";
|
|
111
|
+
}
|
|
112
|
+
export interface LeafletRichTextIdFeature {
|
|
113
|
+
$type: "pub.leaflet.richtext.facet#id";
|
|
114
|
+
id?: string;
|
|
115
|
+
}
|
|
116
|
+
export interface LeafletRichTextBoldFeature {
|
|
117
|
+
$type: "pub.leaflet.richtext.facet#bold";
|
|
118
|
+
}
|
|
119
|
+
export interface LeafletRichTextItalicFeature {
|
|
120
|
+
$type: "pub.leaflet.richtext.facet#italic";
|
|
121
|
+
}
|
|
122
|
+
export interface LeafletBlobRef {
|
|
123
|
+
$type?: string;
|
|
124
|
+
ref?: {
|
|
125
|
+
$link?: string;
|
|
126
|
+
};
|
|
127
|
+
cid?: string;
|
|
128
|
+
mimeType?: string;
|
|
129
|
+
size?: number;
|
|
130
|
+
}
|
|
131
|
+
export interface LeafletPublicationRecord {
|
|
132
|
+
$type?: "pub.leaflet.publication";
|
|
133
|
+
name: string;
|
|
134
|
+
base_path?: string;
|
|
135
|
+
description?: string;
|
|
136
|
+
icon?: LeafletBlobRef;
|
|
137
|
+
theme?: LeafletTheme;
|
|
138
|
+
preferences?: LeafletPublicationPreferences;
|
|
139
|
+
}
|
|
140
|
+
export interface LeafletPublicationPreferences {
|
|
141
|
+
showInDiscover?: boolean;
|
|
142
|
+
showComments?: boolean;
|
|
143
|
+
}
|
|
144
|
+
export interface LeafletTheme {
|
|
145
|
+
backgroundColor?: LeafletThemeColor;
|
|
146
|
+
backgroundImage?: LeafletThemeBackgroundImage;
|
|
147
|
+
primary?: LeafletThemeColor;
|
|
148
|
+
pageBackground?: LeafletThemeColor;
|
|
149
|
+
showPageBackground?: boolean;
|
|
150
|
+
accentBackground?: LeafletThemeColor;
|
|
151
|
+
accentText?: LeafletThemeColor;
|
|
152
|
+
}
|
|
153
|
+
export type LeafletThemeColor = LeafletThemeColorRgb | LeafletThemeColorRgba;
|
|
154
|
+
export interface LeafletThemeColorRgb {
|
|
155
|
+
$type?: "pub.leaflet.theme.color#rgb";
|
|
156
|
+
r: number;
|
|
157
|
+
g: number;
|
|
158
|
+
b: number;
|
|
159
|
+
}
|
|
160
|
+
export interface LeafletThemeColorRgba {
|
|
161
|
+
$type?: "pub.leaflet.theme.color#rgba";
|
|
162
|
+
r: number;
|
|
163
|
+
g: number;
|
|
164
|
+
b: number;
|
|
165
|
+
a: number;
|
|
166
|
+
}
|
|
167
|
+
export interface LeafletThemeBackgroundImage {
|
|
168
|
+
$type?: "pub.leaflet.theme.backgroundImage";
|
|
169
|
+
image: LeafletBlobRef;
|
|
170
|
+
width?: number;
|
|
171
|
+
repeat?: boolean;
|
|
172
|
+
}
|
|
173
|
+
export type LeafletInlineRenderable = LeafletTextBlock | LeafletHeaderBlock | LeafletBlockquoteBlock;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { BaseSchema, InferInput } from "@atcute/lexicons/validations";
|
|
2
|
+
export type RecordTypeName<T> = T extends {
|
|
3
|
+
$type?: infer TypeName extends string;
|
|
4
|
+
} ? TypeName : never;
|
|
5
|
+
export type KnownRecordCollection = "app.bsky.actor.profile" | "app.bsky.feed.post" | "fm.teal.alpha.actor.status" | "fm.teal.alpha.feed.play" | "pub.leaflet.document" | "pub.leaflet.publication" | "sh.tangled.repo" | "sh.tangled.string" | "social.grain.gallery" | "social.grain.gallery.item" | "social.grain.photo";
|
|
6
|
+
export type InferableRecordCollection<T> = Extract<RecordTypeName<T>, KnownRecordCollection>;
|
|
7
|
+
export type InferableLatestRecordCollection<T> = InferableRecordCollection<T>;
|
|
8
|
+
export interface AtcuteRecordRuntimeShape {
|
|
9
|
+
mainSchema?: {
|
|
10
|
+
object?: {
|
|
11
|
+
shape?: {
|
|
12
|
+
$type?: {
|
|
13
|
+
expected?: unknown;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export type AtcuteRecordNamespace<TSchema extends BaseSchema = BaseSchema> = AtcuteRecordRuntimeShape & {
|
|
20
|
+
mainSchema: TSchema;
|
|
21
|
+
};
|
|
22
|
+
export type AtcuteRecordMain<TNamespace> = TNamespace extends {
|
|
23
|
+
mainSchema: infer Schema extends BaseSchema;
|
|
24
|
+
} ? InferInput<Schema> : never;
|
|
25
|
+
export type RecordCollectionInput = string | AtcuteRecordNamespace;
|
|
26
|
+
export declare const DEFAULT_RECORD_COLLECTION = "app.bsky.feed.post";
|
|
27
|
+
export declare const DEFAULT_LATEST_RECORD_COLLECTION = "app.bsky.feed.post";
|
|
28
|
+
export declare function resolveRecordCollection(collection: RecordCollectionInput | undefined, fallback?: string): string | undefined;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
const DEFAULT_RECORD_COLLECTION = "app.bsky.feed.post", DEFAULT_LATEST_RECORD_COLLECTION = DEFAULT_RECORD_COLLECTION;
|
|
2
|
+
function resolveRecordCollection(e, t) {
|
|
3
|
+
if (typeof e == "string") return e;
|
|
4
|
+
let n = e?.mainSchema?.object?.shape?.$type?.expected;
|
|
5
|
+
return typeof n == "string" ? n : t;
|
|
6
|
+
}
|
|
7
|
+
export { DEFAULT_LATEST_RECORD_COLLECTION, DEFAULT_RECORD_COLLECTION, resolveRecordCollection };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ShTangledRepo, ShTangledString } from "@atcute/tangled";
|
|
2
|
+
export type TangledRepoRecord = ShTangledRepo.Main;
|
|
3
|
+
export type TangledStringRecord = ShTangledString.Main;
|
|
4
|
+
/** Language information from sh.tangled.repo.languages endpoint */
|
|
5
|
+
export interface RepoLanguage {
|
|
6
|
+
name: string;
|
|
7
|
+
percentage: number;
|
|
8
|
+
size: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Response from sh.tangled.repo.languages endpoint from tangled knot
|
|
12
|
+
*/
|
|
13
|
+
export interface RepoLanguagesResponse {
|
|
14
|
+
languages: RepoLanguage[];
|
|
15
|
+
/** Branch name */
|
|
16
|
+
ref: string;
|
|
17
|
+
totalFiles: number;
|
|
18
|
+
totalSize: number;
|
|
19
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* teal.fm record types for music listening history
|
|
3
|
+
* Specification: fm.teal.alpha.actor.status and fm.teal.alpha.feed.play
|
|
4
|
+
*/
|
|
5
|
+
export interface TealArtist {
|
|
6
|
+
artistName: string;
|
|
7
|
+
artistMbId?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface TealPlayItem {
|
|
10
|
+
artists: TealArtist[];
|
|
11
|
+
originUrl?: string;
|
|
12
|
+
trackName: string;
|
|
13
|
+
playedTime: string;
|
|
14
|
+
releaseName?: string;
|
|
15
|
+
recordingMbId?: string;
|
|
16
|
+
releaseMbId?: string;
|
|
17
|
+
submissionClientAgent?: string;
|
|
18
|
+
musicServiceBaseDomain?: string;
|
|
19
|
+
isrc?: string;
|
|
20
|
+
duration?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* fm.teal.alpha.actor.status - The last played song
|
|
24
|
+
*/
|
|
25
|
+
export interface TealActorStatusRecord {
|
|
26
|
+
$type: "fm.teal.alpha.actor.status";
|
|
27
|
+
item: TealPlayItem;
|
|
28
|
+
time: string;
|
|
29
|
+
expiry?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* fm.teal.alpha.feed.play - A single play record
|
|
33
|
+
*/
|
|
34
|
+
export interface TealFeedPlayRecord extends TealPlayItem {
|
|
35
|
+
$type: "fm.teal.alpha.feed.play";
|
|
36
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface ParsedAtUri {
|
|
2
|
+
did: string;
|
|
3
|
+
collection: string;
|
|
4
|
+
rkey: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function parseAtUri(uri?: string): ParsedAtUri | undefined;
|
|
7
|
+
export declare function toBlueskyPostUrl(target: ParsedAtUri): string | undefined;
|
|
8
|
+
export declare function formatDidForLabel(did: string): string;
|
|
9
|
+
export declare function normalizeLeafletBasePath(basePath?: string): string | undefined;
|
|
10
|
+
export declare function leafletRkeyUrl(basePath: string | undefined, rkey: string): string | undefined;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
function parseAtUri(e) {
|
|
2
|
+
if (!e || !e.startsWith("at://")) return;
|
|
3
|
+
let [r, i, a] = e.slice(5).split("/");
|
|
4
|
+
if (!(!r || !i || !a)) return {
|
|
5
|
+
did: r,
|
|
6
|
+
collection: i,
|
|
7
|
+
rkey: a
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function toBlueskyPostUrl(e) {
|
|
11
|
+
if (e.collection === "app.bsky.feed.post") return `https://bsky.app/profile/${e.did}/post/${e.rkey}`;
|
|
12
|
+
}
|
|
13
|
+
function formatDidForLabel(e) {
|
|
14
|
+
return e.replace(/^did:(plc:)?/, "");
|
|
15
|
+
}
|
|
16
|
+
var ABSOLUTE_URL_PATTERN = /^[a-zA-Z][a-zA-Z\d+\-.]*:/;
|
|
17
|
+
function normalizeLeafletBasePath(e) {
|
|
18
|
+
if (!e) return;
|
|
19
|
+
let r = e.trim();
|
|
20
|
+
if (!r) return;
|
|
21
|
+
let i = ABSOLUTE_URL_PATTERN.test(r) ? r : `https://${r}`;
|
|
22
|
+
try {
|
|
23
|
+
let e = new URL(i);
|
|
24
|
+
return e.hash = "", e.href.replace(/\/?$/, "");
|
|
25
|
+
} catch {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function leafletRkeyUrl(e, r) {
|
|
30
|
+
let i = normalizeLeafletBasePath(e);
|
|
31
|
+
if (i) return `${i}/${encodeURIComponent(r)}`;
|
|
32
|
+
}
|
|
33
|
+
export { formatDidForLabel, leafletRkeyUrl, normalizeLeafletBasePath, parseAtUri, toBlueskyPostUrl };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { BlobWithCdn } from "../types/blob";
|
|
2
|
+
export declare function isBlobWithCdn(value: unknown): value is BlobWithCdn;
|
|
3
|
+
export declare function getBlobImageUrl(value: unknown): string | undefined;
|
|
4
|
+
export declare function extractCidFromBlob(blob: unknown): string | undefined;
|
|
5
|
+
export declare function extractCidFromCdnUrl(url: string | undefined): string | undefined;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
var CID_PATTERN = /(?:^|\/)(bafk[a-z0-9]+|bafyb[a-z0-9]+|Qm[1-9A-HJ-NP-Za-km-z]+)(?:@[^/?#]+)?(?:[/?#]|$)/i;
|
|
2
|
+
function isBlobWithCdn(e) {
|
|
3
|
+
if (typeof e != "object" || !e) return !1;
|
|
4
|
+
let r = e;
|
|
5
|
+
return r.$type === "blob" && typeof r.cdnUrl == "string" && typeof r.ref == "object" && r.ref !== null && typeof r.ref.$link == "string";
|
|
6
|
+
}
|
|
7
|
+
function getBlobImageUrl(e) {
|
|
8
|
+
if (isBlobWithCdn(e)) return e.cdnUrl;
|
|
9
|
+
if (typeof e != "object" || !e) return;
|
|
10
|
+
let i = e;
|
|
11
|
+
if (i.$type === "blob") return typeof i.cdnUrl == "string" ? i.cdnUrl : void 0;
|
|
12
|
+
}
|
|
13
|
+
function extractCidFromBlob(e) {
|
|
14
|
+
if (typeof e == "string") return e;
|
|
15
|
+
if (typeof e != "object" || !e) return;
|
|
16
|
+
let r = e;
|
|
17
|
+
if (typeof r.cid == "string") return r.cid;
|
|
18
|
+
if (typeof r.ref == "object" && r.ref !== null) {
|
|
19
|
+
let e = r.ref.$link;
|
|
20
|
+
if (typeof e == "string") return e;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function extractCidFromCdnUrl(r) {
|
|
24
|
+
if (r) return r.match(CID_PATTERN)?.[1];
|
|
25
|
+
}
|
|
26
|
+
export { extractCidFromBlob, extractCidFromCdnUrl, getBlobImageUrl, isBlobWithCdn };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AppBskyRichtextFacet } from "@atcute/bluesky";
|
|
2
|
+
export type BlueskyRichTextFacet = AppBskyRichtextFacet.Main;
|
|
3
|
+
export interface TextSegment {
|
|
4
|
+
text: string;
|
|
5
|
+
facet?: BlueskyRichTextFacet;
|
|
6
|
+
}
|
|
7
|
+
export type RichTextSegmentKind = "text" | "link" | "mention" | "tag";
|
|
8
|
+
export interface RichTextSegment {
|
|
9
|
+
text: string;
|
|
10
|
+
kind: RichTextSegmentKind;
|
|
11
|
+
href?: string;
|
|
12
|
+
facet?: BlueskyRichTextFacet;
|
|
13
|
+
feature?: BlueskyRichTextFacet["features"][number];
|
|
14
|
+
}
|
|
15
|
+
export interface RichTextSegmentOptions {
|
|
16
|
+
blueskyAppBaseUrl?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Converts a text string with facets into segments that can be rendered
|
|
20
|
+
* with appropriate styling and interactivity.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createTextSegments(text: string, facets?: BlueskyRichTextFacet[]): TextSegment[];
|
|
23
|
+
/**
|
|
24
|
+
* Converts Bluesky richtext facets into render-ready segments.
|
|
25
|
+
* Framework packages should map linked segments to anchors and plain segments
|
|
26
|
+
* to text nodes; feature interpretation stays centralized here.
|
|
27
|
+
*/
|
|
28
|
+
export declare function createRichTextSegments(text: string, facets?: BlueskyRichTextFacet[], options?: RichTextSegmentOptions): RichTextSegment[];
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
function createTextSegments(e, t) {
|
|
2
|
+
if (!t || t.length === 0) return [{ text: e }];
|
|
3
|
+
let a = buildBytePrefix(e), o = [...t].sort((e, t) => e.index.byteStart - t.index.byteStart), s = [], c = 0;
|
|
4
|
+
for (let t of o) {
|
|
5
|
+
let n = byteOffsetToCharIndex(a, t.index.byteStart), o = byteOffsetToCharIndex(a, t.index.byteEnd);
|
|
6
|
+
n > c && s.push({ text: sliceByCharRange(e, c, n) }), s.push({
|
|
7
|
+
text: sliceByCharRange(e, n, o),
|
|
8
|
+
facet: t
|
|
9
|
+
}), c = o;
|
|
10
|
+
}
|
|
11
|
+
return c < e.length && s.push({ text: sliceByCharRange(e, c, e.length) }), s;
|
|
12
|
+
}
|
|
13
|
+
function createRichTextSegments(t, n, r = {}) {
|
|
14
|
+
let i = (r.blueskyAppBaseUrl ?? "https://bsky.app").replace(/\/+$/, "");
|
|
15
|
+
return createTextSegments(t, n).map((e) => {
|
|
16
|
+
let t = e.facet?.features?.[0];
|
|
17
|
+
if (!t) return {
|
|
18
|
+
...e,
|
|
19
|
+
kind: "text"
|
|
20
|
+
};
|
|
21
|
+
let n = t.$type;
|
|
22
|
+
if (n === "app.bsky.richtext.facet#link") {
|
|
23
|
+
let n = t.uri;
|
|
24
|
+
return n ? {
|
|
25
|
+
...e,
|
|
26
|
+
kind: "link",
|
|
27
|
+
href: n,
|
|
28
|
+
feature: t
|
|
29
|
+
} : {
|
|
30
|
+
...e,
|
|
31
|
+
kind: "text",
|
|
32
|
+
feature: t
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (n === "app.bsky.richtext.facet#mention") {
|
|
36
|
+
let n = t.did;
|
|
37
|
+
return n ? {
|
|
38
|
+
...e,
|
|
39
|
+
kind: "mention",
|
|
40
|
+
href: `${i}/profile/${n}`,
|
|
41
|
+
feature: t
|
|
42
|
+
} : {
|
|
43
|
+
...e,
|
|
44
|
+
kind: "text",
|
|
45
|
+
feature: t
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (n === "app.bsky.richtext.facet#tag") {
|
|
49
|
+
let n = t.tag;
|
|
50
|
+
return n ? {
|
|
51
|
+
...e,
|
|
52
|
+
kind: "tag",
|
|
53
|
+
href: `${i}/hashtag/${encodeURIComponent(n)}`,
|
|
54
|
+
feature: t
|
|
55
|
+
} : {
|
|
56
|
+
...e,
|
|
57
|
+
kind: "text",
|
|
58
|
+
feature: t
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
...e,
|
|
63
|
+
kind: "text",
|
|
64
|
+
feature: t
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function buildBytePrefix(e) {
|
|
69
|
+
let t = new TextEncoder(), n = [0], r = 0;
|
|
70
|
+
for (let i = 0; i < e.length;) {
|
|
71
|
+
let a = e.codePointAt(i);
|
|
72
|
+
if (a === void 0) break;
|
|
73
|
+
let o = String.fromCodePoint(a), s = t.encode(o);
|
|
74
|
+
r += s.length, n.push(r), i += a > 65535 ? 2 : 1;
|
|
75
|
+
}
|
|
76
|
+
return n;
|
|
77
|
+
}
|
|
78
|
+
function byteOffsetToCharIndex(e, t) {
|
|
79
|
+
for (let n = 0; n < e.length; n++) {
|
|
80
|
+
if (e[n] === t) return n;
|
|
81
|
+
if (e[n] > t) return Math.max(0, n - 1);
|
|
82
|
+
}
|
|
83
|
+
return e.length - 1;
|
|
84
|
+
}
|
|
85
|
+
function sliceByCharRange(e, t, n) {
|
|
86
|
+
if (t <= 0 && n >= e.length) return e;
|
|
87
|
+
let r = "", i = 0;
|
|
88
|
+
for (let a = 0; a < e.length && i < n;) {
|
|
89
|
+
let o = e.codePointAt(a);
|
|
90
|
+
if (o === void 0) break;
|
|
91
|
+
i >= t && i < n && (r += String.fromCodePoint(o)), a += o > 65535 ? 2 : 1, i++;
|
|
92
|
+
}
|
|
93
|
+
return r;
|
|
94
|
+
}
|
|
95
|
+
export { createRichTextSegments, createTextSegments };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
function formatRelativeTime(e) {
|
|
2
|
+
let t = (new Date(e).getTime() - Date.now()) / 1e3, n = Math.abs(t), r = [
|
|
3
|
+
{
|
|
4
|
+
limit: 60,
|
|
5
|
+
unit: "second",
|
|
6
|
+
divisor: 1
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
limit: 3600,
|
|
10
|
+
unit: "minute",
|
|
11
|
+
divisor: 60
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
limit: 86400,
|
|
15
|
+
unit: "hour",
|
|
16
|
+
divisor: 3600
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
limit: 604800,
|
|
20
|
+
unit: "day",
|
|
21
|
+
divisor: 86400
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
limit: 2629800,
|
|
25
|
+
unit: "week",
|
|
26
|
+
divisor: 604800
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
limit: 31557600,
|
|
30
|
+
unit: "month",
|
|
31
|
+
divisor: 2629800
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
limit: Infinity,
|
|
35
|
+
unit: "year",
|
|
36
|
+
divisor: 31557600
|
|
37
|
+
}
|
|
38
|
+
], i = r.find((e) => n < e.limit) ?? r[r.length - 1], a = t / i.divisor;
|
|
39
|
+
return new Intl.RelativeTimeFormat("en", { numeric: "auto" }).format(Math.round(a), i.unit);
|
|
40
|
+
}
|
|
41
|
+
export { formatRelativeTime };
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atproto-ui/core",
|
|
3
|
+
"version": "0.13.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Framework-agnostic AT Protocol utilities: cache, client, types, and helpers.",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "vite build && tsc -p tsconfig.json --emitDeclarationOnly",
|
|
24
|
+
"lint": "tsc -p tsconfig.json --noEmit"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@atcute/atproto": "^4.0.0",
|
|
28
|
+
"@atcute/bluesky": "^4.0.2",
|
|
29
|
+
"@atcute/client": "^5.0.0",
|
|
30
|
+
"@atcute/identity": "^2.0.0",
|
|
31
|
+
"@atcute/identity-resolver": "^2.0.0",
|
|
32
|
+
"@atcute/lexicons": "^2.0.0",
|
|
33
|
+
"@atcute/tangled": "^2.0.2"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^24.12.4",
|
|
37
|
+
"typescript": "~5.9.3",
|
|
38
|
+
"unplugin-dts": "^1.0.1",
|
|
39
|
+
"vite": "npm:rolldown-vite@7.1.14"
|
|
40
|
+
}
|
|
41
|
+
}
|