@danhachuel/thunderbolt 0.6.97 → 0.6.98

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.
@@ -186,11 +186,11 @@ def _render_instagram_posts(profile: Mapping[str, Any]) -> None:
186
186
  with st.expander("Últimos posts do Instagram", expanded=False):
187
187
  action_cols = st.columns(3)
188
188
  with action_cols[0]:
189
- load_clicked = st.button("Carregar últimos 10", key=f"load_instagram_posts_{profile_id}", use_container_width=True)
189
+ load_clicked = st.button("Carregar últimos 10", key=f"load_instagram_posts_{profile_id}", width="stretch")
190
190
  with action_cols[1]:
191
- refresh_clicked = st.button("Actualizar tudo", key=f"refresh_instagram_posts_{profile_id}", use_container_width=True)
191
+ refresh_clicked = st.button("Actualizar tudo", key=f"refresh_instagram_posts_{profile_id}", width="stretch")
192
192
  with action_cols[2]:
193
- download_clicked = bool(posts) and st.download_button("Baixar todos", data=_instagram_posts_archive(posts), file_name=f"instagram-{profile_id}-posts.zip", mime="application/zip", key=f"download_instagram_posts_{profile_id}", use_container_width=True)
193
+ download_clicked = bool(posts) and st.download_button("Baixar todos", data=_instagram_posts_archive(posts), file_name=f"instagram-{profile_id}-posts.zip", mime="application/zip", key=f"download_instagram_posts_{profile_id}", width="stretch")
194
194
  if load_clicked or refresh_clicked:
195
195
  target = max(10, len(posts)) if refresh_clicked else 10
196
196
  posts_ok, posts_message, loaded_posts = _load_instagram_posts(profile, limit=target)
@@ -212,13 +212,13 @@ def _render_instagram_posts(profile: Mapping[str, Any]) -> None:
212
212
  post_cols = st.columns([1, 3])
213
213
  with post_cols[0]:
214
214
  if _clean(post.get("image_url")):
215
- st.image(post["image_url"], use_container_width=True)
215
+ st.image(post["image_url"], width="stretch")
216
216
  with post_cols[1]:
217
217
  if _clean(post.get("url")):
218
218
  st.link_button("Abrir post", post["url"])
219
219
  if _clean(post.get("caption")):
220
220
  st.caption(post["caption"])
221
- if st.button("Mostrar + 10", key=f"more_instagram_posts_{profile_id}", use_container_width=True):
221
+ if st.button("Mostrar + 10", key=f"more_instagram_posts_{profile_id}", width="stretch"):
222
222
  posts_ok, posts_message, loaded_posts = _load_instagram_posts(profile, limit=len(posts) + 10)
223
223
  if posts_ok:
224
224
  _persist_instagram_posts(profile, loaded_posts)
@@ -314,11 +314,11 @@ def render_meta_api_cards(settings: dict[str, Any], kind: str) -> None:
314
314
  token = st.text_input("Access Token", value=_clean(card.get("access_token")), type="password", key=f"{kind}_api_{card_id}_token")
315
315
  action_cols = st.columns(3)
316
316
  with action_cols[0]:
317
- test_clicked = st.form_submit_button("Teste chamada API", use_container_width=True)
317
+ test_clicked = st.form_submit_button("Teste chamada API", width="stretch")
318
318
  with action_cols[1]:
319
- save_clicked = st.form_submit_button("Guardar Card", type="primary", use_container_width=True)
319
+ save_clicked = st.form_submit_button("Guardar Card", type="primary", width="stretch")
320
320
  with action_cols[2]:
321
- delete_clicked = st.form_submit_button("Apagar Card", use_container_width=True)
321
+ delete_clicked = st.form_submit_button("Apagar Card", width="stretch")
322
322
  edited = {
323
323
  "id": card_id,
324
324
  "label": label.strip(),
@@ -345,7 +345,7 @@ def render_meta_api_cards(settings: dict[str, Any], kind: str) -> None:
345
345
  _persist_api_cards(settings, kind, cards)
346
346
  st.success(f"{title} apagada.")
347
347
  st.rerun()
348
- if st.button("Adicionar nova API", type="primary", use_container_width=True, key=f"add_{kind}_api_card"):
348
+ if st.button("Adicionar nova API", type="primary", width="stretch", key=f"add_{kind}_api_card"):
349
349
  identifier_key = "account_id" if is_instagram else "page_id"
350
350
  cards.append({"id": f"{kind}-api-{uuid.uuid4().hex[:10]}", "label": f"Conta {len(cards) + 1}", identifier_key: "", "access_token": ""})
351
351
  _persist_api_cards(settings, kind, cards)
@@ -498,29 +498,29 @@ def _render_instagram_card(profile: dict[str, Any], characters: list[dict[str, A
498
498
  st.rerun()
499
499
  st.warning(refreshed_message)
500
500
  with edit_col:
501
- if st.button("Editar", key=f"edit_instagram_button_{profile_id}", use_container_width=True):
501
+ if st.button("Editar", key=f"edit_instagram_button_{profile_id}", width="stretch"):
502
502
  for field in ("name", "handle", "bio", "country", "language", "posts", "followers", "following"):
503
503
  st.session_state.pop(f"instagram_edit_{field}_{profile_id}", None)
504
504
  st.session_state[edit_key] = True
505
505
  st.rerun()
506
- if st.button("Apagar", key=f"delete_instagram_button_{profile_id}", use_container_width=True):
506
+ if st.button("Apagar", key=f"delete_instagram_button_{profile_id}", width="stretch"):
507
507
  st.session_state[f"confirm_delete_instagram_{profile_id}"] = True
508
508
  st.rerun()
509
509
  url = _clean(profile.get("url"))
510
510
  if url:
511
- st.link_button("Abrir Instagram", url, use_container_width=True)
511
+ st.link_button("Abrir Instagram", url, width="stretch")
512
512
 
513
513
  if st.session_state.get(f"confirm_delete_instagram_{profile_id}"):
514
514
  st.warning("Apagar este card remove o cadastro da conta Instagram, mas não os ficheiros associados.")
515
515
  confirm_cols = st.columns(2)
516
516
  with confirm_cols[0]:
517
- if st.button("Confirmar apagar", type="primary", key=f"confirm_delete_instagram_button_{profile_id}", use_container_width=True):
517
+ if st.button("Confirmar apagar", type="primary", key=f"confirm_delete_instagram_button_{profile_id}", width="stretch"):
518
518
  delete_channel(profile_id)
519
519
  st.session_state.pop(f"confirm_delete_instagram_{profile_id}", None)
520
520
  st.success("Conta Instagram apagada.")
521
521
  st.rerun()
522
522
  with confirm_cols[1]:
523
- if st.button("Cancelar", key=f"cancel_delete_instagram_button_{profile_id}", use_container_width=True):
523
+ if st.button("Cancelar", key=f"cancel_delete_instagram_button_{profile_id}", width="stretch"):
524
524
  st.session_state.pop(f"confirm_delete_instagram_{profile_id}", None)
525
525
  st.rerun()
526
526
 
@@ -537,7 +537,7 @@ def _render_instagram_card(profile: dict[str, Any], characters: list[dict[str, A
537
537
  posts = st.number_input("posts", min_value=0, value=_metric_input(_profile_metric(profile, "post_count", "posts", "media_count")), placeholder="Não encontrado", key=f"instagram_edit_posts_{profile_id}")
538
538
  followers = st.number_input("Seguidores", min_value=0, value=_metric_input(_profile_metric(profile, "subscriber_count", "followers_count", "follower_count", "followers", "edge_followed_by")), placeholder="Não encontrado", key=f"instagram_edit_followers_{profile_id}")
539
539
  following = st.number_input("seguindo", min_value=0, value=_metric_input(_profile_metric(profile, "following_count", "following", "follows", "followingCount", "edge_follow")), placeholder="Não encontrado", key=f"instagram_edit_following_{profile_id}")
540
- save_edit = st.form_submit_button("Guardar alterações", type="primary", use_container_width=True)
540
+ save_edit = st.form_submit_button("Guardar alterações", type="primary", width="stretch")
541
541
  if save_edit:
542
542
  update_channel(profile_id, {"name": name.strip(), "handle": handle.strip(), "bio": bio, "bio_raw": bio, "country": _normalise_country(country), "language": language, "post_count": _metric_input(posts), "subscriber_count": _metric_input(followers), "following_count": _metric_input(following)})
543
543
  st.session_state.pop(edit_key, None)
@@ -558,7 +558,7 @@ def _render_instagram_card(profile: dict[str, Any], characters: list[dict[str, A
558
558
  character_labels = {"": "Sem personagem associado"} | {_clean(item.get("id")): _clean(item.get("name")) or _clean(item.get("id")) for item in characters}
559
559
  current_character = _clean(profile.get("character_id"))
560
560
  selected_character = st.selectbox("Personagem", character_options, index=character_options.index(current_character) if current_character in character_options else 0, format_func=lambda value: character_labels.get(value, value), key=f"instagram_character_{profile_id}")
561
- if st.button("Atrelar Personagem", key=f"attach_instagram_character_{profile_id}", use_container_width=True):
561
+ if st.button("Atrelar Personagem", key=f"attach_instagram_character_{profile_id}", width="stretch"):
562
562
  update_channel(profile_id, {"character_id": selected_character})
563
563
  st.success("Personagem associado à conta Instagram.")
564
564
  st.rerun()
@@ -574,9 +574,9 @@ def render_social_networks(settings: dict[str, Any]) -> None:
574
574
  source = st.text_input("URL pública ou @handler Instagram", placeholder="https://www.instagram.com/conta/ ou @conta", key="social_instagram_source")
575
575
  search_cols = st.columns(2)
576
576
  with search_cols[0]:
577
- search_clicked = st.button("Pesquisar conta pública", type="primary", use_container_width=True, key="social_instagram_search")
577
+ search_clicked = st.button("Pesquisar conta pública", type="primary", width="stretch", key="social_instagram_search")
578
578
  with search_cols[1]:
579
- if st.button("Limpar pesquisa", use_container_width=True, key="social_instagram_clear"):
579
+ if st.button("Limpar pesquisa", width="stretch", key="social_instagram_clear"):
580
580
  for key in ("social_instagram_result", "social_instagram_ok", "social_instagram_message"):
581
581
  st.session_state.pop(key, None)
582
582
  st.rerun()
@@ -622,7 +622,7 @@ def render_social_networks(settings: dict[str, Any]) -> None:
622
622
  character_options = [""] + [_clean(item.get("id")) for item in characters if _clean(item.get("id"))]
623
623
  character_labels = {"": "Sem personagem associado"} | {_clean(item.get("id")): _clean(item.get("name")) or _clean(item.get("id")) for item in characters}
624
624
  selected_character = st.selectbox("Personagem", character_options, format_func=lambda value: character_labels.get(value, value), key=f"social_instagram_result_character_{result_widget_id}")
625
- save_profile = st.form_submit_button("Cadastrar conta Instagram", type="primary", use_container_width=True)
625
+ save_profile = st.form_submit_button("Cadastrar conta Instagram", type="primary", width="stretch")
626
626
  if save_profile:
627
627
  profile_data = dict(data)
628
628
  profile_data.update({"name": name, "bio": bio, "bio_raw": bio, "post_count": _metric_input(posts), "subscriber_count": _metric_input(followers), "following_count": _metric_input(following)})
@@ -142,7 +142,7 @@ def render_growth_bilibili() -> None:
142
142
  )
143
143
  with status_col:
144
144
  st.write("")
145
- st.button("ANALISAR CANAL", disabled=True, use_container_width=True, key="growth_bilibili_analyse_disabled")
145
+ st.button("ANALISAR CANAL", disabled=True, width="stretch", key="growth_bilibili_analyse_disabled")
146
146
  st.caption("A ligação ao Studio será usada quando autorizada.")
147
147
 
148
148
  st.subheader("Dashboard de Growth")
@@ -131,7 +131,7 @@ def render_growth_facebook_pages() -> None:
131
131
  selected = st.selectbox("Página a analisar", pages, format_func=lambda item: str(item.get("name") or item.get("page_name") or "Página sem nome"), key="growth_facebook_page")
132
132
  with action_col:
133
133
  st.write("")
134
- analyse_clicked = st.button("ANALISAR PÁGINA", type="primary", use_container_width=True, key="growth_facebook_analyse")
134
+ analyse_clicked = st.button("ANALISAR PÁGINA", type="primary", width="stretch", key="growth_facebook_analyse")
135
135
  st.markdown("**Estado das APIs de Growth**")
136
136
  api_cols = st.columns(2, gap="small")
137
137
  with api_cols[0]:
@@ -161,9 +161,9 @@ def render_growth_facebook_pages() -> None:
161
161
  st.write("")
162
162
  path = Path(str(selected_record.get("report_path") or "")) if selected_record else Path()
163
163
  if path.is_file():
164
- st.download_button("BAIXAR ANALISE COMPLETA", path.read_bytes(), file_name=path.name, mime="text/markdown", use_container_width=True, key=f"facebook_download_{selected_record['code']}")
164
+ st.download_button("BAIXAR ANALISE COMPLETA", path.read_bytes(), file_name=path.name, mime="text/markdown", width="stretch", key=f"facebook_download_{selected_record['code']}")
165
165
  else:
166
- st.button("BAIXAR ANALISE COMPLETA", disabled=True, use_container_width=True, key="facebook_download_disabled")
166
+ st.button("BAIXAR ANALISE COMPLETA", disabled=True, width="stretch", key="facebook_download_disabled")
167
167
  st.subheader("Dashboard de Growth")
168
168
  st.caption("3 pilares críticos em destaque e camadas operacionais abaixo. Vermelho: 0–30 · Amarelo: 31–69 · Verde: 70–100.")
169
169
  def item(label: str, target: str, available: bool = False) -> tuple[str, str, str, str, str]:
@@ -188,7 +188,7 @@ def render_growth_instagram() -> None:
188
188
  selected = st.selectbox("Conta a analisar", channels, format_func=lambda item: str(item.get("name") or item.get("handle") or "Conta sem nome"), key="growth_instagram_channel")
189
189
  with action_col:
190
190
  st.write("")
191
- analyse_clicked = st.button("ANALISAR CONTA", type="primary", use_container_width=True, key="growth_instagram_analyse")
191
+ analyse_clicked = st.button("ANALISAR CONTA", type="primary", width="stretch", key="growth_instagram_analyse")
192
192
  st.markdown("**Estado das APIs de Growth**")
193
193
  api_cols = st.columns(2, gap="small")
194
194
  with api_cols[0]:
@@ -219,9 +219,9 @@ def render_growth_instagram() -> None:
219
219
  st.write("")
220
220
  report_path = Path(str(selected_record.get("report_path") or "")) if selected_record else Path()
221
221
  if report_path.is_file():
222
- st.download_button("BAIXAR ANALISE COMPLETA", report_path.read_bytes(), file_name=report_path.name, mime="text/markdown", use_container_width=True, key=f"instagram_download_{selected_record['code']}")
222
+ st.download_button("BAIXAR ANALISE COMPLETA", report_path.read_bytes(), file_name=report_path.name, mime="text/markdown", width="stretch", key=f"instagram_download_{selected_record['code']}")
223
223
  else:
224
- st.button("BAIXAR ANALISE COMPLETA", disabled=True, use_container_width=True, key="instagram_download_disabled")
224
+ st.button("BAIXAR ANALISE COMPLETA", disabled=True, width="stretch", key="instagram_download_disabled")
225
225
  st.subheader("Dashboard de Growth")
226
226
  st.caption("3 pilares críticos em destaque e camadas operacionais abaixo. Vermelho: 0–30 · Amarelo: 31–69 · Verde: 70–100.")
227
227
  def item(label: str, target: str, available: bool = False) -> tuple[str, str, str, str, str]:
@@ -185,7 +185,7 @@ def render_growth_tiktok() -> None:
185
185
  selected = st.selectbox("Conta a analisar", channels, format_func=lambda item: str(item.get("name") or item.get("handle") or "Conta sem nome"), key="growth_tiktok_channel")
186
186
  with action_col:
187
187
  st.write("")
188
- analyse_clicked = st.button("ANALISAR CONTA", type="primary", use_container_width=True, key="growth_tiktok_analyse")
188
+ analyse_clicked = st.button("ANALISAR CONTA", type="primary", width="stretch", key="growth_tiktok_analyse")
189
189
  st.markdown("**Estado das APIs de Growth**")
190
190
  api_cols = st.columns(2, gap="small")
191
191
  with api_cols[0]:
@@ -216,9 +216,9 @@ def render_growth_tiktok() -> None:
216
216
  st.write("")
217
217
  report_path = Path(str(selected_record.get("report_path") or "")) if selected_record else Path()
218
218
  if report_path.is_file():
219
- st.download_button("BAIXAR ANALISE COMPLETA", report_path.read_bytes(), file_name=report_path.name, mime="text/markdown", use_container_width=True, key=f"tiktok_download_{selected_record['code']}")
219
+ st.download_button("BAIXAR ANALISE COMPLETA", report_path.read_bytes(), file_name=report_path.name, mime="text/markdown", width="stretch", key=f"tiktok_download_{selected_record['code']}")
220
220
  else:
221
- st.button("BAIXAR ANALISE COMPLETA", disabled=True, use_container_width=True, key="tiktok_download_disabled")
221
+ st.button("BAIXAR ANALISE COMPLETA", disabled=True, width="stretch", key="tiktok_download_disabled")
222
222
  st.subheader("Dashboard de Growth")
223
223
  st.caption("3 pilares críticos em destaque e camadas operacionais abaixo. Vermelho: 0–30 · Amarelo: 31–69 · Verde: 70–100.")
224
224
  def item(label: str, target: str, available: bool = False) -> tuple[str, str, str, str, str]:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.6.97",
3
+ "version": "0.6.98",
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",