The Appview for the kipclip.com atproto bookmarking service
1// Shared TypeScript types for kipclip
2
3// AT Protocol bookmark record type
4export interface BookmarkRecord {
5 subject: string; // URI of the bookmarked content
6 createdAt: string; // ISO 8601 datetime
7 tags?: string[]; // Optional tags
8}
9
10// Enriched bookmark with metadata
11export interface EnrichedBookmark extends BookmarkRecord {
12 uri: string; // AT Protocol URI for this record
13 cid: string; // Content ID
14 title?: string; // Extracted page title
15 description?: string; // Extracted meta description
16 favicon?: string; // Extracted favicon URL
17 image?: string; // Preview image (og:image)
18 note?: string; // User note from annotation sidecar
19}
20
21// Kipclip annotation sidecar record (com.kipclip.annotation)
22export interface AnnotationRecord {
23 subject: string; // AT URI of the bookmark this annotates
24 note?: string; // User note
25 title?: string;
26 description?: string;
27 favicon?: string;
28 image?: string;
29 createdAt: string;
30}
31
32// API request/response types
33export interface AddBookmarkRequest {
34 url: string;
35 tags?: string[];
36}
37
38export interface AddBookmarkResponse {
39 success: boolean;
40 bookmark?: EnrichedBookmark;
41 error?: string;
42}
43
44export interface ListBookmarksResponse {
45 bookmarks: EnrichedBookmark[];
46}
47
48// URL metadata extraction result
49export interface UrlMetadata {
50 title?: string;
51 description?: string;
52 favicon?: string;
53 image?: string; // Preview image (og:image)
54}
55
56// Session info
57export interface SessionInfo {
58 did: string;
59 handle: string;
60}
61
62// AT Protocol tag record type
63export interface TagRecord {
64 value: string; // Tag text (max 64 chars)
65 createdAt: string; // ISO 8601 datetime
66}
67
68// Enriched tag with metadata
69export interface EnrichedTag extends TagRecord {
70 uri: string; // AT Protocol URI for this record
71 cid: string; // Content ID
72}
73
74// Tag API request/response types
75export interface AddTagRequest {
76 value: string;
77}
78
79export interface AddTagResponse {
80 success: boolean;
81 tag?: EnrichedTag;
82 error?: string;
83}
84
85export interface ListTagsResponse {
86 tags: EnrichedTag[];
87}
88
89export interface UpdateTagRequest {
90 value: string;
91}
92
93export interface UpdateTagResponse {
94 success: boolean;
95 tag?: EnrichedTag;
96 error?: string;
97}
98
99export interface DeleteTagResponse {
100 success: boolean;
101 error?: string;
102}
103
104// Bookmark tag update request/response types
105export interface UpdateBookmarkTagsRequest {
106 tags: string[];
107 title?: string;
108 url?: string;
109 description?: string;
110 note?: string;
111}
112
113export interface UpdateBookmarkTagsResponse {
114 success: boolean;
115 bookmark?: EnrichedBookmark;
116 error?: string;
117}
118
119// Shared bookmarks API types (public, no auth)
120export interface SharedBookmarksResponse {
121 bookmarks: EnrichedBookmark[];
122 handle: string;
123 tags: string[];
124 error?: string;
125}
126
127// User settings (stored in database)
128export interface UserSettings {
129 instapaperEnabled: boolean;
130 instapaperUsername?: string; // Decrypted, only in memory (never includes password)
131}
132
133// Settings API response
134export interface GetSettingsResponse {
135 settings: UserSettings;
136}
137
138// Settings API update request
139export interface UpdateSettingsRequest {
140 instapaperEnabled?: boolean;
141 instapaperUsername?: string;
142 instapaperPassword?: string; // Only when updating credentials
143}
144
145// Settings API update response
146export interface UpdateSettingsResponse {
147 success: boolean;
148 settings?: UserSettings;
149 error?: string;
150}
151
152// Duplicate check API types
153export interface CheckDuplicatesRequest {
154 url: string;
155}
156
157export interface CheckDuplicatesResponse {
158 duplicates: EnrichedBookmark[];
159}
160
161// User preferences (stored on PDS as com.kipclip.preferences)
162export interface PreferencesRecord {
163 dateFormat: string;
164 readingListTag?: string;
165 createdAt: string;
166}
167
168export interface UserPreferences {
169 dateFormat: string;
170 readingListTag: string;
171}
172
173export interface UpdatePreferencesRequest {
174 dateFormat?: string;
175 readingListTag?: string;
176}
177
178export interface UpdatePreferencesResponse {
179 success: boolean;
180 preferences?: UserPreferences;
181 error?: string;
182}
183
184// Combined initial data response (for optimized page load)
185export interface InitialDataResponse {
186 bookmarks: EnrichedBookmark[];
187 tags: EnrichedTag[];
188 settings: UserSettings;
189 preferences: UserPreferences;
190 /** Cursor for next page of bookmarks (absent when all loaded) */
191 bookmarkCursor?: string;
192 /** Cursor for next page of annotations (absent when all loaded) */
193 annotationCursor?: string;
194}
195
196// Import types
197export interface ImportedBookmark {
198 url: string;
199 title?: string;
200 description?: string;
201 tags: string[];
202 createdAt?: string; // ISO 8601, falls back to now
203}
204
205export interface ImportResult {
206 imported: number;
207 skipped: number;
208 failed: number;
209 total: number;
210 format: string;
211}
212
213// Bulk operations API types
214export interface BulkOperationRequest {
215 action: "delete" | "add-tags" | "remove-tags";
216 uris: string[]; // bookmark URIs to operate on
217 tags?: string[]; // tags to add/remove (required for tag actions)
218}
219
220export interface BulkOperationResponse {
221 success: boolean;
222 succeeded: number;
223 failed: number;
224 errors?: string[];
225 deletedUris?: string[]; // URIs that were actually deleted (for delete action)
226 bookmarks?: EnrichedBookmark[]; // updated bookmarks (for tag operations)
227}
228
229// Tag merge/deduplication response
230export interface MergeTagDuplicatesResponse {
231 merged: number;
232 tagsDeleted: number;
233 bookmarksUpdated: number;
234 details: {
235 canonical: string;
236 merged: string[];
237 bookmarksUpdated: number;
238 }[];
239}
240
241export interface ImportPrepareResponse {
242 success: boolean;
243 jobId?: string;
244 total?: number;
245 skipped?: number;
246 toImport?: number;
247 totalChunks?: number;
248 format?: string;
249 result?: ImportResult; // only when toImport === 0
250 error?: string;
251}
252
253export interface ImportProcessResponse {
254 success: boolean;
255 imported?: number; // this chunk
256 failed?: number; // this chunk
257 totalImported?: number; // cumulative
258 totalFailed?: number; // cumulative
259 remaining?: number; // chunks left
260 done?: boolean;
261 result?: ImportResult; // only when done
262 error?: string;
263}