@kaikybrofc/omnizap-system 2.2.10 → 2.3.1

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.
Files changed (123) hide show
  1. package/README.md +13 -13
  2. package/app/config/adminIdentity.js +1 -3
  3. package/app/connection/socketController.js +10 -20
  4. package/app/controllers/messageController.js +7 -28
  5. package/app/modules/aiModule/catCommand.js +29 -192
  6. package/app/modules/broadcastModule/noticeCommand.js +28 -97
  7. package/app/modules/gameModule/diceCommand.js +6 -32
  8. package/app/modules/playModule/playCommand.js +57 -258
  9. package/app/modules/quoteModule/quoteCommand.js +2 -4
  10. package/app/modules/rpgPokemonModule/rpgPokemonRepository.js +1 -13
  11. package/app/modules/statsModule/noMessageCommand.js +16 -84
  12. package/app/modules/statsModule/rankingCommand.js +5 -25
  13. package/app/modules/statsModule/rankingCommon.js +1 -9
  14. package/app/modules/stickerModule/convertToWebp.js +4 -27
  15. package/app/modules/stickerModule/stickerCommand.js +13 -24
  16. package/app/modules/stickerModule/stickerTextCommand.js +13 -25
  17. package/app/modules/stickerPackModule/autoPackCollectorService.js +16 -7
  18. package/app/modules/stickerPackModule/domainEventOutboxRepository.js +20 -36
  19. package/app/modules/stickerPackModule/domainEvents.js +2 -11
  20. package/app/modules/stickerPackModule/semanticReclassificationEngine.js +13 -50
  21. package/app/modules/stickerPackModule/semanticReclassificationEngine.test.js +2 -15
  22. package/app/modules/stickerPackModule/semanticThemeClusterService.js +14 -41
  23. package/app/modules/stickerPackModule/stickerAssetClassificationRepository.js +25 -95
  24. package/app/modules/stickerPackModule/stickerAssetRepository.js +12 -31
  25. package/app/modules/stickerPackModule/stickerAssetReprocessQueueRepository.js +13 -18
  26. package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +284 -709
  27. package/app/modules/stickerPackModule/stickerClassificationBackgroundRuntime.js +27 -106
  28. package/app/modules/stickerPackModule/stickerClassificationService.js +46 -77
  29. package/app/modules/stickerPackModule/stickerDedicatedTaskWorkerRuntime.js +13 -53
  30. package/app/modules/stickerPackModule/stickerDomainEventBus.js +10 -16
  31. package/app/modules/stickerPackModule/stickerDomainEventConsumerRuntime.js +40 -39
  32. package/app/modules/stickerPackModule/stickerMarketplaceDriftService.js +1 -4
  33. package/app/modules/stickerPackModule/stickerObjectStorageService.js +26 -26
  34. package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +32 -187
  35. package/app/modules/stickerPackModule/stickerPackInteractionEventRepository.js +6 -15
  36. package/app/modules/stickerPackModule/stickerPackItemRepository.js +6 -32
  37. package/app/modules/stickerPackModule/stickerPackMarketplaceService.js +12 -36
  38. package/app/modules/stickerPackModule/stickerPackMessageService.js +12 -40
  39. package/app/modules/stickerPackModule/stickerPackRepository.js +23 -66
  40. package/app/modules/stickerPackModule/stickerPackScoreSnapshotRepository.js +9 -21
  41. package/app/modules/stickerPackModule/stickerPackScoreSnapshotRuntime.js +10 -40
  42. package/app/modules/stickerPackModule/stickerPackService.js +50 -115
  43. package/app/modules/stickerPackModule/stickerPackServiceRuntime.js +2 -21
  44. package/app/modules/stickerPackModule/stickerPackUtils.js +13 -3
  45. package/app/modules/stickerPackModule/stickerStorageService.js +16 -65
  46. package/app/modules/stickerPackModule/stickerWorkerPipelineRuntime.js +4 -22
  47. package/app/modules/stickerPackModule/stickerWorkerTaskQueueRepository.js +14 -29
  48. package/app/modules/systemMetricsModule/pingCommand.js +9 -39
  49. package/app/modules/tiktokModule/tiktokCommand.js +17 -109
  50. package/app/modules/userModule/userCommand.js +2 -88
  51. package/app/observability/metrics.js +5 -16
  52. package/app/services/captchaService.js +1 -6
  53. package/app/services/dbWriteQueue.js +3 -18
  54. package/app/services/featureFlagService.js +2 -8
  55. package/app/services/newsBroadcastService.js +0 -1
  56. package/app/services/queueUtils.js +2 -4
  57. package/app/services/whatsappLoginLinkService.js +7 -9
  58. package/app/store/premiumUserStore.js +1 -2
  59. package/app/utils/antiLink/antiLinkModule.js +3 -233
  60. package/app/utils/logger/loggerModule.js +9 -34
  61. package/app/utils/systemMetrics/systemMetricsModule.js +1 -4
  62. package/database/index.js +1 -0
  63. package/database/init.js +1 -8
  64. package/database/migrations/20260228_0027_web_visit_event.sql +15 -0
  65. package/docker-compose.yml +27 -27
  66. package/docs/seo/omnizap-seo-playbook-br-2026-02-28.md +26 -0
  67. package/docs/seo/satellite-page-template.md +2 -0
  68. package/docs/seo/satellite-pages-phase1.json +40 -177
  69. package/eslint.config.js +2 -15
  70. package/index.js +8 -36
  71. package/ml/clip_classifier/README.md +4 -6
  72. package/observability/alert-rules.yml +12 -12
  73. package/observability/grafana/provisioning/dashboards/dashboards.yml +1 -1
  74. package/package.json +6 -3
  75. package/public/api-docs/index.html +220 -193
  76. package/public/bot-whatsapp-para-grupo/index.html +291 -261
  77. package/public/bot-whatsapp-sem-programar/index.html +291 -261
  78. package/public/comandos/index.html +421 -406
  79. package/public/como-automatizar-avisos-no-whatsapp/index.html +291 -261
  80. package/public/como-criar-comandos-whatsapp/index.html +291 -261
  81. package/public/como-evitar-spam-no-whatsapp/index.html +291 -261
  82. package/public/como-moderar-grupo-whatsapp/index.html +291 -261
  83. package/public/como-organizar-comunidade-whatsapp/index.html +291 -261
  84. package/public/css/github-project-panel.css +13 -8
  85. package/public/css/stickers-admin.css +25 -9
  86. package/public/css/styles.css +23 -16
  87. package/public/index.html +1106 -993
  88. package/public/js/apps/apiDocsApp.js +17 -167
  89. package/public/js/apps/createPackApp.js +69 -332
  90. package/public/js/apps/homeApp.js +274 -101
  91. package/public/js/apps/loginApp.js +3 -12
  92. package/public/js/apps/stickersAdminApp.js +190 -181
  93. package/public/js/apps/stickersApp.js +482 -1411
  94. package/public/js/apps/userApp.js +217 -1
  95. package/public/js/catalog.js +11 -74
  96. package/public/js/github-panel/components/ErrorState.js +1 -8
  97. package/public/js/github-panel/components/GithubProjectPanel.js +2 -9
  98. package/public/js/github-panel/components/SkeletonPanel.js +1 -11
  99. package/public/js/github-panel/components/StatCard.js +1 -7
  100. package/public/js/github-panel/vendor/react.js +1 -9
  101. package/public/js/runtime/react-runtime.js +1 -9
  102. package/public/licenca/index.html +200 -86
  103. package/public/login/index.html +315 -325
  104. package/public/melhor-bot-whatsapp-para-grupos/index.html +291 -261
  105. package/public/stickers/admin/index.html +14 -19
  106. package/public/stickers/create/index.html +39 -44
  107. package/public/stickers/index.html +96 -107
  108. package/public/termos-de-uso/index.html +369 -122
  109. package/public/user/index.html +527 -350
  110. package/scripts/cache-bust.mjs +5 -24
  111. package/scripts/generate-seo-satellite-pages.mjs +10 -13
  112. package/scripts/run-prettier-all.mjs +25 -0
  113. package/scripts/sticker-catalog-loadtest.mjs +13 -11
  114. package/scripts/sticker-worker-task.mjs +1 -4
  115. package/scripts/sync-readme-snapshot.mjs +3 -2
  116. package/server/auth/googleWebAuth/googleWebAuthService.js +614 -0
  117. package/server/controllers/stickerCatalogController.js +297 -632
  118. package/server/http/httpServer.js +2 -10
  119. package/server/routes/stickerCatalog/catalogHandlers/catalogAdminHttp.js +1 -8
  120. package/server/routes/stickerCatalog/catalogHandlers/catalogAuthHttp.js +1 -9
  121. package/server/routes/stickerCatalog/catalogHandlers/catalogPublicHttp.js +10 -11
  122. package/server/routes/stickerCatalog/catalogHandlers/catalogUploadHttp.js +1 -10
  123. package/server/routes/stickerCatalog/catalogRouter.js +11 -13
@@ -1,4 +1,4 @@
1
- /* global document, window, fetch, URLSearchParams */
1
+ /* global document, window, fetch, URL, URLSearchParams */
2
2
 
3
3
  const DEFAULT_API_BASE_PATH = '/api/sticker-packs';
4
4
  const DEFAULT_STICKERS_PATH = '/stickers';
@@ -31,6 +31,24 @@ if (root) {
31
31
  manageHeadLink: document.getElementById('user-manage-head-link'),
32
32
  manageMainLink: document.getElementById('user-manage-main-link'),
33
33
  currentYear: document.getElementById('user-current-year'),
34
+ adminPanel: document.getElementById('user-admin-panel'),
35
+ adminRole: document.getElementById('user-admin-role'),
36
+ adminStatus: document.getElementById('user-admin-status'),
37
+ adminError: document.getElementById('user-admin-error'),
38
+ adminUnlockForm: document.getElementById('user-admin-unlock-form'),
39
+ adminPassword: document.getElementById('user-admin-password'),
40
+ adminUnlockBtn: document.getElementById('user-admin-unlock-btn'),
41
+ adminOverview: document.getElementById('user-admin-overview'),
42
+ adminRefreshBtn: document.getElementById('user-admin-refresh-btn'),
43
+ adminLogoutBtn: document.getElementById('user-admin-logout-btn'),
44
+ adminTotalPacks: document.getElementById('user-admin-total-packs'),
45
+ adminTotalStickers: document.getElementById('user-admin-total-stickers'),
46
+ adminActiveBans: document.getElementById('user-admin-active-bans'),
47
+ adminKnownUsers: document.getElementById('user-admin-known-users'),
48
+ adminActiveSessions: document.getElementById('user-admin-active-sessions'),
49
+ adminVisits24h: document.getElementById('user-admin-visits-24h'),
50
+ adminVisits7d: document.getElementById('user-admin-visits-7d'),
51
+ adminUniqueVisitors7d: document.getElementById('user-admin-unique-visitors-7d'),
34
52
  };
35
53
 
36
54
  const state = {
@@ -38,11 +56,16 @@ if (root) {
38
56
  stickersPath: String(root.dataset.stickersPath || DEFAULT_STICKERS_PATH).trim() || DEFAULT_STICKERS_PATH,
39
57
  loginPath: String(root.dataset.loginPath || DEFAULT_LOGIN_PATH).trim() || DEFAULT_LOGIN_PATH,
40
58
  botPhone: '',
59
+ adminBusy: false,
60
+ adminStatusPayload: null,
61
+ adminOverviewPayload: null,
41
62
  };
42
63
 
43
64
  const sessionApiPath = `${state.apiBasePath}/auth/google/session`;
44
65
  const myProfileApiPath = `${state.apiBasePath}/me`;
45
66
  const botContactApiPath = `${state.apiBasePath}/bot-contact`;
67
+ const adminSessionApiPath = `${state.apiBasePath}/admin/session`;
68
+ const adminOverviewApiPath = `${state.apiBasePath}/admin/overview`;
46
69
 
47
70
  const setText = (el, value) => {
48
71
  if (!el) return;
@@ -56,6 +79,13 @@ if (root) {
56
79
  if (safeMessage) ui.error.textContent = safeMessage;
57
80
  };
58
81
 
82
+ const showAdminError = (message) => {
83
+ if (!ui.adminError) return;
84
+ const safeMessage = String(message || '').trim();
85
+ ui.adminError.hidden = !safeMessage;
86
+ if (safeMessage) ui.adminError.textContent = safeMessage;
87
+ };
88
+
59
89
  const normalizeDigits = (value) => String(value || '').replace(/\D+/g, '');
60
90
 
61
91
  const formatPhone = (digits) => {
@@ -181,6 +211,172 @@ if (root) {
181
211
  if (ui.grid) ui.grid.hidden = false;
182
212
  };
183
213
 
214
+ const getAdminSession = () => state.adminStatusPayload?.session || null;
215
+ const isAdminAuthenticated = () => Boolean(getAdminSession()?.authenticated);
216
+ const isAdminEligible = () => Boolean(state.adminStatusPayload?.eligible_google_login || isAdminAuthenticated());
217
+
218
+ const resolveAdminRole = () =>
219
+ String(getAdminSession()?.role || state.adminStatusPayload?.eligible_role || '')
220
+ .trim()
221
+ .toLowerCase();
222
+
223
+ const formatAdminRole = (role) => {
224
+ if (role === 'owner') return 'dono';
225
+ if (role === 'moderator') return 'moderador';
226
+ return 'admin';
227
+ };
228
+
229
+ const setAdminBusy = (value) => {
230
+ const busy = Boolean(value);
231
+ state.adminBusy = busy;
232
+
233
+ if (ui.adminPassword) ui.adminPassword.disabled = busy || isAdminAuthenticated();
234
+ if (ui.adminUnlockBtn) ui.adminUnlockBtn.disabled = busy || !isAdminEligible() || isAdminAuthenticated();
235
+ if (ui.adminRefreshBtn) ui.adminRefreshBtn.disabled = busy || !isAdminAuthenticated();
236
+ if (ui.adminLogoutBtn) ui.adminLogoutBtn.disabled = busy || !isAdminAuthenticated();
237
+ };
238
+
239
+ const resetAdminMetrics = () => {
240
+ setText(ui.adminTotalPacks, '0');
241
+ setText(ui.adminTotalStickers, '0');
242
+ setText(ui.adminActiveBans, '0');
243
+ setText(ui.adminKnownUsers, '0');
244
+ setText(ui.adminActiveSessions, '0');
245
+ setText(ui.adminVisits24h, '0');
246
+ setText(ui.adminVisits7d, '0');
247
+ setText(ui.adminUniqueVisitors7d, '0');
248
+ };
249
+
250
+ const renderAdminOverview = () => {
251
+ const counters = state.adminOverviewPayload?.counters || {};
252
+ setText(ui.adminTotalPacks, formatNumber(counters.total_packs_any_status || 0));
253
+ setText(ui.adminTotalStickers, formatNumber(counters.total_stickers_any_status || 0));
254
+ setText(ui.adminActiveBans, formatNumber(counters.active_bans || 0));
255
+ setText(ui.adminKnownUsers, formatNumber(counters.known_google_users || 0));
256
+ setText(ui.adminActiveSessions, formatNumber(counters.active_google_sessions || 0));
257
+ setText(ui.adminVisits24h, formatNumber(counters.visit_events_24h || 0));
258
+ setText(ui.adminVisits7d, formatNumber(counters.visit_events_7d || 0));
259
+ setText(ui.adminUniqueVisitors7d, formatNumber(counters.unique_visitors_7d || 0));
260
+ };
261
+
262
+ const renderAdminPanel = () => {
263
+ if (!ui.adminPanel) return;
264
+
265
+ const enabled = state.adminStatusPayload?.enabled !== false;
266
+ const authenticated = isAdminAuthenticated();
267
+ const eligible = isAdminEligible();
268
+
269
+ if (!enabled || (!eligible && !authenticated)) {
270
+ ui.adminPanel.hidden = true;
271
+ if (ui.adminUnlockForm) ui.adminUnlockForm.hidden = true;
272
+ if (ui.adminOverview) ui.adminOverview.hidden = true;
273
+ showAdminError('');
274
+ return;
275
+ }
276
+
277
+ ui.adminPanel.hidden = false;
278
+ const role = resolveAdminRole();
279
+ setText(ui.adminRole, formatAdminRole(role));
280
+
281
+ if (authenticated) {
282
+ setText(ui.adminStatus, `Sessão admin ativa como ${formatAdminRole(role)}.`);
283
+ if (ui.adminUnlockForm) ui.adminUnlockForm.hidden = true;
284
+ if (ui.adminOverview) ui.adminOverview.hidden = false;
285
+ renderAdminOverview();
286
+ } else {
287
+ setText(ui.adminStatus, `Conta elegível para admin (${formatAdminRole(role)}). Informe a senha para liberar os dados.`);
288
+ if (ui.adminUnlockForm) ui.adminUnlockForm.hidden = false;
289
+ if (ui.adminOverview) ui.adminOverview.hidden = true;
290
+ resetAdminMetrics();
291
+ }
292
+
293
+ setAdminBusy(state.adminBusy);
294
+ };
295
+
296
+ const loadAdminStatus = async () => {
297
+ const payload = await fetchJson(adminSessionApiPath, { method: 'GET' });
298
+ state.adminStatusPayload = payload?.data || null;
299
+ };
300
+
301
+ const loadAdminOverview = async () => {
302
+ if (!isAdminAuthenticated()) {
303
+ state.adminOverviewPayload = null;
304
+ return;
305
+ }
306
+ const payload = await fetchJson(adminOverviewApiPath, { method: 'GET' });
307
+ state.adminOverviewPayload = payload?.data || null;
308
+ };
309
+
310
+ const refreshAdminArea = async ({ keepCurrentError = false } = {}) => {
311
+ if (!keepCurrentError) showAdminError('');
312
+ try {
313
+ await loadAdminStatus();
314
+ await loadAdminOverview();
315
+ } catch (error) {
316
+ if (error?.statusCode === 404) {
317
+ state.adminStatusPayload = { enabled: false };
318
+ state.adminOverviewPayload = null;
319
+ } else {
320
+ showAdminError(error?.message || 'Falha ao carregar área admin.');
321
+ }
322
+ }
323
+ renderAdminPanel();
324
+ };
325
+
326
+ const handleAdminUnlock = async () => {
327
+ const password = String(ui.adminPassword?.value || '').trim();
328
+ if (!password) {
329
+ showAdminError('Informe a senha do painel admin.');
330
+ return;
331
+ }
332
+ if (state.adminBusy) return;
333
+
334
+ showAdminError('');
335
+ setAdminBusy(true);
336
+ try {
337
+ const payload = await fetchJson(adminSessionApiPath, {
338
+ method: 'POST',
339
+ headers: { 'Content-Type': 'application/json; charset=utf-8' },
340
+ body: JSON.stringify({ password }),
341
+ });
342
+ state.adminStatusPayload = payload?.data || null;
343
+ if (ui.adminPassword) ui.adminPassword.value = '';
344
+ await loadAdminOverview();
345
+ } catch (error) {
346
+ showAdminError(error?.message || 'Falha ao desbloquear área admin.');
347
+ await loadAdminStatus().catch(() => {});
348
+ state.adminOverviewPayload = null;
349
+ } finally {
350
+ setAdminBusy(false);
351
+ renderAdminPanel();
352
+ }
353
+ };
354
+
355
+ const handleAdminLogout = async () => {
356
+ if (state.adminBusy) return;
357
+ showAdminError('');
358
+ setAdminBusy(true);
359
+ try {
360
+ await fetchJson(adminSessionApiPath, { method: 'DELETE' });
361
+ } catch {
362
+ // no-op: we still revalidate status below
363
+ }
364
+ state.adminOverviewPayload = null;
365
+ await loadAdminStatus().catch(() => {
366
+ state.adminStatusPayload = null;
367
+ });
368
+ setAdminBusy(false);
369
+ renderAdminPanel();
370
+ };
371
+
372
+ const handleAdminRefresh = async () => {
373
+ if (state.adminBusy) return;
374
+ setAdminBusy(true);
375
+ await refreshAdminArea({ keepCurrentError: false });
376
+ setAdminBusy(false);
377
+ renderAdminPanel();
378
+ };
379
+
184
380
  const handleLogout = async () => {
185
381
  if (!ui.logoutBtn) return;
186
382
  ui.logoutBtn.disabled = true;
@@ -218,6 +414,7 @@ if (root) {
218
414
 
219
415
  renderSession(sessionData);
220
416
  await loadBotPhone();
417
+ await refreshAdminArea();
221
418
 
222
419
  try {
223
420
  const myProfilePayload = await fetchJson(myProfileApiPath, { method: 'GET' });
@@ -240,5 +437,24 @@ if (root) {
240
437
  });
241
438
  }
242
439
 
440
+ if (ui.adminUnlockForm) {
441
+ ui.adminUnlockForm.addEventListener('submit', (event) => {
442
+ event.preventDefault();
443
+ void handleAdminUnlock();
444
+ });
445
+ }
446
+
447
+ if (ui.adminLogoutBtn) {
448
+ ui.adminLogoutBtn.addEventListener('click', () => {
449
+ void handleAdminLogout();
450
+ });
451
+ }
452
+
453
+ if (ui.adminRefreshBtn) {
454
+ ui.adminRefreshBtn.addEventListener('click', () => {
455
+ void handleAdminRefresh();
456
+ });
457
+ }
458
+
243
459
  void init();
244
460
  }
@@ -90,51 +90,11 @@
90
90
  packPageWhatsApp: document.getElementById('pack-page-whatsapp'),
91
91
  };
92
92
 
93
- if (
94
- !els.hero ||
95
- !els.form ||
96
- !els.search ||
97
- !els.visibility ||
98
- !els.categories ||
99
- !els.categoriesPicker ||
100
- !els.categoriesSearch ||
101
- !els.categoriesChips ||
102
- !els.categoriesOptions ||
103
- !els.status ||
104
- !els.grid ||
105
- !els.more ||
106
- !els.orphanStatus ||
107
- !els.orphanGrid ||
108
- !els.orphanPrev ||
109
- !els.orphanNext ||
110
- !els.orphanPageInfo ||
111
- !els.panel ||
112
- !els.panelTitle ||
113
- !els.panelSub ||
114
- !els.panelChip ||
115
- !els.panelError ||
116
- !els.panelPrev ||
117
- !els.panelNext ||
118
- !els.panelPageInfo ||
119
- !els.copy ||
120
- !els.useWhatsAppLink ||
121
- !els.stickers ||
122
- !els.packPage ||
123
- !els.packPageTitle ||
124
- !els.packPageSub ||
125
- !els.packPageChip ||
126
- !els.packPageStatus ||
127
- !els.packPageGrid ||
128
- !els.packPageBack ||
129
- !els.packPageCopy ||
130
- !els.packPageWhatsApp
131
- ) {
93
+ if (!els.hero || !els.form || !els.search || !els.visibility || !els.categories || !els.categoriesPicker || !els.categoriesSearch || !els.categoriesChips || !els.categoriesOptions || !els.status || !els.grid || !els.more || !els.orphanStatus || !els.orphanGrid || !els.orphanPrev || !els.orphanNext || !els.orphanPageInfo || !els.panel || !els.panelTitle || !els.panelSub || !els.panelChip || !els.panelError || !els.panelPrev || !els.panelNext || !els.panelPageInfo || !els.copy || !els.useWhatsAppLink || !els.stickers || !els.packPage || !els.packPageTitle || !els.packPageSub || !els.packPageChip || !els.packPageStatus || !els.packPageGrid || !els.packPageBack || !els.packPageCopy || !els.packPageWhatsApp) {
132
94
  return;
133
95
  }
134
96
 
135
- const panelModal = window.bootstrap?.Modal
136
- ? window.bootstrap.Modal.getOrCreateInstance(els.panel)
137
- : null;
97
+ const panelModal = window.bootstrap?.Modal ? window.bootstrap.Modal.getOrCreateInstance(els.panel) : null;
138
98
 
139
99
  let shouldReplaceStateOnHide = false;
140
100
 
@@ -208,10 +168,7 @@
208
168
  chip.className = 'category-chip';
209
169
  chip.dataset.value = value;
210
170
  chip.setAttribute('aria-label', 'Remover categoria ' + (entry?.label || value));
211
- chip.innerHTML =
212
- '<span class="category-chip-label">' +
213
- (entry?.label || value) +
214
- '</span><i class="fa-solid fa-xmark category-chip-remove" aria-hidden="true"></i>';
171
+ chip.innerHTML = '<span class="category-chip-label">' + (entry?.label || value) + '</span><i class="fa-solid fa-xmark category-chip-remove" aria-hidden="true"></i>';
215
172
  els.categoriesChips.appendChild(chip);
216
173
  });
217
174
  };
@@ -240,10 +197,7 @@
240
197
  button.type = 'button';
241
198
  button.className = 'category-option' + (selected.has(entry.value) ? ' selected' : '');
242
199
  button.dataset.value = entry.value;
243
- button.innerHTML =
244
- '<span class="category-option-label">' +
245
- entry.label +
246
- '</span><i class="fa-solid fa-check category-option-check" aria-hidden="true"></i>';
200
+ button.innerHTML = '<span class="category-option-label">' + entry.label + '</span><i class="fa-solid fa-check category-option-check" aria-hidden="true"></i>';
247
201
  els.categoriesOptions.appendChild(button);
248
202
  });
249
203
  };
@@ -401,9 +355,7 @@
401
355
  .map(([label]) => toTagToken(label))
402
356
  .filter(Boolean);
403
357
 
404
- const merged = [...rankedFromScores, ...classificationTags, ...explicitTags]
405
- .map((tag) => toTagToken(tag))
406
- .filter(Boolean);
358
+ const merged = [...rankedFromScores, ...classificationTags, ...explicitTags].map((tag) => toTagToken(tag)).filter(Boolean);
407
359
 
408
360
  return Array.from(new Set(merged)).slice(0, 3);
409
361
  };
@@ -430,9 +382,7 @@
430
382
  const explicitTags = Array.isArray(pack?.tags) ? pack.tags : [];
431
383
  const classificationTags = Array.isArray(pack?.classification?.tags) ? pack.classification.tags : [];
432
384
 
433
- const merged = [...classificationTags, ...explicitTags]
434
- .map((tag) => toTagToken(tag))
435
- .filter(Boolean);
385
+ const merged = [...classificationTags, ...explicitTags].map((tag) => toTagToken(tag)).filter(Boolean);
436
386
 
437
387
  return Array.from(new Set(merged)).slice(0, 3);
438
388
  };
@@ -473,8 +423,7 @@
473
423
  const visibilityBadge = document.createElement('span');
474
424
  visibilityBadge.className = 'pack-visibility-badge';
475
425
  const visibility = String(pack.visibility || '').toLowerCase();
476
- visibilityBadge.textContent =
477
- visibility === 'public' ? 'Público' : visibility === 'unlisted' ? 'Não listado' : 'Privado';
426
+ visibilityBadge.textContent = visibility === 'public' ? 'Público' : visibility === 'unlisted' ? 'Não listado' : 'Privado';
478
427
 
479
428
  const countBadge = document.createElement('span');
480
429
  countBadge.className = 'pack-count-badge';
@@ -546,13 +495,7 @@
546
495
  for (let index = 0; index < count; index += 1) {
547
496
  const col = document.createElement('div');
548
497
  col.className = 'col-4 col-sm-6 col-md-4 col-lg-3';
549
- col.innerHTML =
550
- '<div class="card h-100 bg-dark text-light shadow-sm pack-card">' +
551
- '<div class="card-body d-flex flex-column gap-2">' +
552
- '<div class="skeleton skeleton-thumb"></div>' +
553
- '<div class="skeleton skeleton-line"></div>' +
554
- '<div class="skeleton skeleton-line short"></div>' +
555
- '</div></div>';
498
+ col.innerHTML = '<div class="card h-100 bg-dark text-light shadow-sm pack-card">' + '<div class="card-body d-flex flex-column gap-2">' + '<div class="skeleton skeleton-thumb"></div>' + '<div class="skeleton skeleton-line"></div>' + '<div class="skeleton skeleton-line short"></div>' + '</div></div>';
556
499
  els.grid.appendChild(col);
557
500
  }
558
501
  };
@@ -607,8 +550,7 @@
607
550
 
608
551
  els.packPageTitle.textContent = pack?.name || 'Pack';
609
552
  els.packPageSub.textContent = (pack?.publisher || '-') + ' | ' + (pack?.description || 'Sem descrição');
610
- els.packPageChip.textContent =
611
- String(pack?.sticker_count || items.length || 0) + ' itens | ' + (pack?.visibility || '-') + ' | ' + (pack?.pack_key || '-');
553
+ els.packPageChip.textContent = String(pack?.sticker_count || items.length || 0) + ' itens | ' + (pack?.visibility || '-') + ' | ' + (pack?.pack_key || '-');
612
554
  applyWhatsAppLink(els.packPageWhatsApp, pack?.whatsapp?.url);
613
555
 
614
556
  els.packPageGrid.innerHTML = '';
@@ -643,11 +585,7 @@
643
585
  for (let index = 0; index < count; index += 1) {
644
586
  const col = document.createElement('div');
645
587
  col.className = 'col-6 col-md-3 col-lg-2';
646
- col.innerHTML =
647
- '<article class="orphan-card card h-100"><div class="card-body p-2">' +
648
- '<div class="skeleton skeleton-orphan"></div>' +
649
- '<div class="skeleton skeleton-line short mt-2"></div>' +
650
- '</div></article>';
588
+ col.innerHTML = '<article class="orphan-card card h-100"><div class="card-body p-2">' + '<div class="skeleton skeleton-orphan"></div>' + '<div class="skeleton skeleton-line short mt-2"></div>' + '</div></article>';
651
589
  els.orphanGrid.appendChild(col);
652
590
  }
653
591
  };
@@ -779,8 +717,7 @@
779
717
 
780
718
  els.panelPrev.disabled = state.panelPagination.page <= 1;
781
719
  els.panelNext.disabled = state.panelPagination.page >= totalPages;
782
- els.panelPageInfo.textContent =
783
- 'Página ' + state.panelPagination.page + ' de ' + totalPages + ' - ' + safeTotal + ' stickers';
720
+ els.panelPageInfo.textContent = 'Página ' + state.panelPagination.page + ' de ' + totalPages + ' - ' + safeTotal + ' stickers';
784
721
  };
785
722
 
786
723
  const renderPanelStickersPage = () => {
@@ -3,14 +3,7 @@ import { React, memo } from '../vendor/react.js';
3
3
  const h = React.createElement;
4
4
 
5
5
  function ErrorStateComponent({ message, onRetry, isRateLimited }) {
6
- return h(
7
- 'section',
8
- { className: 'ghp-error', role: 'alert' },
9
- h('h3', null, 'Não foi possível carregar os dados do GitHub'),
10
- h('p', null, message || 'Tente novamente em alguns instantes.'),
11
- isRateLimited ? h('p', { className: 'ghp-rate-limit' }, 'Limite de requisições do GitHub atingido. Tente novamente em breve.') : null,
12
- h('button', { type: 'button', className: 'ghp-retry', onClick: onRetry }, 'Tentar novamente'),
13
- );
6
+ return h('section', { className: 'ghp-error', role: 'alert' }, h('h3', null, 'Não foi possível carregar os dados do GitHub'), h('p', null, message || 'Tente novamente em alguns instantes.'), isRateLimited ? h('p', { className: 'ghp-rate-limit' }, 'Limite de requisições do GitHub atingido. Tente novamente em breve.') : null, h('button', { type: 'button', className: 'ghp-retry', onClick: onRetry }, 'Tentar novamente'));
14
7
  }
15
8
 
16
9
  export const ErrorState = memo(ErrorStateComponent);
@@ -92,15 +92,8 @@ export function GithubProjectPanel({ owner, repo, endpoint }) {
92
92
  ),
93
93
  ),
94
94
  ),
95
- error
96
- ? h('p', { className: 'ghp-inline-warning', role: 'status' }, error.rateLimited ? 'Limite da API do GitHub atingido; exibindo cache local.' : 'Exibindo último snapshot disponível.')
97
- : null,
95
+ error ? h('p', { className: 'ghp-inline-warning', role: 'status' }, error.rateLimited ? 'Limite da API do GitHub atingido; exibindo cache local.' : 'Exibindo último snapshot disponível.') : null,
98
96
  h('div', { className: 'ghp-stat-grid' }, ...statCards.map((card) => h(StatCard, { key: card.label, ...card }))),
99
- h(
100
- 'div',
101
- { className: 'ghp-list-grid' },
102
- h('section', { className: 'ghp-list-card' }, h('h4', { className: 'ghp-list-title' }, 'Últimos Commits'), h(CommitList, { items: data?.latest_commits || [], formatDate: fmtDate })),
103
- h('section', { className: 'ghp-list-card' }, h('h4', { className: 'ghp-list-title' }, 'Últimas Releases'), h(ReleaseList, { items: data?.latest_releases || [], formatDate: fmtDate })),
104
- ),
97
+ h('div', { className: 'ghp-list-grid' }, h('section', { className: 'ghp-list-card' }, h('h4', { className: 'ghp-list-title' }, 'Últimos Commits'), h(CommitList, { items: data?.latest_commits || [], formatDate: fmtDate })), h('section', { className: 'ghp-list-card' }, h('h4', { className: 'ghp-list-title' }, 'Últimas Releases'), h(ReleaseList, { items: data?.latest_releases || [], formatDate: fmtDate }))),
105
98
  );
106
99
  }
@@ -6,17 +6,7 @@ function SkeletonPanelComponent() {
6
6
  const statNodes = Array.from({ length: 10 }).map((_, index) => h('div', { key: `s-${index}`, className: 'ghp-skeleton-card' }));
7
7
  const listNodes = Array.from({ length: 4 }).map((_, index) => h('li', { key: `l-${index}`, className: 'ghp-skeleton-line' }));
8
8
 
9
- return h(
10
- 'section',
11
- { className: 'ghp-panel', 'aria-busy': 'true', 'aria-live': 'polite' },
12
- h('div', { className: 'ghp-stat-grid' }, ...statNodes),
13
- h(
14
- 'div',
15
- { className: 'ghp-list-grid' },
16
- h('div', { className: 'ghp-list-card' }, h('div', { className: 'ghp-skeleton-title' }), h('ul', { className: 'ghp-list' }, ...listNodes)),
17
- h('div', { className: 'ghp-list-card' }, h('div', { className: 'ghp-skeleton-title' }), h('ul', { className: 'ghp-list' }, ...listNodes)),
18
- ),
19
- );
9
+ return h('section', { className: 'ghp-panel', 'aria-busy': 'true', 'aria-live': 'polite' }, h('div', { className: 'ghp-stat-grid' }, ...statNodes), h('div', { className: 'ghp-list-grid' }, h('div', { className: 'ghp-list-card' }, h('div', { className: 'ghp-skeleton-title' }), h('ul', { className: 'ghp-list' }, ...listNodes)), h('div', { className: 'ghp-list-card' }, h('div', { className: 'ghp-skeleton-title' }), h('ul', { className: 'ghp-list' }, ...listNodes))));
20
10
  }
21
11
 
22
12
  export const SkeletonPanel = memo(SkeletonPanelComponent);
@@ -3,13 +3,7 @@ import { React, memo } from '../vendor/react.js';
3
3
  const h = React.createElement;
4
4
 
5
5
  function StatCardComponent({ label, value, detail, iconClass }) {
6
- return h(
7
- 'article',
8
- { className: 'ghp-stat-card' },
9
- h('header', { className: 'ghp-stat-head' }, h('span', { className: 'ghp-stat-label' }, label), iconClass ? h('i', { className: iconClass, 'aria-hidden': 'true' }) : null),
10
- h('p', { className: 'ghp-stat-value' }, value),
11
- h('p', { className: 'ghp-stat-detail', title: detail || '' }, detail || ' '),
12
- );
6
+ return h('article', { className: 'ghp-stat-card' }, h('header', { className: 'ghp-stat-head' }, h('span', { className: 'ghp-stat-label' }, label), iconClass ? h('i', { className: iconClass, 'aria-hidden': 'true' }) : null), h('p', { className: 'ghp-stat-value' }, value), h('p', { className: 'ghp-stat-detail', title: detail || '' }, detail || ' '));
13
7
  }
14
8
 
15
9
  export const StatCard = memo(StatCardComponent);
@@ -1,11 +1,3 @@
1
- export {
2
- default as React,
3
- memo,
4
- useMemo,
5
- useCallback,
6
- useEffect,
7
- useRef,
8
- useState,
9
- } from 'https://esm.sh/react@18.3.1';
1
+ export { default as React, memo, useMemo, useCallback, useEffect, useRef, useState } from 'https://esm.sh/react@18.3.1';
10
2
 
11
3
  export { createRoot } from 'https://esm.sh/react-dom@18.3.1/client';
@@ -7,14 +7,6 @@ if (typeof document !== 'undefined' && !window.__omnizapTwindReady) {
7
7
  window.__omnizapTwindReady = true;
8
8
  }
9
9
 
10
- export {
11
- default as React,
12
- useCallback,
13
- useEffect,
14
- useMemo,
15
- useRef,
16
- useState,
17
- memo,
18
- } from 'https://esm.sh/react@18.3.1';
10
+ export { default as React, useCallback, useEffect, useMemo, useRef, useState, memo } from 'https://esm.sh/react@18.3.1';
19
11
 
20
12
  export { createRoot } from 'https://esm.sh/react-dom@18.3.1/client';