|
|
@@ -68,6 +68,10 @@ export class AnalyzerComponent implements OnInit, OnDestroy {
|
|
|
|
|
|
// ── Engine selection ───────────────────────────────────────────────────────
|
|
|
engine = signal<SnapEngine>('onnx');
|
|
|
+ /** 'local' for browser engines, 'backend' for socket */
|
|
|
+ engineMode = computed<'local' | 'backend'>(() =>
|
|
|
+ this.engine() === 'socket' ? 'backend' : 'local'
|
|
|
+ );
|
|
|
|
|
|
// ── Browser-engine state ───────────────────────────────────────────────────
|
|
|
selectedFile: File | null = null;
|
|
|
@@ -76,6 +80,15 @@ export class AnalyzerComponent implements OnInit, OnDestroy {
|
|
|
loading = false;
|
|
|
isDragging = false;
|
|
|
|
|
|
+ // ── Batch ingestion state (backend/socket mode only) ───────────────────────
|
|
|
+ batchQueue = signal<File[]>([]);
|
|
|
+ currentBatchIndex = signal<number>(0);
|
|
|
+ isBatchActive = computed(() => this.batchQueue().length > 0);
|
|
|
+ /** Accumulates per-image results for 4.2/4.3 audit trail */
|
|
|
+ sessionManifest: any[] = [];
|
|
|
+
|
|
|
+ get totalBatchCount(): number { return this.batchQueue().length + this.currentBatchIndex(); }
|
|
|
+
|
|
|
// ── Socket-engine state ────────────────────────────────────────────────────
|
|
|
/** 'webcam' = live camera snap | 'gallery' = file from device storage */
|
|
|
socketInputMode = signal<'webcam' | 'gallery'>('webcam');
|
|
|
@@ -99,6 +112,14 @@ export class AnalyzerComponent implements OnInit, OnDestroy {
|
|
|
this.stopWebcam();
|
|
|
}
|
|
|
});
|
|
|
+
|
|
|
+ // Safety: switching to local while a batch is queued must clear the queue
|
|
|
+ // immediately — local WASM cannot handle multi-file sequential load
|
|
|
+ effect(() => {
|
|
|
+ if (this.engineMode() === 'local' && this.isBatchActive()) {
|
|
|
+ this.abortBatch();
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
ngOnInit(): void {}
|
|
|
@@ -127,8 +148,32 @@ export class AnalyzerComponent implements OnInit, OnDestroy {
|
|
|
// ── Browser engine — file upload ───────────────────────────────────────────
|
|
|
|
|
|
onFileSelected(event: any): void {
|
|
|
- const file = event.target.files[0];
|
|
|
- if (file) this.handleFile(file);
|
|
|
+ const files: FileList = event.target.files;
|
|
|
+ if (!files || files.length === 0) return;
|
|
|
+
|
|
|
+ if (this.engineMode() === 'local') {
|
|
|
+ this.processSingleFile(files[0]);
|
|
|
+ } else {
|
|
|
+ this.batchQueue.set(Array.from(files));
|
|
|
+ this.currentBatchIndex.set(0);
|
|
|
+ this.sessionManifest = [];
|
|
|
+ this.startBatchProcessing();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private processSingleFile(file: File): void {
|
|
|
+ this.handleFile(file);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Placeholder — actual network loop is Task 4.2. Do NOT send files here. */
|
|
|
+ private startBatchProcessing(): void {
|
|
|
+ // Queue is loaded; progress HUD is now visible via isBatchActive().
|
|
|
+ // Task 4.2 will drive the sequential sendBase64() loop from here.
|
|
|
+ }
|
|
|
+
|
|
|
+ abortBatch(): void {
|
|
|
+ this.batchQueue.set([]);
|
|
|
+ this.currentBatchIndex.set(0);
|
|
|
}
|
|
|
|
|
|
onDragOver(event: DragEvent): void {
|