notionary 0.2.22__py3-none-any.whl → 0.2.23__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.
@@ -8,6 +8,7 @@ from notionary.blocks.syntax_prompt_builder import BlockElementMarkdownInformati
8
8
  from notionary.blocks.models import Block, BlockType
9
9
  from notionary.util import LoggingMixin
10
10
 
11
+
11
12
  class ChildDatabaseElement(BaseBlockElement, LoggingMixin):
12
13
  """
13
14
  Handles conversion between Markdown database references and Notion child database blocks.
@@ -23,14 +24,11 @@ class ChildDatabaseElement(BaseBlockElement, LoggingMixin):
23
24
  return block.type == BlockType.CHILD_DATABASE and block.child_database
24
25
 
25
26
  @classmethod
26
- async def markdown_to_notion(
27
- cls, text: str
28
- ) -> Optional[str]:
27
+ async def markdown_to_notion(cls, text: str) -> Optional[str]:
29
28
  """
30
29
  Convert markdown database syntax to actual Notion database.
31
30
  Returns the database_id if successful, None otherwise.
32
31
  """
33
- cls.logger.warning("Creating database from markdown is not supported via the block api. Call the create_child_page method in NotionPage instead.s")
34
32
  return None
35
33
 
36
34
  @classmethod
@@ -10,7 +10,7 @@ from notionary.blocks.rich_text.rich_text_models import (
10
10
  MentionTemplateMention,
11
11
  )
12
12
  from notionary.blocks.types import BlockColor
13
- from notionary.blocks.rich_text.name_to_id_resolver import NameIdResolver
13
+ from notionary.shared import NameIdResolver
14
14
 
15
15
 
16
16
  class TextInlineFormatter:
@@ -0,0 +1,26 @@
1
+ from .client import CommentClient
2
+ from .models import (
3
+ Comment,
4
+ CommentAttachment,
5
+ CommentAttachmentExternal,
6
+ CommentAttachmentFile,
7
+ CommentDisplayName,
8
+ CommentListResponse,
9
+ CommentParent,
10
+ FileWithExpiry,
11
+ UserRef,
12
+ )
13
+
14
+
15
+ __all__ = [
16
+ "CommentClient",
17
+ "Comment",
18
+ "CommentAttachment",
19
+ "CommentAttachmentExternal",
20
+ "CommentAttachmentFile",
21
+ "CommentDisplayName",
22
+ "CommentListResponse",
23
+ "CommentParent",
24
+ "FileWithExpiry",
25
+ "UserRef",
26
+ ]
@@ -0,0 +1,211 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, AsyncGenerator, Optional
4
+
5
+ from notionary.base_notion_client import BaseNotionClient
6
+ from notionary.blocks.rich_text.rich_text_models import RichTextObject
7
+ from notionary.comments.models import Comment, CommentListResponse
8
+
9
+
10
+ class CommentClient(BaseNotionClient):
11
+ """
12
+ Client for Notion comment operations.
13
+ Uses Pydantic models for typed responses.
14
+
15
+ Notes / API constraints:
16
+ - Listing returns only *unresolved* comments. Resolved comments are not returned.
17
+ - You can create:
18
+ 1) a top-level comment on a page
19
+ 2) a reply in an existing discussion (requires discussion_id)
20
+ You cannot start a brand-new inline thread via API.
21
+ - Read/Insert comment capabilities must be enabled for the integration.
22
+ """
23
+
24
+ async def retrieve_comment(self, comment_id: str) -> Comment:
25
+ """
26
+ Retrieve a single Comment object by its ID.
27
+
28
+ Requires the integration to have "Read comment" capability enabled.
29
+ Raises 403 (restricted_resource) without it.
30
+ """
31
+ resp = await self.get(f"comments/{comment_id}")
32
+ if resp is None:
33
+ raise RuntimeError("Failed to retrieve comment.")
34
+ return Comment.model_validate(resp)
35
+
36
+ async def list_all_comments_for_page(
37
+ self, *, page_id: str, page_size: int = 100
38
+ ) -> list[Comment]:
39
+ """Returns all unresolved comments for a page (handles pagination)."""
40
+ results: list[Comment] = []
41
+ cursor: str | None = None
42
+ while True:
43
+ page = await self.list_comments(
44
+ block_id=page_id, start_cursor=cursor, page_size=page_size
45
+ )
46
+ results.extend(page.results)
47
+ if not page.has_more:
48
+ break
49
+ cursor = page.next_cursor
50
+ return results
51
+
52
+ async def list_comments(
53
+ self,
54
+ *,
55
+ block_id: str,
56
+ start_cursor: Optional[str] = None,
57
+ page_size: Optional[int] = None,
58
+ ) -> CommentListResponse:
59
+ """
60
+ List unresolved comments for a page or block.
61
+
62
+ Args:
63
+ block_id: Page ID or block ID to list comments for.
64
+ start_cursor: Pagination cursor.
65
+ page_size: Max items per page (<= 100).
66
+
67
+ Returns:
68
+ CommentListResponse with results, next_cursor, has_more, etc.
69
+ """
70
+ params: dict[str, str | int] = {"block_id": block_id}
71
+ if start_cursor:
72
+ params["start_cursor"] = start_cursor
73
+ if page_size:
74
+ params["page_size"] = page_size
75
+
76
+ resp = await self.get("comments", params=params)
77
+ if resp is None:
78
+ raise RuntimeError("Failed to list comments.")
79
+ return CommentListResponse.model_validate(resp)
80
+
81
+ async def iter_comments(
82
+ self,
83
+ *,
84
+ block_id: str,
85
+ page_size: int = 100,
86
+ ) -> AsyncGenerator[Comment, None]:
87
+ """
88
+ Async generator over all unresolved comments for a given page/block.
89
+ Handles pagination under the hood.
90
+ """
91
+ cursor: Optional[str] = None
92
+ while True:
93
+ page = await self.list_comments(
94
+ block_id=block_id, start_cursor=cursor, page_size=page_size
95
+ )
96
+ for item in page.results:
97
+ yield item
98
+ if not page.has_more:
99
+ break
100
+ cursor = page.next_cursor
101
+
102
+ async def create_comment_on_page(
103
+ self,
104
+ *,
105
+ page_id: str,
106
+ text: str,
107
+ display_name: Optional[dict] = None,
108
+ attachments: Optional[list[dict]] = None,
109
+ ) -> Comment:
110
+ """
111
+ Create a top-level comment on a page.
112
+
113
+ Args:
114
+ page_id: Target page ID.
115
+ text: Plain text content for the comment (rich_text will be constructed).
116
+ display_name: Optional "Comment Display Name" object to override author label.
117
+ attachments: Optional list of "Comment Attachment" objects (max 3).
118
+
119
+ Returns:
120
+ The created Comment object.
121
+ """
122
+ body: dict = {
123
+ "parent": {"page_id": page_id},
124
+ "rich_text": [{"type": "text", "text": {"content": text}}],
125
+ }
126
+ if display_name:
127
+ body["display_name"] = display_name
128
+ if attachments:
129
+ body["attachments"] = attachments
130
+
131
+ resp = await self.post("comments", data=body)
132
+ if resp is None:
133
+ raise RuntimeError("Failed to create page comment.")
134
+ return Comment.model_validate(resp)
135
+
136
+ async def create_comment(
137
+ self,
138
+ *,
139
+ page_id: Optional[str] = None,
140
+ discussion_id: Optional[str] = None,
141
+ content: Optional[str] = None,
142
+ rich_text: Optional[list[RichTextObject]] = None,
143
+ display_name: Optional[dict[str, Any]] = None,
144
+ attachments: Optional[list[dict[str, Any]]] = None,
145
+ ) -> Comment:
146
+ """
147
+ Create a comment on a page OR reply to an existing discussion.
148
+
149
+ Rules:
150
+ - Exactly one of page_id or discussion_id must be provided.
151
+ - Provide either rich_text OR content (plain text). If both given, rich_text wins.
152
+ - Up to 3 attachments allowed by Notion.
153
+ """
154
+ # validate parent
155
+ if (page_id is None) == (discussion_id is None):
156
+ raise ValueError("Specify exactly one parent: page_id OR discussion_id")
157
+
158
+ # build rich_text if only content is provided
159
+ rt = rich_text if rich_text else None
160
+ if rt is None:
161
+ if not content:
162
+ raise ValueError("Provide either 'rich_text' or 'content'.")
163
+ rt = [{"type": "text", "text": {"content": content}}]
164
+
165
+ body: dict[str, Any] = {"rich_text": rt}
166
+ if page_id:
167
+ body["parent"] = {"page_id": page_id}
168
+ else:
169
+ body["discussion_id"] = discussion_id
170
+
171
+ if display_name:
172
+ body["display_name"] = display_name
173
+ if attachments:
174
+ body["attachments"] = attachments
175
+
176
+ resp = await self.post("comments", data=body)
177
+ if resp is None:
178
+ raise RuntimeError("Failed to create comment.")
179
+ return Comment.model_validate(resp)
180
+
181
+ # ---------- Convenience wrappers ----------
182
+
183
+ async def create_comment_on_page(
184
+ self,
185
+ *,
186
+ page_id: str,
187
+ text: str,
188
+ display_name: Optional[dict] = None,
189
+ attachments: Optional[list[dict]] = None,
190
+ ) -> Comment:
191
+ return await self.create_comment(
192
+ page_id=page_id,
193
+ content=text,
194
+ display_name=display_name,
195
+ attachments=attachments,
196
+ )
197
+
198
+ async def reply_to_discussion(
199
+ self,
200
+ *,
201
+ discussion_id: str,
202
+ text: str,
203
+ display_name: Optional[dict] = None,
204
+ attachments: Optional[list[dict]] = None,
205
+ ) -> Comment:
206
+ return await self.create_comment(
207
+ discussion_id=discussion_id,
208
+ content=text,
209
+ display_name=display_name,
210
+ attachments=attachments,
211
+ )
@@ -0,0 +1,129 @@
1
+ # notionary/comments/models.py
2
+ from __future__ import annotations
3
+
4
+ from datetime import datetime
5
+ from typing import Literal, Optional, Union
6
+
7
+ from pydantic import BaseModel, Field, ConfigDict
8
+
9
+ from notionary.blocks.rich_text import RichTextObject
10
+
11
+
12
+ class UserRef(BaseModel):
13
+ """Minimal Notion user reference."""
14
+
15
+ model_config = ConfigDict(extra="ignore")
16
+ object: Literal["user"] = "user"
17
+ id: str
18
+
19
+
20
+ class CommentParent(BaseModel):
21
+ """
22
+ Parent of a comment. Can be page_id or block_id.
23
+ Notion responds with the active one; the other remains None.
24
+ """
25
+
26
+ model_config = ConfigDict(extra="ignore")
27
+ type: Literal["page_id", "block_id"]
28
+ page_id: Optional[str] = None
29
+ block_id: Optional[str] = None
30
+
31
+
32
+ class FileWithExpiry(BaseModel):
33
+ """File object with temporary URL (common Notion pattern)."""
34
+
35
+ model_config = ConfigDict(extra="ignore")
36
+ url: str
37
+ expiry_time: Optional[datetime] = None
38
+
39
+
40
+ class CommentAttachmentFile(BaseModel):
41
+ """Attachment stored by Notion with expiring download URL."""
42
+
43
+ model_config = ConfigDict(extra="ignore")
44
+ type: Literal["file"] = "file"
45
+ name: Optional[str] = None
46
+ file: FileWithExpiry
47
+
48
+
49
+ class CommentAttachmentExternal(BaseModel):
50
+ """External attachment referenced by URL."""
51
+
52
+ model_config = ConfigDict(extra="ignore")
53
+ type: Literal["external"] = "external"
54
+ name: Optional[str] = None
55
+ external: dict # {"url": "..."} – kept generic
56
+
57
+
58
+ CommentAttachment = Union[CommentAttachmentFile, CommentAttachmentExternal]
59
+
60
+
61
+ # ---------------------------
62
+ # Display name override (optional)
63
+ # ---------------------------
64
+
65
+
66
+ class CommentDisplayName(BaseModel):
67
+ """
68
+ Optional display name override for comments created by an integration.
69
+ Example: {"type": "integration", "resolved_name": "int"}.
70
+ """
71
+
72
+ model_config = ConfigDict(extra="ignore")
73
+ type: str
74
+ resolved_name: Optional[str] = None
75
+
76
+
77
+ # ---------------------------
78
+ # Core Comment object
79
+ # ---------------------------
80
+
81
+
82
+ class Comment(BaseModel):
83
+ """
84
+ Notion Comment object as returned by:
85
+ - GET /v1/comments/{comment_id} (retrieve)
86
+ - GET /v1/comments?block_id=... (list -> in results[])
87
+ - POST /v1/comments (create)
88
+ """
89
+
90
+ model_config = ConfigDict(extra="ignore")
91
+
92
+ object: Literal["comment"] = "comment"
93
+ id: str
94
+
95
+ parent: CommentParent
96
+ discussion_id: str
97
+
98
+ created_time: datetime
99
+ last_edited_time: datetime
100
+
101
+ created_by: UserRef
102
+
103
+ rich_text: list[RichTextObject] = Field(default_factory=list)
104
+
105
+ # Optional fields that may appear depending on capabilities/payload
106
+ display_name: Optional[CommentDisplayName] = None
107
+ attachments: Optional[list[CommentAttachment]] = None
108
+
109
+
110
+ # ---------------------------
111
+ # List envelope (for list-comments)
112
+ # ---------------------------
113
+
114
+
115
+ class CommentListResponse(BaseModel):
116
+ """
117
+ Envelope for GET /v1/comments?block_id=...
118
+ """
119
+
120
+ model_config = ConfigDict(extra="ignore")
121
+
122
+ object: Literal["list"] = "list"
123
+ results: list[Comment] = Field(default_factory=list)
124
+ next_cursor: Optional[str] = None
125
+ has_more: bool = False
126
+
127
+ # Notion includes these two fields on the list envelope.
128
+ type: Optional[Literal["comment"]] = None
129
+ comment: Optional[dict] = None
notionary/page/client.py CHANGED
@@ -42,18 +42,13 @@ class NotionPageClient(BaseNotionClient):
42
42
  )
43
43
 
44
44
  properties: dict[str, Any] = {
45
- "title": {
46
- "title": [
47
- {"type": "text", "text": {"content": title}}
48
- ]
49
- }
45
+ "title": {"title": [{"type": "text", "text": {"content": title}}]}
50
46
  }
51
47
 
52
48
  payload = {"parent": parent, "properties": properties}
53
49
  response = await self.post("pages", payload)
54
50
  return NotionPageResponse.model_validate(response)
55
51
 
56
-
57
52
  async def patch_page(
58
53
  self, page_id: str, data: Optional[dict[str, Any]] = None
59
54
  ) -> NotionPageResponse:
@@ -5,6 +5,7 @@ import random
5
5
  from typing import TYPE_CHECKING, Any, Callable, Optional, Union
6
6
 
7
7
  from notionary.blocks.client import NotionBlockClient
8
+ from notionary.comments import CommentClient, Comment
8
9
  from notionary.blocks.syntax_prompt_builder import SyntaxPromptBuilder
9
10
  from notionary.blocks.models import DatabaseParent
10
11
  from notionary.blocks.registry.block_registry import BlockRegistry
@@ -24,6 +25,7 @@ from notionary.util.fuzzy import find_best_match
24
25
  if TYPE_CHECKING:
25
26
  from notionary import NotionDatabase
26
27
 
28
+
27
29
  class NotionPage(LoggingMixin):
28
30
  """
29
31
  Managing content and metadata of a Notion page.
@@ -56,8 +58,9 @@ class NotionPage(LoggingMixin):
56
58
 
57
59
  self._client = NotionPageClient(token=token)
58
60
  self._block_client = NotionBlockClient(token=token)
61
+ self._comment_client = CommentClient(token=token)
59
62
  self._page_data = None
60
-
63
+
61
64
  self.block_element_registry = BlockRegistry.create_registry()
62
65
 
63
66
  self._page_content_writer = PageContentWriter(
@@ -213,6 +216,41 @@ class NotionPage(LoggingMixin):
213
216
  markdown_syntax_builder = SyntaxPromptBuilder()
214
217
  return markdown_syntax_builder.build_concise_reference()
215
218
 
219
+ async def get_comments(self) -> list[Comment]:
220
+ return await self._comment_client.list_all_comments_for_page(page_id=self._page_id)
221
+
222
+ async def post_comment(
223
+ self,
224
+ content: str,
225
+ *,
226
+ discussion_id: Optional[str] = None,
227
+ rich_text: Optional[list[dict[str, Any]]] = None,
228
+ ) -> Optional[Comment]:
229
+ """
230
+ Post a comment on this page.
231
+
232
+ Args:
233
+ content: The plain text content of the comment
234
+ discussion_id: Optional discussion ID to reply to an existing discussion
235
+ rich_text: Optional rich text formatting for the comment content
236
+
237
+ Returns:
238
+ Comment: The created comment object, or None if creation failed
239
+ """
240
+ try:
241
+ # Use the comment client to create the comment
242
+ comment = await self._comment_client.create_comment(
243
+ page_id=self._page_id,
244
+ content=content,
245
+ discussion_id=discussion_id,
246
+ rich_text=rich_text,
247
+ )
248
+ self.logger.info(f"Successfully posted comment on page '{self._title}'")
249
+ return comment
250
+ except Exception as e:
251
+ self.logger.error(f"Failed to post comment on page '{self._title}': {str(e)}")
252
+ return None
253
+
216
254
  async def set_title(self, title: str) -> str:
217
255
  """
218
256
  Set the title of the page.
@@ -320,27 +358,33 @@ class NotionPage(LoggingMixin):
320
358
 
321
359
  self.logger.error(f"Error updating page emoji: {str(e)}")
322
360
  return None
323
-
361
+
324
362
  async def create_child_database(self, title: str) -> NotionDatabase:
325
363
  from notionary import NotionDatabase
364
+
326
365
  database_client = NotionDatabaseClient(token=self._client.token)
327
-
328
- create_database_response = await database_client.create_database(
366
+
367
+ create_database_response = await database_client.create_database(
329
368
  title=title,
330
369
  parent_page_id=self._page_id,
331
370
  )
332
-
333
- return await NotionDatabase.from_database_id(id=create_database_response.id, token=self._client.token)
334
-
371
+
372
+ return await NotionDatabase.from_database_id(
373
+ id=create_database_response.id, token=self._client.token
374
+ )
375
+
335
376
  async def create_child_page(self, title: str) -> NotionPage:
336
377
  from notionary import NotionPage
378
+
337
379
  child_page_response = await self._client.create_page(
338
380
  parent_page_id=self._page_id,
339
381
  title=title,
340
382
  )
341
-
342
- return await NotionPage.from_page_id(page_id=child_page_response.id, token=self._client.token)
343
-
383
+
384
+ return await NotionPage.from_page_id(
385
+ page_id=child_page_response.id, token=self._client.token
386
+ )
387
+
344
388
  async def set_external_icon(self, url: str) -> Optional[str]:
345
389
  """
346
390
  Sets the page icon to an external image.
@@ -636,4 +680,4 @@ class NotionPage(LoggingMixin):
636
680
  """Extract parent database ID from page response."""
637
681
  parent = page_response.parent
638
682
  if isinstance(parent, DatabaseParent):
639
- return parent.database_id
683
+ return parent.database_id
@@ -1,4 +1,3 @@
1
- # notionary/blocks/context/page_context.py
2
1
  from __future__ import annotations
3
2
 
4
3
  from typing import TYPE_CHECKING, Optional
@@ -0,0 +1,5 @@
1
+ from .name_to_id_resolver import NameIdResolver
2
+
3
+ __all__ = [
4
+ "NameIdResolver"
5
+ ]
@@ -1,7 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import logging
4
- from contextlib import contextmanager
5
3
  from typing import Optional
6
4
 
7
5
  from notionary.user.notion_user_manager import NotionUserManager
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: notionary
3
- Version: 0.2.22
3
+ Version: 0.2.23
4
4
  Summary: Python library for programmatic Notion workspace management - databases, pages, and content with advanced Markdown support
5
5
  License: MIT
6
6
  Author: Mathis Arends
@@ -13,9 +13,7 @@ Classifier: Programming Language :: Python :: 3.10
13
13
  Classifier: Programming Language :: Python :: 3.11
14
14
  Classifier: Programming Language :: Python :: 3.12
15
15
  Classifier: Programming Language :: Python :: 3.13
16
- Requires-Dist: aiofiles (>=24.1.0,<25.0.0)
17
16
  Requires-Dist: httpx (>=0.28.0)
18
- Requires-Dist: isort (>=6.0.1,<7.0.0)
19
17
  Requires-Dist: posthog (>=6.3.1,<7.0.0)
20
18
  Requires-Dist: pydantic (>=2.11.4)
21
19
  Requires-Dist: python-dotenv (>=1.1.0)
@@ -24,7 +24,7 @@ notionary/blocks/callout/callout_element.py,sha256=6Ss1jv1cqAM4GuNGvuy7dCFjpp-dL
24
24
  notionary/blocks/callout/callout_markdown_node.py,sha256=TMd2wQ0BfoQ0mgHSy5MrqCvpJvovSw7zsLrXnRrJoQs,948
25
25
  notionary/blocks/callout/callout_models.py,sha256=KabWhRCkW1KmTGqt23QTc5iCeVczIzRHIVevEagcKE0,860
26
26
  notionary/blocks/child_database/__init__.py,sha256=FzjjHOS9Je2oXuxLK9S9Oq56-EDEZRTJBfDzkacFpY0,368
27
- notionary/blocks/child_database/child_database_element.py,sha256=9Sho1ZuA6GA4xZHpj_iaK18WYd9mXLGr0yvkFIKLqGU,2450
27
+ notionary/blocks/child_database/child_database_element.py,sha256=1pxtoB_XTIWorYp984TpsPMZy8A5IdQ15nJALyzme-c,2279
28
28
  notionary/blocks/child_database/child_database_models.py,sha256=SP6S9tKTetKNdCkYY3QCxeXd2lq1DyfdNvDhKlhD22Q,264
29
29
  notionary/blocks/child_page/__init__.py,sha256=qaHvJqF8Bfzj0NVE1Q5uN_4o3Jl4RuNYspMZ6E7bUuk,182
30
30
  notionary/blocks/child_page/child_page_element.py,sha256=iZIJ91CQ9ZztivtSNKKA8-OD6YIGZvGsxeuD8vZ1PLg,3389
@@ -89,9 +89,8 @@ notionary/blocks/registry/__init__.py,sha256=Het-sHkwxg8c0A1GlPvpm6Ca-GwfN-II55Y
89
89
  notionary/blocks/registry/block_registry.py,sha256=B01_6jwrIExvMpgxFHDD8P3mXtrkK0SDriNhV7o7NWY,3195
90
90
  notionary/blocks/registry/block_registry_builder.py,sha256=a-oID7OpW4b1bEhhsQOOnwK8evlJem-MralvdhaMYno,8866
91
91
  notionary/blocks/rich_text/__init__.py,sha256=UuOn0MmGKNqK3fLBnEcH2surwbDkZ4GF8Gpn4TLNwLc,773
92
- notionary/blocks/rich_text/name_to_id_resolver.py,sha256=crAiY1mzE8RZVvWfkGcEPdGGvRmXa51WTDxca10oWA0,6373
93
92
  notionary/blocks/rich_text/rich_text_models.py,sha256=QPCHA-vo8DoH7sLAVzOXervoeeV39JRuJkR-IGVA63Y,6278
94
- notionary/blocks/rich_text/text_inline_formatter.py,sha256=ICvQFiXDBX56EDrrTha8OvE9eao_9QDxybHP6Bex9ls,18409
93
+ notionary/blocks/rich_text/text_inline_formatter.py,sha256=8J8y27OuMLX-IvSJs0Ogmvw0gw1bgQD9n55nRjSldbg,18379
95
94
  notionary/blocks/syntax_prompt_builder.py,sha256=VwvpR8JyyKR13_ni0jUt--F7w6DoD_nzJB2LbwiJXJc,4626
96
95
  notionary/blocks/table/__init__.py,sha256=Vrs6w_P63cFptV-gVuDpFbU5Rg-bDf3Tf_jUk0n-s-I,511
97
96
  notionary/blocks/table/table_element.py,sha256=eeqFHlyQ1dG1ZxB3zdrdvF5qXPsLqSEVyNQJ70W9Rwg,8002
@@ -117,6 +116,9 @@ notionary/blocks/video/__init__.py,sha256=z_lz1agYtSRv9A9bFRwPlpzmpfgf49w9uJMVkH
117
116
  notionary/blocks/video/video_element.py,sha256=nDYcr1HwCZYVBSLPEo1qHuqU9AQ8yzS9OHQTIe3rVyA,4417
118
117
  notionary/blocks/video/video_element_models.py,sha256=CYK2tq0qhrOxjO_ctOgRvEHM9hLjQCBXrP7wcTNlUPw,229
119
118
  notionary/blocks/video/video_markdown_node.py,sha256=u5OaoxLzg10PAdvo70OYgi1d-eFtTK_kUdYLAKQWdtM,1151
119
+ notionary/comments/__init__.py,sha256=G0OWsaN2VgbykvhLlNSweL_f0bl16j9NpidH2UNbFcQ,529
120
+ notionary/comments/client.py,sha256=c8mgHKVjMxC-ssuu4nv34S3788icCWSZQS-a1AZXM48,7309
121
+ notionary/comments/models.py,sha256=O2-yg-0Xrs8xrzLUa4793FlyNn3HbTRsv4dqaskI6ps,3409
120
122
  notionary/database/__init__.py,sha256=4tdML0fBzkOCpiWT6q-L--5NELFLbTPD0IUA_E8yZno,155
121
123
  notionary/database/client.py,sha256=mN_8XHvIQkRxiWbgBfvncgw4IRiCFX3bVLMLBvtk1Ig,5398
122
124
  notionary/database/database.py,sha256=sdo830KVInR4enaEaeMHaPadNfYnPLy5R3OW2f74w28,16558
@@ -136,12 +138,12 @@ notionary/markdown/markdown_builder.py,sha256=q9J7FduIxrf8hSPu_5P4ZwYaGVZdvpe-qa
136
138
  notionary/markdown/markdown_document_model.py,sha256=i9zMINcTlTwlqpHV9zeQfMVlni2VAl6BMjAdKSdoUeg,6409
137
139
  notionary/markdown/markdown_node.py,sha256=u8ApehnTCo1KnTFbtYzGuTAyUQrYPc3RSMJaUMyg0LI,717
138
140
  notionary/models/notion_database_response.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
139
- notionary/page/client.py,sha256=qSXWWeSpi0EnNpUOjqmvgdAMDCyKYvK9AxoMWZpIJzM,4594
141
+ notionary/page/client.py,sha256=IOsjWOsmODxOq-7OF0y-gcuC2HUbMIT0SvJZHrH9weQ,4520
140
142
  notionary/page/models.py,sha256=pD8FlK8KtnUrIITTd2jg_yJICakgPE4ysZiS9KiMpr0,7088
141
- notionary/page/notion_page.py,sha256=qgu4Oxfx2asyiXi2a0BmeRE3AOgsfmfVNcXv9kMC_3c,22438
143
+ notionary/page/notion_page.py,sha256=4X13kJWzJIoB7pFrfNbrIvLCQ2j4HKPOesblr6FM6d8,23903
142
144
  notionary/page/page_content_deleting_service.py,sha256=ZxGUMmT7k85IPLALfZkIJ5oQA3tMHWVW4acjdp5cB0k,4540
143
145
  notionary/page/page_content_writer.py,sha256=6WMDTbgjYRgJhmCrMnK4XokgVDhRWSRm55_1xceo0CA,6856
144
- notionary/page/page_context.py,sha256=qpy9J5zDdE_v7kHEE4SA14NQJP9Dj8j5gfkqccz23dU,1982
146
+ notionary/page/page_context.py,sha256=fIeCF9gjpiAtRK0GZ-3CUZ5Ina8xIVCvmOnt4MtS3ek,1938
145
147
  notionary/page/property_formatter.py,sha256=_978ViH83gfcr-XtDscWTfyBI2srGW2hzC-gzgp5NR8,3788
146
148
  notionary/page/reader/handler/__init__.py,sha256=ROMH1KvGGdQ0ANLhmIP_4LtMks9jsTTwnZKP8W0uwls,644
147
149
  notionary/page/reader/handler/base_block_renderer.py,sha256=HZwv727bvu6suT1-uzK4y-4ci54VCdi0FF_yOJYBB1g,1513
@@ -174,6 +176,8 @@ notionary/page/writer/markdown_to_notion_formatting_post_processor.py,sha256=E41
174
176
  notionary/page/writer/markdown_to_notion_post_processor.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
175
177
  notionary/page/writer/markdown_to_notion_text_length_post_processor.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
176
178
  notionary/page/writer/notion_text_length_processor.py,sha256=WfXGouHs2KUFH1ASqloRkV6y7q2jS4vqbiEEMrmd7Qw,5744
179
+ notionary/shared/__init__.py,sha256=7dKlDC1BpKy7LxceHTKVamLc9X0mc6stO5MOxBxidWY,87
180
+ notionary/shared/name_to_id_resolver.py,sha256=SlxB2vmq1cMIZnL0o3U27wjYQSXeQkKURmKFy-BAqk4,6318
177
181
  notionary/telemetry/__init__.py,sha256=uzPrvkPIpbzPSHCu-tObCTmZA18l3VWqk42L8WLT-XM,511
178
182
  notionary/telemetry/service.py,sha256=FujT1ZuSHzTO0Rxa6oKlycE_Y-Qpe5lc59EGzzDf-jc,4725
179
183
  notionary/telemetry/views.py,sha256=FgFZGYaxP7pRYx-9wg18skMh_MJAwf4W3rCfe9JOZe4,1796
@@ -194,7 +198,7 @@ notionary/util/page_id_utils.py,sha256=AA00kRO-g3Cc50tf_XW_tb5RBuPKLuBxRa0D8LYhL
194
198
  notionary/util/singleton.py,sha256=CKAvykndwPRZsA3n3MAY_XdCR59MBjjKP0vtm2BcvF0,428
195
199
  notionary/util/singleton_metaclass.py,sha256=DMvrh0IbAV8nIG1oX-2Yz57Uk1YHB647DNxoI3pAT3s,809
196
200
  notionary/workspace.py,sha256=QP4WcOIdQnlVr4M7hpGaFT-Fori_QRhsV-SBC2lnwTU,3809
197
- notionary-0.2.22.dist-info/LICENSE,sha256=zOm3cRT1qD49eg7vgw95MI79rpUAZa1kRBFwL2FkAr8,1120
198
- notionary-0.2.22.dist-info/METADATA,sha256=fCrsj7caTmiKxc9Vo-hLyF1yCfxPdX_puiJIzT7DNjU,9116
199
- notionary-0.2.22.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
200
- notionary-0.2.22.dist-info/RECORD,,
201
+ notionary-0.2.23.dist-info/LICENSE,sha256=zOm3cRT1qD49eg7vgw95MI79rpUAZa1kRBFwL2FkAr8,1120
202
+ notionary-0.2.23.dist-info/METADATA,sha256=asanJv6EUiN6zgUVVplKEM7MUrafPfkGdxV4EQNGQ-Y,9035
203
+ notionary-0.2.23.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
204
+ notionary-0.2.23.dist-info/RECORD,,