@danhachuel/thunderbolt 0.5.18 → 0.5.19
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.
|
@@ -99,6 +99,22 @@ def _first_text_element(value: Any, *, text_context: bool = False) -> str:
|
|
|
99
99
|
return ""
|
|
100
100
|
|
|
101
101
|
|
|
102
|
+
def _has_richtext(value: Any) -> bool:
|
|
103
|
+
"""Return whether a Canva payload contains at least one richtext item."""
|
|
104
|
+
if isinstance(value, Mapping):
|
|
105
|
+
for key, child in value.items():
|
|
106
|
+
if str(key).lower() in {"richtexts", "rich_texts", "text_elements", "textelements"}:
|
|
107
|
+
if isinstance(child, list) and any(isinstance(item, Mapping) for item in child):
|
|
108
|
+
return True
|
|
109
|
+
if isinstance(child, Mapping) and child:
|
|
110
|
+
return True
|
|
111
|
+
if _has_richtext(child):
|
|
112
|
+
return True
|
|
113
|
+
elif isinstance(value, list):
|
|
114
|
+
return any(_has_richtext(child) for child in value)
|
|
115
|
+
return False
|
|
116
|
+
|
|
117
|
+
|
|
102
118
|
def _first_page(value: Any) -> tuple[str, list[dict[str, Any]]]:
|
|
103
119
|
pages: list[dict[str, Any]] = []
|
|
104
120
|
if isinstance(value, Mapping) and isinstance(value.get("pages"), list):
|
|
@@ -132,17 +148,27 @@ def run_direct_canva_thumbnail(
|
|
|
132
148
|
candidates = _items(result)
|
|
133
149
|
if not candidates:
|
|
134
150
|
raise CanvaMCPError("Canva não encontrou designs para as palavras-chave da thumbnail.")
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if not design_id:
|
|
151
|
+
candidates = [candidate for candidate in candidates if _design_id(candidate)]
|
|
152
|
+
if not candidates:
|
|
138
153
|
raise CanvaMCPError("A pesquisa Canva devolveu um design sem ID.")
|
|
139
154
|
|
|
140
155
|
get_name = "get-design-content" if "get-design-content" in available else "get_design_content"
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
156
|
+
selected = None
|
|
157
|
+
content: Any = {}
|
|
158
|
+
for candidate in candidates:
|
|
159
|
+
candidate_id = _design_id(candidate)
|
|
160
|
+
candidate_content = _payload(client.call(get_name, {
|
|
161
|
+
"design_id": candidate_id,
|
|
162
|
+
"content_types": ["richtexts"],
|
|
163
|
+
"user_intent": "Inspect the selected Canva design before applying the thumbnail edit.",
|
|
164
|
+
}))
|
|
165
|
+
if _has_richtext(candidate_content):
|
|
166
|
+
selected = candidate
|
|
167
|
+
content = candidate_content
|
|
168
|
+
break
|
|
169
|
+
if selected is None:
|
|
170
|
+
raise CanvaMCPError("Os designs Canva encontrados não têm elementos de texto editáveis.")
|
|
171
|
+
design_id = _design_id(selected)
|
|
146
172
|
start_name = "start-editing-transaction" if "start-editing-transaction" in available else "start_editing_transaction"
|
|
147
173
|
started = _payload(client.call(start_name, {
|
|
148
174
|
"design_id": design_id,
|
|
@@ -154,6 +180,12 @@ def run_direct_canva_thumbnail(
|
|
|
154
180
|
page_id, pages = _first_page(started)
|
|
155
181
|
element_id = _first_text_element(started) or _first_text_element(content)
|
|
156
182
|
if not element_id:
|
|
183
|
+
cancel_name = "cancel-editing-transaction" if "cancel-editing-transaction" in available else "cancel_editing_transaction"
|
|
184
|
+
if cancel_name in available:
|
|
185
|
+
client.call(cancel_name, {
|
|
186
|
+
"transaction_id": transaction_id,
|
|
187
|
+
"user_intent": "Discard the Canva editing transaction because no editable text element was returned.",
|
|
188
|
+
})
|
|
157
189
|
raise CanvaMCPError("O design Canva não devolveu um elemento de texto editável.")
|
|
158
190
|
perform_name = "perform-editing-operations" if "perform-editing-operations" in available else "perform_editing_operations"
|
|
159
191
|
operations = [{"type": "replace_text", "element_id": element_id, "text": title[:255]}]
|
package/package.json
CHANGED