OVAPortableText 0.1.0__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.
- ova_portable_text/__init__.py +93 -0
- ova_portable_text/base.py +165 -0
- ova_portable_text/block_objects.py +139 -0
- ova_portable_text/content.py +74 -0
- ova_portable_text/document.py +303 -0
- ova_portable_text/exceptions.py +234 -0
- ova_portable_text/helpers.py +203 -0
- ova_portable_text/inline.py +87 -0
- ova_portable_text/numbering.py +227 -0
- ova_portable_text/py.typed +0 -0
- ova_portable_text/registry.py +555 -0
- ova_portable_text/resolver.py +340 -0
- ova_portable_text/section.py +412 -0
- ova_portable_text/text.py +426 -0
- ova_portable_text/theme.py +46 -0
- ova_portable_text/validator.py +511 -0
- ova_portable_text/version.py +9 -0
- ovaportabletext-0.1.0.dist-info/METADATA +241 -0
- ovaportabletext-0.1.0.dist-info/RECORD +21 -0
- ovaportabletext-0.1.0.dist-info/WHEEL +4 -0
- ovaportabletext-0.1.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Public package exports for OVAPortableText.
|
|
3
|
+
OVAPortableText 的公开导出入口。
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .base import OvaBaseModel
|
|
7
|
+
from .block_objects import BlockObject, CalloutBlock, ChartBlock, ImageBlock, MathBlock, TableBlock
|
|
8
|
+
from .content import (
|
|
9
|
+
ALLOWED_DECORATOR_MARKS,
|
|
10
|
+
ALLOWED_TEXT_STYLES,
|
|
11
|
+
AnnotationMarkDef,
|
|
12
|
+
BlockElement,
|
|
13
|
+
ContentItem,
|
|
14
|
+
DecoratorMark,
|
|
15
|
+
LinkMarkDef,
|
|
16
|
+
ListItemStyle,
|
|
17
|
+
MarkDef,
|
|
18
|
+
Span,
|
|
19
|
+
TextBlock,
|
|
20
|
+
TextStyle,
|
|
21
|
+
)
|
|
22
|
+
from .document import Document, DocumentMeta
|
|
23
|
+
from .exceptions import DocumentValidationError, ValidationIssue, ValidationReport
|
|
24
|
+
from .helpers import (
|
|
25
|
+
annotation_def,
|
|
26
|
+
attachment_asset,
|
|
27
|
+
background_asset,
|
|
28
|
+
bibliography_entry,
|
|
29
|
+
blocks_from_items,
|
|
30
|
+
bullet_item,
|
|
31
|
+
callout,
|
|
32
|
+
chart_block,
|
|
33
|
+
citation_ref,
|
|
34
|
+
code_span,
|
|
35
|
+
create_document,
|
|
36
|
+
document,
|
|
37
|
+
em,
|
|
38
|
+
footnote_entry,
|
|
39
|
+
footnote_ref,
|
|
40
|
+
glossary_entry,
|
|
41
|
+
glossary_term,
|
|
42
|
+
hard_break,
|
|
43
|
+
icon_asset,
|
|
44
|
+
image_asset,
|
|
45
|
+
image_block,
|
|
46
|
+
link_def,
|
|
47
|
+
logo_asset,
|
|
48
|
+
marked,
|
|
49
|
+
math_block,
|
|
50
|
+
metric_dataset,
|
|
51
|
+
metric_value,
|
|
52
|
+
number_item,
|
|
53
|
+
paragraph,
|
|
54
|
+
pie_chart_dataset,
|
|
55
|
+
pie_chart_from_parallel_arrays,
|
|
56
|
+
pie_slice,
|
|
57
|
+
section,
|
|
58
|
+
span,
|
|
59
|
+
strong,
|
|
60
|
+
table_block,
|
|
61
|
+
table_column,
|
|
62
|
+
table_dataset,
|
|
63
|
+
underline,
|
|
64
|
+
xref,
|
|
65
|
+
)
|
|
66
|
+
from .inline import CitationRef, FootnoteRef, GlossaryTerm, HardBreak, InlineObject, XRef
|
|
67
|
+
from .numbering import DocumentNumbering, NumberingConfig, NumberedTarget
|
|
68
|
+
from .registry import (
|
|
69
|
+
AssetsRegistry,
|
|
70
|
+
AttachmentAsset,
|
|
71
|
+
BackgroundAsset,
|
|
72
|
+
BibliographyEntry,
|
|
73
|
+
DatasetsRegistry,
|
|
74
|
+
FootnoteEntry,
|
|
75
|
+
GlossaryEntry,
|
|
76
|
+
IconAsset,
|
|
77
|
+
ImageAsset,
|
|
78
|
+
LogoAsset,
|
|
79
|
+
MetricDataset,
|
|
80
|
+
MetricValue,
|
|
81
|
+
PieChartDataset,
|
|
82
|
+
PieSlice,
|
|
83
|
+
RegistryEntryBase,
|
|
84
|
+
TableColumn,
|
|
85
|
+
TableDataset,
|
|
86
|
+
)
|
|
87
|
+
from .resolver import DocumentResolver, ResolvedTarget
|
|
88
|
+
from .section import Section
|
|
89
|
+
from .theme import ThemeConfig
|
|
90
|
+
from .validator import assert_valid_document, validate_document
|
|
91
|
+
from .version import __version__
|
|
92
|
+
|
|
93
|
+
__all__ = [name for name in globals() if not name.startswith("_")]
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Base model infrastructure for OVAPortableText.
|
|
5
|
+
OVAPortableText 的基础模型设施。
|
|
6
|
+
|
|
7
|
+
This module contains the common base class used by almost all public models.
|
|
8
|
+
本模块包含几乎所有公开模型都会继承的公共基类。
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any, TypeVar
|
|
13
|
+
|
|
14
|
+
from pydantic import BaseModel, ConfigDict
|
|
15
|
+
|
|
16
|
+
ModelT = TypeVar("ModelT", bound="OvaBaseModel")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class OvaBaseModel(BaseModel):
|
|
20
|
+
"""
|
|
21
|
+
Common base model for all OVAPortableText models.
|
|
22
|
+
OVAPortableText 所有模型共用的基础类。
|
|
23
|
+
|
|
24
|
+
Design goals / 设计目标:
|
|
25
|
+
1. Forbid unexpected fields by default, so protocol mistakes are caught early.
|
|
26
|
+
默认禁止未声明字段,尽早暴露协议拼写或结构错误。
|
|
27
|
+
2. Allow population by alias, because Portable Text uses `_type`.
|
|
28
|
+
允许通过别名赋值,因为 Portable Text 使用 `_type` 这类字段。
|
|
29
|
+
3. Provide convenient export helpers for dict / JSON output.
|
|
30
|
+
提供统一的 dict / JSON 导出辅助方法。
|
|
31
|
+
4. Provide round-trip helpers used by this package's document-builder workflow.
|
|
32
|
+
提供 round-trip 辅助方法,便于本包的“生成 → 导出 → 读回”工作流。
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
36
|
+
|
|
37
|
+
def to_dict(self, *, exclude_none: bool = True) -> dict[str, Any]:
|
|
38
|
+
"""
|
|
39
|
+
Export the model as a plain Python dictionary.
|
|
40
|
+
将模型导出为普通 Python 字典。
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
exclude_none:
|
|
44
|
+
Whether to omit fields whose value is None.
|
|
45
|
+
是否忽略值为 None 的字段。
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
A JSON-serializable dictionary using protocol aliases.
|
|
49
|
+
一个可直接 JSON 序列化、并使用协议别名字段的字典。
|
|
50
|
+
"""
|
|
51
|
+
return self.model_dump(by_alias=True, exclude_none=exclude_none)
|
|
52
|
+
|
|
53
|
+
def to_json(
|
|
54
|
+
self,
|
|
55
|
+
*,
|
|
56
|
+
indent: int | None = 2,
|
|
57
|
+
exclude_none: bool = True,
|
|
58
|
+
) -> str:
|
|
59
|
+
"""
|
|
60
|
+
Export the model as a JSON string.
|
|
61
|
+
将模型导出为 JSON 字符串。
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
indent:
|
|
65
|
+
JSON indentation width.
|
|
66
|
+
JSON 缩进宽度。
|
|
67
|
+
exclude_none:
|
|
68
|
+
Whether to omit fields whose value is None.
|
|
69
|
+
是否忽略值为 None 的字段。
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
A JSON string using protocol aliases.
|
|
73
|
+
一个使用协议别名字段的 JSON 字符串。
|
|
74
|
+
"""
|
|
75
|
+
return self.model_dump_json(by_alias=True, exclude_none=exclude_none, indent=indent)
|
|
76
|
+
|
|
77
|
+
def save_json(
|
|
78
|
+
self,
|
|
79
|
+
path: str | Path,
|
|
80
|
+
*,
|
|
81
|
+
indent: int | None = 2,
|
|
82
|
+
exclude_none: bool = True,
|
|
83
|
+
encoding: str = "utf-8",
|
|
84
|
+
) -> Path:
|
|
85
|
+
"""
|
|
86
|
+
Save the current model as a JSON file.
|
|
87
|
+
将当前模型保存为 JSON 文件。
|
|
88
|
+
|
|
89
|
+
Why add file I/O at the base-model layer?
|
|
90
|
+
为什么把文件读写能力放在基础模型层?
|
|
91
|
+
Because package users often need a very practical workflow:
|
|
92
|
+
因为本包用户经常需要一个非常实用的工作流:
|
|
93
|
+
- build the document in Python / 在 Python 中构建文档
|
|
94
|
+
- save the JSON to disk / 把 JSON 落盘
|
|
95
|
+
- hand the file to Java or inspect it manually / 交给 Java 或手工检查
|
|
96
|
+
- reload it later for debugging or regression tests / 后续再读回做调试或回归测试
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
path:
|
|
100
|
+
Output file path.
|
|
101
|
+
输出文件路径。
|
|
102
|
+
indent:
|
|
103
|
+
JSON indentation width.
|
|
104
|
+
JSON 缩进宽度。
|
|
105
|
+
exclude_none:
|
|
106
|
+
Whether to omit fields whose value is None.
|
|
107
|
+
是否忽略值为 None 的字段。
|
|
108
|
+
encoding:
|
|
109
|
+
Text encoding used when writing the file.
|
|
110
|
+
写文件时使用的文本编码。
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
The normalized ``Path`` object that was written.
|
|
114
|
+
实际写入的标准化 ``Path`` 对象。
|
|
115
|
+
"""
|
|
116
|
+
target = Path(path)
|
|
117
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
118
|
+
target.write_text(
|
|
119
|
+
self.to_json(indent=indent, exclude_none=exclude_none),
|
|
120
|
+
encoding=encoding,
|
|
121
|
+
)
|
|
122
|
+
return target
|
|
123
|
+
|
|
124
|
+
@classmethod
|
|
125
|
+
def load_json(
|
|
126
|
+
cls: type[ModelT],
|
|
127
|
+
path: str | Path,
|
|
128
|
+
*,
|
|
129
|
+
encoding: str = "utf-8",
|
|
130
|
+
) -> ModelT:
|
|
131
|
+
"""
|
|
132
|
+
Read a JSON file from disk and rebuild the model.
|
|
133
|
+
从磁盘读取 JSON 文件并重建模型。
|
|
134
|
+
|
|
135
|
+
This is the file-based counterpart of ``from_json``.
|
|
136
|
+
这是 ``from_json`` 的文件版对应方法。
|
|
137
|
+
"""
|
|
138
|
+
source = Path(path)
|
|
139
|
+
return cls.from_json(source.read_text(encoding=encoding))
|
|
140
|
+
|
|
141
|
+
@classmethod
|
|
142
|
+
def from_dict(cls: type[ModelT], data: dict[str, Any]) -> ModelT:
|
|
143
|
+
"""
|
|
144
|
+
Rebuild a model object from a Python dictionary.
|
|
145
|
+
从 Python 字典重建模型对象。
|
|
146
|
+
|
|
147
|
+
Why provide this helper when Pydantic already has `model_validate`?
|
|
148
|
+
为什么在已有 `model_validate` 的情况下还要封装这个 helper?
|
|
149
|
+
Because package users often think in terms of:
|
|
150
|
+
因为本包用户更容易按下面的心智理解:
|
|
151
|
+
- `to_dict()` / 导出 dict
|
|
152
|
+
- `from_dict()` / 再从 dict 读回
|
|
153
|
+
|
|
154
|
+
This makes the round-trip API more discoverable.
|
|
155
|
+
这样 round-trip API 会更直观、更容易发现。
|
|
156
|
+
"""
|
|
157
|
+
return cls.model_validate(data)
|
|
158
|
+
|
|
159
|
+
@classmethod
|
|
160
|
+
def from_json(cls: type[ModelT], json_text: str | bytes | bytearray) -> ModelT:
|
|
161
|
+
"""
|
|
162
|
+
Rebuild a model object from a JSON string / bytes payload.
|
|
163
|
+
从 JSON 字符串 / 字节串重建模型对象。
|
|
164
|
+
"""
|
|
165
|
+
return cls.model_validate_json(json_text)
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Block-object models for OVAPortableText.
|
|
5
|
+
OVAPortableText 的块级对象模型。
|
|
6
|
+
|
|
7
|
+
In the protocol, these block objects can appear inside `content.blocks[]`
|
|
8
|
+
alongside normal text blocks in reading order.
|
|
9
|
+
根据协议,这些块级对象可以和普通文本块一起,按阅读顺序出现在 `content.blocks[]` 中。
|
|
10
|
+
|
|
11
|
+
This module covers the first batch of block objects:
|
|
12
|
+
本模块覆盖第一批块级对象:
|
|
13
|
+
- image
|
|
14
|
+
- chart
|
|
15
|
+
- table
|
|
16
|
+
- math_block
|
|
17
|
+
- callout
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from typing import Literal, TypeAlias
|
|
21
|
+
|
|
22
|
+
from pydantic import Field, model_validator
|
|
23
|
+
|
|
24
|
+
from .base import OvaBaseModel
|
|
25
|
+
from .text import TextBlock
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ReferenceableBlockBase(OvaBaseModel):
|
|
29
|
+
"""
|
|
30
|
+
Common base for block objects that may become cross-reference targets.
|
|
31
|
+
可作为交叉引用目标的块级对象公共基类。
|
|
32
|
+
|
|
33
|
+
Most visual objects in the body should have:
|
|
34
|
+
正文中的大多数可视对象都应该具备:
|
|
35
|
+
- `id`: instance identity in the body / 正文实例身份
|
|
36
|
+
- `anchor`: anchor used by renderer / 渲染侧锚点
|
|
37
|
+
|
|
38
|
+
Design note / 设计说明:
|
|
39
|
+
`id` here belongs to the body instance, not the underlying registry entry.
|
|
40
|
+
这里的 `id` 属于正文实例本身,不是底层 registry 条目 ID。
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
id: str
|
|
44
|
+
anchor: str | None = None
|
|
45
|
+
|
|
46
|
+
@model_validator(mode="after")
|
|
47
|
+
def set_default_anchor(self) -> "ReferenceableBlockBase":
|
|
48
|
+
"""
|
|
49
|
+
Reuse `id` as default anchor when anchor is omitted.
|
|
50
|
+
当未提供 anchor 时,默认复用 `id`。
|
|
51
|
+
"""
|
|
52
|
+
if self.anchor is None:
|
|
53
|
+
self.anchor = self.id
|
|
54
|
+
return self
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class ImageBlock(ReferenceableBlockBase):
|
|
58
|
+
"""
|
|
59
|
+
Body image instance.
|
|
60
|
+
正文中的图片实例。
|
|
61
|
+
|
|
62
|
+
`imageRef` points to an entry in `assets.images`.
|
|
63
|
+
`imageRef` 指向 `assets.images` 中的图片资源条目。
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
type_: Literal["image"] = Field(default="image", alias="_type", serialization_alias="_type")
|
|
67
|
+
imageRef: str
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class ChartBlock(ReferenceableBlockBase):
|
|
71
|
+
"""
|
|
72
|
+
Body chart instance.
|
|
73
|
+
正文中的图表实例。
|
|
74
|
+
|
|
75
|
+
`chartRef` points to an entry in `datasets.charts`.
|
|
76
|
+
`chartRef` 指向 `datasets.charts` 中的图表数据条目。
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
type_: Literal["chart"] = Field(default="chart", alias="_type", serialization_alias="_type")
|
|
80
|
+
chartRef: str
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class TableBlock(ReferenceableBlockBase):
|
|
84
|
+
"""
|
|
85
|
+
Body table instance.
|
|
86
|
+
正文中的表格实例。
|
|
87
|
+
|
|
88
|
+
`tableRef` points to an entry in `datasets.tables`.
|
|
89
|
+
`tableRef` 指向 `datasets.tables` 中的表格数据条目。
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
type_: Literal["table"] = Field(default="table", alias="_type", serialization_alias="_type")
|
|
93
|
+
tableRef: str
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class MathBlock(ReferenceableBlockBase):
|
|
97
|
+
"""
|
|
98
|
+
Embedded LaTeX math block.
|
|
99
|
+
内嵌 LaTeX 公式块。
|
|
100
|
+
|
|
101
|
+
Current v1 decision / 当前 v1 决策:
|
|
102
|
+
we directly embed `latex` inside the body object.
|
|
103
|
+
当前直接在正文对象里内嵌 `latex`。
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
type_: Literal["math_block"] = Field(default="math_block", alias="_type", serialization_alias="_type")
|
|
107
|
+
latex: str
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class CalloutBlock(ReferenceableBlockBase):
|
|
111
|
+
"""
|
|
112
|
+
Callout block containing its own text blocks.
|
|
113
|
+
带自有文本块数组的 callout 块。
|
|
114
|
+
|
|
115
|
+
Boundary / 边界:
|
|
116
|
+
`callout.blocks` reuses a restricted subset of the normal text-block rules.
|
|
117
|
+
`callout.blocks` 复用正文文本块规则的受限子集。
|
|
118
|
+
|
|
119
|
+
In this implementation we only allow `TextBlock` inside callout for clarity.
|
|
120
|
+
当前实现为了保持清晰,只允许 `TextBlock` 作为 callout 内部的 block。
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
type_: Literal["callout"] = Field(default="callout", alias="_type", serialization_alias="_type")
|
|
124
|
+
blocks: list[TextBlock] = Field(default_factory=list)
|
|
125
|
+
|
|
126
|
+
def append_block(self, block: TextBlock) -> "CalloutBlock":
|
|
127
|
+
"""
|
|
128
|
+
Append a text block into the callout.
|
|
129
|
+
向 callout 中追加一个文本块。
|
|
130
|
+
"""
|
|
131
|
+
self.blocks.append(block)
|
|
132
|
+
return self
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
BlockObject: TypeAlias = ImageBlock | ChartBlock | TableBlock | MathBlock | CalloutBlock
|
|
136
|
+
"""
|
|
137
|
+
Union of all currently supported block objects.
|
|
138
|
+
当前已支持的全部块级对象联合类型。
|
|
139
|
+
"""
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Body-content models for OVAPortableText.
|
|
5
|
+
OVAPortableText 的正文内容模型。
|
|
6
|
+
|
|
7
|
+
This module plays two roles:
|
|
8
|
+
本模块承担两个角色:
|
|
9
|
+
1. Define `ContentItem`, the body-item type for continuous content flow.
|
|
10
|
+
定义 `ContentItem`,即“连续内容流”对应的 body-item 类型。
|
|
11
|
+
2. Re-export common text-layer classes for user convenience.
|
|
12
|
+
为了方便使用者导入,顺手重新导出常见文本层类。
|
|
13
|
+
|
|
14
|
+
Why `ContentItem.blocks` is not text-only?
|
|
15
|
+
为什么 `ContentItem.blocks` 不再只允许文本块?
|
|
16
|
+
Because the protocol explicitly allows text blocks and object blocks to share
|
|
17
|
+
one reading order inside the same content flow.
|
|
18
|
+
因为协议明确允许文本块与块级对象共享同一内容流中的阅读顺序。
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from typing import Literal, TypeAlias
|
|
22
|
+
|
|
23
|
+
from pydantic import Field
|
|
24
|
+
|
|
25
|
+
from .base import OvaBaseModel
|
|
26
|
+
from .block_objects import CalloutBlock, ChartBlock, ImageBlock, MathBlock, TableBlock
|
|
27
|
+
from .text import (
|
|
28
|
+
ALLOWED_DECORATOR_MARKS,
|
|
29
|
+
ALLOWED_TEXT_STYLES,
|
|
30
|
+
AnnotationMarkDef,
|
|
31
|
+
DecoratorMark,
|
|
32
|
+
LinkMarkDef,
|
|
33
|
+
ListItemStyle,
|
|
34
|
+
MarkDef,
|
|
35
|
+
Span,
|
|
36
|
+
TextBlock,
|
|
37
|
+
TextChild,
|
|
38
|
+
TextStyle,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
BlockElement: TypeAlias = TextBlock | ImageBlock | ChartBlock | TableBlock | MathBlock | CalloutBlock
|
|
42
|
+
"""
|
|
43
|
+
Any block-level element allowed inside `content.blocks[]`.
|
|
44
|
+
`content.blocks[]` 中允许出现的任意块级元素类型。
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ContentItem(OvaBaseModel):
|
|
49
|
+
"""
|
|
50
|
+
One continuous content-flow body item.
|
|
51
|
+
一个连续内容流 body item。
|
|
52
|
+
|
|
53
|
+
A section body may contain multiple `ContentItem`s and `SubsectionItem`s.
|
|
54
|
+
一个 section 的 body 中可以混排多个 `ContentItem` 与 `SubsectionItem`。
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
itemType: Literal["content"] = "content"
|
|
58
|
+
blocks: list[BlockElement] = Field(default_factory=list)
|
|
59
|
+
|
|
60
|
+
def append_block(self, block: BlockElement) -> "ContentItem":
|
|
61
|
+
"""
|
|
62
|
+
Append one block-level element.
|
|
63
|
+
追加一个块级元素。
|
|
64
|
+
"""
|
|
65
|
+
self.blocks.append(block)
|
|
66
|
+
return self
|
|
67
|
+
|
|
68
|
+
def append_text_block(self, block: TextBlock) -> "ContentItem":
|
|
69
|
+
"""
|
|
70
|
+
Explicit helper for appending a text block.
|
|
71
|
+
针对文本块的显式追加辅助方法。
|
|
72
|
+
"""
|
|
73
|
+
self.blocks.append(block)
|
|
74
|
+
return self
|