html2apk 0.7.0 → 0.9.0

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.
@@ -19,6 +19,8 @@
19
19
  "link:opened": "link:aberto",
20
20
  "network:changed": "rede:mudou",
21
21
  "battery:changed": "bateria:mudou",
22
+ "location:changed": "localizacao:mudou",
23
+ "biometric:failed": "biometria:falhou",
22
24
  "notification:received": "notificacao:recebida",
23
25
  "notification:clicked": "notificacao:clicada"
24
26
  };
@@ -406,6 +408,268 @@
406
408
  });
407
409
  }
408
410
 
411
+ function hasOwn(object, key) {
412
+ return Object.prototype.hasOwnProperty.call(object || {}, key);
413
+ }
414
+
415
+ function storedFileOptions(nameOrOptions, value, options) {
416
+ var normalized;
417
+ if (nameOrOptions && typeof nameOrOptions === "object" && !Array.isArray(nameOrOptions)) {
418
+ return cloneSerializable(nameOrOptions) || {};
419
+ }
420
+
421
+ normalized = cloneSerializable(options || {}) || {};
422
+ normalized.name = String(nameOrOptions || "");
423
+ normalized.nome = normalized.name;
424
+ normalized.value = cloneSerializable(value);
425
+ normalized.valor = normalized.value;
426
+ if (typeof value !== "string" && !hasOwn(normalized, "json")) {
427
+ normalized.json = true;
428
+ }
429
+ return normalized;
430
+ }
431
+
432
+ function storedFileNameOptions(nameOrOptions, options) {
433
+ var normalized;
434
+ if (nameOrOptions && typeof nameOrOptions === "object" && !Array.isArray(nameOrOptions)) {
435
+ return cloneSerializable(nameOrOptions) || {};
436
+ }
437
+ normalized = cloneSerializable(options || {}) || {};
438
+ normalized.name = String(nameOrOptions || "");
439
+ normalized.nome = normalized.name;
440
+ return normalized;
441
+ }
442
+
443
+ function applyDownloadName(normalized, nameOrOptions) {
444
+ if (typeof nameOrOptions === "string") {
445
+ normalized.name = nameOrOptions;
446
+ normalized.nome = nameOrOptions;
447
+ normalized.fileName = nameOrOptions;
448
+ normalized.nomeArquivo = nameOrOptions;
449
+ } else if (nameOrOptions && typeof nameOrOptions === "object" && !Array.isArray(nameOrOptions)) {
450
+ Object.assign(normalized, cloneSerializable(nameOrOptions) || {});
451
+ }
452
+ return normalized;
453
+ }
454
+
455
+ function applyBase64DownloadSource(normalized, value) {
456
+ var source = String(value || "");
457
+ var comma;
458
+ var header;
459
+ var mimeType;
460
+
461
+ if (source.indexOf("data:") === 0) {
462
+ comma = source.indexOf(",");
463
+ header = comma >= 0 ? source.slice(5, comma) : "";
464
+ mimeType = header.split(";")[0];
465
+ normalized.base64 = comma >= 0 ? source.slice(comma + 1) : source;
466
+ if (mimeType) {
467
+ normalized.mimeType = normalized.mimeType || mimeType;
468
+ normalized.tipoMime = normalized.tipoMime || mimeType;
469
+ }
470
+ return normalized;
471
+ }
472
+
473
+ normalized.base64 = source;
474
+ return normalized;
475
+ }
476
+
477
+ function downloadFileOptions(urlOrOptions, nameOrOptions) {
478
+ var normalized;
479
+ var source;
480
+
481
+ if (urlOrOptions && typeof urlOrOptions === "object" && !Array.isArray(urlOrOptions)) {
482
+ normalized = cloneSerializable(urlOrOptions) || {};
483
+ return applyDownloadName(normalized, nameOrOptions);
484
+ }
485
+
486
+ normalized = {};
487
+ source = String(urlOrOptions || "").trim();
488
+ if (source.indexOf("data:") === 0) {
489
+ applyBase64DownloadSource(normalized, source);
490
+ } else {
491
+ normalized.url = source;
492
+ }
493
+ return applyDownloadName(normalized, nameOrOptions);
494
+ }
495
+
496
+ function downloadBase64Options(nameOrOptions, base64, options) {
497
+ var normalized;
498
+ if (nameOrOptions && typeof nameOrOptions === "object" && !Array.isArray(nameOrOptions)) {
499
+ normalized = cloneSerializable(nameOrOptions) || {};
500
+ if (typeof base64 === "string") {
501
+ applyBase64DownloadSource(normalized, base64);
502
+ } else if (base64 && typeof base64 === "object" && !Array.isArray(base64)) {
503
+ Object.assign(normalized, cloneSerializable(base64) || {});
504
+ }
505
+ return normalized;
506
+ }
507
+
508
+ normalized = cloneSerializable(options || {}) || {};
509
+ applyDownloadName(normalized, String(nameOrOptions || ""));
510
+ return applyBase64DownloadSource(normalized, base64);
511
+ }
512
+
513
+ function downloadLocalFileOptions(fileOrOptions, nameOrOptions) {
514
+ var normalized;
515
+ var source;
516
+
517
+ if (fileOrOptions && typeof fileOrOptions === "object" && !Array.isArray(fileOrOptions)) {
518
+ normalized = cloneSerializable(fileOrOptions) || {};
519
+ return applyDownloadName(normalized, nameOrOptions);
520
+ }
521
+
522
+ normalized = {};
523
+ source = String(fileOrOptions || "").trim();
524
+ if (/^(content|file):\/\//.test(source)) {
525
+ normalized.uri = source;
526
+ normalized.contentUri = source;
527
+ } else if (/^[A-Za-z]:[\\/]/.test(source) || source.charAt(0) === "/" || source.charAt(0) === "\\") {
528
+ normalized.path = source;
529
+ normalized.caminho = source;
530
+ } else {
531
+ normalized.sourceName = source;
532
+ normalized.arquivoOrigem = source;
533
+ }
534
+ return applyDownloadName(normalized, nameOrOptions);
535
+ }
536
+
537
+ function wallpaperOptions(sourceOrOptions, options) {
538
+ var normalized;
539
+ var source;
540
+ var comma;
541
+ var header;
542
+ var mimeType;
543
+
544
+ if (sourceOrOptions && typeof sourceOrOptions === "object" && !Array.isArray(sourceOrOptions)) {
545
+ normalized = cloneSerializable(sourceOrOptions) || {};
546
+ } else {
547
+ normalized = cloneSerializable(options || {}) || {};
548
+ source = String(sourceOrOptions || "").trim();
549
+ if (source.indexOf("data:") === 0) {
550
+ comma = source.indexOf(",");
551
+ header = comma >= 0 ? source.slice(5, comma) : "";
552
+ mimeType = header.split(";")[0];
553
+ normalized.base64 = comma >= 0 ? source.slice(comma + 1) : source;
554
+ if (mimeType) {
555
+ normalized.mimeType = normalized.mimeType || mimeType;
556
+ normalized.tipoMime = normalized.tipoMime || mimeType;
557
+ }
558
+ } else if (/^(content|file):\/\//.test(source)) {
559
+ normalized.uri = source;
560
+ normalized.contentUri = source;
561
+ } else if (/^[A-Za-z]:[\\/]/.test(source) || source.charAt(0) === "/" || source.charAt(0) === "\\") {
562
+ normalized.path = source;
563
+ normalized.caminho = source;
564
+ } else {
565
+ normalized.name = source;
566
+ normalized.nome = source;
567
+ normalized.fileName = source;
568
+ normalized.nomeArquivo = source;
569
+ }
570
+ }
571
+
572
+ if (normalized.alvo && !normalized.target) {
573
+ normalized.target = normalized.alvo;
574
+ }
575
+ if (normalized.target && !normalized.alvo) {
576
+ normalized.alvo = normalized.target;
577
+ }
578
+ return normalized;
579
+ }
580
+
581
+ function secureItemOptions(keyOrOptions, value, options) {
582
+ var normalized;
583
+ if (keyOrOptions && typeof keyOrOptions === "object" && !Array.isArray(keyOrOptions)) {
584
+ return cloneSerializable(keyOrOptions) || {};
585
+ }
586
+ normalized = cloneSerializable(options || {}) || {};
587
+ normalized.key = String(keyOrOptions || "");
588
+ normalized.chave = normalized.key;
589
+ normalized.value = cloneSerializable(value);
590
+ normalized.valor = normalized.value;
591
+ if (typeof value !== "string" && !hasOwn(normalized, "json")) {
592
+ normalized.json = true;
593
+ }
594
+ return normalized;
595
+ }
596
+
597
+ function secureKeyOptions(keyOrOptions, options) {
598
+ var normalized;
599
+ if (keyOrOptions && typeof keyOrOptions === "object" && !Array.isArray(keyOrOptions)) {
600
+ return cloneSerializable(keyOrOptions) || {};
601
+ }
602
+ normalized = cloneSerializable(options || {}) || {};
603
+ normalized.key = String(keyOrOptions || "");
604
+ normalized.chave = normalized.key;
605
+ return normalized;
606
+ }
607
+
608
+ function unwrapStoredValue(result) {
609
+ if (!result || result.exists === false || result.existe === false) {
610
+ return null;
611
+ }
612
+ if (hasOwn(result, "value")) {
613
+ return result.value;
614
+ }
615
+ if (hasOwn(result, "valor")) {
616
+ return result.valor;
617
+ }
618
+ if (hasOwn(result, "base64")) {
619
+ return result.base64;
620
+ }
621
+ if (hasOwn(result, "content")) {
622
+ return result.content;
623
+ }
624
+ if (hasOwn(result, "conteudo")) {
625
+ return result.conteudo;
626
+ }
627
+ return result;
628
+ }
629
+
630
+ function detectQRCodeFromPhoto(photo) {
631
+ var source;
632
+ var detector;
633
+
634
+ if (!photo || !photo.base64) {
635
+ return null;
636
+ }
637
+ if (typeof BarcodeDetector !== "function" || typeof createImageBitmap !== "function" || typeof fetch !== "function") {
638
+ throw new Error("QR code scanning requires BarcodeDetector support in this Android WebView.");
639
+ }
640
+
641
+ detector = new BarcodeDetector({ formats: ["qr_code"] });
642
+ source = "data:" + (photo.mimeType || "image/jpeg") + ";base64," + photo.base64;
643
+ return fetch(source)
644
+ .then(function (response) {
645
+ return response.blob();
646
+ })
647
+ .then(function (blob) {
648
+ return createImageBitmap(blob);
649
+ })
650
+ .then(function (image) {
651
+ return detector.detect(image);
652
+ })
653
+ .then(function (codes) {
654
+ var first = codes && codes[0];
655
+ if (!first) {
656
+ return null;
657
+ }
658
+ return {
659
+ text: first.rawValue || "",
660
+ texto: first.rawValue || "",
661
+ rawValue: first.rawValue || "",
662
+ valorBruto: first.rawValue || "",
663
+ format: first.format || "qr_code",
664
+ formato: first.format || "qr_code",
665
+ codes: codes,
666
+ codigos: codes,
667
+ photo: photo,
668
+ foto: photo
669
+ };
670
+ });
671
+ }
672
+
409
673
  function normalizeEventType(type) {
410
674
  var eventType = String(type || "");
411
675
  return eventAliases[eventType] || eventType;
@@ -577,6 +841,15 @@
577
841
  statusLanterna: function () {
578
842
  return call("flashlightStatus");
579
843
  },
844
+ tirarFoto: function (options) {
845
+ return call("capturePhoto", [options || {}]);
846
+ },
847
+ capturarVideo: function (options) {
848
+ return call("captureVideo", [options || {}]);
849
+ },
850
+ escanearQRCode: function (options) {
851
+ return api.tirarFoto(Object.assign({ base64: true }, options || {})).then(detectQRCodeFromPhoto);
852
+ },
580
853
  solicitarPermissaoCamera: function () {
581
854
  return call("requestCameraPermission");
582
855
  },
@@ -640,8 +913,55 @@
640
913
  escolherPasta: function () {
641
914
  return call("pickFolder");
642
915
  },
643
- salvarArquivo: function (options) {
644
- return call("saveFile", [options || {}]);
916
+ salvarArquivo: function (nameOrOptions, value, options) {
917
+ if (typeof nameOrOptions === "string") {
918
+ return call("saveStoredFile", [storedFileOptions(nameOrOptions, value, options)]);
919
+ }
920
+ return call("saveFile", [nameOrOptions || {}]);
921
+ },
922
+ lerArquivo: function (nameOrOptions, options) {
923
+ return call("readStoredFile", [storedFileNameOptions(nameOrOptions, options)]).then(unwrapStoredValue);
924
+ },
925
+ lerArquivoCompleto: function (nameOrOptions, options) {
926
+ return call("readStoredFile", [storedFileNameOptions(nameOrOptions, options)]);
927
+ },
928
+ excluirArquivo: function (nameOrOptions, options) {
929
+ return call("deleteStoredFile", [storedFileNameOptions(nameOrOptions, options)]);
930
+ },
931
+ infoArquivo: function (nameOrOptions, options) {
932
+ return call("storedFileInfo", [storedFileNameOptions(nameOrOptions, options)]);
933
+ },
934
+ arquivoExiste: function (nameOrOptions, options) {
935
+ return api.infoArquivo(nameOrOptions, options).then(function (info) {
936
+ return Boolean(info && (info.exists || info.existe));
937
+ });
938
+ },
939
+ listarArquivos: function () {
940
+ return call("listStoredFiles");
941
+ },
942
+ abrirArquivo: function (nameOrOptions, options) {
943
+ return call("openStoredFile", [storedFileNameOptions(nameOrOptions, options)]);
944
+ },
945
+ compartilharArquivo: function (nameOrOptions, options) {
946
+ return call("shareStoredFile", [storedFileNameOptions(nameOrOptions, options)]);
947
+ },
948
+ baixarArquivo: function (urlOrOptions, nameOrOptions) {
949
+ return call("downloadFile", [downloadFileOptions(urlOrOptions, nameOrOptions)]);
950
+ },
951
+ baixarBase64: function (nameOrOptions, base64, options) {
952
+ return call("downloadFile", [downloadBase64Options(nameOrOptions, base64, options)]);
953
+ },
954
+ baixarArquivoLocal: function (fileOrOptions, nameOrOptions) {
955
+ return call("downloadFile", [downloadLocalFileOptions(fileOrOptions, nameOrOptions)]);
956
+ },
957
+ definirPapelParede: function (sourceOrOptions, options) {
958
+ return call("setWallpaper", [wallpaperOptions(sourceOrOptions, options)]);
959
+ },
960
+ infoPapelParede: function () {
961
+ return call("wallpaperInfo");
962
+ },
963
+ abrirConfiguracaoPapelParede: function () {
964
+ return call("openWallpaperSettings");
645
965
  },
646
966
  infoDispositivo: function () {
647
967
  return call("deviceInfo");
@@ -667,6 +987,39 @@
667
987
  infoAppsAbertos: function () {
668
988
  return call("openAppsMemory");
669
989
  },
990
+ obterLocalizacao: function (options) {
991
+ return call("getLocation", [options || {}]);
992
+ },
993
+ acompanharLocalizacao: function (options) {
994
+ return call("watchLocation", [options || {}]);
995
+ },
996
+ pararLocalizacao: function (id) {
997
+ return call("stopLocationWatch", [id || ""]);
998
+ },
999
+ aoMudarLocalizacao: function (listener) {
1000
+ return onEvent("localizacao:mudou", listener);
1001
+ },
1002
+ autenticarBiometria: function (options) {
1003
+ return call("authenticateBiometric", [options || {}]);
1004
+ },
1005
+ salvarSeguro: function (keyOrOptions, value, options) {
1006
+ return call("saveSecureItem", [secureItemOptions(keyOrOptions, value, options)]);
1007
+ },
1008
+ lerSeguro: function (keyOrOptions, options) {
1009
+ return call("readSecureItem", [secureKeyOptions(keyOrOptions, options)]).then(unwrapStoredValue);
1010
+ },
1011
+ lerSeguroCompleto: function (keyOrOptions, options) {
1012
+ return call("readSecureItem", [secureKeyOptions(keyOrOptions, options)]);
1013
+ },
1014
+ removerSeguro: function (keyOrOptions, options) {
1015
+ return call("deleteSecureItem", [secureKeyOptions(keyOrOptions, options)]);
1016
+ },
1017
+ listarSeguro: function () {
1018
+ return call("listSecureKeys");
1019
+ },
1020
+ limparSeguro: function () {
1021
+ return call("clearSecureStorage");
1022
+ },
670
1023
  statusPermissoes: function (permissions) {
671
1024
  return call("permissionStatus", [permissions || []]);
672
1025
  },
@@ -763,6 +1116,11 @@
763
1116
  flashlight: api.lanterna,
764
1117
  toggleFlashlight: api.alternarLanterna,
765
1118
  flashlightStatus: api.statusLanterna,
1119
+ takePhoto: api.tirarFoto,
1120
+ capturePhoto: api.tirarFoto,
1121
+ captureVideo: api.capturarVideo,
1122
+ scanQRCode: api.escanearQRCode,
1123
+ scanQrCode: api.escanearQRCode,
766
1124
  requestCameraPermission: api.solicitarPermissaoCamera,
767
1125
  requestMicrophonePermission: api.solicitarPermissaoMicrofone,
768
1126
  microphoneStatus: api.statusMicrofone,
@@ -791,6 +1149,35 @@
791
1149
  pickVideo: api.escolherVideo,
792
1150
  pickFolder: api.escolherPasta,
793
1151
  saveFile: api.salvarArquivo,
1152
+ readFile: api.lerArquivo,
1153
+ readStoredFile: api.lerArquivo,
1154
+ readStoredFileInfo: api.lerArquivoCompleto,
1155
+ deleteFile: api.excluirArquivo,
1156
+ removeFile: api.excluirArquivo,
1157
+ excluirArquivoArmazenado: api.excluirArquivo,
1158
+ removerArquivo: api.excluirArquivo,
1159
+ apagarArquivo: api.excluirArquivo,
1160
+ fileInfo: api.infoArquivo,
1161
+ storedFileInfo: api.infoArquivo,
1162
+ fileExists: api.arquivoExiste,
1163
+ listFiles: api.listarArquivos,
1164
+ listStoredFiles: api.listarArquivos,
1165
+ openFile: api.abrirArquivo,
1166
+ openStoredFile: api.abrirArquivo,
1167
+ shareFile: api.compartilharArquivo,
1168
+ shareStoredFile: api.compartilharArquivo,
1169
+ downloadFile: api.baixarArquivo,
1170
+ downloadBase64: api.baixarBase64,
1171
+ downloadFromBase64: api.baixarBase64,
1172
+ baixarArquivoBase64: api.baixarBase64,
1173
+ downloadLocalFile: api.baixarArquivoLocal,
1174
+ downloadFromFile: api.baixarArquivoLocal,
1175
+ baixarArquivoNormal: api.baixarArquivoLocal,
1176
+ setWallpaper: api.definirPapelParede,
1177
+ setPhoneWallpaper: api.definirPapelParede,
1178
+ wallpaper: api.definirPapelParede,
1179
+ wallpaperInfo: api.infoPapelParede,
1180
+ openWallpaperSettings: api.abrirConfiguracaoPapelParede,
794
1181
  deviceInfo: api.infoDispositivo,
795
1182
  networkInfo: api.infoRede,
796
1183
  batteryInfo: api.infoBateria,
@@ -799,6 +1186,21 @@
799
1186
  performanceInfo: api.infoDesempenho,
800
1187
  openAppsMemory: api.appsAbertos,
801
1188
  openAppsInfo: api.infoAppsAbertos,
1189
+ getLocation: api.obterLocalizacao,
1190
+ watchLocation: api.acompanharLocalizacao,
1191
+ stopLocationWatch: api.pararLocalizacao,
1192
+ onLocationChange: api.aoMudarLocalizacao,
1193
+ authenticateBiometric: api.autenticarBiometria,
1194
+ saveSecure: api.salvarSeguro,
1195
+ secureSet: api.salvarSeguro,
1196
+ readSecure: api.lerSeguro,
1197
+ secureGet: api.lerSeguro,
1198
+ readSecureItem: api.lerSeguroCompleto,
1199
+ deleteSecure: api.removerSeguro,
1200
+ removeSecure: api.removerSeguro,
1201
+ secureDelete: api.removerSeguro,
1202
+ listSecureKeys: api.listarSeguro,
1203
+ clearSecureStorage: api.limparSeguro,
802
1204
  permissionStatus: api.statusPermissoes,
803
1205
  requestNotificationPermission: api.solicitarPermissaoNotificacoes,
804
1206
  notificationPermissionStatus: api.statusPermissaoNotificacoes,
@@ -29,6 +29,9 @@
29
29
  "cancelarNotificacao", "cancelNotification",
30
30
  "lanterna", "flashlight",
31
31
  "alternarLanterna", "toggleFlashlight",
32
+ "tirarFoto", "takePhoto", "capturePhoto",
33
+ "capturarVideo", "captureVideo",
34
+ "escanearQRCode", "scanQRCode", "scanQrCode",
32
35
  "ouvirMic", "listenMic", "startMic",
33
36
  "pararMic", "stopMic",
34
37
  "solicitarPermissaoCamera", "requestCameraPermission",
@@ -36,9 +39,28 @@
36
39
  "solicitarPermissaoNotificacoes", "requestNotificationPermission",
37
40
  "escolherArquivo", "pickFile",
38
41
  "escolherImagem", "pickImage",
42
+ "salvarArquivo", "saveFile",
43
+ "lerArquivo", "readFile", "readStoredFile",
44
+ "excluirArquivo", "deleteFile", "removeFile",
45
+ "listarArquivos", "listFiles",
46
+ "abrirArquivo", "openFile",
47
+ "compartilharArquivo", "shareFile",
48
+ "baixarArquivo", "downloadFile",
49
+ "baixarBase64", "downloadBase64", "downloadFromBase64",
50
+ "baixarArquivoLocal", "downloadLocalFile", "downloadFromFile",
51
+ "definirPapelParede", "setWallpaper", "setPhoneWallpaper",
52
+ "infoPapelParede", "wallpaperInfo",
53
+ "abrirConfiguracaoPapelParede", "openWallpaperSettings",
39
54
  "abrirNoApp", "openInApp",
40
55
  "abrirForaDoApp", "openOutsideApp",
41
56
  "abrirUrl", "openUrl",
57
+ "obterLocalizacao", "getLocation",
58
+ "acompanharLocalizacao", "watchLocation",
59
+ "pararLocalizacao", "stopLocationWatch",
60
+ "autenticarBiometria", "authenticateBiometric",
61
+ "salvarSeguro", "saveSecure", "secureSet",
62
+ "lerSeguro", "readSecure", "secureGet",
63
+ "removerSeguro", "deleteSecure", "removeSecure",
42
64
  "vibrar", "vibrate",
43
65
  "toast",
44
66
  "statusPermissoes", "permissionStatus",
@@ -177,10 +199,11 @@
177
199
  }
178
200
 
179
201
  function add(kind, message, detail) {
202
+ var hasDetail = arguments.length > 2 && typeof detail !== "undefined";
180
203
  var entry = {
181
204
  kind: kind || "info",
182
205
  time: now(),
183
- message: detail ? message + "\n" + short(detail, MAX_DETAIL) : String(message || "")
206
+ message: hasDetail ? String(message || "") + "\n" + short(detail, MAX_DETAIL) : String(message || "")
184
207
  };
185
208
 
186
209
  keepEntry(entries, entry);
@@ -204,6 +227,114 @@
204
227
  }
205
228
  }
206
229
 
230
+ function consoleEntryText(entry) {
231
+ return "[" + (entry.time || "") + "] " + String(entry.kind || "info").toUpperCase() + "\n" + String(entry.message || "");
232
+ }
233
+
234
+ function networkEntryText(entry) {
235
+ var status = entry.status || "ERR";
236
+ var method = entry.method || "GET";
237
+ var title = method + " " + entry.url + " -> " + status + " (" + (entry.durationMs || 0) + "ms)";
238
+ var detail = {
239
+ type: entry.type,
240
+ ok: entry.ok,
241
+ status: entry.status,
242
+ statusText: entry.statusText,
243
+ durationMs: entry.durationMs,
244
+ requestBody: entry.requestBody,
245
+ responseBody: entry.responseBody,
246
+ error: entry.error
247
+ };
248
+
249
+ return "[" + (entry.time || "") + "] " + (entry.ok ? "NETWORK" : "ERROR") + "\n" + title + "\n" + short(detail, MAX_DETAIL);
250
+ }
251
+
252
+ function runtimeConsoleText(onlyErrors) {
253
+ var output = [];
254
+ entries.forEach(function (entry) {
255
+ if (!onlyErrors || entry.kind === "error") {
256
+ output.push(consoleEntryText(entry));
257
+ }
258
+ });
259
+ networkEntries.forEach(function (entry) {
260
+ if (!onlyErrors || !entry.ok) {
261
+ output.push(networkEntryText(entry));
262
+ }
263
+ });
264
+ if (!output.length) {
265
+ return onlyErrors ? "Nenhum erro registrado." : "Console vazio.";
266
+ }
267
+ return output.join("\n\n");
268
+ }
269
+
270
+ function fallbackCopyText(text) {
271
+ return new Promise(function (resolve, reject) {
272
+ var target = document.body || document.documentElement;
273
+ var textarea;
274
+ var copied = false;
275
+
276
+ if (!target || !document.createElement) {
277
+ reject(new Error("Clipboard indisponivel."));
278
+ return;
279
+ }
280
+
281
+ textarea = document.createElement("textarea");
282
+ textarea.value = text;
283
+ textarea.setAttribute("readonly", "readonly");
284
+ textarea.style.position = "fixed";
285
+ textarea.style.left = "-9999px";
286
+ textarea.style.top = "0";
287
+ textarea.style.opacity = "0";
288
+ target.appendChild(textarea);
289
+ textarea.focus();
290
+ textarea.select();
291
+
292
+ try {
293
+ copied = document.execCommand("copy");
294
+ } catch (error) {
295
+ copied = false;
296
+ }
297
+
298
+ target.removeChild(textarea);
299
+ if (copied) {
300
+ resolve();
301
+ } else {
302
+ reject(new Error("Nao foi possivel copiar."));
303
+ }
304
+ });
305
+ }
306
+
307
+ function copyTextToClipboard(text) {
308
+ if (typeof navigator !== "undefined" && navigator.clipboard && typeof navigator.clipboard.writeText === "function") {
309
+ return navigator.clipboard.writeText(text).catch(function () {
310
+ return fallbackCopyText(text);
311
+ });
312
+ }
313
+ return fallbackCopyText(text);
314
+ }
315
+
316
+ function copyConsole() {
317
+ var text = runtimeConsoleText(false);
318
+ return copyTextToClipboard(text).then(function () {
319
+ add("info", "Console copiado.");
320
+ return { ok: true, copied: "console" };
321
+ }, function (error) {
322
+ add("error", "Falha ao copiar console", error);
323
+ return { ok: false, copied: "console", message: error && error.message ? error.message : String(error) };
324
+ });
325
+ }
326
+
327
+ function copyErrors() {
328
+ var text = runtimeConsoleText(true);
329
+ return copyTextToClipboard(text).then(function () {
330
+ add("info", "Erros copiados.");
331
+ return { ok: true, copied: "errors" };
332
+ }, function (error) {
333
+ add("error", "Falha ao copiar erros", error);
334
+ return { ok: false, copied: "errors", message: error && error.message ? error.message : String(error) };
335
+ });
336
+ }
337
+
207
338
  function setActiveTab(tab) {
208
339
  activeTab = tab === "network" ? "network" : "console";
209
340
  if (!modal) {
@@ -359,6 +490,8 @@
359
490
  var consoleTab;
360
491
  var networkTab;
361
492
  var actions;
493
+ var copyButton;
494
+ var copyErrorsButton;
362
495
  var clearButton;
363
496
  var closeButton;
364
497
  var body;
@@ -375,14 +508,15 @@
375
508
  ".html2apk-console-modal{position:fixed;inset:0;z-index:2147483641;display:none;background:rgba(8,13,22,.42);padding:16px}",
376
509
  ".html2apk-console-modal.open{display:flex;align-items:flex-end;justify-content:center}",
377
510
  ".html2apk-console-panel{width:min(980px,calc(100vw - 24px));max-height:min(74vh,700px);background:#10141b;color:#edf4ff;border:1px solid rgba(255,255,255,.14);border-radius:14px;box-shadow:0 28px 90px rgba(0,0,0,.42);overflow:hidden;font-family:system-ui,Segoe UI,Arial}",
378
- ".html2apk-console-header{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:12px;padding:12px 14px;border-bottom:1px solid rgba(255,255,255,.12);background:#161d27;touch-action:none}",
379
- ".html2apk-console-header strong{font-size:15px}.html2apk-console-tabs{display:flex;gap:8px}.html2apk-console-actions{display:flex;gap:8px}",
511
+ ".html2apk-console-header{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:12px;padding:12px 14px;border-bottom:1px solid rgba(255,255,255,.12);background:#161d27;touch-action:none}",
512
+ ".html2apk-console-header strong{font-size:15px}.html2apk-console-tabs{display:flex;gap:8px;flex-wrap:wrap}.html2apk-console-actions{grid-column:1/-1;display:flex;gap:8px;flex-wrap:wrap;justify-content:flex-end}",
380
513
  ".html2apk-console-header button{border:1px solid rgba(255,255,255,.18);background:#202b3a;color:#edf4ff;border-radius:8px;padding:7px 10px;font:700 12px system-ui,Segoe UI,Arial}",
381
514
  ".html2apk-console-header button.active{background:#126fff;border-color:#126fff;color:#fff}",
382
515
  ".html2apk-console-body{height:min(59vh,570px);background:#0d1118}.html2apk-console-list{display:none;height:100%;overflow:auto;padding:10px}.html2apk-console-list.active{display:block}",
383
516
  ".html2apk-console-entry{border:1px solid rgba(255,255,255,.1);border-radius:10px;padding:8px 10px;margin-bottom:8px;background:#141b25}",
384
517
  ".html2apk-console-entry.error{border-color:rgba(255,110,123,.65)}.html2apk-console-entry.warn{border-color:rgba(231,173,76,.6)}.html2apk-console-entry.call,.html2apk-console-entry.network{border-color:rgba(98,160,255,.55)}.html2apk-console-entry.ok{border-color:rgba(67,201,133,.55)}",
385
- ".html2apk-console-meta{color:#9baabd;font-size:11px;font-weight:800;margin-bottom:4px}.html2apk-console-entry pre{white-space:pre-wrap;word-break:break-word;margin:0;font:12px ui-monospace,SFMono-Regular,Consolas,monospace;line-height:1.45}"
518
+ ".html2apk-console-meta{color:#9baabd;font-size:11px;font-weight:800;margin-bottom:4px}.html2apk-console-entry pre{white-space:pre-wrap;word-break:break-word;margin:0;font:12px ui-monospace,SFMono-Regular,Consolas,monospace;line-height:1.45}",
519
+ "@media (min-width:700px){.html2apk-console-header{grid-template-columns:auto minmax(0,1fr) auto}.html2apk-console-actions{grid-column:auto}}"
386
520
  ].join("");
387
521
  document.head.appendChild(style);
388
522
 
@@ -446,6 +580,14 @@
446
580
 
447
581
  actions = document.createElement("div");
448
582
  actions.className = "html2apk-console-actions";
583
+ copyButton = document.createElement("button");
584
+ copyButton.type = "button";
585
+ copyButton.textContent = "Copiar console";
586
+ copyButton.addEventListener("click", copyConsole);
587
+ copyErrorsButton = document.createElement("button");
588
+ copyErrorsButton.type = "button";
589
+ copyErrorsButton.textContent = "Copiar apenas erros";
590
+ copyErrorsButton.addEventListener("click", copyErrors);
449
591
  clearButton = document.createElement("button");
450
592
  clearButton.type = "button";
451
593
  clearButton.textContent = "Limpar";
@@ -454,6 +596,8 @@
454
596
  closeButton.type = "button";
455
597
  closeButton.textContent = "Fechar";
456
598
  closeButton.addEventListener("click", closeConsole);
599
+ actions.appendChild(copyButton);
600
+ actions.appendChild(copyErrorsButton);
457
601
  actions.appendChild(clearButton);
458
602
  actions.appendChild(closeButton);
459
603
 
@@ -766,6 +910,14 @@
766
910
  open: openConsole,
767
911
  close: closeConsole,
768
912
  clear: clearActiveTab,
913
+ copy: copyConsole,
914
+ copyErrors: copyErrors,
915
+ text: function (onlyErrors) {
916
+ return runtimeConsoleText(!!onlyErrors);
917
+ },
918
+ errorText: function () {
919
+ return runtimeConsoleText(true);
920
+ },
769
921
  log: add,
770
922
  network: addNetwork,
771
923
  entries: function () {