@jaak.ai/stamps 2.5.8-dev.1 → 2.5.8-dev.2

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.
@@ -144,6 +144,18 @@ export class JaakStamps {
144
144
  // Canvas pool for optimized screenshot capture
145
145
  canvasPool = [];
146
146
  MAX_CANVAS_POOL_SIZE = 3;
147
+ // Readiness gate for startCapture(). The host may call startCapture() before the
148
+ // component finishes loading its resources (ONNX Runtime + WASM/models from the CDN).
149
+ // On slow devices/networks this race made startCapture() abort with "El componente
150
+ // está cargando recursos", leaving the screen black (TO-807). Instead of aborting we
151
+ // wait for initialization to settle and retry. `initializationSettled` flips once
152
+ // finalizeInitialization() has run; pending waiters are released via these callbacks.
153
+ initializationSettled = false;
154
+ initializationWaiters = [];
155
+ // Max time startCapture() waits for a still-initializing component before giving up.
156
+ // Sized for slow Android/network asset downloads; if resources never load (CDN down,
157
+ // 404, CORS) the timeout fires and startCapture() returns the original error.
158
+ START_CAPTURE_READY_TIMEOUT_MS = 15000;
147
159
  async componentWillLoad() {
148
160
  // @Watch handlers fire synchronously when Angular sets properties, which happens
149
161
  // BEFORE componentWillLoad (Stencil schedules componentWillLoad as a microtask after
@@ -283,6 +295,13 @@ export class JaakStamps {
283
295
  else {
284
296
  this._pendingEnvironmentRetry = false;
285
297
  }
298
+ // Release any startCapture() calls waiting on readiness. On success this is a
299
+ // no-op (finalizeInitialization already settled); on a terminal failure (invalid
300
+ // license / init error) it lets startCapture() return its error without waiting
301
+ // out the full timeout. Idempotent, so a completed retry above stays settled.
302
+ if (!this._initializing) {
303
+ this.settleInitialization();
304
+ }
286
305
  }
287
306
  }
288
307
  async initializeServices() {
@@ -473,6 +492,42 @@ export class JaakStamps {
473
492
  };
474
493
  }
475
494
  this.emitReadyEvent();
495
+ this.settleInitialization();
496
+ }
497
+ // Releases any startCapture() calls that arrived while the component was still
498
+ // initializing. Idempotent: safe to call more than once.
499
+ settleInitialization() {
500
+ if (this.initializationSettled) {
501
+ return;
502
+ }
503
+ this.initializationSettled = true;
504
+ const waiters = this.initializationWaiters;
505
+ this.initializationWaiters = [];
506
+ waiters.forEach(resolve => resolve());
507
+ }
508
+ // Waits until initialization settles (finalizeInitialization ran) or the timeout
509
+ // elapses, whichever comes first. Resolves regardless of outcome; the caller
510
+ // re-checks isComponentReady() afterwards to decide success/failure.
511
+ waitForInitialization(timeoutMs) {
512
+ if (this.initializationSettled) {
513
+ return Promise.resolve();
514
+ }
515
+ return new Promise(resolve => {
516
+ let settled = false;
517
+ const done = () => {
518
+ if (settled) {
519
+ return;
520
+ }
521
+ settled = true;
522
+ clearTimeout(timer);
523
+ resolve();
524
+ };
525
+ const timer = setTimeout(() => {
526
+ this.initializationWaiters = this.initializationWaiters.filter(w => w !== done);
527
+ done();
528
+ }, timeoutMs);
529
+ this.initializationWaiters.push(done);
530
+ });
476
531
  }
477
532
  updateStatus(message, description, type = 'loading') {
478
533
  this.currentStatus = {
@@ -664,7 +719,20 @@ export class JaakStamps {
664
719
  }
665
720
  async startCapture() {
666
721
  const span = this.tracingService?.startSpan('capture.start');
667
- const readyCheck = this.isComponentReady();
722
+ let readyCheck = this.isComponentReady();
723
+ // TO-807: absorb the start-before-ready race instead of aborting. When the
724
+ // component is still loading its resources (transient initializing/loading
725
+ // state) we wait for initialization to settle and re-check, rather than
726
+ // returning an error immediately (which left the host with a black screen).
727
+ // A terminal state ('error' / missing services) is not waited on.
728
+ if (!readyCheck.ready && (readyCheck.status === 'initializing' || readyCheck.status === 'loading')) {
729
+ this.updateStatus('El componente está cargando recursos', 'Espere a que termine la inicialización', 'loading');
730
+ if (span) {
731
+ this.tracingService?.setSpanAttribute(span, 'startCapture.waitedForReady', true);
732
+ }
733
+ await this.waitForInitialization(this.START_CAPTURE_READY_TIMEOUT_MS);
734
+ readyCheck = this.isComponentReady();
735
+ }
668
736
  if (!readyCheck.ready) {
669
737
  this.updateStatus(readyCheck.message, 'Espere a que termine la inicialización', readyCheck.status);
670
738
  if (span) {
@@ -1657,7 +1725,7 @@ export class JaakStamps {
1657
1725
  isCapturing: false
1658
1726
  };
1659
1727
  const cameraInfo = this.cameraInfoWithAutofocus;
1660
- return (h("div", { key: 'c0a28d9fd8a66fc8589c0111508ea959f07c143f', class: "detector-container" }, !this.licenseValid && this.licenseError && (h("div", { key: '1d0162e9d56c65f646884d12a3c73f4ed7e4c1fc', class: "license-error-container" }, h("div", { key: 'a50de01af3d60f4dbe2d17c9d059f79ad0fc8191', class: "license-error-card" }, h("svg", { key: '1dd6b599a3ca9bf818aec832638bab23b4ae4074', class: "license-error-icon", width: "64", height: "64", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, h("path", { key: 'c7175baf6b409428d820326e24522c7dd0b28811', d: "M12 22C12 22 20 18 20 12V5L12 2L4 5V12C4 18 12 22 12 22Z", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }), h("path", { key: '1ff3c700981b50ed670a499054ceb7b4afc12bc2', d: "M12 8V12", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round" }), h("circle", { key: 'f0adaa57bc3a218eedf92478f577a381fdb29268', cx: "12", cy: "16", r: "0.5", fill: "currentColor", stroke: "currentColor", "stroke-width": "1" })), h("h2", { key: '076e4aaf687e47d2553ba264af3de5ff67b03c60', class: "license-error-title" }, "Licencia Requerida"), h("p", { key: '607d2793649698c5686ee98af1e4c48921cba094', class: "license-error-message" }, this.licenseError), h("p", { key: '2d5eef84366fabdfea2106e7e5a198ab085c3a01', class: "license-error-help" }, "Contacte a soporte: ", h("a", { key: '91d7d22d72f78ae52bc8ddc0955b3ac559474579', href: "mailto:support@jaak.ai" }, "support@jaak.ai")), h("div", { key: '0ccf2e04602f740b6dd8551068c044d17691ba38', class: "license-error-footer" }, "JAAK Stamps")))), this.licenseValid && (h("div", { key: '339a6066a03d820c74cfd77ce7d582d2282dbf05', class: "video-container" }, h("video", { key: '680e86ddef25468ea253febbbc2a5b895b9be955', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: {
1728
+ return (h("div", { key: 'ed400c7cc247fb12e5915e270207d3fa916362fd', class: "detector-container" }, !this.licenseValid && this.licenseError && (h("div", { key: 'd2f7507026d01d2fd381d365c9954067eb37c3f4', class: "license-error-container" }, h("div", { key: 'fc4ae7ae434a038a3730057a99a7d96d851dba43', class: "license-error-card" }, h("svg", { key: '7361da72e3fda0147044873bc770460098a4b4f1', class: "license-error-icon", width: "64", height: "64", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, h("path", { key: '57a3dce14d47d4b91af6ff84ab39cde58814f573', d: "M12 22C12 22 20 18 20 12V5L12 2L4 5V12C4 18 12 22 12 22Z", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }), h("path", { key: '8c96a1638602eface0ae8c7377cdda04dffde5c7', d: "M12 8V12", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round" }), h("circle", { key: '7455af58b1827570713d778a4ef01bb0ffcfb553', cx: "12", cy: "16", r: "0.5", fill: "currentColor", stroke: "currentColor", "stroke-width": "1" })), h("h2", { key: '5779f8a2c4468ea185df620edecb3f79b7ae8bfb', class: "license-error-title" }, "Licencia Requerida"), h("p", { key: '8180147597c077798ff4ea4eb9309a3bec69c77f', class: "license-error-message" }, this.licenseError), h("p", { key: '2103af42045a8ee8385de273da8f98e23395469b', class: "license-error-help" }, "Contacte a soporte: ", h("a", { key: '8a7fe987b24600b123216c952c5b69f2c2afc0a0', href: "mailto:support@jaak.ai" }, "support@jaak.ai")), h("div", { key: 'fd00ef42e259669b7b7cc512478c9c925bd03415', class: "license-error-footer" }, "JAAK Stamps")))), this.licenseValid && (h("div", { key: '44a92ba0a4cc066bb4497f4f548fce0bd5dbc121', class: "video-container" }, h("video", { key: '9ef40fb28c0fea1b59657ae50134b4dc3df4cf24', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: {
1661
1729
  // Keep the element rendered (display: block) so the browser
1662
1730
  // loads stream metadata and fires 'loadedmetadata'. Hiding it
1663
1731
  // with display:none prevents metadata loading in Chrome and
@@ -1666,7 +1734,7 @@ export class JaakStamps {
1666
1734
  // render context alive.
1667
1735
  opacity: captureState.isVideoActive ? '1' : '0',
1668
1736
  visibility: captureState.isVideoActive ? 'visible' : 'hidden',
1669
- } }), h("div", { key: '521004bb615866dcf160086009810a543fe26a24', ref: el => this.detectionContainer = el, class: `detection-overlay ${this.shouldMirrorVideo ? 'mirror' : ''}` }, this.debug && this.detectionBoxes.map((box, index) => (h("div", { key: index, class: "detection-box", style: {
1737
+ } }), h("div", { key: 'f3d6e544bb8b13214894f71216badf092d2516dd', ref: el => this.detectionContainer = el, class: `detection-overlay ${this.shouldMirrorVideo ? 'mirror' : ''}` }, this.debug && this.detectionBoxes.map((box, index) => (h("div", { key: index, class: "detection-box", style: {
1670
1738
  position: 'absolute',
1671
1739
  left: `${box.x}px`,
1672
1740
  top: `${box.y}px`,
@@ -1675,9 +1743,9 @@ export class JaakStamps {
1675
1743
  border: '2px solid #32406C',
1676
1744
  pointerEvents: 'none',
1677
1745
  boxSizing: 'border-box'
1678
- } })))), this.isMaskReady && (h("div", { key: '7b4657df6b922001c726e004c326fe2bc5b20e65', class: "overlay-mask" }, h("div", { key: 'c5075a15c4cb994f5628df2be55c0b01077f20f3', class: "card-outline" }, h("div", { key: '5028c29cb564a77d7e29ea8fa5877da2d93659e3', class: "side side-top" }), h("div", { key: 'e8ce9c8bf2e771ad7862e5127a7d137755f85038', class: "side side-right" }), h("div", { key: '351b74462df5ae239fa0519a4cbd994b5e5c5bac', class: "side side-bottom" }), h("div", { key: 'e0e8bbe59c665db264ed6174d18cb258dfaddc30', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '6e995dbe5753cdbb54773c519389aa0822c3913a', class: `guide-text ${this.showPerformanceMessage ? 'performance-warning-text' : ''}` }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'e1a10fcb0e57765247aea911eb24d2c079cde8ee', class: "back-capture-section" }, h("div", { key: '3da2aacdce5badcab9017ea5316a124690773b55', class: "back-capture-buttons" }, (!this.useDocumentDetector || this.performanceDegradedMode || this.showManualCaptureButton) && (h("button", { key: 'e04c02cfa13728d7a771243167e9c94c92e3c934', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-back' && (h("span", { key: '47759f79990eb34c69e39b2b9f6d71e6c2d8bb33', class: "button-spinner" })), "Capturar Reverso")), h("button", { key: 'e41860634d1df34bfa30c2559fc85322088888ee', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'skip-back' && (h("span", { key: '44358375377638cd7a7c0699fba5739f38230727', class: "button-spinner" })), this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
1746
+ } })))), this.isMaskReady && (h("div", { key: '312b9d7733e550a444e84d699c0e14834fc41057', class: "overlay-mask" }, h("div", { key: '2af1c83efb4d360ecada3301f4479bbf8845f032', class: "card-outline" }, h("div", { key: 'b6f57f153953e059056fae6d9f8a8220bfff4357', class: "side side-top" }), h("div", { key: 'e465f72b8834a04174dc88897b813517afa372fb', class: "side side-right" }), h("div", { key: '32e2785c7570f2002b6bc68b491007925d9fcad5', class: "side side-bottom" }), h("div", { key: 'bf2133811df7f516d9e3b37ae6019c90c0a68d0b', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'de00829ac42fe7f3645cb515fb46cfabe415190b', class: `guide-text ${this.showPerformanceMessage ? 'performance-warning-text' : ''}` }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'ae09d9c23635d87d31110781dc0ab3acb8ec4667', class: "back-capture-section" }, h("div", { key: 'a30a50ee0272140d16e32f582dd93f8de87d2a44', class: "back-capture-buttons" }, (!this.useDocumentDetector || this.performanceDegradedMode || this.showManualCaptureButton) && (h("button", { key: 'fb104cf6feaeca2f9542a2d5933bbf4f15c6771f', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-back' && (h("span", { key: '7a1bff56bae244c5b8d1d4c3fd523e7a20b4b5ce', class: "button-spinner" })), "Capturar Reverso")), h("button", { key: '59125b29ad55122e5ee9e8c2aaf9689a1fef588e', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'skip-back' && (h("span", { key: '4d0b8184b4eb82824d5d864fb1b196f22302506d', class: "button-spinner" })), this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
1679
1747
  ? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
1680
- : 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: '2f3d35c91e490da2cb458f317fb2a7ece836ca01', class: "camera-controls" }, h("button", { key: '21718e9ab6932bb8bd88ca16cc820081b1ed6469', class: `camera-selector-button ${this.isSwitchingCamera ? 'loading' : ''}`, onClick: () => this.toggleCameraSelector(), type: "button", title: "Seleccionar c\u00E1mara", disabled: this.isSwitchingCamera }, this.isSwitchingCamera ? (h("div", { class: "button-spinner" })) : ('Cámaras')))), this.showCameraSelector && cameraInfo.availableCameras.length > 0 && (h("div", { key: '1873af9e9d2ff2761e750713606a3b06295fb2f7', class: "camera-selector-dropdown" }, h("div", { key: 'df3f445f7ff51efe298f070c56dbfdd217397ea0', class: "camera-selector-header" }, h("span", { key: 'f9f33b0c4ed4bf62e18020f54b90f6ce09e923aa' }, "Seleccionar C\u00E1mara"), h("button", { key: '28c29c4c82557c80e42b3f524041ab8e160cfce1', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: '95d06551595ca0b7d23ed16c8c4ead633f98ed2f', class: "camera-list" }, cameraInfo.availableCameras.map((camera) => (h("button", { key: camera.id, class: `camera-option ${cameraInfo.selectedCameraId === camera.id ? 'selected' : ''}`, onClick: () => this.handleCameraSwitch(camera.id), type: "button" }, h("span", { class: "camera-label" }, camera.label || `Cámara ${cameraInfo.availableCameras.indexOf(camera) + 1}`, camera.hasAutofocus && (h("span", { class: "autofocus-icon", title: "Autofoco disponible" }, "\u29BF"))), cameraInfo.selectedCameraId === camera.id && (h("span", { class: "selected-indicator" }, "\u2713")))))), h("div", { key: '6844114f4fa6e6a49d1194c4eaac71db4e2c2030', class: "device-info" }, h("small", { key: 'db82272ae5d253dd59fb42326e11ca1d50c70e3e' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '04bc5e0a469478dab6a5e1e957fa830be15555bc', class: "manual-capture-section" }, h("button", { key: '4fae0d4ca89e718a69143d6167dc3f295bc2fafa', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-front' && (h("span", { key: '5cb26e80c3216ce4940cf24ff1c18e0664a45eb7', class: "button-spinner" })), "Capturar Frente"))))), captureState.isCapturing && (h("div", { key: 'c4e99932aca8940f339aa80d1c9659223d026395', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: '4102bccccbae2163e7bb520761d871e07a30e34d', class: "flip-animation" }, h("div", { key: '1f9cd0f6d54f643251d24553fe6022f9045a50d6', class: "id-card-icon" }), h("div", { key: '1fba0dd3d1b25a1b39f3bda66c8706fd43bf996d', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: '08a0df4e7f7dbf5134cc89d2e300c04825b00495', class: "success-animation" }, h("div", { key: 'f94b29abea00c8293da5420981215f929fda19c3', class: "check-icon" }), h("div", { key: '01380c7e83e147bf841540178ba56d705c7f3258', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: '37d9824236643701b13de9d54adf83662625e6d5', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: '37d6da93b822edf62161a1657887ae1050d16e29', class: "status-spinner" })), h("div", { key: '6275ed024cbbeb436b7f04e0dd3bb7cfed1a6a9d', class: "status-content" }, h("div", { key: '9c983c432febd71eff737e395e3104711bf8b994', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: '721704c457c378f9563cd28e1e52fa35c68636d8', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: '0e0ed8e26c2ce73601a88ea74f97da447ad957a3', class: "performance-monitor" }, h("div", { key: 'ce46a8da032a38c4c7e6afaf706af5a7ba258f74', class: "performance-expanded" }, h("div", { key: '50566325fd8e54d9b5146c95a7965b262b5cbe7e', class: "metrics-row" }, h("div", { key: '2c255f1ab96cc0fb3e454b58a37dfe22f6df8ef2', class: "metric-compact" }, h("span", { key: '151e4bbf19f362bf32bb4ec838c5ff858ae5c8e6', class: "metric-label" }, "FPS"), h("span", { key: '4dcf58fd6d13106ca1fdb28331aebf6ff71c7886', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: '58d0745fb27b0b216fd3865e8fac4731811536c8', class: "metric-compact" }, h("span", { key: '0c16975cb5f84351fbad0b6daa6a8d1214f3226e', class: "metric-label" }, "MEM"), h("span", { key: 'cf8edc14a7ad690c80dfaeae4f2eaa0b07f88ca3', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: 'c50c14ac229f3a71fc37b256fd5caab77f0c43b2', class: "metrics-row" }, h("div", { key: 'b67d2719f42ae342c791d64a52e702af4d54bdbb', class: "metric-compact" }, h("span", { key: 'c2ecf87d853dc935b1dad5dd77adee52d1c56404', class: "metric-label" }, "INF"), h("span", { key: '86cad05a1846ed3e4e73175acd9a6acabbecb801', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: '82268593f3edea53553b35d4a7ef4c4677ab56c6', class: "metric-compact" }, h("span", { key: '937ff57182396d280cb753818b62829d73f7ca4d', class: "metric-label" }, "FRAME"), h("span", { key: '6f605b44484ed9e916f16fd50c91e7eb55d45172', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: '5c3c1bd6bde1d43338be02d3f711314fc7c9603c', class: "metrics-row" }, h("div", { key: 'bfa796075c5be71dbd8f78471ad2243392561edd', class: "metric-compact" }, h("span", { key: '9c2a2999f7026c96f24ef049c2779b1b63b7759a', class: "metric-label" }, "DET"), h("span", { key: '5ea705bcde4f534d23d23cf1bfbdbf228d394b22', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: 'cd73965baaed7267be05bf6a8990707b94ad3761', class: "metric-compact" }, h("span", { key: 'f7ddd8f95353e21c776c31298c4508e0ee9fffc3', class: "metric-label" }, "RATE"), h("span", { key: '2388af4b2ec742889a01b922a44f0d169e747b56', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: '822ae802489dc1621a2edebfb513c0966ea1d4b8', class: "watermark" }, h("img", { key: 'bc98d9afb9cb8336bfac25d3f1af3a09f49aafb3', src: "https://static.jaak.ai/commons/powered-by-jaak.png", alt: "Powered by Jaak", onError: (e) => {
1748
+ : 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: '1febf678f87e32916d3990677b41345ce034db4e', class: "camera-controls" }, h("button", { key: '79f00bac9d56347de1fc08b2e06be1c0e996a855', class: `camera-selector-button ${this.isSwitchingCamera ? 'loading' : ''}`, onClick: () => this.toggleCameraSelector(), type: "button", title: "Seleccionar c\u00E1mara", disabled: this.isSwitchingCamera }, this.isSwitchingCamera ? (h("div", { class: "button-spinner" })) : ('Cámaras')))), this.showCameraSelector && cameraInfo.availableCameras.length > 0 && (h("div", { key: '4f6d101ce789b7fff227a6a2ce2479cb6255fd58', class: "camera-selector-dropdown" }, h("div", { key: 'f82222bb25162d2717d84c098c2a6540caa821f3', class: "camera-selector-header" }, h("span", { key: '0b1648857467c5749fe5d1d846d1786fa2e394c3' }, "Seleccionar C\u00E1mara"), h("button", { key: 'f406f08ad9dc2dcaced7e95aed013665c5fcfb4e', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: 'a051beeab1c8447730d41e963cd3b95027922cab', class: "camera-list" }, cameraInfo.availableCameras.map((camera) => (h("button", { key: camera.id, class: `camera-option ${cameraInfo.selectedCameraId === camera.id ? 'selected' : ''}`, onClick: () => this.handleCameraSwitch(camera.id), type: "button" }, h("span", { class: "camera-label" }, camera.label || `Cámara ${cameraInfo.availableCameras.indexOf(camera) + 1}`, camera.hasAutofocus && (h("span", { class: "autofocus-icon", title: "Autofoco disponible" }, "\u29BF"))), cameraInfo.selectedCameraId === camera.id && (h("span", { class: "selected-indicator" }, "\u2713")))))), h("div", { key: 'c9c0702a57c105b02062f325cd413b531c7f802c', class: "device-info" }, h("small", { key: '584f4c1ce525b8ab03107329c92797737d5afad5' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'e3e2a270d3c4e7accdd9bc8b37861af4fceaad5d', class: "manual-capture-section" }, h("button", { key: '1283629ce59fc82d7b3fc08d5573bc69238f7aab', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-front' && (h("span", { key: '5de009e4d2ff0de43355e95e1d69f12d7f2e65f8', class: "button-spinner" })), "Capturar Frente"))))), captureState.isCapturing && (h("div", { key: '975142144bb104d3ed532ac80fca19a7f237bb2b', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: '5d54b9130ed4d1e13b95cfeee8f9d694b5de68d3', class: "flip-animation" }, h("div", { key: 'c8b955180065d2b99f8e5f405b1ff92bc050af77', class: "id-card-icon" }), h("div", { key: 'f103b1871266886455d5bba094a0e119aa484595', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: '5845c045d37099cf906f7c56d3c91568dd198bdf', class: "success-animation" }, h("div", { key: '430496db95f10834a43842e9ac97bdf0f025eb3b', class: "check-icon" }), h("div", { key: '0815cf6feffe8bd4ba84712c14d8f29c1a945577', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: 'f8a8a529850fc93e93f12810b7928e4700bac880', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: '929ab612be046e97bb02ebe366b531864b90cc7a', class: "status-spinner" })), h("div", { key: 'defaf70aa54f2ccc222ddbe392cc47f7c143fc06', class: "status-content" }, h("div", { key: '685cca31f70e26d062f2d01b9ffd8c5a4a6b6f86', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: '21bce9f6de9e5a50763ef83a25787225319dc5bf', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: '6e43560821e469e397b78ab44d1679a3ff8e330f', class: "performance-monitor" }, h("div", { key: 'afcde6083be4a1b4a76d5523e0a239926b76ff9a', class: "performance-expanded" }, h("div", { key: '0706e93c06bcaf2e573c0c2ac414c3ca7d3605ba', class: "metrics-row" }, h("div", { key: '32b755f0f81ae758d9ab0b2e75dd9d55ef52eef0', class: "metric-compact" }, h("span", { key: 'b1d8e773fdd1e333cd55bb5bbed2878a7e106474', class: "metric-label" }, "FPS"), h("span", { key: 'a6c53d389e7424f6696b6279bef267b28e112201', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: 'a8c4157b696ae53e8ae5cee14f9e8b37be34a0a6', class: "metric-compact" }, h("span", { key: '0fa82dd84f6e0a9c16c1894021932e8ba355991a', class: "metric-label" }, "MEM"), h("span", { key: 'fc7f19be4e3fbbc82da538703ef9049e26571496', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: '84bd7b6da8e91004ef6c554264b97b89fa6123e1', class: "metrics-row" }, h("div", { key: 'a2bc7af57fe87f5ef53ee00f76aa27675736c404', class: "metric-compact" }, h("span", { key: '59e9114f50cf4d69bfc447162001e8ab3214906a', class: "metric-label" }, "INF"), h("span", { key: 'c92e1f7e92127d85e0cdd647af44f1da4c46da57', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: '5751e9dee9d55ddc559d1c60e68c4be7da2eb7a2', class: "metric-compact" }, h("span", { key: 'a34e394ab188e74a39eb77af4cf0108c8a2be1c3', class: "metric-label" }, "FRAME"), h("span", { key: '19165e851fc7f348e6cee93deccff0159c3bb982', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: 'dde9e547b8c9e200624e525d15baaa4a18a27417', class: "metrics-row" }, h("div", { key: '770301e51025a446e29638d38e8e8a8dfb911882', class: "metric-compact" }, h("span", { key: '96750b7a455ef1f3353b870467afdff402fa605b', class: "metric-label" }, "DET"), h("span", { key: 'a7951b69998e1d13beee40b6677d55091a413a4c', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: 'a82589fbccc749ef904a68933c6b850c3b69018c', class: "metric-compact" }, h("span", { key: 'ced7d42d2ebf2c4f20671c696b845263f4de4f4a', class: "metric-label" }, "RATE"), h("span", { key: '693c9735796ec276ca1d7e559aea250dafb0e5cf', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: '3ff1698eae36352f9268839b86cef29144de297c', class: "watermark" }, h("img", { key: 'efa9735216e7caafdb67e10a5fc9363b503307b5', src: "https://static.jaak.ai/commons/powered-by-jaak.png", alt: "Powered by Jaak", onError: (e) => {
1681
1749
  const img = e.target;
1682
1750
  img.onerror = null;
1683
1751
  img.src = POWERED_BY_JAAK_FALLBACK;