@jaak.ai/stamps 2.5.7 → 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.
Files changed (45) hide show
  1. package/dist/cjs/{index-D6NBn_qu.js → index-BcFBOmM6.js} +30 -4
  2. package/dist/cjs/index-BcFBOmM6.js.map +1 -0
  3. package/dist/cjs/jaak-stamps-webcomponent.cjs.js +2 -2
  4. package/dist/cjs/jaak-stamps.cjs.entry.js +264 -75
  5. package/dist/cjs/jaak-stamps.cjs.entry.js.map +1 -1
  6. package/dist/cjs/jaak-stamps.entry.cjs.js.map +1 -1
  7. package/dist/cjs/loader.cjs.js +2 -2
  8. package/dist/collection/collection-manifest.json +1 -1
  9. package/dist/collection/components/{my-component/my-component.js → jaak-stamps/jaak-stamps.js} +209 -37
  10. package/dist/collection/components/jaak-stamps/jaak-stamps.js.map +1 -0
  11. package/dist/collection/components/jaak-stamps/watermark-fallback.js.map +1 -0
  12. package/dist/collection/services/DetectionService.js +60 -38
  13. package/dist/collection/services/DetectionService.js.map +1 -1
  14. package/dist/components/index.js +20 -2
  15. package/dist/components/index.js.map +1 -1
  16. package/dist/components/jaak-stamps.js +266 -74
  17. package/dist/components/jaak-stamps.js.map +1 -1
  18. package/dist/esm/{index-BCfAsrzL.js → index-Cb0ILUvS.js} +30 -4
  19. package/dist/esm/index-Cb0ILUvS.js.map +1 -0
  20. package/dist/esm/jaak-stamps-webcomponent.js +3 -3
  21. package/dist/esm/jaak-stamps.entry.js +264 -75
  22. package/dist/esm/jaak-stamps.entry.js.map +1 -1
  23. package/dist/esm/loader.js +3 -3
  24. package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js +1 -1
  25. package/dist/jaak-stamps-webcomponent/jaak-stamps.entry.esm.js.map +1 -1
  26. package/dist/jaak-stamps-webcomponent/p-Cb0ILUvS.js +3 -0
  27. package/dist/jaak-stamps-webcomponent/p-Cb0ILUvS.js.map +1 -0
  28. package/dist/jaak-stamps-webcomponent/{p-63c2c085.entry.js → p-b4834625.entry.js} +3 -3
  29. package/dist/jaak-stamps-webcomponent/p-b4834625.entry.js.map +1 -0
  30. package/dist/types/components/{my-component/my-component.d.ts → jaak-stamps/jaak-stamps.d.ts} +12 -0
  31. package/dist/types/services/DetectionService.d.ts +5 -4
  32. package/package.json +4 -4
  33. package/dist/cjs/index-D6NBn_qu.js.map +0 -1
  34. package/dist/collection/components/my-component/my-component.js.map +0 -1
  35. package/dist/collection/components/my-component/watermark-fallback.js.map +0 -1
  36. package/dist/collection/utils/utils.js +0 -4
  37. package/dist/collection/utils/utils.js.map +0 -1
  38. package/dist/esm/index-BCfAsrzL.js.map +0 -1
  39. package/dist/jaak-stamps-webcomponent/p-63c2c085.entry.js.map +0 -1
  40. package/dist/jaak-stamps-webcomponent/p-BCfAsrzL.js +0 -3
  41. package/dist/jaak-stamps-webcomponent/p-BCfAsrzL.js.map +0 -1
  42. package/dist/types/utils/utils.d.ts +0 -1
  43. /package/dist/collection/components/{my-component/my-component.css → jaak-stamps/jaak-stamps.css} +0 -0
  44. /package/dist/collection/components/{my-component → jaak-stamps}/watermark-fallback.js +0 -0
  45. /package/dist/types/components/{my-component → jaak-stamps}/watermark-fallback.d.ts +0 -0
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-D6NBn_qu.js');
3
+ var index = require('./index-BcFBOmM6.js');
4
4
 
5
5
  class EventBusService {
6
6
  events = new Map();
@@ -798,15 +798,6 @@ class DetectionService {
798
798
  INPUT_SIZE = 320;
799
799
  CONFIDENCE_THRESHOLD = 0.6;
800
800
  useFloat16 = true;
801
- // Hoisted views for float32->float16 bit-twiddling. A fresh ArrayBuffer
802
- // per call was costing ~614K allocations per frame.
803
- _f32View = new Float32Array(1);
804
- _u32View = new Uint32Array(this._f32View.buffer);
805
- // Pre-allocated tensor buffers reused across frames. Same shape every time
806
- // (3 * INPUT_SIZE * INPUT_SIZE), and runInference is awaited before the next
807
- // preprocess runs, so reuse is safe.
808
- _tensorF32 = new Float32Array(3 * 320 * 320);
809
- _tensorF16 = new Uint16Array(3 * 320 * 320);
810
801
  // Canvas pool for optimization
811
802
  preprocessCanvas;
812
803
  preprocessCtx;
@@ -833,7 +824,7 @@ class DetectionService {
833
824
  try {
834
825
  const sessionOptions = this.deviceStrategy.getSessionOptions(this.debug);
835
826
  try {
836
- this.session = await window.ort.InferenceSession.create(this.MODEL_PATH, sessionOptions);
827
+ this.session = await this.createSession(this.MODEL_PATH, sessionOptions);
837
828
  this.detectFloat16Support();
838
829
  }
839
830
  catch (error) {
@@ -848,7 +839,7 @@ class DetectionService {
848
839
  if (isFloat16Error) {
849
840
  this.useFloat16 = false;
850
841
  try {
851
- this.session = await window.ort.InferenceSession.create(this.MODEL_PATH_FP32, sessionOptions);
842
+ this.session = await this.createSession(this.MODEL_PATH_FP32, sessionOptions);
852
843
  }
853
844
  catch (fp32Error) {
854
845
  // Intentar con opciones minimas de WASM
@@ -857,7 +848,7 @@ class DetectionService {
857
848
  graphOptimizationLevel: 'all',
858
849
  logSeverityLevel: this.debug ? 2 : 4,
859
850
  };
860
- this.session = await window.ort.InferenceSession.create(this.MODEL_PATH_FP32, wasmOptions);
851
+ this.session = await this.createSession(this.MODEL_PATH_FP32, wasmOptions);
861
852
  }
862
853
  }
863
854
  else if (isMemoryError) {
@@ -876,7 +867,7 @@ class DetectionService {
876
867
  };
877
868
  this.useFloat16 = false;
878
869
  try {
879
- this.session = await window.ort.InferenceSession.create(this.MODEL_PATH_FP32, fallbackOptions);
870
+ this.session = await this.createSession(this.MODEL_PATH_FP32, fallbackOptions);
880
871
  }
881
872
  catch (fallbackError) {
882
873
  throw new Error(`no available backend found. ERR: [wasm] RangeError: Out of memory`);
@@ -917,7 +908,7 @@ class DetectionService {
917
908
  // Load model
918
909
  const sessionOptions = this.deviceStrategy.getSessionOptions(this.debug);
919
910
  try {
920
- this.mobileNetSession = await window.ort.InferenceSession.create(this.MOBILENET_MODEL_PATH, sessionOptions);
911
+ this.mobileNetSession = await this.createSession(this.MOBILENET_MODEL_PATH, sessionOptions);
921
912
  }
922
913
  catch (error) {
923
914
  // Múltiples tipos de errores de memoria que pueden ocurrir
@@ -941,7 +932,7 @@ class DetectionService {
941
932
  sessionLogVerbosityLevel: 0,
942
933
  };
943
934
  try {
944
- this.mobileNetSession = await window.ort.InferenceSession.create(this.MOBILENET_MODEL_PATH, fallbackOptions);
935
+ this.mobileNetSession = await this.createSession(this.MOBILENET_MODEL_PATH, fallbackOptions);
945
936
  }
946
937
  catch (fallbackError) {
947
938
  throw new Error(`no available backend found. ERR: [wasm] RangeError: Out of memory`);
@@ -967,31 +958,25 @@ class DetectionService {
967
958
  if (!this.preprocessCanvas || !this.preprocessCtx) {
968
959
  this.initializeCanvasPool();
969
960
  }
970
- const SIZE = this.INPUT_SIZE;
971
- this.preprocessCtx.clearRect(0, 0, SIZE, SIZE);
972
- this.preprocessCtx.drawImage(video, 0, 0, SIZE, SIZE);
973
- const imageData = this.preprocessCtx.getImageData(0, 0, SIZE, SIZE);
961
+ this.preprocessCtx.clearRect(0, 0, this.INPUT_SIZE, this.INPUT_SIZE);
962
+ this.preprocessCtx.drawImage(video, 0, 0, this.INPUT_SIZE, this.INPUT_SIZE);
963
+ const imageData = this.preprocessCtx.getImageData(0, 0, this.INPUT_SIZE, this.INPUT_SIZE);
964
+ const [R, G, B] = [[], [], []];
974
965
  const { data } = imageData;
975
- const pixels = SIZE * SIZE;
976
- const gOffset = pixels;
977
- const bOffset = pixels * 2;
978
- const inv255 = 1 / 255;
966
+ for (let i = 0; i < data.length; i += 4) {
967
+ R.push(data[i] / 255);
968
+ G.push(data[i + 1] / 255);
969
+ B.push(data[i + 2] / 255);
970
+ }
971
+ const transposedData = new Float32Array(R.concat(G, B));
979
972
  if (this.useFloat16) {
980
- const out = this._tensorF16;
981
- for (let i = 0, p = 0; i < data.length; i += 4, p++) {
982
- out[p] = this.float32ToFloat16(data[i] * inv255);
983
- out[gOffset + p] = this.float32ToFloat16(data[i + 1] * inv255);
984
- out[bOffset + p] = this.float32ToFloat16(data[i + 2] * inv255);
973
+ const float16Data = new Uint16Array(transposedData.length);
974
+ for (let i = 0; i < transposedData.length; i++) {
975
+ float16Data[i] = this.float32ToFloat16(transposedData[i]);
985
976
  }
986
- return new window.ort.Tensor("float16", out, [1, 3, SIZE, SIZE]);
987
- }
988
- const out = this._tensorF32;
989
- for (let i = 0, p = 0; i < data.length; i += 4, p++) {
990
- out[p] = data[i] * inv255;
991
- out[gOffset + p] = data[i + 1] * inv255;
992
- out[bOffset + p] = data[i + 2] * inv255;
977
+ return new window.ort.Tensor("float16", float16Data, [1, 3, this.INPUT_SIZE, this.INPUT_SIZE]);
993
978
  }
994
- return new window.ort.Tensor("float32", out, [1, 3, SIZE, SIZE]);
979
+ return new window.ort.Tensor("float32", transposedData, [1, 3, this.INPUT_SIZE, this.INPUT_SIZE]);
995
980
  }
996
981
  async runInference(inputTensor) {
997
982
  if (!this.useDocumentDetector) {
@@ -1139,6 +1124,41 @@ class DetectionService {
1139
1124
  areAllSidesAligned(alignment) {
1140
1125
  return alignment.top && alignment.right && alignment.bottom && alignment.left;
1141
1126
  }
1127
+ // Reference counter so concurrent sessions share one filter and the last
1128
+ // one to finish restores the original console.warn.
1129
+ static _ortWarnActiveCount = 0;
1130
+ static _originalWarn = null;
1131
+ static _acquireWarnFilter() {
1132
+ if (DetectionService._ortWarnActiveCount === 0) {
1133
+ DetectionService._originalWarn = console.warn;
1134
+ console.warn = (...args) => {
1135
+ const msg = typeof args[0] === 'string' ? args[0] : '';
1136
+ if (!msg.includes('removing requested execution provider')) {
1137
+ DetectionService._originalWarn.apply(console, args);
1138
+ }
1139
+ };
1140
+ }
1141
+ DetectionService._ortWarnActiveCount++;
1142
+ }
1143
+ static _releaseWarnFilter() {
1144
+ DetectionService._ortWarnActiveCount--;
1145
+ if (DetectionService._ortWarnActiveCount === 0 && DetectionService._originalWarn) {
1146
+ console.warn = DetectionService._originalWarn;
1147
+ DetectionService._originalWarn = null;
1148
+ }
1149
+ }
1150
+ async createSession(modelPath, options) {
1151
+ // Keep the filter active for the full await: ORT may emit
1152
+ // "removing requested execution provider" during async resolution,
1153
+ // not only in the synchronous preamble of create().
1154
+ DetectionService._acquireWarnFilter();
1155
+ try {
1156
+ return await window.ort.InferenceSession.create(modelPath, options);
1157
+ }
1158
+ finally {
1159
+ DetectionService._releaseWarnFilter();
1160
+ }
1161
+ }
1142
1162
  cleanup() {
1143
1163
  this.cleanupCanvasPool();
1144
1164
  if (this.session) {
@@ -1183,8 +1203,10 @@ class DetectionService {
1183
1203
  }
1184
1204
  }
1185
1205
  float32ToFloat16(value) {
1186
- this._f32View[0] = value;
1187
- const f = this._u32View[0];
1206
+ const buffer = new ArrayBuffer(4);
1207
+ const view = new DataView(buffer);
1208
+ view.setFloat32(0, value, true);
1209
+ const f = view.getUint32(0, true);
1188
1210
  const sign = (f >> 31) & 0x1;
1189
1211
  const exp = (f >> 23) & 0xFF;
1190
1212
  const frac = f & 0x7FFFFF;
@@ -18647,7 +18669,7 @@ class LicenseValidationService {
18647
18669
  }
18648
18670
  }
18649
18671
 
18650
- const myComponentCss = ":host{display:block;width:100%;height:100%;font-family:system-ui, -apple-system, sans-serif;color:#1a1a1a}.detector-container{display:flex;flex-direction:column;align-items:center;width:100%;height:100%}h1{font-size:24px;font-weight:500;color:#333;margin:0 0 24px 0}.video-container{position:relative;width:100%;height:100%;background-color:#000;border:none;border-radius:8px;overflow:hidden}video,.detection-overlay{position:absolute;width:100%;height:100%;border-radius:8px}video.mirror,.detection-overlay.mirror{transform:rotateY(180deg)}.detection-overlay{z-index:1;pointer-events:none}video{z-index:0}video::-webkit-media-controls{display:none !important}video::-webkit-media-controls-panel{display:none !important}video::-webkit-media-controls-play-button{display:none !important}video::-webkit-media-controls-start-playback-button{display:none !important}video::-webkit-media-controls-enclosure{display:none !important}.detection-box{transition:opacity 0.2s ease}.status{margin-top:16px;font-size:12px;color:#666}.overlay-mask{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10;display:block;pointer-events:none}.card-outline{position:absolute;top:var(--mask-center-y, 50%);left:var(--mask-center-x, 50%);transform:translate(-50%, -50%);width:var(--mask-width, 88%);height:var(--mask-height, 55%);border:none;border-radius:4px;background:transparent;opacity:0.8}.side{position:absolute;background:#999;transition:background-color 0.3s ease;border-radius:1px}.side-top.aligned{border-left-color:#28a745;border-top-color:#28a745}.side-right.aligned{border-right-color:#28a745;border-top-color:#28a745}.side-bottom.aligned{border-left-color:#28a745;border-bottom-color:#28a745}.side-left.aligned{border-right-color:#28a745;border-bottom-color:#28a745}.side-top{top:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-right{top:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-bottom{bottom:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.side-left{bottom:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.guide-text{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);color:#ffffff;font-size:12px;font-weight:500;text-align:center;white-space:normal;padding:13px;max-width:300px;z-index:20;height:fit-content;width:fit-content;animation:slideInFromTopCentered 0.3s ease-out}.quality-indicator{position:absolute;top:20px;right:20px;display:flex;align-items:center;gap:8px;background:rgba(0, 0, 0, 0.8);padding:8px 12px;border-radius:20px;z-index:25;transition:all 0.3s ease}.quality-indicator.warning{background:rgba(255, 152, 0, 0.9)}.quality-indicator.good{background:rgba(76, 175, 80, 0.9)}.quality-score{color:#fff;font-size:12px;font-weight:600;min-width:30px;text-align:center}.quality-status{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:bold;color:#fff;transition:all 0.3s ease}.quality-status.ready{background:#4CAF50}.quality-status.not-ready{background:#f44336}.card-outline.quality-warning{animation:qualityWarning 1s ease-in-out infinite alternate}@keyframes qualityWarning{0%{box-shadow:0 0 0 3px rgba(255, 152, 0, 0.6)}100%{box-shadow:0 0 0 6px rgba(255, 152, 0, 0.3)}}.capture-animation{position:absolute;top:0;left:0;width:100%;height:100%;background:#fff;opacity:0;z-index:30;pointer-events:none;animation:captureFlash 0.6s ease-out}@keyframes captureFlash{0%{opacity:0}15%{opacity:0.8}30%{opacity:0}45%{opacity:0.4}60%{opacity:0}100%{opacity:0}}.card-outline.capturing{animation:pulseGreen 0.6s ease-out}@keyframes pulseGreen{0%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}50%{border-color:#28a745;box-shadow:0 0 0 8px rgba(40, 167, 69, 0.6)}100%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}}.flip-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showFlipInstruction 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.id-card-icon{width:80px;height:50px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:8px;position:relative;animation:flipCard 2s ease-in-out infinite;box-shadow:0 4px 12px rgba(0, 0, 0, 0.3)}.id-card-icon::before{content:'';position:absolute;top:8px;left:8px;width:16px;height:12px;background:rgba(255, 255, 255, 0.9);border-radius:2px}.id-card-icon::after{content:'';position:absolute;top:25px;left:8px;width:64px;height:3px;background:rgba(255, 255, 255, 0.7);border-radius:1px;box-shadow:0 6px 0 rgba(255, 255, 255, 0.7), 0 12px 0 rgba(255, 255, 255, 0.7)}.flip-text{margin-top:12px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px}@keyframes showFlipInstruction{0%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}}@keyframes flipCard{0%{transform:rotateY(0deg)}50%{transform:rotateY(180deg)}100%{transform:rotateY(360deg)}}.success-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showSuccessMessage 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.check-icon{width:80px;height:80px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:50%;position:relative;animation:bounceSuccess 0.6s ease-out;box-shadow:0 4px 16px rgba(108, 117, 125, 0.4);display:flex;align-items:center;justify-content:center}.check-icon::before{content:'';position:absolute;width:20px;height:35px;border:4px solid #fff;border-top:none;border-left:none;transform:rotate(45deg);}.success-text{margin-top:16px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;animation:fadeInUp 0.5s ease-out 0.4s both}@keyframes showSuccessMessage{0%{opacity:0;transform:translate(-50%, -50%) scale(0.5)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.9)}}@keyframes bounceSuccess{0%{transform:scale(0)}50%{transform:scale(1.1)}100%{transform:scale(1)}}@keyframes drawCheck{0%{width:0;height:0}50%{width:20px;height:0}100%{width:20px;height:35px}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}.skip-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:300px}.back-capture-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:320px}.back-capture-buttons{display:flex;gap:12px;align-items:center;justify-content:center;flex-wrap:wrap}.capture-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.capture-button:hover{background:#f8f9fa}.capture-button:active{background:#e9ecef;transform:translateY(1px)}.capture-button:disabled,.capture-button.primary-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}@media (max-width: 480px){.back-capture-section{bottom:16px;max-width:300px}.back-capture-buttons{flex-direction:column;gap:8px;width:100%}.capture-button,.back-capture-buttons .skip-button{width:100%;max-width:200px;padding:10px 20px;font-size:13px}.capture-button:disabled,.capture-button.primary-button:disabled,.skip-button:disabled{opacity:0.7 !important;cursor:not-allowed !important}}.skip-explanation{background:rgba(0, 0, 0, 0.5);color:#ffffff;padding:10px 16px;border-radius:8px;font-size:14px;font-weight:500;text-align:center;margin-bottom:12px;box-shadow:0 2px 8px rgba(0, 0, 0, 0.2);backdrop-filter:blur(12px);border:1px solid rgba(255, 255, 255, 0.1);animation:fadeIn 0.3s ease-in-out}@keyframes fadeIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.guide-text.performance-warning-text{animation:gentlePulse 2s ease-in-out infinite;background:rgba(255, 107, 107, 0.3);border:1px solid rgba(255, 107, 107, 0.5)}@keyframes gentlePulse{0%,100%{transform:translate(-50%, -50%) scale(1);background:rgba(255, 107, 107, 0.3)}50%{transform:translate(-50%, -50%) scale(1.02);background:rgba(255, 107, 107, 0.4)}}.skip-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.skip-button:hover{background:#f8f9fa}.skip-button:active{background:#e9ecef;transform:translateY(1px)}.skip-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}.camera-controls{position:absolute;top:16px;right:16px;z-index:25;display:flex;gap:8px;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}.camera-selector-button{height:32px;padding:0 10px;border:none;border-radius:8px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(12px);color:#ffffff;font-size:12px;font-weight:500;cursor:pointer;transition:all 0.2s ease;display:flex;align-items:center;justify-content:center;border:1px solid rgba(255, 255, 255, 0.1);white-space:nowrap;min-width:fit-content}.camera-selector-button:hover{background:rgba(0, 0, 0, 0.8);border-color:rgba(255, 255, 255, 0.2);transform:translateY(-1px)}.camera-selector-button:active{transform:translateY(0);background:rgba(0, 0, 0, 0.9)}.camera-selector-button:disabled,.camera-selector-button.loading{opacity:0.7;cursor:not-allowed;pointer-events:none}.camera-selector-button:disabled:hover,.camera-selector-button.loading:hover{transform:none;background:rgba(0, 0, 0, 0.6);border-color:rgba(255, 255, 255, 0.1)}.button-spinner{width:16px;height:16px;border:2px solid rgba(0, 0, 0, 0.2);border-top:2px solid #333333;border-radius:50%;animation:spin 1s linear infinite;display:inline-block;margin-right:8px;vertical-align:middle}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.camera-selector-dropdown{position:absolute;top:56px;right:16px;z-index:30;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;min-width:260px;max-width:300px;border:1px solid rgba(255, 255, 255, 0.1);overflow:hidden;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}@keyframes slideInFromTop{0%{opacity:0;transform:translateY(-8px) scale(0.95)}100%{opacity:1;transform:translateY(0) scale(1)}}@keyframes slideInFromTopCentered{0%{opacity:0;transform:translate(-50%, -50%) translateY(-8px) scale(0.95)}100%{opacity:1;transform:translate(-50%, -50%) translateY(0) scale(1)}}.camera-selector-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-bottom:1px solid rgba(255, 255, 255, 0.1)}.camera-selector-header span{font-weight:500;color:#ffffff;font-size:13px;opacity:0.9}.close-selector{width:20px;height:20px;border:none;background:none;color:rgba(255, 255, 255, 0.7);font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:all 0.2s ease}.close-selector:hover{background:rgba(255, 255, 255, 0.1);color:#ffffff}.camera-list{padding:4px 0;max-height:240px;overflow-y:auto}.camera-option{width:100%;padding:8px 12px;border:none;background:none;text-align:left;cursor:pointer;transition:all 0.2s ease;display:flex;justify-content:flex-start;align-items:center;gap:8px;color:rgba(255, 255, 255, 0.9)}.camera-option:hover{background:rgba(255, 255, 255, 0.08)}.camera-option.selected{background:rgba(255, 255, 255, 0.12);color:#ffffff}.camera-label{font-size:13px;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-right:0;font-weight:400;display:flex;align-items:center;gap:6px}.camera-label .autofocus-icon{font-size:12px;color:#ffffff !important;opacity:0.8;flex-shrink:0}.selected-indicator{font-size:14px;color:#ffffff;opacity:0.9;flex-shrink:0}.device-info{padding:6px 12px;border-top:1px solid rgba(255, 255, 255, 0.1);background:rgba(255, 255, 255, 0.05)}.device-info small{color:rgba(255, 255, 255, 0.6);font-size:11px;text-transform:capitalize;font-weight:400}@media (max-width: 480px){.camera-controls{top:12px;right:12px;gap:6px}.camera-selector-button{height:36px;padding:0 12px;font-size:11px;border-radius:6px}.camera-selector-dropdown{right:12px;top:48px;min-width:240px;max-width:calc(100vw - 24px)}.camera-selector-header{padding:6px 10px}.camera-option{padding:6px 10px}.device-info{padding:4px 10px}}.watermark{position:absolute;bottom:12px;right:12px;z-index:15;pointer-events:none;opacity:0.7}.watermark img{height:24px;width:auto;filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.component-status{position:absolute;top:16px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);display:flex;align-items:center;gap:6px;padding:6px 10px;z-index:40;height:32px;width:fit-content;animation:slideInFromTop 0.3s ease-out}.status-spinner{width:16px;height:16px;border:1px solid rgba(255, 255, 255, 0.3);border-top:1px solid #ffffff;border-radius:50%;animation:statusSpin 1s linear infinite;flex-shrink:0}.status-content{display:flex;flex-direction:column;gap:1px}.status-message{color:#ffffff;font-size:12px;font-weight:500;margin:0}.status-description{color:rgba(255, 255, 255, 0.7);font-size:10px;line-height:1.2;margin:0}@keyframes statusSpin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}@media (max-width: 480px){.component-status{top:12px;left:12px;padding:4px 8px;gap:4px;height:28px;border-radius:8px}.status-spinner{width:14px;height:14px}.status-message{font-size:11px}.status-description{font-size:9px}.guide-text{font-size:11px;padding:4px 8px;border-radius:8px}}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0, 0, 0, 0.85);backdrop-filter:blur(4px);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:30;border-radius:8px}.loading-spinner{width:50px;height:50px;border:3px solid rgba(255, 255, 255, 0.15);border-top:3px solid #28a745;border-radius:50%;animation:spin 1s ease-in-out infinite;margin-bottom:16px}.loading-text{color:#ffffff;font-size:14px;font-weight:500;text-align:center;opacity:0.9;margin-bottom:4px}.loading-description{color:rgba(255, 255, 255, 0.7);font-size:12px;text-align:center;opacity:0.8}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.performance-monitor{position:absolute;top:70px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromTop 0.3s ease-out;transition:all 0.3s ease}@keyframes slideInFromLeft{0%{opacity:0;transform:translateX(-20px) scale(0.95)}100%{opacity:1;transform:translateX(0) scale(1)}}.performance-expanded{padding:6px 10px}.metrics-row{display:flex;gap:8px;margin-bottom:4px}.metrics-row:last-child{margin-bottom:0}.metric-compact{display:flex;flex-direction:column;align-items:center;gap:2px;min-width:50px}.metric-label{font-size:9px;color:rgba(255, 255, 255, 0.6);font-weight:500;text-transform:uppercase;letter-spacing:0.3px}.metric-value{font-size:10px;font-weight:600;font-family:'Monaco', 'Menlo', 'Consolas', monospace;padding:2px 4px;border-radius:3px;text-align:center;white-space:nowrap}.metric-value.good{color:#4ade80;background:rgba(74, 222, 128, 0.1);border:1px solid rgba(74, 222, 128, 0.2)}.metric-value.warning{color:#fbbf24;background:rgba(251, 191, 36, 0.1);border:1px solid rgba(251, 191, 36, 0.2)}.metric-value.danger{color:#f87171;background:rgba(248, 113, 113, 0.1);border:1px solid rgba(248, 113, 113, 0.2)}@media (max-width: 480px){.performance-monitor{top:60px;left:12px;width:fit-content}.performance-expanded{padding:4px 8px}.metrics-row{gap:6px;margin-bottom:3px}.metric-compact{min-width:45px;gap:1px}.metric-label{font-size:8px}.metric-value{font-size:9px;padding:1px 3px}}.quality-monitor{position:absolute;top:200px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromLeft 0.3s ease-out;transition:all 0.3s ease}.quality-text{padding:6px 10px;font-size:11px;color:white;font-family:'Monaco', 'Menlo', 'Consolas', monospace;line-height:1.4}.quality-fail{color:#ff9999}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.7}}.metric-value.danger{animation:pulse 2s infinite}@media (max-width: 480px){.quality-monitor{top:160px;left:12px}.quality-text{padding:4px 8px;font-size:10px}}.manual-capture-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:300px}.manual-capture-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.manual-capture-button:hover{background:#f8f9fa}.manual-capture-button:active{background:#e9ecef;transform:translateY(1px)}.manual-capture-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}@media (max-width: 480px){.manual-capture-section{bottom:16px;max-width:280px}.manual-capture-button{padding:10px 20px;font-size:13px}.manual-capture-button:disabled{opacity:0.7 !important;cursor:not-allowed !important}}.license-error-container{display:flex;align-items:center;justify-content:center;padding:40px 20px;height:100%;width:100%;background:#f5f7fa;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif}.license-error-card{background:#ffffff;border-radius:8px;box-shadow:0 2px 12px rgba(0, 0, 0, 0.1);max-width:480px;width:100%;padding:48px 32px;text-align:center}.license-error-icon{color:#dc3545;margin-bottom:24px}.license-error-title{color:#2c3e50;font-size:24px;font-weight:600;margin:0 0 16px 0}.license-error-message{color:#495057;font-size:16px;line-height:1.6;margin:0 0 24px 0}.license-error-help{color:#6c757d;font-size:14px;margin:0 0 32px 0}.license-error-help a{color:#007bff;text-decoration:none;font-weight:500}.license-error-help a:hover{text-decoration:underline}.license-error-footer{color:#adb5bd;font-size:12px;font-weight:500;padding-top:24px;border-top:1px solid #e9ecef}@media (max-width: 640px){.license-error-card{padding:32px 24px}.license-error-title{font-size:20px}.license-error-message{font-size:14px}}";
18672
+ const jaakStampsCss = ":host{display:block;width:100%;height:100%;font-family:system-ui, -apple-system, sans-serif;color:#1a1a1a}.detector-container{display:flex;flex-direction:column;align-items:center;width:100%;height:100%}h1{font-size:24px;font-weight:500;color:#333;margin:0 0 24px 0}.video-container{position:relative;width:100%;height:100%;background-color:#000;border:none;border-radius:8px;overflow:hidden}video,.detection-overlay{position:absolute;width:100%;height:100%;border-radius:8px}video.mirror,.detection-overlay.mirror{transform:rotateY(180deg)}.detection-overlay{z-index:1;pointer-events:none}video{z-index:0}video::-webkit-media-controls{display:none !important}video::-webkit-media-controls-panel{display:none !important}video::-webkit-media-controls-play-button{display:none !important}video::-webkit-media-controls-start-playback-button{display:none !important}video::-webkit-media-controls-enclosure{display:none !important}.detection-box{transition:opacity 0.2s ease}.status{margin-top:16px;font-size:12px;color:#666}.overlay-mask{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10;display:block;pointer-events:none}.card-outline{position:absolute;top:var(--mask-center-y, 50%);left:var(--mask-center-x, 50%);transform:translate(-50%, -50%);width:var(--mask-width, 88%);height:var(--mask-height, 55%);border:none;border-radius:4px;background:transparent;opacity:0.8}.side{position:absolute;background:#999;transition:background-color 0.3s ease;border-radius:1px}.side-top.aligned{border-left-color:#28a745;border-top-color:#28a745}.side-right.aligned{border-right-color:#28a745;border-top-color:#28a745}.side-bottom.aligned{border-left-color:#28a745;border-bottom-color:#28a745}.side-left.aligned{border-right-color:#28a745;border-bottom-color:#28a745}.side-top{top:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-right{top:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-bottom{bottom:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.side-left{bottom:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.guide-text{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);color:#ffffff;font-size:12px;font-weight:500;text-align:center;white-space:normal;padding:13px;max-width:300px;z-index:20;height:fit-content;width:fit-content;animation:slideInFromTopCentered 0.3s ease-out}.quality-indicator{position:absolute;top:20px;right:20px;display:flex;align-items:center;gap:8px;background:rgba(0, 0, 0, 0.8);padding:8px 12px;border-radius:20px;z-index:25;transition:all 0.3s ease}.quality-indicator.warning{background:rgba(255, 152, 0, 0.9)}.quality-indicator.good{background:rgba(76, 175, 80, 0.9)}.quality-score{color:#fff;font-size:12px;font-weight:600;min-width:30px;text-align:center}.quality-status{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:bold;color:#fff;transition:all 0.3s ease}.quality-status.ready{background:#4CAF50}.quality-status.not-ready{background:#f44336}.card-outline.quality-warning{animation:qualityWarning 1s ease-in-out infinite alternate}@keyframes qualityWarning{0%{box-shadow:0 0 0 3px rgba(255, 152, 0, 0.6)}100%{box-shadow:0 0 0 6px rgba(255, 152, 0, 0.3)}}.capture-animation{position:absolute;top:0;left:0;width:100%;height:100%;background:#fff;opacity:0;z-index:30;pointer-events:none;animation:captureFlash 0.6s ease-out}@keyframes captureFlash{0%{opacity:0}15%{opacity:0.8}30%{opacity:0}45%{opacity:0.4}60%{opacity:0}100%{opacity:0}}.card-outline.capturing{animation:pulseGreen 0.6s ease-out}@keyframes pulseGreen{0%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}50%{border-color:#28a745;box-shadow:0 0 0 8px rgba(40, 167, 69, 0.6)}100%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}}.flip-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showFlipInstruction 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.id-card-icon{width:80px;height:50px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:8px;position:relative;animation:flipCard 2s ease-in-out infinite;box-shadow:0 4px 12px rgba(0, 0, 0, 0.3)}.id-card-icon::before{content:'';position:absolute;top:8px;left:8px;width:16px;height:12px;background:rgba(255, 255, 255, 0.9);border-radius:2px}.id-card-icon::after{content:'';position:absolute;top:25px;left:8px;width:64px;height:3px;background:rgba(255, 255, 255, 0.7);border-radius:1px;box-shadow:0 6px 0 rgba(255, 255, 255, 0.7), 0 12px 0 rgba(255, 255, 255, 0.7)}.flip-text{margin-top:12px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px}@keyframes showFlipInstruction{0%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}}@keyframes flipCard{0%{transform:rotateY(0deg)}50%{transform:rotateY(180deg)}100%{transform:rotateY(360deg)}}.success-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showSuccessMessage 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.check-icon{width:80px;height:80px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:50%;position:relative;animation:bounceSuccess 0.6s ease-out;box-shadow:0 4px 16px rgba(108, 117, 125, 0.4);display:flex;align-items:center;justify-content:center}.check-icon::before{content:'';position:absolute;width:20px;height:35px;border:4px solid #fff;border-top:none;border-left:none;transform:rotate(45deg);}.success-text{margin-top:16px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;animation:fadeInUp 0.5s ease-out 0.4s both}@keyframes showSuccessMessage{0%{opacity:0;transform:translate(-50%, -50%) scale(0.5)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.9)}}@keyframes bounceSuccess{0%{transform:scale(0)}50%{transform:scale(1.1)}100%{transform:scale(1)}}@keyframes drawCheck{0%{width:0;height:0}50%{width:20px;height:0}100%{width:20px;height:35px}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}.skip-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:300px}.back-capture-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:320px}.back-capture-buttons{display:flex;gap:12px;align-items:center;justify-content:center;flex-wrap:wrap}.capture-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.capture-button:hover{background:#f8f9fa}.capture-button:active{background:#e9ecef;transform:translateY(1px)}.capture-button:disabled,.capture-button.primary-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}@media (max-width: 480px){.back-capture-section{bottom:16px;max-width:300px}.back-capture-buttons{flex-direction:column;gap:8px;width:100%}.capture-button,.back-capture-buttons .skip-button{width:100%;max-width:200px;padding:10px 20px;font-size:13px}.capture-button:disabled,.capture-button.primary-button:disabled,.skip-button:disabled{opacity:0.7 !important;cursor:not-allowed !important}}.skip-explanation{background:rgba(0, 0, 0, 0.5);color:#ffffff;padding:10px 16px;border-radius:8px;font-size:14px;font-weight:500;text-align:center;margin-bottom:12px;box-shadow:0 2px 8px rgba(0, 0, 0, 0.2);backdrop-filter:blur(12px);border:1px solid rgba(255, 255, 255, 0.1);animation:fadeIn 0.3s ease-in-out}@keyframes fadeIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.guide-text.performance-warning-text{animation:gentlePulse 2s ease-in-out infinite;background:rgba(255, 107, 107, 0.3);border:1px solid rgba(255, 107, 107, 0.5)}@keyframes gentlePulse{0%,100%{transform:translate(-50%, -50%) scale(1);background:rgba(255, 107, 107, 0.3)}50%{transform:translate(-50%, -50%) scale(1.02);background:rgba(255, 107, 107, 0.4)}}.skip-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.skip-button:hover{background:#f8f9fa}.skip-button:active{background:#e9ecef;transform:translateY(1px)}.skip-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}.camera-controls{position:absolute;top:16px;right:16px;z-index:25;display:flex;gap:8px;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}.camera-selector-button{height:32px;padding:0 10px;border:none;border-radius:8px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(12px);color:#ffffff;font-size:12px;font-weight:500;cursor:pointer;transition:all 0.2s ease;display:flex;align-items:center;justify-content:center;border:1px solid rgba(255, 255, 255, 0.1);white-space:nowrap;min-width:fit-content}.camera-selector-button:hover{background:rgba(0, 0, 0, 0.8);border-color:rgba(255, 255, 255, 0.2);transform:translateY(-1px)}.camera-selector-button:active{transform:translateY(0);background:rgba(0, 0, 0, 0.9)}.camera-selector-button:disabled,.camera-selector-button.loading{opacity:0.7;cursor:not-allowed;pointer-events:none}.camera-selector-button:disabled:hover,.camera-selector-button.loading:hover{transform:none;background:rgba(0, 0, 0, 0.6);border-color:rgba(255, 255, 255, 0.1)}.button-spinner{width:16px;height:16px;border:2px solid rgba(0, 0, 0, 0.2);border-top:2px solid #333333;border-radius:50%;animation:spin 1s linear infinite;display:inline-block;margin-right:8px;vertical-align:middle}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.camera-selector-dropdown{position:absolute;top:56px;right:16px;z-index:30;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;min-width:260px;max-width:300px;border:1px solid rgba(255, 255, 255, 0.1);overflow:hidden;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}@keyframes slideInFromTop{0%{opacity:0;transform:translateY(-8px) scale(0.95)}100%{opacity:1;transform:translateY(0) scale(1)}}@keyframes slideInFromTopCentered{0%{opacity:0;transform:translate(-50%, -50%) translateY(-8px) scale(0.95)}100%{opacity:1;transform:translate(-50%, -50%) translateY(0) scale(1)}}.camera-selector-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-bottom:1px solid rgba(255, 255, 255, 0.1)}.camera-selector-header span{font-weight:500;color:#ffffff;font-size:13px;opacity:0.9}.close-selector{width:20px;height:20px;border:none;background:none;color:rgba(255, 255, 255, 0.7);font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:all 0.2s ease}.close-selector:hover{background:rgba(255, 255, 255, 0.1);color:#ffffff}.camera-list{padding:4px 0;max-height:240px;overflow-y:auto}.camera-option{width:100%;padding:8px 12px;border:none;background:none;text-align:left;cursor:pointer;transition:all 0.2s ease;display:flex;justify-content:flex-start;align-items:center;gap:8px;color:rgba(255, 255, 255, 0.9)}.camera-option:hover{background:rgba(255, 255, 255, 0.08)}.camera-option.selected{background:rgba(255, 255, 255, 0.12);color:#ffffff}.camera-label{font-size:13px;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-right:0;font-weight:400;display:flex;align-items:center;gap:6px}.camera-label .autofocus-icon{font-size:12px;color:#ffffff !important;opacity:0.8;flex-shrink:0}.selected-indicator{font-size:14px;color:#ffffff;opacity:0.9;flex-shrink:0}.device-info{padding:6px 12px;border-top:1px solid rgba(255, 255, 255, 0.1);background:rgba(255, 255, 255, 0.05)}.device-info small{color:rgba(255, 255, 255, 0.6);font-size:11px;text-transform:capitalize;font-weight:400}@media (max-width: 480px){.camera-controls{top:12px;right:12px;gap:6px}.camera-selector-button{height:36px;padding:0 12px;font-size:11px;border-radius:6px}.camera-selector-dropdown{right:12px;top:48px;min-width:240px;max-width:calc(100vw - 24px)}.camera-selector-header{padding:6px 10px}.camera-option{padding:6px 10px}.device-info{padding:4px 10px}}.watermark{position:absolute;bottom:12px;right:12px;z-index:15;pointer-events:none;opacity:0.7}.watermark img{height:24px;width:auto;filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.component-status{position:absolute;top:16px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);display:flex;align-items:center;gap:6px;padding:6px 10px;z-index:40;height:32px;width:fit-content;animation:slideInFromTop 0.3s ease-out}.status-spinner{width:16px;height:16px;border:1px solid rgba(255, 255, 255, 0.3);border-top:1px solid #ffffff;border-radius:50%;animation:statusSpin 1s linear infinite;flex-shrink:0}.status-content{display:flex;flex-direction:column;gap:1px}.status-message{color:#ffffff;font-size:12px;font-weight:500;margin:0}.status-description{color:rgba(255, 255, 255, 0.7);font-size:10px;line-height:1.2;margin:0}@keyframes statusSpin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}@media (max-width: 480px){.component-status{top:12px;left:12px;padding:4px 8px;gap:4px;height:28px;border-radius:8px}.status-spinner{width:14px;height:14px}.status-message{font-size:11px}.status-description{font-size:9px}.guide-text{font-size:11px;padding:4px 8px;border-radius:8px}}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0, 0, 0, 0.85);backdrop-filter:blur(4px);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:30;border-radius:8px}.loading-spinner{width:50px;height:50px;border:3px solid rgba(255, 255, 255, 0.15);border-top:3px solid #28a745;border-radius:50%;animation:spin 1s ease-in-out infinite;margin-bottom:16px}.loading-text{color:#ffffff;font-size:14px;font-weight:500;text-align:center;opacity:0.9;margin-bottom:4px}.loading-description{color:rgba(255, 255, 255, 0.7);font-size:12px;text-align:center;opacity:0.8}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.performance-monitor{position:absolute;top:70px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromTop 0.3s ease-out;transition:all 0.3s ease}@keyframes slideInFromLeft{0%{opacity:0;transform:translateX(-20px) scale(0.95)}100%{opacity:1;transform:translateX(0) scale(1)}}.performance-expanded{padding:6px 10px}.metrics-row{display:flex;gap:8px;margin-bottom:4px}.metrics-row:last-child{margin-bottom:0}.metric-compact{display:flex;flex-direction:column;align-items:center;gap:2px;min-width:50px}.metric-label{font-size:9px;color:rgba(255, 255, 255, 0.6);font-weight:500;text-transform:uppercase;letter-spacing:0.3px}.metric-value{font-size:10px;font-weight:600;font-family:'Monaco', 'Menlo', 'Consolas', monospace;padding:2px 4px;border-radius:3px;text-align:center;white-space:nowrap}.metric-value.good{color:#4ade80;background:rgba(74, 222, 128, 0.1);border:1px solid rgba(74, 222, 128, 0.2)}.metric-value.warning{color:#fbbf24;background:rgba(251, 191, 36, 0.1);border:1px solid rgba(251, 191, 36, 0.2)}.metric-value.danger{color:#f87171;background:rgba(248, 113, 113, 0.1);border:1px solid rgba(248, 113, 113, 0.2)}@media (max-width: 480px){.performance-monitor{top:60px;left:12px;width:fit-content}.performance-expanded{padding:4px 8px}.metrics-row{gap:6px;margin-bottom:3px}.metric-compact{min-width:45px;gap:1px}.metric-label{font-size:8px}.metric-value{font-size:9px;padding:1px 3px}}.quality-monitor{position:absolute;top:200px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromLeft 0.3s ease-out;transition:all 0.3s ease}.quality-text{padding:6px 10px;font-size:11px;color:white;font-family:'Monaco', 'Menlo', 'Consolas', monospace;line-height:1.4}.quality-fail{color:#ff9999}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.7}}.metric-value.danger{animation:pulse 2s infinite}@media (max-width: 480px){.quality-monitor{top:160px;left:12px}.quality-text{padding:4px 8px;font-size:10px}}.manual-capture-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:300px}.manual-capture-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.manual-capture-button:hover{background:#f8f9fa}.manual-capture-button:active{background:#e9ecef;transform:translateY(1px)}.manual-capture-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}@media (max-width: 480px){.manual-capture-section{bottom:16px;max-width:280px}.manual-capture-button{padding:10px 20px;font-size:13px}.manual-capture-button:disabled{opacity:0.7 !important;cursor:not-allowed !important}}.license-error-container{display:flex;align-items:center;justify-content:center;padding:40px 20px;height:100%;width:100%;background:#f5f7fa;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif}.license-error-card{background:#ffffff;border-radius:8px;box-shadow:0 2px 12px rgba(0, 0, 0, 0.1);max-width:480px;width:100%;padding:48px 32px;text-align:center}.license-error-icon{color:#dc3545;margin-bottom:24px}.license-error-title{color:#2c3e50;font-size:24px;font-weight:600;margin:0 0 16px 0}.license-error-message{color:#495057;font-size:16px;line-height:1.6;margin:0 0 24px 0}.license-error-help{color:#6c757d;font-size:14px;margin:0 0 32px 0}.license-error-help a{color:#007bff;text-decoration:none;font-weight:500}.license-error-help a:hover{text-decoration:underline}.license-error-footer{color:#adb5bd;font-size:12px;font-weight:500;padding-top:24px;border-top:1px solid #e9ecef}@media (max-width: 640px){.license-error-card{padding:32px 24px}.license-error-title{font-size:20px}.license-error-message{font-size:14px}}";
18651
18673
 
18652
18674
  const JaakStamps = class {
18653
18675
  constructor(hostRef) {
@@ -18677,7 +18699,7 @@ const JaakStamps = class {
18677
18699
  propagateTraceHeaderCorsUrls; // Comma-separated URLs or regex patterns
18678
18700
  metricsExportIntervalMillis = 60000; // Export metrics every 60 seconds
18679
18701
  license; // License key for validation
18680
- licenseEnvironment = 'prod'; // Environment for license validation
18702
+ licenseEnvironment = 'prod';
18681
18703
  traceId; // Optional trace ID for tracking
18682
18704
  appId = 'jaak-stamps-web'; // Application ID for license validation
18683
18705
  captureCompleted;
@@ -18725,6 +18747,10 @@ const JaakStamps = class {
18725
18747
  licenseValid = true; // License validation status
18726
18748
  licenseError = null; // License validation error message
18727
18749
  classificationDisabled = false; // Classification disabled at runtime due to errors
18750
+ _initialized = false;
18751
+ _initializing = false;
18752
+ _pendingEnvironmentRetry = false;
18753
+ _pendingInit = null;
18728
18754
  // Services
18729
18755
  licenseValidationService = null;
18730
18756
  serviceContainer;
@@ -18793,46 +18819,124 @@ const JaakStamps = class {
18793
18819
  // Canvas pool for optimized screenshot capture
18794
18820
  canvasPool = [];
18795
18821
  MAX_CANVAS_POOL_SIZE = 3;
18822
+ // Readiness gate for startCapture(). The host may call startCapture() before the
18823
+ // component finishes loading its resources (ONNX Runtime + WASM/models from the CDN).
18824
+ // On slow devices/networks this race made startCapture() abort with "El componente
18825
+ // está cargando recursos", leaving the screen black (TO-807). Instead of aborting we
18826
+ // wait for initialization to settle and retry. `initializationSettled` flips once
18827
+ // finalizeInitialization() has run; pending waiters are released via these callbacks.
18828
+ initializationSettled = false;
18829
+ initializationWaiters = [];
18830
+ // Max time startCapture() waits for a still-initializing component before giving up.
18831
+ // Sized for slow Android/network asset downloads; if resources never load (CDN down,
18832
+ // 404, CORS) the timeout fires and startCapture() returns the original error.
18833
+ START_CAPTURE_READY_TIMEOUT_MS = 15000;
18796
18834
  async componentWillLoad() {
18835
+ // @Watch handlers fire synchronously when Angular sets properties, which happens
18836
+ // BEFORE componentWillLoad (Stencil schedules componentWillLoad as a microtask after
18837
+ // connectedCallback, while Angular sets props synchronously in that same call stack).
18838
+ // @Watch('license') defers init to a macrotask so @Watch('licenseEnvironment') fires
18839
+ // first. By the time we resume here, init may already be in progress.
18840
+ await new Promise(resolve => setTimeout(resolve, 50));
18797
18841
  if (this.debug) {
18798
- console.log('[JAAK-DEBUG] componentWillLoad() - Props at initialization:');
18842
+ console.log('[JAAK-DEBUG] componentWillLoad() - Props after yield:');
18843
+ console.log('[JAAK-DEBUG] license:', this.license ? this.license.substring(0, 8) + '...' : 'EMPTY');
18844
+ console.log('[JAAK-DEBUG] licenseEnvironment:', this.licenseEnvironment);
18845
+ console.log('[JAAK-DEBUG] _initializing:', this._initializing);
18846
+ console.log('[JAAK-DEBUG] _initialized:', this._initialized);
18799
18847
  console.log('[JAAK-DEBUG] useDocumentDetector:', this.useDocumentDetector, '(type:', typeof this.useDocumentDetector, ')');
18800
18848
  console.log('[JAAK-DEBUG] useDocumentClassification:', this.useDocumentClassification);
18801
18849
  console.log('[JAAK-DEBUG] debug:', this.debug);
18802
- console.log('[JAAK-DEBUG] license:', this.license ? this.license.substring(0, 8) + '...' : 'EMPTY');
18803
- console.log('[JAAK-DEBUG] licenseEnvironment:', this.licenseEnvironment);
18804
18850
  console.log('[JAAK-DEBUG] alignmentTolerance:', this.alignmentTolerance);
18805
18851
  console.log('[JAAK-DEBUG] maskSize:', this.maskSize);
18806
18852
  console.log('[JAAK-DEBUG] captureDelay:', this.captureDelay);
18807
18853
  console.log('[JAAK-DEBUG] preferredCamera:', this.preferredCamera);
18808
18854
  console.log('[JAAK-DEBUG] appId:', this.appId);
18809
18855
  }
18810
- // License is required - always validate
18811
- // Wait a bit to ensure props are set from attributes
18812
- await new Promise(resolve => setTimeout(resolve, 50));
18813
- if (this.debug) {
18814
- console.log('[JAAK-DEBUG] Props AFTER 50ms wait:');
18815
- console.log('[JAAK-DEBUG] useDocumentDetector:', this.useDocumentDetector, '(type:', typeof this.useDocumentDetector, ')');
18816
- }
18817
- this.updateStatus('Validando licencia...', 'Verificando autorización de uso', 'initializing');
18818
- await this.validateLicense();
18819
- // If license validation failed, stop initialization
18820
- if (!this.licenseValid) {
18821
- if (this.debug) {
18822
- console.error('[JAAK-DEBUG] License validation FAILED. Stopping initialization.');
18856
+ if (!this.license) {
18857
+ if (!this._initialized && !this._initializing) {
18858
+ // No license and init not started — show blocking error UI
18859
+ this.licenseValid = false;
18860
+ this.licenseError = 'Se requiere una licencia para continuar. Por favor, proporcione una licencia usando el atributo "license".';
18861
+ this.updateStatus('Licencia requerida', 'Proporcione una licencia para continuar', 'error');
18823
18862
  }
18824
- return;
18825
18863
  }
18826
- if (this.debug) {
18827
- console.log('[JAAK-DEBUG] License validation OK');
18864
+ else if (!this._initialized && !this._initializing) {
18865
+ // License present but init hasn't started yet (no @Watch fired) — set initial status
18866
+ // so the first render already shows it without a redundant state change in componentDidLoad
18867
+ this.updateStatus('Validando licencia...', 'Verificando autorización de uso', 'initializing');
18828
18868
  }
18869
+ // Heavy initialization is deferred to componentDidLoad so the first render is not blocked
18829
18870
  }
18830
18871
  async componentDidLoad() {
18872
+ // If @Watch('license') already scheduled or started init, let it proceed
18873
+ if (!this.license || this._initialized || this._initializing || this._pendingInit !== null) {
18874
+ return;
18875
+ }
18876
+ await this.validateAndInitialize();
18877
+ }
18878
+ onLicenseChanged(newLicense) {
18879
+ if (!newLicense || this._initialized || this._initializing) {
18880
+ return;
18881
+ }
18882
+ // Defer init by one macrotask (setTimeout 0) so @Watch('licenseEnvironment') fires
18883
+ // first. Angular applies [license] before [licenseEnvironment] in template order,
18884
+ // meaning a synchronous start here would use the Stencil default "prod" for the env.
18885
+ if (this._pendingInit !== null)
18886
+ clearTimeout(this._pendingInit);
18887
+ this._pendingInit = setTimeout(async () => {
18888
+ this._pendingInit = null;
18889
+ if (!this._initialized && !this._initializing && this.license) {
18890
+ this.updateStatus('Validando licencia...', 'Verificando autorización de uso', 'initializing');
18891
+ await this.validateAndInitialize();
18892
+ }
18893
+ }, 0);
18894
+ }
18895
+ async onLicenseEnvironmentChanged() {
18896
+ // Always recreate the service so the correct environment URL is ready.
18897
+ this.licenseValidationService = new LicenseValidationService(this.licenseEnvironment);
18898
+ if (this._initializing) {
18899
+ // Validation is already in-flight with the wrong environment.
18900
+ // Flag a retry so validateAndInitialize restarts once the current attempt ends.
18901
+ this._pendingEnvironmentRetry = true;
18902
+ return;
18903
+ }
18904
+ // If @Watch('license') already scheduled a deferred init, that init will read
18905
+ // this.licenseEnvironment at fire time (after this handler returns), so the
18906
+ // correct URL will be used — no need to trigger a second init here.
18907
+ if (this._pendingInit !== null) {
18908
+ return;
18909
+ }
18910
+ if (this._initialized) {
18911
+ // Component was fully initialized with the wrong env URL (e.g. Ionic animation
18912
+ // delayed Angular's CD past the initial validateAndInitialize() cycle).
18913
+ // Re-validate the license only — services and camera are already running.
18914
+ await this.validateLicense();
18915
+ return;
18916
+ }
18917
+ if (this.license) {
18918
+ this.updateStatus('Validando licencia...', 'Verificando autorización de uso', 'initializing');
18919
+ await this.validateAndInitialize();
18920
+ }
18921
+ }
18922
+ async validateAndInitialize() {
18923
+ if (this._initialized || this._initializing) {
18924
+ return;
18925
+ }
18926
+ this._initializing = true;
18831
18927
  try {
18832
- // If license validation failed, stop initialization
18928
+ // Status was already set synchronously by the caller (componentWillLoad or @Watch)
18929
+ // to avoid a state change inside componentDidLoad that Stencil warns about.
18930
+ await this.validateLicense();
18833
18931
  if (!this.licenseValid) {
18932
+ if (this.debug) {
18933
+ console.error('[JAAK-DEBUG] License validation FAILED. Stopping initialization.');
18934
+ }
18834
18935
  return;
18835
18936
  }
18937
+ if (this.debug) {
18938
+ console.log('[JAAK-DEBUG] License validation OK');
18939
+ }
18836
18940
  this.updateStatus('Preparando capturador...', 'Configurando herramientas de captura', 'initializing');
18837
18941
  await this.initializeServices();
18838
18942
  this.updateStatus('Preparando funciones...', 'Configurando herramientas de trabajo', 'initializing');
@@ -18842,9 +18946,37 @@ const JaakStamps = class {
18842
18946
  if (this.debug) {
18843
18947
  this.initializePerformanceMonitor();
18844
18948
  }
18949
+ this._initialized = true;
18845
18950
  }
18846
18951
  catch (error) {
18847
- this.updateStatus('Error de inicialización', error.message, 'error');
18952
+ const errorMessage = error instanceof Error ? error.message : String(error);
18953
+ this.updateStatus('Error de inicialización', errorMessage, 'error');
18954
+ }
18955
+ finally {
18956
+ this._initializing = false;
18957
+ if (this._pendingEnvironmentRetry && this.license) {
18958
+ this._pendingEnvironmentRetry = false;
18959
+ if (this._initialized) {
18960
+ // Already fully initialized with wrong env URL — re-validate only the license.
18961
+ // Services and camera are already running and don't depend on the env URL.
18962
+ await this.validateLicense();
18963
+ }
18964
+ else {
18965
+ // Not yet initialized — full retry with the correct environment.
18966
+ this.updateStatus('Validando licencia...', 'Verificando autorización de uso', 'initializing');
18967
+ await this.validateAndInitialize();
18968
+ }
18969
+ }
18970
+ else {
18971
+ this._pendingEnvironmentRetry = false;
18972
+ }
18973
+ // Release any startCapture() calls waiting on readiness. On success this is a
18974
+ // no-op (finalizeInitialization already settled); on a terminal failure (invalid
18975
+ // license / init error) it lets startCapture() return its error without waiting
18976
+ // out the full timeout. Idempotent, so a completed retry above stays settled.
18977
+ if (!this._initializing) {
18978
+ this.settleInitialization();
18979
+ }
18848
18980
  }
18849
18981
  }
18850
18982
  async initializeServices() {
@@ -18894,14 +19026,14 @@ const JaakStamps = class {
18894
19026
  }
18895
19027
  async validateLicense() {
18896
19028
  if (!this.license) {
18897
- console.error('❌ No license provided for JAAK Stamps component');
18898
19029
  this.licenseError = 'Este componente requiere una licencia válida. Por favor, proporcione una licencia usando el atributo "license".';
18899
19030
  this.licenseValid = false;
18900
19031
  this.updateStatus('Error de licencia', 'Licencia no proporcionada', 'error');
18901
19032
  return;
18902
19033
  }
18903
19034
  try {
18904
- console.log('🔑 Validating license for JAAK Stamps...');
19035
+ if (this.debug)
19036
+ console.log('[jaak-stamps] validating license...');
18905
19037
  // Initialize license validation service
18906
19038
  this.licenseValidationService = new LicenseValidationService(this.licenseEnvironment);
18907
19039
  // Validate the license, passing traceId and appId if provided
@@ -18909,18 +19041,21 @@ const JaakStamps = class {
18909
19041
  if (response && response.access_token) {
18910
19042
  this.licenseValid = true;
18911
19043
  this.licenseError = null;
18912
- console.log('✅ License validated successfully for JAAK Stamps');
19044
+ if (this.debug)
19045
+ console.log('[jaak-stamps] license validated successfully');
18913
19046
  // Emit the traceId (either provided or generated)
18914
19047
  if (response.traceId) {
18915
19048
  this.traceIdGenerated.emit({ traceId: response.traceId });
18916
- console.log('📋 TraceId:', response.traceId);
19049
+ if (this.debug)
19050
+ console.log('[jaak-stamps] traceId:', response.traceId);
18917
19051
  }
18918
19052
  }
18919
19053
  else {
18920
19054
  this.licenseValid = false;
18921
19055
  this.licenseError = 'La licencia proporcionada no es válida. Por favor, verifique su licencia e intente nuevamente.';
18922
19056
  this.updateStatus('Error de licencia', 'Licencia inválida', 'error');
18923
- console.error('❌ Invalid license for JAAK Stamps');
19057
+ if (this.debug)
19058
+ console.error('[jaak-stamps] invalid license');
18924
19059
  }
18925
19060
  }
18926
19061
  catch (error) {
@@ -18928,7 +19063,8 @@ const JaakStamps = class {
18928
19063
  const licenseError = error;
18929
19064
  this.licenseError = licenseError.error_description || 'Error al validar la licencia. Por favor, verifique su conexión e intente nuevamente.';
18930
19065
  this.updateStatus('Error de licencia', 'Fallo en la validación', 'error');
18931
- console.error('❌ License validation failed for JAAK Stamps:', error);
19066
+ if (this.debug)
19067
+ console.error('[jaak-stamps] license validation failed:', error);
18932
19068
  }
18933
19069
  }
18934
19070
  async setupEventListeners() {
@@ -19031,6 +19167,42 @@ const JaakStamps = class {
19031
19167
  };
19032
19168
  }
19033
19169
  this.emitReadyEvent();
19170
+ this.settleInitialization();
19171
+ }
19172
+ // Releases any startCapture() calls that arrived while the component was still
19173
+ // initializing. Idempotent: safe to call more than once.
19174
+ settleInitialization() {
19175
+ if (this.initializationSettled) {
19176
+ return;
19177
+ }
19178
+ this.initializationSettled = true;
19179
+ const waiters = this.initializationWaiters;
19180
+ this.initializationWaiters = [];
19181
+ waiters.forEach(resolve => resolve());
19182
+ }
19183
+ // Waits until initialization settles (finalizeInitialization ran) or the timeout
19184
+ // elapses, whichever comes first. Resolves regardless of outcome; the caller
19185
+ // re-checks isComponentReady() afterwards to decide success/failure.
19186
+ waitForInitialization(timeoutMs) {
19187
+ if (this.initializationSettled) {
19188
+ return Promise.resolve();
19189
+ }
19190
+ return new Promise(resolve => {
19191
+ let settled = false;
19192
+ const done = () => {
19193
+ if (settled) {
19194
+ return;
19195
+ }
19196
+ settled = true;
19197
+ clearTimeout(timer);
19198
+ resolve();
19199
+ };
19200
+ const timer = setTimeout(() => {
19201
+ this.initializationWaiters = this.initializationWaiters.filter(w => w !== done);
19202
+ done();
19203
+ }, timeoutMs);
19204
+ this.initializationWaiters.push(done);
19205
+ });
19034
19206
  }
19035
19207
  updateStatus(message, description, type = 'loading') {
19036
19208
  this.currentStatus = {
@@ -19222,7 +19394,20 @@ const JaakStamps = class {
19222
19394
  }
19223
19395
  async startCapture() {
19224
19396
  const span = this.tracingService?.startSpan('capture.start');
19225
- const readyCheck = this.isComponentReady();
19397
+ let readyCheck = this.isComponentReady();
19398
+ // TO-807: absorb the start-before-ready race instead of aborting. When the
19399
+ // component is still loading its resources (transient initializing/loading
19400
+ // state) we wait for initialization to settle and re-check, rather than
19401
+ // returning an error immediately (which left the host with a black screen).
19402
+ // A terminal state ('error' / missing services) is not waited on.
19403
+ if (!readyCheck.ready && (readyCheck.status === 'initializing' || readyCheck.status === 'loading')) {
19404
+ this.updateStatus('El componente está cargando recursos', 'Espere a que termine la inicialización', 'loading');
19405
+ if (span) {
19406
+ this.tracingService?.setSpanAttribute(span, 'startCapture.waitedForReady', true);
19407
+ }
19408
+ await this.waitForInitialization(this.START_CAPTURE_READY_TIMEOUT_MS);
19409
+ readyCheck = this.isComponentReady();
19410
+ }
19226
19411
  if (!readyCheck.ready) {
19227
19412
  this.updateStatus(readyCheck.message, 'Espere a que termine la inicialización', readyCheck.status);
19228
19413
  if (span) {
@@ -20214,7 +20399,7 @@ const JaakStamps = class {
20214
20399
  isCapturing: false
20215
20400
  };
20216
20401
  const cameraInfo = this.cameraInfoWithAutofocus;
20217
- return (index.h("div", { key: 'a4f5254bb0f5198cfea553fb46d8841c4a410853', class: "detector-container" }, !this.licenseValid && this.licenseError && (index.h("div", { key: '48353567b7c23df1b44e035cd6b52dd692b338a6', class: "license-error-container" }, index.h("div", { key: '8a520b2cb936cae9ed64c513ae9e583f62d31b99', class: "license-error-card" }, index.h("svg", { key: 'dccdda293c28bd5d1e76bbff0060859a394a800c', class: "license-error-icon", width: "64", height: "64", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, index.h("path", { key: '09015453d8230c523abe8e5b4b800b4a55a81202', 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" }), index.h("path", { key: '5f9fd50412d21de2274e124c02dcc8e971fd23ee', d: "M12 8V12", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round" }), index.h("circle", { key: 'e534af1f29c6382c2c47623129a94d3aac3ec71e', cx: "12", cy: "16", r: "0.5", fill: "currentColor", stroke: "currentColor", "stroke-width": "1" })), index.h("h2", { key: '6ed105bb6e16d8ce7419bc64a0f69e7e2d7ed0ac', class: "license-error-title" }, "Licencia Requerida"), index.h("p", { key: 'c1d7e06b03a4af316e28836eb7a0f3729993ca1f', class: "license-error-message" }, this.licenseError), index.h("p", { key: '349ae281248b56524460cb18f36300e69c11ab5e', class: "license-error-help" }, "Contacte a soporte: ", index.h("a", { key: '62546d77296d220338953c3bd1820946164ff96c', href: "mailto:support@jaak.ai" }, "support@jaak.ai")), index.h("div", { key: 'b45ac9c2d9b9d65e4156d07a067e6c9214da8113', class: "license-error-footer" }, "JAAK Stamps")))), this.licenseValid && (index.h("div", { key: '77c274b14b69fe4220f71ef789de8c4943833fee', class: "video-container" }, index.h("video", { key: 'e036a408939aa5875e36a1f038bed98bfbfba134', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: {
20402
+ return (index.h("div", { key: 'ed400c7cc247fb12e5915e270207d3fa916362fd', class: "detector-container" }, !this.licenseValid && this.licenseError && (index.h("div", { key: 'd2f7507026d01d2fd381d365c9954067eb37c3f4', class: "license-error-container" }, index.h("div", { key: 'fc4ae7ae434a038a3730057a99a7d96d851dba43', class: "license-error-card" }, index.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" }, index.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" }), index.h("path", { key: '8c96a1638602eface0ae8c7377cdda04dffde5c7', d: "M12 8V12", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round" }), index.h("circle", { key: '7455af58b1827570713d778a4ef01bb0ffcfb553', cx: "12", cy: "16", r: "0.5", fill: "currentColor", stroke: "currentColor", "stroke-width": "1" })), index.h("h2", { key: '5779f8a2c4468ea185df620edecb3f79b7ae8bfb', class: "license-error-title" }, "Licencia Requerida"), index.h("p", { key: '8180147597c077798ff4ea4eb9309a3bec69c77f', class: "license-error-message" }, this.licenseError), index.h("p", { key: '2103af42045a8ee8385de273da8f98e23395469b', class: "license-error-help" }, "Contacte a soporte: ", index.h("a", { key: '8a7fe987b24600b123216c952c5b69f2c2afc0a0', href: "mailto:support@jaak.ai" }, "support@jaak.ai")), index.h("div", { key: 'fd00ef42e259669b7b7cc512478c9c925bd03415', class: "license-error-footer" }, "JAAK Stamps")))), this.licenseValid && (index.h("div", { key: '44a92ba0a4cc066bb4497f4f548fce0bd5dbc121', class: "video-container" }, index.h("video", { key: '9ef40fb28c0fea1b59657ae50134b4dc3df4cf24', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: {
20218
20403
  // Keep the element rendered (display: block) so the browser
20219
20404
  // loads stream metadata and fires 'loadedmetadata'. Hiding it
20220
20405
  // with display:none prevents metadata loading in Chrome and
@@ -20223,7 +20408,7 @@ const JaakStamps = class {
20223
20408
  // render context alive.
20224
20409
  opacity: captureState.isVideoActive ? '1' : '0',
20225
20410
  visibility: captureState.isVideoActive ? 'visible' : 'hidden',
20226
- } }), index.h("div", { key: '9389f1495814409aa849ce409a28cb578761df76', ref: el => this.detectionContainer = el, class: `detection-overlay ${this.shouldMirrorVideo ? 'mirror' : ''}` }, this.debug && this.detectionBoxes.map((box, index$1) => (index.h("div", { key: index$1, class: "detection-box", style: {
20411
+ } }), index.h("div", { key: 'f3d6e544bb8b13214894f71216badf092d2516dd', ref: el => this.detectionContainer = el, class: `detection-overlay ${this.shouldMirrorVideo ? 'mirror' : ''}` }, this.debug && this.detectionBoxes.map((box, index$1) => (index.h("div", { key: index$1, class: "detection-box", style: {
20227
20412
  position: 'absolute',
20228
20413
  left: `${box.x}px`,
20229
20414
  top: `${box.y}px`,
@@ -20232,9 +20417,9 @@ const JaakStamps = class {
20232
20417
  border: '2px solid #32406C',
20233
20418
  pointerEvents: 'none',
20234
20419
  boxSizing: 'border-box'
20235
- } })))), this.isMaskReady && (index.h("div", { key: '8050beb9a72279f3a2d54884fe598eca8b1929af', class: "overlay-mask" }, index.h("div", { key: '25028f1c5bab6b457563c098899c7645083be3a0', class: "card-outline" }, index.h("div", { key: 'd125b627160c671f1be837d1f957704d016dcf47', class: "side side-top" }), index.h("div", { key: '78ed55f578fcadba60e80c50315f4a82bbe9c15c', class: "side side-right" }), index.h("div", { key: '263d29d0844bfa88d68698d71630fd9b08debc26', class: "side side-bottom" }), index.h("div", { key: '18e528b4d877699add45d9b4e1e029a2bb993582', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("div", { key: '7e09980601c146cd48f80a04640a8a7d635e14d4', class: `guide-text ${this.showPerformanceMessage ? 'performance-warning-text' : ''}` }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("div", { key: '102e95d211e79d5c5d8e13ae717cb8913eae7ea1', class: "back-capture-section" }, index.h("div", { key: 'cde593bf62825392df6e950585e9950351bac430', class: "back-capture-buttons" }, (!this.useDocumentDetector || this.performanceDegradedMode || this.showManualCaptureButton) && (index.h("button", { key: '823b35d70e2bb1cf242e01578e921c8a091c7639', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-back' && (index.h("span", { key: 'b6f6e82f515cd588eeca60e7abe220d89b40cfe8', class: "button-spinner" })), "Capturar Reverso")), index.h("button", { key: '7d91e6b780958a36df9a859fb02d8224b0de46dc', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'skip-back' && (index.h("span", { key: '0d5bce58452c7f7b5e197dfa0eff959971357b3c', class: "button-spinner" })), this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
20420
+ } })))), this.isMaskReady && (index.h("div", { key: '312b9d7733e550a444e84d699c0e14834fc41057', class: "overlay-mask" }, index.h("div", { key: '2af1c83efb4d360ecada3301f4479bbf8845f032', class: "card-outline" }, index.h("div", { key: 'b6f57f153953e059056fae6d9f8a8220bfff4357', class: "side side-top" }), index.h("div", { key: 'e465f72b8834a04174dc88897b813517afa372fb', class: "side side-right" }), index.h("div", { key: '32e2785c7570f2002b6bc68b491007925d9fcad5', class: "side side-bottom" }), index.h("div", { key: 'bf2133811df7f516d9e3b37ae6019c90c0a68d0b', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("div", { key: 'de00829ac42fe7f3645cb515fb46cfabe415190b', class: `guide-text ${this.showPerformanceMessage ? 'performance-warning-text' : ''}` }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("div", { key: 'ae09d9c23635d87d31110781dc0ab3acb8ec4667', class: "back-capture-section" }, index.h("div", { key: 'a30a50ee0272140d16e32f582dd93f8de87d2a44', class: "back-capture-buttons" }, (!this.useDocumentDetector || this.performanceDegradedMode || this.showManualCaptureButton) && (index.h("button", { key: 'fb104cf6feaeca2f9542a2d5933bbf4f15c6771f', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-back' && (index.h("span", { key: '7a1bff56bae244c5b8d1d4c3fd523e7a20b4b5ce', class: "button-spinner" })), "Capturar Reverso")), index.h("button", { key: '59125b29ad55122e5ee9e8c2aaf9689a1fef588e', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'skip-back' && (index.h("span", { key: '4d0b8184b4eb82824d5d864fb1b196f22302506d', class: "button-spinner" })), this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
20236
20421
  ? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
20237
- : 'Saltar reverso')))), captureState.isVideoActive && (index.h("div", { key: 'e8fc7f2bb1a0ca8235a82ed368717205c7b63ad8', class: "camera-controls" }, index.h("button", { key: '691e4a97f2804666db5e32236619196d616227f7', class: `camera-selector-button ${this.isSwitchingCamera ? 'loading' : ''}`, onClick: () => this.toggleCameraSelector(), type: "button", title: "Seleccionar c\u00E1mara", disabled: this.isSwitchingCamera }, this.isSwitchingCamera ? (index.h("div", { class: "button-spinner" })) : ('Cámaras')))), this.showCameraSelector && cameraInfo.availableCameras.length > 0 && (index.h("div", { key: '80233723131262f516574a4dd2bb125970f7aeea', class: "camera-selector-dropdown" }, index.h("div", { key: 'c9284a77cd9f2d3e600924c9e5f38614f3d06dc4', class: "camera-selector-header" }, index.h("span", { key: '92a495d2b605a73a067791442a8c8617706d96bf' }, "Seleccionar C\u00E1mara"), index.h("button", { key: 'dc06382c154443ea9928740d53546dc2ab3ce662', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), index.h("div", { key: 'b520d0940ef51dc1a46641adaf834e105a16c964', class: "camera-list" }, cameraInfo.availableCameras.map((camera) => (index.h("button", { key: camera.id, class: `camera-option ${cameraInfo.selectedCameraId === camera.id ? 'selected' : ''}`, onClick: () => this.handleCameraSwitch(camera.id), type: "button" }, index.h("span", { class: "camera-label" }, camera.label || `Cámara ${cameraInfo.availableCameras.indexOf(camera) + 1}`, camera.hasAutofocus && (index.h("span", { class: "autofocus-icon", title: "Autofoco disponible" }, "\u29BF"))), cameraInfo.selectedCameraId === camera.id && (index.h("span", { class: "selected-indicator" }, "\u2713")))))), index.h("div", { key: '7df8908982af0142d5c02d9b09827f44e697cbb5', class: "device-info" }, index.h("small", { key: '5669aa375b7aa6cd98d36e5a77eb217e549a5d4d' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("div", { key: '40ed9bde6c2a0afadd8b949ce0cba59344b09379', class: "manual-capture-section" }, index.h("button", { key: 'f394d0a578888782a0a569e721f06bb642ebfbd4', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-front' && (index.h("span", { key: 'ec5ee370a578705ccc3a562763c906701a1e4da4', class: "button-spinner" })), "Capturar Frente"))))), captureState.isCapturing && (index.h("div", { key: '60520ec5cdd2632ac9cd645a5d8d820298b7388b', class: "capture-animation" })), captureState.showFlipAnimation && (index.h("div", { key: 'af3647bc1dd4e58630bf06bae37d7e5cd522c900', class: "flip-animation" }, index.h("div", { key: 'e974ee4fc12ebdef1cdcef288951abb0b98b368a', class: "id-card-icon" }), index.h("div", { key: 'd80764bf0e01069d4fa1367b250215024c8e41ee', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (index.h("div", { key: '07956e4e03dee2ebe64d5eeb1d0c6559dbd8c944', class: "success-animation" }, index.h("div", { key: 'd8cb39f4f7ad3adc9c16b2226b75d14b788636f6', class: "check-icon" }), index.h("div", { key: '9e3ddbac4946cd585de7c406d55915f532a390ed', class: "success-text" }, "\u00A1Proceso completado!"))), index.h("div", { key: 'b1ba412c9c4664eda9c024c3fa22e53f11acae4f', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (index.h("div", { key: 'b466904829c87bcb050500c9f24f507a6d42d56d', class: "status-spinner" })), index.h("div", { key: '3dcfe509f83adf031fadd2fa322a110456e5993b', class: "status-content" }, index.h("div", { key: '3a6c820251b30c0cbd63538b23872ac1191b5213', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (index.h("div", { key: 'a7c35a8a773d852035a35e77fb99cf475307a5f7', class: "status-description" }, this.currentStatus.description)))), this.debug && (index.h("div", { key: 'd581099e2e1aedc1aea88a4c26af59a118042070', class: "performance-monitor" }, index.h("div", { key: '18bf5ba4feb6515a454d0769fe8eb02fd37ef167', class: "performance-expanded" }, index.h("div", { key: '7765f916c26556046b8326befc796f9188aeca3f', class: "metrics-row" }, index.h("div", { key: '9533109a7a762a7d8a95f12f45dddb6833fa7326', class: "metric-compact" }, index.h("span", { key: 'c236d06e692694a58519e86f133f39a5184a485f', class: "metric-label" }, "FPS"), index.h("span", { key: '4a7b53c89c4822dc03c6683f6d6895f96458b95d', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), index.h("div", { key: 'd362ceb5edf7585fc7c05da7367a6affee83b942', class: "metric-compact" }, index.h("span", { key: '08779e8da11b1593f7d91b409b36184ed405dee6', class: "metric-label" }, "MEM"), index.h("span", { key: '64c15bcbaeca6760e578e4ff9386583938c0be87', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), index.h("div", { key: '1ef4f7a5aaae9ed9131bd45c7a4c227ad6419d93', class: "metrics-row" }, index.h("div", { key: 'c5d5ab2a81f7c8f5145253ece37e43981c68ccb8', class: "metric-compact" }, index.h("span", { key: '7ba45cae45b336c52adf1eb4588851be20475f4e', class: "metric-label" }, "INF"), index.h("span", { key: '1341edaafa1de81b304605378a8b1262f2f8a084', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), index.h("div", { key: '4c6edc10f88b776825a5a07643864fef4295c295', class: "metric-compact" }, index.h("span", { key: '7d58a7250a133b84f001a5313dce606fcd9eda82', class: "metric-label" }, "FRAME"), index.h("span", { key: 'de6a8e8475cd893546b939d5e3a7045a9d7df6dc', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), index.h("div", { key: 'a26794337b9fd17fc9bc6718461c5251b57c1949', class: "metrics-row" }, index.h("div", { key: 'd790ac359b04532a5e7aa44e2a2952522c1ceb64', class: "metric-compact" }, index.h("span", { key: '581c72f166de867bbbfe3bb654cdf89e94cfc27b', class: "metric-label" }, "DET"), index.h("span", { key: '8d478a6d5e84151504e2dd136e2b1ff1e26e0853', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), index.h("div", { key: 'bb7f6dad291a79b5f683e51e07558ca73c22ad98', class: "metric-compact" }, index.h("span", { key: '1d2626fb63026ce3eb9d0696501b08c0ddf4be09', class: "metric-label" }, "RATE"), index.h("span", { key: '8f8a67276ad40ce3fe9acab86c2a807f426d2a23', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), index.h("div", { key: 'a9ae57be92d04f8d5d7eae801c0a0ae7c548f770', class: "watermark" }, index.h("img", { key: 'c9cd4510dfff2265e3ae5d213845e7b5d8f9a343', src: "https://static.jaak.ai/commons/powered-by-jaak.png", alt: "Powered by Jaak", onError: (e) => {
20422
+ : 'Saltar reverso')))), captureState.isVideoActive && (index.h("div", { key: '1febf678f87e32916d3990677b41345ce034db4e', class: "camera-controls" }, index.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 ? (index.h("div", { class: "button-spinner" })) : ('Cámaras')))), this.showCameraSelector && cameraInfo.availableCameras.length > 0 && (index.h("div", { key: '4f6d101ce789b7fff227a6a2ce2479cb6255fd58', class: "camera-selector-dropdown" }, index.h("div", { key: 'f82222bb25162d2717d84c098c2a6540caa821f3', class: "camera-selector-header" }, index.h("span", { key: '0b1648857467c5749fe5d1d846d1786fa2e394c3' }, "Seleccionar C\u00E1mara"), index.h("button", { key: 'f406f08ad9dc2dcaced7e95aed013665c5fcfb4e', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), index.h("div", { key: 'a051beeab1c8447730d41e963cd3b95027922cab', class: "camera-list" }, cameraInfo.availableCameras.map((camera) => (index.h("button", { key: camera.id, class: `camera-option ${cameraInfo.selectedCameraId === camera.id ? 'selected' : ''}`, onClick: () => this.handleCameraSwitch(camera.id), type: "button" }, index.h("span", { class: "camera-label" }, camera.label || `Cámara ${cameraInfo.availableCameras.indexOf(camera) + 1}`, camera.hasAutofocus && (index.h("span", { class: "autofocus-icon", title: "Autofoco disponible" }, "\u29BF"))), cameraInfo.selectedCameraId === camera.id && (index.h("span", { class: "selected-indicator" }, "\u2713")))))), index.h("div", { key: 'c9c0702a57c105b02062f325cd413b531c7f802c', class: "device-info" }, index.h("small", { key: '584f4c1ce525b8ab03107329c92797737d5afad5' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("div", { key: 'e3e2a270d3c4e7accdd9bc8b37861af4fceaad5d', class: "manual-capture-section" }, index.h("button", { key: '1283629ce59fc82d7b3fc08d5573bc69238f7aab', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-front' && (index.h("span", { key: '5de009e4d2ff0de43355e95e1d69f12d7f2e65f8', class: "button-spinner" })), "Capturar Frente"))))), captureState.isCapturing && (index.h("div", { key: '975142144bb104d3ed532ac80fca19a7f237bb2b', class: "capture-animation" })), captureState.showFlipAnimation && (index.h("div", { key: '5d54b9130ed4d1e13b95cfeee8f9d694b5de68d3', class: "flip-animation" }, index.h("div", { key: 'c8b955180065d2b99f8e5f405b1ff92bc050af77', class: "id-card-icon" }), index.h("div", { key: 'f103b1871266886455d5bba094a0e119aa484595', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (index.h("div", { key: '5845c045d37099cf906f7c56d3c91568dd198bdf', class: "success-animation" }, index.h("div", { key: '430496db95f10834a43842e9ac97bdf0f025eb3b', class: "check-icon" }), index.h("div", { key: '0815cf6feffe8bd4ba84712c14d8f29c1a945577', class: "success-text" }, "\u00A1Proceso completado!"))), index.h("div", { key: 'f8a8a529850fc93e93f12810b7928e4700bac880', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (index.h("div", { key: '929ab612be046e97bb02ebe366b531864b90cc7a', class: "status-spinner" })), index.h("div", { key: 'defaf70aa54f2ccc222ddbe392cc47f7c143fc06', class: "status-content" }, index.h("div", { key: '685cca31f70e26d062f2d01b9ffd8c5a4a6b6f86', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (index.h("div", { key: '21bce9f6de9e5a50763ef83a25787225319dc5bf', class: "status-description" }, this.currentStatus.description)))), this.debug && (index.h("div", { key: '6e43560821e469e397b78ab44d1679a3ff8e330f', class: "performance-monitor" }, index.h("div", { key: 'afcde6083be4a1b4a76d5523e0a239926b76ff9a', class: "performance-expanded" }, index.h("div", { key: '0706e93c06bcaf2e573c0c2ac414c3ca7d3605ba', class: "metrics-row" }, index.h("div", { key: '32b755f0f81ae758d9ab0b2e75dd9d55ef52eef0', class: "metric-compact" }, index.h("span", { key: 'b1d8e773fdd1e333cd55bb5bbed2878a7e106474', class: "metric-label" }, "FPS"), index.h("span", { key: 'a6c53d389e7424f6696b6279bef267b28e112201', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), index.h("div", { key: 'a8c4157b696ae53e8ae5cee14f9e8b37be34a0a6', class: "metric-compact" }, index.h("span", { key: '0fa82dd84f6e0a9c16c1894021932e8ba355991a', class: "metric-label" }, "MEM"), index.h("span", { key: 'fc7f19be4e3fbbc82da538703ef9049e26571496', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), index.h("div", { key: '84bd7b6da8e91004ef6c554264b97b89fa6123e1', class: "metrics-row" }, index.h("div", { key: 'a2bc7af57fe87f5ef53ee00f76aa27675736c404', class: "metric-compact" }, index.h("span", { key: '59e9114f50cf4d69bfc447162001e8ab3214906a', class: "metric-label" }, "INF"), index.h("span", { key: 'c92e1f7e92127d85e0cdd647af44f1da4c46da57', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), index.h("div", { key: '5751e9dee9d55ddc559d1c60e68c4be7da2eb7a2', class: "metric-compact" }, index.h("span", { key: 'a34e394ab188e74a39eb77af4cf0108c8a2be1c3', class: "metric-label" }, "FRAME"), index.h("span", { key: '19165e851fc7f348e6cee93deccff0159c3bb982', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), index.h("div", { key: 'dde9e547b8c9e200624e525d15baaa4a18a27417', class: "metrics-row" }, index.h("div", { key: '770301e51025a446e29638d38e8e8a8dfb911882', class: "metric-compact" }, index.h("span", { key: '96750b7a455ef1f3353b870467afdff402fa605b', class: "metric-label" }, "DET"), index.h("span", { key: 'a7951b69998e1d13beee40b6677d55091a413a4c', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), index.h("div", { key: 'a82589fbccc749ef904a68933c6b850c3b69018c', class: "metric-compact" }, index.h("span", { key: 'ced7d42d2ebf2c4f20671c696b845263f4de4f4a', class: "metric-label" }, "RATE"), index.h("span", { key: '693c9735796ec276ca1d7e559aea250dafb0e5cf', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), index.h("div", { key: '3ff1698eae36352f9268839b86cef29144de297c', class: "watermark" }, index.h("img", { key: 'efa9735216e7caafdb67e10a5fc9363b503307b5', src: "https://static.jaak.ai/commons/powered-by-jaak.png", alt: "Powered by Jaak", onError: (e) => {
20238
20423
  const img = e.target;
20239
20424
  img.onerror = null;
20240
20425
  img.src = POWERED_BY_JAAK_FALLBACK;
@@ -20782,7 +20967,7 @@ const JaakStamps = class {
20782
20967
  await this.tracingService.flush();
20783
20968
  }
20784
20969
  catch (error) {
20785
- console.error('[my-component] Failed to flush traces:', error);
20970
+ console.error('[jaak-stamps] Failed to flush traces:', error);
20786
20971
  }
20787
20972
  }
20788
20973
  this.captureCompleted.emit(finalImages);
@@ -21055,8 +21240,12 @@ const JaakStamps = class {
21055
21240
  // If pool is full, let the canvas be garbage collected
21056
21241
  }
21057
21242
  static get assetsDirs() { return ["../../assets"]; }
21243
+ static get watchers() { return {
21244
+ "license": ["onLicenseChanged"],
21245
+ "licenseEnvironment": ["onLicenseEnvironmentChanged"]
21246
+ }; }
21058
21247
  };
21059
- JaakStamps.style = myComponentCss;
21248
+ JaakStamps.style = jaakStampsCss;
21060
21249
 
21061
21250
  exports.jaak_stamps = JaakStamps;
21062
21251
  //# sourceMappingURL=jaak-stamps.entry.cjs.js.map