notionary 0.2.17__py3-none-any.whl → 0.2.19__py3-none-any.whl

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 (113) hide show
  1. notionary/__init__.py +3 -2
  2. notionary/blocks/__init__.py +54 -25
  3. notionary/blocks/audio/__init__.py +7 -0
  4. notionary/blocks/audio/audio_element.py +152 -0
  5. notionary/blocks/audio/audio_markdown_node.py +29 -0
  6. notionary/blocks/audio/audio_models.py +59 -0
  7. notionary/blocks/bookmark/__init__.py +7 -0
  8. notionary/blocks/{bookmark_element.py → bookmark/bookmark_element.py} +20 -65
  9. notionary/blocks/bookmark/bookmark_markdown_node.py +43 -0
  10. notionary/blocks/bookmark/bookmark_models.py +0 -0
  11. notionary/blocks/bulleted_list/__init__.py +7 -0
  12. notionary/blocks/{bulleted_list_element.py → bulleted_list/bulleted_list_element.py} +7 -3
  13. notionary/blocks/bulleted_list/bulleted_list_markdown_node.py +33 -0
  14. notionary/blocks/bulleted_list/bulleted_list_models.py +0 -0
  15. notionary/blocks/callout/__init__.py +7 -0
  16. notionary/blocks/callout/callout_element.py +132 -0
  17. notionary/blocks/callout/callout_markdown_node.py +31 -0
  18. notionary/blocks/callout/callout_models.py +0 -0
  19. notionary/blocks/code/__init__.py +7 -0
  20. notionary/blocks/{code_block_element.py → code/code_element.py} +72 -40
  21. notionary/blocks/code/code_markdown_node.py +43 -0
  22. notionary/blocks/code/code_models.py +0 -0
  23. notionary/blocks/column/__init__.py +5 -0
  24. notionary/blocks/{column_element.py → column/column_element.py} +24 -55
  25. notionary/blocks/column/column_models.py +0 -0
  26. notionary/blocks/divider/__init__.py +7 -0
  27. notionary/blocks/{divider_element.py → divider/divider_element.py} +11 -3
  28. notionary/blocks/divider/divider_markdown_node.py +24 -0
  29. notionary/blocks/divider/divider_models.py +0 -0
  30. notionary/blocks/document/__init__.py +7 -0
  31. notionary/blocks/document/document_element.py +102 -0
  32. notionary/blocks/document/document_markdown_node.py +31 -0
  33. notionary/blocks/document/document_models.py +0 -0
  34. notionary/blocks/embed/__init__.py +7 -0
  35. notionary/blocks/{embed_element.py → embed/embed_element.py} +50 -32
  36. notionary/blocks/embed/embed_markdown_node.py +30 -0
  37. notionary/blocks/embed/embed_models.py +0 -0
  38. notionary/blocks/heading/__init__.py +7 -0
  39. notionary/blocks/{heading_element.py → heading/heading_element.py} +25 -17
  40. notionary/blocks/heading/heading_markdown_node.py +29 -0
  41. notionary/blocks/heading/heading_models.py +0 -0
  42. notionary/blocks/image/__init__.py +7 -0
  43. notionary/blocks/{image_element.py → image/image_element.py} +62 -42
  44. notionary/blocks/image/image_markdown_node.py +33 -0
  45. notionary/blocks/image/image_models.py +0 -0
  46. notionary/blocks/markdown_builder.py +356 -0
  47. notionary/blocks/markdown_node.py +29 -0
  48. notionary/blocks/mention/__init__.py +7 -0
  49. notionary/blocks/{mention_element.py → mention/mention_element.py} +6 -2
  50. notionary/blocks/mention/mention_markdown_node.py +38 -0
  51. notionary/blocks/mention/mention_models.py +0 -0
  52. notionary/blocks/numbered_list/__init__.py +7 -0
  53. notionary/blocks/{numbered_list_element.py → numbered_list/numbered_list_element.py} +10 -6
  54. notionary/blocks/numbered_list/numbered_list_markdown_node.py +29 -0
  55. notionary/blocks/numbered_list/numbered_list_models.py +0 -0
  56. notionary/blocks/paragraph/__init__.py +7 -0
  57. notionary/blocks/{paragraph_element.py → paragraph/paragraph_element.py} +7 -3
  58. notionary/blocks/paragraph/paragraph_markdown_node.py +25 -0
  59. notionary/blocks/paragraph/paragraph_models.py +0 -0
  60. notionary/blocks/quote/__init__.py +7 -0
  61. notionary/blocks/quote/quote_element.py +92 -0
  62. notionary/blocks/quote/quote_markdown_node.py +23 -0
  63. notionary/blocks/quote/quote_models.py +0 -0
  64. notionary/blocks/registry/block_registry.py +17 -3
  65. notionary/blocks/registry/block_registry_builder.py +90 -178
  66. notionary/blocks/shared/__init__.py +0 -0
  67. notionary/blocks/shared/block_client.py +256 -0
  68. notionary/blocks/shared/models.py +713 -0
  69. notionary/blocks/{notion_block_element.py → shared/notion_block_element.py} +8 -5
  70. notionary/blocks/{text_inline_formatter.py → shared/text_inline_formatter.py} +14 -14
  71. notionary/blocks/shared/text_inline_formatter_new.py +139 -0
  72. notionary/blocks/table/__init__.py +7 -0
  73. notionary/blocks/{table_element.py → table/table_element.py} +23 -11
  74. notionary/blocks/table/table_markdown_node.py +40 -0
  75. notionary/blocks/table/table_models.py +0 -0
  76. notionary/blocks/todo/__init__.py +7 -0
  77. notionary/blocks/{todo_element.py → todo/todo_element.py} +8 -4
  78. notionary/blocks/todo/todo_markdown_node.py +31 -0
  79. notionary/blocks/todo/todo_models.py +0 -0
  80. notionary/blocks/toggle/__init__.py +4 -0
  81. notionary/blocks/{toggle_element.py → toggle/toggle_element.py} +7 -3
  82. notionary/blocks/toggle/toggle_markdown_node.py +35 -0
  83. notionary/blocks/toggle/toggle_models.py +0 -0
  84. notionary/blocks/toggleable_heading/__init__.py +9 -0
  85. notionary/blocks/{toggleable_heading_element.py → toggleable_heading/toggleable_heading_element.py} +8 -4
  86. notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +43 -0
  87. notionary/blocks/toggleable_heading/toggleable_heading_models.py +0 -0
  88. notionary/blocks/video/__init__.py +7 -0
  89. notionary/blocks/{video_element.py → video/video_element.py} +82 -57
  90. notionary/blocks/video/video_markdown_node.py +30 -0
  91. notionary/file_upload/notion_file_upload.py +1 -1
  92. notionary/page/content/markdown_whitespace_processor.py +80 -0
  93. notionary/page/content/notion_text_length_utils.py +87 -0
  94. notionary/page/content/page_content_retriever.py +18 -10
  95. notionary/page/content/page_content_writer.py +97 -148
  96. notionary/page/formatting/line_processor.py +153 -0
  97. notionary/page/formatting/markdown_to_notion_converter.py +104 -425
  98. notionary/page/notion_page.py +9 -11
  99. notionary/page/notion_to_markdown_converter.py +9 -13
  100. notionary/util/factory_decorator.py +0 -0
  101. notionary/workspace.py +0 -1
  102. {notionary-0.2.17.dist-info → notionary-0.2.19.dist-info}/METADATA +1 -1
  103. notionary-0.2.19.dist-info/RECORD +150 -0
  104. notionary/blocks/audio_element.py +0 -144
  105. notionary/blocks/callout_element.py +0 -122
  106. notionary/blocks/document_element.py +0 -194
  107. notionary/blocks/notion_block_client.py +0 -26
  108. notionary/blocks/qoute_element.py +0 -169
  109. notionary/page/content/notion_page_content_chunker.py +0 -84
  110. notionary/page/formatting/spacer_rules.py +0 -483
  111. notionary-0.2.17.dist-info/RECORD +0 -85
  112. {notionary-0.2.17.dist-info → notionary-0.2.19.dist-info}/LICENSE +0 -0
  113. {notionary-0.2.17.dist-info → notionary-0.2.19.dist-info}/WHEEL +0 -0
@@ -0,0 +1,713 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Literal, Optional, Union, Any, Dict
5
+ from pydantic import BaseModel
6
+
7
+
8
+ # ============================================================================
9
+ # ENUMS AND COMMON TYPES
10
+ # ============================================================================
11
+
12
+ BlockColor = Literal[
13
+ "blue",
14
+ "blue_background",
15
+ "brown",
16
+ "brown_background",
17
+ "default",
18
+ "gray",
19
+ "gray_background",
20
+ "green",
21
+ "green_background",
22
+ "orange",
23
+ "orange_background",
24
+ "yellow",
25
+ "yellow_background",
26
+ "pink",
27
+ "pink_background",
28
+ "purple",
29
+ "purple_background",
30
+ "red",
31
+ "red_background",
32
+ "default_background"
33
+ ]
34
+
35
+ BlockType = Literal[
36
+ "bookmark",
37
+ "breadcrumb",
38
+ "bulleted_list_item",
39
+ "callout",
40
+ "child_database",
41
+ "child_page",
42
+ "column",
43
+ "column_list",
44
+ "code",
45
+ "divider",
46
+ "embed",
47
+ "equation",
48
+ "file",
49
+ "heading_1",
50
+ "heading_2",
51
+ "heading_3",
52
+ "image",
53
+ "link_preview",
54
+ "link_to_page",
55
+ "numbered_list_item",
56
+ "paragraph",
57
+ "pdf",
58
+ "quote",
59
+ "synced_block",
60
+ "table",
61
+ "table_of_contents",
62
+ "table_row",
63
+ "template",
64
+ "to_do",
65
+ "toggle",
66
+ "unsupported",
67
+ "video",
68
+ "audio",
69
+ ]
70
+
71
+ CodeLanguage = Literal[
72
+ "abap",
73
+ "arduino",
74
+ "bash",
75
+ "basic",
76
+ "c",
77
+ "clojure",
78
+ "coffeescript",
79
+ "c++",
80
+ "c#",
81
+ "css",
82
+ "dart",
83
+ "diff",
84
+ "docker",
85
+ "elixir",
86
+ "elm",
87
+ "erlang",
88
+ "flow",
89
+ "fortran",
90
+ "f#",
91
+ "gherkin",
92
+ "glsl",
93
+ "go",
94
+ "graphql",
95
+ "groovy",
96
+ "haskell",
97
+ "html",
98
+ "java",
99
+ "javascript",
100
+ "json",
101
+ "julia",
102
+ "kotlin",
103
+ "latex",
104
+ "less",
105
+ "lisp",
106
+ "livescript",
107
+ "lua",
108
+ "makefile",
109
+ "markdown",
110
+ "markup",
111
+ "matlab",
112
+ "mermaid",
113
+ "nix",
114
+ "objective-c",
115
+ "ocaml",
116
+ "pascal",
117
+ "perl",
118
+ "php",
119
+ "plain text",
120
+ "powershell",
121
+ "prolog",
122
+ "protobuf",
123
+ "python",
124
+ "r",
125
+ "reason",
126
+ "ruby",
127
+ "rust",
128
+ "sass",
129
+ "scala",
130
+ "scheme",
131
+ "scss",
132
+ "shell",
133
+ "sql",
134
+ "swift",
135
+ "typescript",
136
+ "vb.net",
137
+ "verilog",
138
+ "vhdl",
139
+ "visual basic",
140
+ "webassembly",
141
+ "xml",
142
+ "yaml",
143
+ "java/c/c++/c#",
144
+ ]
145
+
146
+
147
+ # ============================================================================
148
+ # RICH TEXT AND COMMON OBJECTS
149
+ # ============================================================================
150
+
151
+
152
+ class TextAnnotations(BaseModel):
153
+ bold: bool = False
154
+ italic: bool = False
155
+ strikethrough: bool = False
156
+ underline: bool = False
157
+ code: bool = False
158
+ color: BlockColor = "default"
159
+
160
+
161
+ class TextContent(BaseModel):
162
+ content: str
163
+ link: Optional[Dict[str, str]] = None
164
+
165
+
166
+ class TextObject(BaseModel):
167
+ type: Literal["text"]
168
+ text: TextContent
169
+ annotations: TextAnnotations = TextAnnotations()
170
+ plain_text: str
171
+ href: Optional[str] = None
172
+
173
+
174
+ class MentionDate(BaseModel):
175
+ start: str
176
+ end: Optional[str] = None
177
+ time_zone: Optional[str] = None
178
+
179
+
180
+ class MentionUser(BaseModel):
181
+ id: str
182
+ object: Literal["user"] = "user"
183
+
184
+
185
+ class MentionPage(BaseModel):
186
+ id: str
187
+
188
+
189
+ class MentionDatabase(BaseModel):
190
+ id: str
191
+
192
+
193
+ class MentionObject(BaseModel):
194
+ type: Literal[
195
+ "user", "page", "database", "date", "link_preview", "template_mention"
196
+ ]
197
+ user: Optional[MentionUser] = None
198
+ page: Optional[MentionPage] = None
199
+ database: Optional[MentionDatabase] = None
200
+ date: Optional[MentionDate] = None
201
+ # Add other mention types as needed
202
+
203
+
204
+ class TextAnnotations(BaseModel):
205
+ """Text formatting annotations"""
206
+
207
+ bold: bool = False
208
+ italic: bool = False
209
+ strikethrough: bool = False
210
+ underline: bool = False
211
+ code: bool = False
212
+ color: str = "default"
213
+
214
+
215
+ class TextContent(BaseModel):
216
+ """Text content with optional link"""
217
+
218
+ content: str
219
+ link: Optional[str] = None
220
+
221
+
222
+ class TextAnnotations(BaseModel):
223
+ bold: bool = False
224
+ italic: bool = False
225
+ strikethrough: bool = False
226
+ underline: bool = False
227
+ code: bool = False
228
+ color: str = "default"
229
+
230
+
231
+ class TextContent(BaseModel):
232
+ content: str
233
+ link: Optional[str] = None
234
+
235
+
236
+ class RichTextObject(BaseModel):
237
+ type: str = "text"
238
+ text: TextContent
239
+ annotations: TextAnnotations
240
+ plain_text: str
241
+ href: Optional[str] = None
242
+
243
+ @classmethod
244
+ def from_plain_text(cls, content: str, **kwargs) -> RichTextObject:
245
+ """Create rich text object from plain text with optional formatting."""
246
+ annotations = TextAnnotations(**kwargs)
247
+ text_content = TextContent(content=content, link=None)
248
+
249
+ return cls(
250
+ text=text_content, annotations=annotations, plain_text=content, href=None
251
+ )
252
+
253
+
254
+ class PageMention(BaseModel):
255
+ id: str
256
+
257
+
258
+ class MentionContent(BaseModel):
259
+ type: Literal["page"] = "page"
260
+ page: PageMention
261
+
262
+
263
+ class MentionRichText(BaseModel):
264
+ type: Literal["mention"] = "mention"
265
+ mention: MentionContent
266
+ annotations: TextAnnotations
267
+ plain_text: str
268
+ href: Optional[str] = None
269
+
270
+ @classmethod
271
+ def from_page_id(cls, page_id: str) -> MentionRichText:
272
+ page_mention = PageMention(id=page_id)
273
+ mention_content = MentionContent(page=page_mention)
274
+ return cls(
275
+ mention=mention_content,
276
+ annotations=TextAnnotations(),
277
+ plain_text=f"@{page_id}",
278
+ href=None,
279
+ )
280
+
281
+
282
+ # ============================================================================
283
+ # FILE OBJECTS
284
+ # ============================================================================
285
+
286
+
287
+ class ExternalFile(BaseModel):
288
+ url: str
289
+
290
+
291
+ class NotionHostedFile(BaseModel):
292
+ url: str
293
+ expiry_time: str
294
+
295
+
296
+ class FileUploadFile(BaseModel):
297
+ id: str
298
+
299
+
300
+ class FileObject(BaseModel):
301
+ type: Literal["external", "file", "file_upload"]
302
+ external: Optional[ExternalFile] = None
303
+ file: Optional[NotionHostedFile] = None
304
+ file_upload: Optional[FileUploadFile] = None
305
+ caption: Optional[list[RichTextObject]] = None
306
+ name: Optional[str] = None
307
+
308
+
309
+ # ============================================================================
310
+ # EMOJI AND ICON OBJECTS
311
+ # ============================================================================
312
+
313
+
314
+ class EmojiIcon(BaseModel):
315
+ type: Literal["emoji"] = "emoji"
316
+ emoji: str
317
+
318
+
319
+ class FileIcon(BaseModel):
320
+ type: Literal["file"] = "file"
321
+ file: FileObject
322
+
323
+
324
+ IconObject = Union[EmojiIcon, FileIcon]
325
+
326
+
327
+ # ============================================================================
328
+ # PARENT OBJECTS
329
+ # ============================================================================
330
+
331
+
332
+ class PageParent(BaseModel):
333
+ type: Literal["page_id"]
334
+ page_id: str
335
+
336
+
337
+ class DatabaseParent(BaseModel):
338
+ type: Literal["database_id"]
339
+ database_id: str
340
+
341
+
342
+ class BlockParent(BaseModel):
343
+ type: Literal["block_id"]
344
+ block_id: str
345
+
346
+
347
+ class WorkspaceParent(BaseModel):
348
+ type: Literal["workspace"]
349
+ workspace: bool = True
350
+
351
+
352
+ ParentObject = Union[PageParent, DatabaseParent, BlockParent, WorkspaceParent]
353
+
354
+
355
+ # ============================================================================
356
+ # USER OBJECTS
357
+ # ============================================================================
358
+
359
+
360
+ class PartialUser(BaseModel):
361
+ object: Literal["user"]
362
+ id: str
363
+
364
+
365
+ # ============================================================================
366
+ # BLOCK TYPE OBJECTS
367
+ # ============================================================================
368
+
369
+
370
+ class ExternalUrl(BaseModel):
371
+ url: str
372
+
373
+
374
+ class AudioContent(BaseModel):
375
+ type: Literal["external"]
376
+ external: ExternalUrl
377
+ caption: Optional[list[RichTextObject]] = None
378
+
379
+
380
+ class AudioBlockCreate(BaseModel):
381
+ type: Literal["audio"]
382
+ audio: AudioContent
383
+
384
+
385
+ class AudioBlock(BaseModel):
386
+ type: Literal["external", "file", "file_upload"]
387
+ external: Optional[ExternalFile] = None
388
+ file: Optional[NotionHostedFile] = None
389
+ file_upload: Optional[FileUploadFile] = None
390
+
391
+
392
+ class BookmarkBlock(BaseModel):
393
+ caption: list[RichTextObject] = []
394
+ url: str
395
+
396
+
397
+ class BreadcrumbBlock(BaseModel):
398
+ pass
399
+
400
+
401
+ class BulletedListItemBlock(BaseModel):
402
+ rich_text: list[RichTextObject]
403
+ color: BlockColor = "default"
404
+ children: Optional[list["Block"]] = None
405
+
406
+
407
+ class CalloutBlock(BaseModel):
408
+ rich_text: list[RichTextObject]
409
+ icon: Optional[IconObject] = None
410
+ color: BlockColor = "default"
411
+ children: Optional[list["Block"]] = None
412
+
413
+
414
+ class ChildDatabaseBlock(BaseModel):
415
+ title: str
416
+
417
+
418
+ class ChildPageBlock(BaseModel):
419
+ title: str
420
+
421
+
422
+ class CodeBlock(BaseModel):
423
+ caption: list[RichTextObject] = []
424
+ rich_text: list[RichTextObject]
425
+ language: CodeLanguage = "plain text"
426
+
427
+
428
+ class ColumnListBlock(BaseModel):
429
+ pass
430
+
431
+
432
+ class ColumnBlock(BaseModel):
433
+ width_ratio: Optional[float] = None
434
+
435
+
436
+ class DividerBlock(BaseModel):
437
+ pass
438
+
439
+
440
+ class EmbedBlock(BaseModel):
441
+ url: str
442
+
443
+
444
+ class EquationBlock(BaseModel):
445
+ expression: str
446
+
447
+
448
+ class FileBlock(BaseModel):
449
+ caption: list[RichTextObject] = []
450
+ type: Literal["external", "file", "file_upload"]
451
+ external: Optional[ExternalFile] = None
452
+ file: Optional[NotionHostedFile] = None
453
+ file_upload: Optional[FileUploadFile] = None
454
+ name: Optional[str] = None
455
+
456
+
457
+ class HeadingBlock(BaseModel):
458
+ rich_text: list[RichTextObject]
459
+ color: BlockColor = "default"
460
+ is_toggleable: bool = False
461
+ children: Optional[list["Block"]] = None
462
+
463
+
464
+ class ImageBlock(BaseModel):
465
+ type: Literal["external", "file", "file_upload"]
466
+ external: Optional[ExternalFile] = None
467
+ file: Optional[NotionHostedFile] = None
468
+ file_upload: Optional[FileUploadFile] = None
469
+ caption: Optional[list[RichTextObject]] = None
470
+
471
+
472
+ class LinkPreviewBlock(BaseModel):
473
+ url: str
474
+
475
+
476
+ class LinkToPageBlock(BaseModel):
477
+ type: Literal["page_id", "database_id"]
478
+ page_id: Optional[str] = None
479
+ database_id: Optional[str] = None
480
+
481
+
482
+ class NumberedListItemBlock(BaseModel):
483
+ rich_text: list[RichTextObject]
484
+ color: BlockColor = "default"
485
+ children: Optional[list["Block"]] = None
486
+
487
+
488
+ class ParagraphBlock(BaseModel):
489
+ rich_text: list[RichTextObject]
490
+ color: BlockColor = "default"
491
+ children: Optional[list["Block"]] = None
492
+
493
+
494
+ class PdfBlock(BaseModel):
495
+ caption: list[RichTextObject] = []
496
+ type: Literal["external", "file", "file_upload"]
497
+ external: Optional[ExternalFile] = None
498
+ file: Optional[NotionHostedFile] = None
499
+ file_upload: Optional[FileUploadFile] = None
500
+
501
+
502
+ class QuoteBlock(BaseModel):
503
+ rich_text: list[RichTextObject]
504
+ color: BlockColor = "default"
505
+ children: Optional[list["Block"]] = None
506
+
507
+
508
+ class SyncedFromObject(BaseModel):
509
+ type: Literal["block_id"]
510
+ block_id: str
511
+
512
+
513
+ class SyncedBlock(BaseModel):
514
+ synced_from: Optional[SyncedFromObject] = (
515
+ None # None for original, object for duplicate
516
+ )
517
+ children: Optional[list["Block"]] = None
518
+
519
+
520
+ class TableBlock(BaseModel):
521
+ table_width: int
522
+ has_column_header: bool = False
523
+ has_row_header: bool = False
524
+
525
+
526
+ class TableRowBlock(BaseModel):
527
+ cells: list[list[RichTextObject]]
528
+
529
+
530
+ class TableOfContentsBlock(BaseModel):
531
+ color: BlockColor = "default"
532
+
533
+
534
+ class TemplateBlock(BaseModel):
535
+ rich_text: list[RichTextObject]
536
+ children: Optional[list["Block"]] = None
537
+
538
+
539
+ class ToDoBlock(BaseModel):
540
+ rich_text: list[RichTextObject]
541
+ checked: bool = False
542
+ color: BlockColor = "default"
543
+ children: Optional[list["Block"]] = None
544
+
545
+
546
+ class ToggleBlock(BaseModel):
547
+ rich_text: list[RichTextObject]
548
+ color: BlockColor = "default"
549
+ children: Optional[list["Block"]] = None
550
+
551
+
552
+ class VideoBlock(BaseModel):
553
+ type: Literal["external", "file", "file_upload"]
554
+ external: Optional[ExternalFile] = None
555
+ file: Optional[NotionHostedFile] = None
556
+ file_upload: Optional[FileUploadFile] = None
557
+ caption: Optional[list[RichTextObject]] = None
558
+
559
+
560
+ class UnsupportedBlock(BaseModel):
561
+ pass
562
+
563
+
564
+ # ============================================================================
565
+ # MAIN BLOCK MODEL
566
+ # ============================================================================
567
+
568
+
569
+ class Block(BaseModel):
570
+ object: Literal["block"]
571
+ id: str
572
+ parent: Optional[ParentObject] = None
573
+ type: BlockType
574
+ created_time: str
575
+ last_edited_time: str
576
+ created_by: PartialUser
577
+ last_edited_by: PartialUser
578
+ archived: bool = False
579
+ in_trash: bool = False
580
+ has_children: bool = False
581
+
582
+ children: Optional[list[Block]] = None # for recursive structure
583
+
584
+ # Block type-specific content (only one will be populated based on type)
585
+ audio: Optional[AudioBlock] = None
586
+ bookmark: Optional[BookmarkBlock] = None
587
+ breadcrumb: Optional[BreadcrumbBlock] = None
588
+ bulleted_list_item: Optional[BulletedListItemBlock] = None
589
+ callout: Optional[CalloutBlock] = None
590
+ child_database: Optional[ChildDatabaseBlock] = None
591
+ child_page: Optional[ChildPageBlock] = None
592
+ code: Optional[CodeBlock] = None
593
+ column_list: Optional[ColumnListBlock] = None
594
+ column: Optional[ColumnBlock] = None
595
+ divider: Optional[DividerBlock] = None
596
+ embed: Optional[EmbedBlock] = None
597
+ equation: Optional[EquationBlock] = None
598
+ file: Optional[FileBlock] = None
599
+ heading_1: Optional[HeadingBlock] = None
600
+ heading_2: Optional[HeadingBlock] = None
601
+ heading_3: Optional[HeadingBlock] = None
602
+ image: Optional[ImageBlock] = None
603
+ link_preview: Optional[LinkPreviewBlock] = None
604
+ link_to_page: Optional[LinkToPageBlock] = None
605
+ numbered_list_item: Optional[NumberedListItemBlock] = None
606
+ paragraph: Optional[ParagraphBlock] = None
607
+ pdf: Optional[PdfBlock] = None
608
+ quote: Optional[QuoteBlock] = None
609
+ synced_block: Optional[SyncedBlock] = None
610
+ table: Optional[TableBlock] = None
611
+ table_row: Optional[TableRowBlock] = None
612
+ table_of_contents: Optional[TableOfContentsBlock] = None
613
+ template: Optional[TemplateBlock] = None
614
+ to_do: Optional[ToDoBlock] = None
615
+ toggle: Optional[ToggleBlock] = None
616
+ video: Optional[VideoBlock] = None
617
+ unsupported: Optional[UnsupportedBlock] = None
618
+
619
+ def get_block_content(self) -> Optional[Any]:
620
+ """Get the content object for this block based on its type."""
621
+ return getattr(self, self.type, None)
622
+
623
+
624
+ # Forward reference resolution
625
+ Block.model_rebuild()
626
+ BulletedListItemBlock.model_rebuild()
627
+ CalloutBlock.model_rebuild()
628
+ HeadingBlock.model_rebuild()
629
+ NumberedListItemBlock.model_rebuild()
630
+ ParagraphBlock.model_rebuild()
631
+ QuoteBlock.model_rebuild()
632
+ SyncedBlock.model_rebuild()
633
+ TemplateBlock.model_rebuild()
634
+ ToDoBlock.model_rebuild()
635
+ ToggleBlock.model_rebuild()
636
+
637
+
638
+ # ============================================================================
639
+ # API REQUEST/RESPONSE MODELS
640
+ # ============================================================================
641
+
642
+
643
+ class BlockChildrenResponse(BaseModel):
644
+ object: Literal["list"]
645
+ results: list[Block]
646
+ next_cursor: Optional[str] = None
647
+ has_more: bool
648
+ type: Literal["block"]
649
+ block: Dict = {}
650
+ request_id: str
651
+
652
+
653
+ class AppendBlockChildrenRequest(BaseModel):
654
+ children: list[Dict[str, Any]] # Block creation objects
655
+ after: Optional[str] = None
656
+
657
+
658
+ class UpdateBlockRequest(BaseModel):
659
+ # Type-specific updates based on block type
660
+ # This would contain the block-specific content to update
661
+ pass
662
+
663
+
664
+ class BlockResponse(BaseModel):
665
+ """Single block response from the API"""
666
+
667
+ object: Literal["block"]
668
+ id: str
669
+ parent: Optional[ParentObject] = None
670
+ type: BlockType
671
+ created_time: str
672
+ last_edited_time: str
673
+ created_by: PartialUser
674
+ last_edited_by: PartialUser
675
+ archived: bool = False
676
+ in_trash: bool = False
677
+ has_children: bool = False
678
+ request_id: str
679
+
680
+ # Block type-specific content
681
+ audio: Optional[AudioBlock] = None
682
+ bookmark: Optional[BookmarkBlock] = None
683
+ breadcrumb: Optional[BreadcrumbBlock] = None
684
+ bulleted_list_item: Optional[BulletedListItemBlock] = None
685
+ callout: Optional[CalloutBlock] = None
686
+ child_database: Optional[ChildDatabaseBlock] = None
687
+ child_page: Optional[ChildPageBlock] = None
688
+ code: Optional[CodeBlock] = None
689
+ column_list: Optional[ColumnListBlock] = None
690
+ column: Optional[ColumnBlock] = None
691
+ divider: Optional[DividerBlock] = None
692
+ embed: Optional[EmbedBlock] = None
693
+ equation: Optional[EquationBlock] = None
694
+ file: Optional[FileBlock] = None
695
+ heading_1: Optional[HeadingBlock] = None
696
+ heading_2: Optional[HeadingBlock] = None
697
+ heading_3: Optional[HeadingBlock] = None
698
+ image: Optional[ImageBlock] = None
699
+ link_preview: Optional[LinkPreviewBlock] = None
700
+ link_to_page: Optional[LinkToPageBlock] = None
701
+ numbered_list_item: Optional[NumberedListItemBlock] = None
702
+ paragraph: Optional[ParagraphBlock] = None
703
+ pdf: Optional[PdfBlock] = None
704
+ quote: Optional[QuoteBlock] = None
705
+ synced_block: Optional[SyncedBlock] = None
706
+ table: Optional[TableBlock] = None
707
+ table_row: Optional[TableRowBlock] = None
708
+ table_of_contents: Optional[TableOfContentsBlock] = None
709
+ template: Optional[TemplateBlock] = None
710
+ to_do: Optional[ToDoBlock] = None
711
+ toggle: Optional[ToggleBlock] = None
712
+ video: Optional[VideoBlock] = None
713
+ unsupported: Optional[UnsupportedBlock] = None