@danhachuel/thunderbolt 0.5.19 → 0.5.20

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.
@@ -27,7 +27,7 @@ def _payload(result: Mapping[str, Any]) -> Any:
27
27
 
28
28
  def _items(value: Any) -> list[dict[str, Any]]:
29
29
  if isinstance(value, Mapping):
30
- for key in ("items", "designs", "results"):
30
+ for key in ("items", "designs", "results", "candidates"):
31
31
  if isinstance(value.get(key), list):
32
32
  return [dict(item) for item in value[key] if isinstance(item, Mapping)]
33
33
  for key in ("data", "result", "response"):
@@ -38,6 +38,25 @@ def _items(value: Any) -> list[dict[str, Any]]:
38
38
  return []
39
39
 
40
40
 
41
+ def _first_named_id(value: Any, keys: tuple[str, ...]) -> str:
42
+ """Find an ID by field name without treating unrelated nested IDs as matches."""
43
+ if isinstance(value, Mapping):
44
+ for key in keys:
45
+ candidate = str(value.get(key) or "").strip()
46
+ if candidate:
47
+ return candidate
48
+ for child in value.values():
49
+ found = _first_named_id(child, keys)
50
+ if found:
51
+ return found
52
+ elif isinstance(value, list):
53
+ for child in value:
54
+ found = _first_named_id(child, keys)
55
+ if found:
56
+ return found
57
+ return ""
58
+
59
+
41
60
  def _design_id(value: Any) -> str:
42
61
  """Extract a Canva design identifier from REST/MCP result variants."""
43
62
  if isinstance(value, Mapping):
@@ -167,7 +186,41 @@ def run_direct_canva_thumbnail(
167
186
  content = candidate_content
168
187
  break
169
188
  if selected is None:
170
- raise CanvaMCPError("Os designs Canva encontrados não têm elementos de texto editáveis.")
189
+ generate_name = "generate-design" if "generate-design" in available else "generate_design"
190
+ create_name = "create-design-from-candidate" if "create-design-from-candidate" in available else "create_design_from_candidate"
191
+ if generate_name not in available or create_name not in available:
192
+ raise CanvaMCPError("Os designs Canva encontrados não têm elementos de texto editáveis e esta sessão MCP não disponibiliza geração de design.")
193
+ blueprint_text = str(blueprint.get("content") or "")[:5000]
194
+ generated = _payload(client.call(generate_name, {
195
+ "design_type": "youtube_thumbnail",
196
+ "query": (
197
+ "Create one editable YouTube thumbnail in 16:9 format for this topic: "
198
+ f"{title}. Context: {topic}. Thumbnail prompt: {prompt[:1200]}. "
199
+ "Use clearly editable headline text boxes, a strong background, readable text "
200
+ "background shapes, relevant icons, depth/effects, and high-contrast lettering. "
201
+ "Follow these local Thumbnail Blueprint rules: " + blueprint_text
202
+ ),
203
+ "user_intent": "Create an editable YouTube thumbnail with text boxes and visual elements when searched designs are not editable.",
204
+ }))
205
+ job_id = _first_named_id(generated, ("job_id", "jobId"))
206
+ generated_candidates = _items(generated)
207
+ candidate_id = _first_named_id(generated_candidates[0] if generated_candidates else {}, ("candidate_id", "candidateId"))
208
+ if not job_id or not candidate_id:
209
+ raise CanvaMCPError("Canva não devolveu um candidato editável para a thumbnail.")
210
+ created = _payload(client.call(create_name, {
211
+ "job_id": job_id,
212
+ "candidate_id": candidate_id,
213
+ "user_intent": "Create the selected editable YouTube thumbnail candidate in the user's Canva account.",
214
+ }))
215
+ design_id = _design_id(created)
216
+ if not design_id:
217
+ raise CanvaMCPError("Canva não devolveu o ID do design gerado.")
218
+ selected = {"id": design_id}
219
+ content = _payload(client.call(get_name, {
220
+ "design_id": design_id,
221
+ "content_types": ["richtexts"],
222
+ "user_intent": "Inspect the generated editable thumbnail before applying the final lettering.",
223
+ }))
171
224
  design_id = _design_id(selected)
172
225
  start_name = "start-editing-transaction" if "start-editing-transaction" in available else "start_editing_transaction"
173
226
  started = _payload(client.call(start_name, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.5.19",
3
+ "version": "0.5.20",
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",