@ewanc26/svelte-standard-site 0.2.3 → 0.2.4

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 (69) hide show
  1. package/dist/components/ActionBar.svelte +85 -0
  2. package/dist/components/ActionBar.svelte.d.ts +13 -0
  3. package/dist/components/Avatar.svelte +104 -0
  4. package/dist/components/Avatar.svelte.d.ts +19 -0
  5. package/dist/components/Comment.svelte +172 -0
  6. package/dist/components/Comment.svelte.d.ts +22 -0
  7. package/dist/components/CommentsSection.svelte +89 -0
  8. package/dist/components/DocumentCard.svelte +126 -56
  9. package/dist/components/DocumentCard.svelte.d.ts +51 -0
  10. package/dist/components/Footnotes.svelte +72 -0
  11. package/dist/components/Footnotes.svelte.d.ts +13 -0
  12. package/dist/components/RecommendButton.svelte +153 -0
  13. package/dist/components/RecommendButton.svelte.d.ts +17 -0
  14. package/dist/components/ThemeProvider.svelte +92 -0
  15. package/dist/components/ThemeProvider.svelte.d.ts +13 -0
  16. package/dist/components/Toast.svelte +177 -0
  17. package/dist/components/Toast.svelte.d.ts +32 -0
  18. package/dist/components/Watermark.svelte +100 -0
  19. package/dist/components/Watermark.svelte.d.ts +17 -0
  20. package/dist/components/common/ThemedCard.svelte +15 -15
  21. package/dist/components/common/ThemedCard.svelte.d.ts +5 -0
  22. package/dist/components/document/BlockRenderer.svelte +3 -0
  23. package/dist/components/document/DocumentRenderer.svelte +41 -1
  24. package/dist/components/document/RichText.svelte +87 -2
  25. package/dist/components/document/RichText.svelte.d.ts +2 -0
  26. package/dist/components/document/blocks/OrderedListBlock.svelte +152 -0
  27. package/dist/components/document/blocks/UnorderedListBlock.svelte +1 -1
  28. package/dist/components/index.d.ts +28 -0
  29. package/dist/components/index.js +30 -0
  30. package/dist/index.d.ts +5 -4
  31. package/dist/index.js +6 -4
  32. package/dist/publisher.d.ts +73 -0
  33. package/dist/publisher.js +185 -0
  34. package/dist/schemas.d.ts +1162 -2
  35. package/dist/schemas.js +316 -0
  36. package/dist/types.d.ts +393 -2
  37. package/dist/types.js +1 -1
  38. package/dist/utils/native-comments.d.ts +68 -0
  39. package/dist/utils/native-comments.js +149 -0
  40. package/dist/utils/theme-helpers.d.ts +41 -1
  41. package/dist/utils/theme-helpers.js +98 -1
  42. package/dist/utils/theme.d.ts +48 -1
  43. package/dist/utils/theme.js +158 -0
  44. package/package.json +20 -20
  45. package/src/lib/components/ActionBar.svelte +85 -0
  46. package/src/lib/components/Avatar.svelte +104 -0
  47. package/src/lib/components/Comment.svelte +172 -0
  48. package/src/lib/components/CommentsSection.svelte +89 -0
  49. package/src/lib/components/DocumentCard.svelte +126 -56
  50. package/src/lib/components/Footnotes.svelte +72 -0
  51. package/src/lib/components/RecommendButton.svelte +153 -0
  52. package/src/lib/components/ThemeProvider.svelte +92 -0
  53. package/src/lib/components/Toast.svelte +177 -0
  54. package/src/lib/components/Watermark.svelte +100 -0
  55. package/src/lib/components/common/ThemedCard.svelte +15 -15
  56. package/src/lib/components/document/BlockRenderer.svelte +3 -0
  57. package/src/lib/components/document/DocumentRenderer.svelte +41 -1
  58. package/src/lib/components/document/RichText.svelte +87 -2
  59. package/src/lib/components/document/blocks/OrderedListBlock.svelte +152 -0
  60. package/src/lib/components/document/blocks/UnorderedListBlock.svelte +1 -1
  61. package/src/lib/components/index.ts +32 -0
  62. package/src/lib/index.ts +119 -5
  63. package/src/lib/publisher.ts +251 -0
  64. package/src/lib/schemas.ts +411 -0
  65. package/src/lib/types.ts +506 -2
  66. package/src/lib/utils/native-comments.ts +197 -0
  67. package/src/lib/utils/theme-helpers.ts +136 -3
  68. package/src/lib/utils/theme.ts +189 -1
  69. package/dist/components/document/blocks/UnorderedListBlock.svelte.d.ts +0 -9
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Core types for site.standard.* lexicons
2
+ * Core types for site.standard.* and pub.leaflet.* lexicons
3
3
  */
4
4
  /**
5
5
  * AT Protocol blob reference
@@ -27,6 +27,28 @@ export interface RGBColor {
27
27
  g: number;
28
28
  b: number;
29
29
  }
30
+ /**
31
+ * RGBA Color (with alpha)
32
+ */
33
+ export interface RGBAColor {
34
+ r: number;
35
+ g: number;
36
+ b: number;
37
+ a: number;
38
+ }
39
+ /**
40
+ * Color union (RGB or RGBA)
41
+ */
42
+ export type Color = RGBColor | RGBAColor;
43
+ /**
44
+ * Background image configuration
45
+ */
46
+ export interface BackgroundImage {
47
+ $type?: 'pub.leaflet.theme.backgroundImage';
48
+ url: string;
49
+ opacity?: number;
50
+ blur?: number;
51
+ }
30
52
  /**
31
53
  * Basic theme for publications
32
54
  */
@@ -37,6 +59,22 @@ export interface BasicTheme {
37
59
  accent: RGBColor;
38
60
  accentForeground: RGBColor;
39
61
  }
62
+ /**
63
+ * Extended theme for publications (pub.leaflet.publication#theme)
64
+ */
65
+ export interface ExtendedTheme {
66
+ $type?: 'pub.leaflet.theme';
67
+ backgroundColor?: Color;
68
+ pageBackground?: Color;
69
+ showPageBackground?: boolean;
70
+ primary?: Color;
71
+ accentBackground?: Color;
72
+ accentText?: Color;
73
+ headingFont?: string;
74
+ bodyFont?: string;
75
+ pageWidth?: number;
76
+ backgroundImage?: BackgroundImage;
77
+ }
40
78
  /**
41
79
  * Publication preferences
42
80
  */
@@ -57,6 +95,7 @@ export interface Publication {
57
95
  icon?: string;
58
96
  description?: string;
59
97
  basicTheme?: BasicTheme;
98
+ theme?: ExtendedTheme;
60
99
  preferences?: PublicationPreferences;
61
100
  }
62
101
  /**
@@ -69,14 +108,366 @@ export interface Document {
69
108
  path?: string;
70
109
  description?: string;
71
110
  coverImage?: string;
72
- content?: any;
111
+ content?: Content;
73
112
  textContent?: string;
74
113
  bskyPostRef?: StrongRef;
75
114
  tags?: string[];
76
115
  publishedAt: string;
77
116
  updatedAt?: string;
117
+ theme?: ExtendedTheme;
78
118
  preferences?: PublicationPreferences;
79
119
  }
120
+ /**
121
+ * Byte slice for facet index
122
+ */
123
+ export interface ByteSlice {
124
+ byteStart: number;
125
+ byteEnd: number;
126
+ }
127
+ /**
128
+ * Link facet feature
129
+ */
130
+ export interface LinkFeature {
131
+ $type: 'pub.leaflet.richtext.facet#link';
132
+ uri: string;
133
+ }
134
+ /**
135
+ * DID Mention facet feature
136
+ */
137
+ export interface DidMentionFeature {
138
+ $type: 'pub.leaflet.richtext.facet#didMention';
139
+ did: string;
140
+ }
141
+ /**
142
+ * AT URI Mention facet feature
143
+ */
144
+ export interface AtMentionFeature {
145
+ $type: 'pub.leaflet.richtext.facet#atMention';
146
+ atURI: string;
147
+ }
148
+ /**
149
+ * Code facet feature (inline code)
150
+ */
151
+ export interface CodeFeature {
152
+ $type: 'pub.leaflet.richtext.facet#code';
153
+ }
154
+ /**
155
+ * Highlight facet feature
156
+ */
157
+ export interface HighlightFeature {
158
+ $type: 'pub.leaflet.richtext.facet#highlight';
159
+ }
160
+ /**
161
+ * Underline facet feature
162
+ */
163
+ export interface UnderlineFeature {
164
+ $type: 'pub.leaflet.richtext.facet#underline';
165
+ }
166
+ /**
167
+ * Strikethrough facet feature
168
+ */
169
+ export interface StrikethroughFeature {
170
+ $type: 'pub.leaflet.richtext.facet#strikethrough';
171
+ }
172
+ /**
173
+ * Bold facet feature
174
+ */
175
+ export interface BoldFeature {
176
+ $type: 'pub.leaflet.richtext.facet#bold';
177
+ }
178
+ /**
179
+ * Italic facet feature
180
+ */
181
+ export interface ItalicFeature {
182
+ $type: 'pub.leaflet.richtext.facet#italic';
183
+ }
184
+ /**
185
+ * ID facet feature (for anchor links)
186
+ */
187
+ export interface IdFeature {
188
+ $type: 'pub.leaflet.richtext.facet#id';
189
+ id: string;
190
+ }
191
+ /**
192
+ * Footnote facet feature
193
+ */
194
+ export interface FootnoteFeature {
195
+ $type: 'pub.leaflet.richtext.facet#footnote';
196
+ footnoteId: string;
197
+ contentPlaintext: string;
198
+ contentFacets?: Facet[];
199
+ }
200
+ /**
201
+ * Facet feature union
202
+ */
203
+ export type FacetFeature = LinkFeature | DidMentionFeature | AtMentionFeature | CodeFeature | HighlightFeature | UnderlineFeature | StrikethroughFeature | BoldFeature | ItalicFeature | IdFeature | FootnoteFeature;
204
+ /**
205
+ * Rich Text Facet
206
+ */
207
+ export interface Facet {
208
+ index: ByteSlice;
209
+ features: FacetFeature[];
210
+ }
211
+ /**
212
+ * Text Block
213
+ */
214
+ export interface TextBlock {
215
+ $type: 'pub.leaflet.blocks.text';
216
+ plaintext: string;
217
+ textSize?: 'default' | 'small' | 'large';
218
+ facets?: Facet[];
219
+ }
220
+ /**
221
+ * Header Block
222
+ */
223
+ export interface HeaderBlock {
224
+ $type: 'pub.leaflet.blocks.header';
225
+ plaintext: string;
226
+ level?: number;
227
+ facets?: Facet[];
228
+ }
229
+ /**
230
+ * Blockquote Block
231
+ */
232
+ export interface BlockquoteBlock {
233
+ $type: 'pub.leaflet.blocks.blockquote';
234
+ plaintext: string;
235
+ facets?: Facet[];
236
+ }
237
+ /**
238
+ * Image Block
239
+ */
240
+ export interface ImageBlock {
241
+ $type: 'pub.leaflet.blocks.image';
242
+ image: AtProtoBlob;
243
+ alt?: string;
244
+ aspectRatio?: {
245
+ width: number;
246
+ height: number;
247
+ };
248
+ }
249
+ /**
250
+ * Code Block
251
+ */
252
+ export interface CodeBlock {
253
+ $type: 'pub.leaflet.blocks.code';
254
+ code: string;
255
+ language?: string;
256
+ filename?: string;
257
+ }
258
+ /**
259
+ * Math Block
260
+ */
261
+ export interface MathBlock {
262
+ $type: 'pub.leaflet.blocks.math';
263
+ tex: string;
264
+ display?: boolean;
265
+ }
266
+ /**
267
+ * Ordered List Item
268
+ */
269
+ export interface OrderedListItem {
270
+ content?: TextBlock | HeaderBlock | ImageBlock;
271
+ checked?: boolean;
272
+ children?: OrderedListItem[];
273
+ unorderedListChildren?: UnorderedListBlock;
274
+ }
275
+ /**
276
+ * Ordered List Block
277
+ */
278
+ export interface OrderedListBlock {
279
+ $type: 'pub.leaflet.blocks.orderedList';
280
+ children: OrderedListItem[];
281
+ startIndex?: number;
282
+ }
283
+ /**
284
+ * Unordered List Item
285
+ */
286
+ export interface UnorderedListItem {
287
+ content?: TextBlock | HeaderBlock | ImageBlock;
288
+ children?: UnorderedListItem[];
289
+ }
290
+ /**
291
+ * Unordered List Block
292
+ */
293
+ export interface UnorderedListBlock {
294
+ $type: 'pub.leaflet.blocks.unorderedList';
295
+ children: UnorderedListItem[];
296
+ }
297
+ /**
298
+ * Horizontal Rule Block
299
+ */
300
+ export interface HorizontalRuleBlock {
301
+ $type: 'pub.leaflet.blocks.horizontalRule';
302
+ }
303
+ /**
304
+ * Iframe Block
305
+ */
306
+ export interface IframeBlock {
307
+ $type: 'pub.leaflet.blocks.iframe';
308
+ src: string;
309
+ title?: string;
310
+ width?: number;
311
+ height?: number;
312
+ }
313
+ /**
314
+ * Website Embed Block
315
+ */
316
+ export interface WebsiteBlock {
317
+ $type: 'pub.leaflet.blocks.website';
318
+ url: string;
319
+ title?: string;
320
+ description?: string;
321
+ image?: AtProtoBlob;
322
+ }
323
+ /**
324
+ * Button Block
325
+ */
326
+ export interface ButtonBlock {
327
+ $type: 'pub.leaflet.blocks.button';
328
+ text: string;
329
+ href: string;
330
+ variant?: 'primary' | 'secondary' | 'outline';
331
+ }
332
+ /**
333
+ * Bluesky Post Block
334
+ */
335
+ export interface BskyPostBlock {
336
+ $type: 'pub.leaflet.blocks.bskyPost';
337
+ uri: string;
338
+ }
339
+ /**
340
+ * Poll Block
341
+ */
342
+ export interface PollBlock {
343
+ $type: 'pub.leaflet.blocks.poll';
344
+ question: string;
345
+ options: Array<{
346
+ text: string;
347
+ votes?: number;
348
+ }>;
349
+ endsAt?: string;
350
+ }
351
+ /**
352
+ * Page Block (internal page reference)
353
+ */
354
+ export interface PageBlock {
355
+ $type: 'pub.leaflet.blocks.page';
356
+ pageId: string;
357
+ }
358
+ /**
359
+ * Block union type
360
+ */
361
+ export type Block = TextBlock | HeaderBlock | BlockquoteBlock | ImageBlock | CodeBlock | MathBlock | OrderedListBlock | UnorderedListBlock | HorizontalRuleBlock | IframeBlock | WebsiteBlock | ButtonBlock | BskyPostBlock | PollBlock | PageBlock;
362
+ /**
363
+ * Position for quotes
364
+ */
365
+ export interface Position {
366
+ block: number[];
367
+ offset: number;
368
+ }
369
+ /**
370
+ * Quote reference
371
+ */
372
+ export interface Quote {
373
+ start: Position;
374
+ end: Position;
375
+ }
376
+ /**
377
+ * Linear Document Page Block Wrapper
378
+ */
379
+ export interface LinearDocumentBlockWrapper {
380
+ $type: 'pub.leaflet.pages.linearDocument#block';
381
+ block: Block;
382
+ alignment?: '#textAlignLeft' | '#textAlignCenter' | '#textAlignRight' | '#textAlignJustify';
383
+ }
384
+ /**
385
+ * Linear Document Page
386
+ */
387
+ export interface LinearDocumentPage {
388
+ $type: 'pub.leaflet.pages.linearDocument';
389
+ id?: string;
390
+ blocks: LinearDocumentBlockWrapper[];
391
+ quote?: Quote;
392
+ }
393
+ /**
394
+ * Canvas Page Block Wrapper
395
+ */
396
+ export interface CanvasBlockWrapper {
397
+ $type: 'pub.leaflet.pages.canvas#block';
398
+ block: Block;
399
+ x: number;
400
+ y: number;
401
+ width: number;
402
+ height?: number;
403
+ rotation?: number;
404
+ }
405
+ /**
406
+ * Canvas Page
407
+ */
408
+ export interface CanvasPage {
409
+ $type: 'pub.leaflet.pages.canvas';
410
+ id?: string;
411
+ blocks: CanvasBlockWrapper[];
412
+ quote?: Quote;
413
+ }
414
+ /**
415
+ * Content (pub.leaflet.content)
416
+ */
417
+ export interface Content {
418
+ $type: 'pub.leaflet.content';
419
+ pages: (LinearDocumentPage | CanvasPage)[];
420
+ }
421
+ /**
422
+ * Linear Document Quote (for comment attachments)
423
+ */
424
+ export interface LinearDocumentQuote {
425
+ $type?: 'pub.leaflet.comment#linearDocumentQuote';
426
+ document: string;
427
+ quote?: Quote;
428
+ }
429
+ /**
430
+ * Comment Reply Reference
431
+ */
432
+ export interface CommentReplyRef {
433
+ $type?: 'pub.leaflet.comment#replyRef';
434
+ parent: string;
435
+ }
436
+ /**
437
+ * Comment record
438
+ */
439
+ export interface CommentRecord {
440
+ $type: 'pub.leaflet.comment';
441
+ subject: string;
442
+ plaintext: string;
443
+ createdAt: string;
444
+ reply?: CommentReplyRef;
445
+ facets?: Facet[];
446
+ onPage?: string;
447
+ attachment?: LinearDocumentQuote;
448
+ }
449
+ /**
450
+ * Recommend record
451
+ */
452
+ export interface RecommendRecord {
453
+ $type: 'pub.leaflet.interactions.recommend';
454
+ subject: string;
455
+ createdAt: string;
456
+ }
457
+ /**
458
+ * Subscription record (site.standard.graph.subscription)
459
+ */
460
+ export interface SubscriptionRecord {
461
+ $type: 'site.standard.graph.subscription';
462
+ publication: string;
463
+ }
464
+ /**
465
+ * Leaflet Subscription record (pub.leaflet.graph.subscription)
466
+ */
467
+ export interface LeafletSubscriptionRecord {
468
+ $type: 'pub.leaflet.graph.subscription';
469
+ publication: string;
470
+ }
80
471
  /**
81
472
  * AT Protocol record response
82
473
  */
package/dist/types.js CHANGED
@@ -1,4 +1,4 @@
1
1
  /**
2
- * Core types for site.standard.* lexicons
2
+ * Core types for site.standard.* and pub.leaflet.* lexicons
3
3
  */
4
4
  export {};
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Native Comments utilities for pub.leaflet.comment records
3
+ */
4
+ import type { CommentRecord, Facet, LinearDocumentQuote } from '../types.js';
5
+ /** Collection name for comments */
6
+ export declare const COMMENTS_COLLECTION = "pub.leaflet.comment";
7
+ /**
8
+ * Create a comment record
9
+ */
10
+ export declare function createCommentRecord(params: {
11
+ subject: string;
12
+ plaintext: string;
13
+ reply?: {
14
+ parent: string;
15
+ };
16
+ facets?: Facet[];
17
+ onPage?: string;
18
+ attachment?: LinearDocumentQuote;
19
+ }): CommentRecord;
20
+ /**
21
+ * Parse a comment AT-URI to extract components
22
+ */
23
+ export declare function parseCommentUri(uri: string): {
24
+ did: string;
25
+ collection: string;
26
+ rkey: string;
27
+ } | null;
28
+ /**
29
+ * Build a comment AT-URI
30
+ */
31
+ export declare function buildCommentUri(did: string, rkey: string): string;
32
+ /**
33
+ * Fetch comments for a document
34
+ */
35
+ export declare function fetchComments(subject: string, service?: string): Promise<Array<CommentRecord & {
36
+ uri: string;
37
+ cid: string;
38
+ author: {
39
+ did: string;
40
+ };
41
+ }>>;
42
+ /**
43
+ * Organize comments into a threaded structure
44
+ */
45
+ export declare function organizeCommentsIntoThreads(comments: Array<CommentRecord & {
46
+ uri: string;
47
+ cid: string;
48
+ author: {
49
+ did: string;
50
+ };
51
+ }>): Array<CommentRecord & {
52
+ uri: string;
53
+ cid: string;
54
+ author: {
55
+ did: string;
56
+ };
57
+ replies: any[];
58
+ }>;
59
+ /**
60
+ * Count total comments in a thread
61
+ */
62
+ export declare function countThreadComments(thread: {
63
+ replies: any[];
64
+ }): number;
65
+ /**
66
+ * Get quoted text from a document
67
+ */
68
+ export declare function extractQuotedText(document: any, quote: LinearDocumentQuote['quote']): string | null;
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Native Comments utilities for pub.leaflet.comment records
3
+ */
4
+ /** Collection name for comments */
5
+ export const COMMENTS_COLLECTION = 'pub.leaflet.comment';
6
+ /**
7
+ * Create a comment record
8
+ */
9
+ export function createCommentRecord(params) {
10
+ const record = {
11
+ $type: 'pub.leaflet.comment',
12
+ subject: params.subject,
13
+ plaintext: params.plaintext,
14
+ createdAt: new Date().toISOString()
15
+ };
16
+ if (params.reply) {
17
+ record.reply = {
18
+ $type: 'pub.leaflet.comment#replyRef',
19
+ parent: params.reply.parent
20
+ };
21
+ }
22
+ if (params.facets) {
23
+ record.facets = params.facets;
24
+ }
25
+ if (params.onPage) {
26
+ record.onPage = params.onPage;
27
+ }
28
+ if (params.attachment) {
29
+ record.attachment = {
30
+ $type: 'pub.leaflet.comment#linearDocumentQuote',
31
+ ...params.attachment
32
+ };
33
+ }
34
+ return record;
35
+ }
36
+ /**
37
+ * Parse a comment AT-URI to extract components
38
+ */
39
+ export function parseCommentUri(uri) {
40
+ const match = uri.match(/^at:\/\/([^/]+)\/([^/]+)\/([^/]+)$/);
41
+ if (!match)
42
+ return null;
43
+ return {
44
+ did: match[1],
45
+ collection: match[2],
46
+ rkey: match[3]
47
+ };
48
+ }
49
+ /**
50
+ * Build a comment AT-URI
51
+ */
52
+ export function buildCommentUri(did, rkey) {
53
+ return `at://${did}/${COMMENTS_COLLECTION}/${rkey}`;
54
+ }
55
+ /**
56
+ * Fetch comments for a document
57
+ */
58
+ export async function fetchComments(subject, service = 'https://public.api.bsky.app') {
59
+ try {
60
+ const response = await fetch(`${service}/xrpc/com.atproto.repo.listRecords?collection=${COMMENTS_COLLECTION}&limit=100`);
61
+ if (!response.ok) {
62
+ throw new Error(`Failed to fetch comments: ${response.status}`);
63
+ }
64
+ const data = await response.json();
65
+ // Filter comments for this subject
66
+ const comments = data.records
67
+ ?.filter((record) => record.value?.subject === subject)
68
+ ?.map((record) => ({
69
+ ...record.value,
70
+ uri: record.uri,
71
+ cid: record.cid,
72
+ author: { did: record.uri.split('/')[2] }
73
+ })) ?? [];
74
+ return comments;
75
+ }
76
+ catch (error) {
77
+ console.error('Error fetching comments:', error);
78
+ return [];
79
+ }
80
+ }
81
+ /**
82
+ * Organize comments into a threaded structure
83
+ */
84
+ export function organizeCommentsIntoThreads(comments) {
85
+ const byId = new Map();
86
+ const rootComments = [];
87
+ // First pass: create all comment objects
88
+ for (const comment of comments) {
89
+ byId.set(comment.uri, { ...comment, replies: [] });
90
+ }
91
+ // Second pass: organize into threads
92
+ for (const comment of comments) {
93
+ const node = byId.get(comment.uri);
94
+ if (!node)
95
+ continue;
96
+ if (comment.reply?.parent) {
97
+ const parent = byId.get(comment.reply.parent);
98
+ if (parent) {
99
+ parent.replies.push(node);
100
+ }
101
+ else {
102
+ // Parent not found, treat as root
103
+ rootComments.push(node);
104
+ }
105
+ }
106
+ else {
107
+ rootComments.push(node);
108
+ }
109
+ }
110
+ // Sort root comments by date (newest first)
111
+ rootComments.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
112
+ return rootComments;
113
+ }
114
+ /**
115
+ * Count total comments in a thread
116
+ */
117
+ export function countThreadComments(thread) {
118
+ let count = 1;
119
+ for (const reply of thread.replies) {
120
+ count += countThreadComments(reply);
121
+ }
122
+ return count;
123
+ }
124
+ /**
125
+ * Get quoted text from a document
126
+ */
127
+ export function extractQuotedText(document, quote) {
128
+ if (!quote || !document?.content?.pages)
129
+ return null;
130
+ // Find the linear document page
131
+ const page = document.content.pages.find((p) => quote.start.block.every((b, i) => b === p.id || i === 0));
132
+ if (!page)
133
+ return null;
134
+ // Extract text based on block indices
135
+ const blocks = page.blocks || [];
136
+ const startBlockIndex = quote.start.block[quote.start.block.length - 1];
137
+ const endBlockIndex = quote.end.block[quote.end.block.length - 1];
138
+ if (startBlockIndex === undefined || endBlockIndex === undefined)
139
+ return null;
140
+ // For simplicity, return the text from the first block
141
+ // In a real implementation, you'd handle multi-block quotes
142
+ const startBlock = blocks[startBlockIndex]?.block;
143
+ if (!startBlock)
144
+ return null;
145
+ if (startBlock.plaintext) {
146
+ return startBlock.plaintext.slice(quote.start.offset, quote.end.offset);
147
+ }
148
+ return null;
149
+ }
@@ -1,4 +1,4 @@
1
- import type { BasicTheme } from '../types.js';
1
+ import type { BasicTheme, ExtendedTheme, BackgroundImage } from '../types.js';
2
2
  /**
3
3
  * Generate color-mix CSS for theme colors with transparency
4
4
  */
@@ -28,7 +28,47 @@ export declare function getThemedAccent(hasTheme: boolean, opacity?: number): {
28
28
  color?: string;
29
29
  backgroundColor?: string;
30
30
  };
31
+ /**
32
+ * Get theme-aware page background
33
+ */
34
+ export declare function getThemedPageBackground(hasTheme: boolean, showPageBackground?: boolean): {
35
+ backgroundColor?: string;
36
+ };
37
+ /**
38
+ * Get background image styles
39
+ */
40
+ export declare function getBackgroundImageStyles(bgImage: BackgroundImage | undefined): {
41
+ backgroundImage?: string;
42
+ backgroundSize?: string;
43
+ backgroundPosition?: string;
44
+ };
45
+ /**
46
+ * Get font styles from theme
47
+ */
48
+ export declare function getFontStyles(theme: ExtendedTheme | undefined): {
49
+ fontFamily?: string;
50
+ };
51
+ /**
52
+ * Get heading font styles from theme
53
+ */
54
+ export declare function getHeadingFontStyles(theme: ExtendedTheme | undefined): {
55
+ fontFamily?: string;
56
+ };
57
+ /**
58
+ * Get page width styles from theme
59
+ */
60
+ export declare function getPageWidthStyles(theme: ExtendedTheme | undefined): {
61
+ maxWidth?: string;
62
+ };
31
63
  /**
32
64
  * Convert BasicTheme to CSS custom properties
33
65
  */
34
66
  export declare function themeToCssVars(theme?: BasicTheme): Record<string, string>;
67
+ /**
68
+ * Convert ExtendedTheme to CSS custom properties
69
+ */
70
+ export declare function extendedThemeToCssVars(theme?: ExtendedTheme): Record<string, string>;
71
+ /**
72
+ * Convert any theme to CSS custom properties
73
+ */
74
+ export declare function anyThemeToCssVars(theme?: BasicTheme | ExtendedTheme): Record<string, string>;