@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
- selected = next((candidate for candidate in candidates if _design_id(candidate)), candidates[0])
136
- design_id = _design_id(selected)
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
- content = _payload(client.call(get_name, {
142
- "design_id": design_id,
143
- "content_types": ["richtexts"],
144
- "user_intent": "Inspect the selected Canva design before applying the thumbnail edit.",
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.5.18",
3
+ "version": "0.5.19",
4
4
  "description": "Thunderbolt — interface local para operação de canais faceless e motor MoneyPrinterTurbo",
5
5
  "license": "MIT",
6
6
  "main": "scripts/cli.mjs",