ACLASCollege commited on
Commit
a416849
·
verified ·
1 Parent(s): 921b721

Complete dashboard demo QA and localization

Browse files
Files changed (1) hide show
  1. index.html +254 -37
index.html CHANGED
@@ -184,6 +184,15 @@
184
  margin-top: 3px;
185
  }
186
 
 
 
 
 
 
 
 
 
 
187
  .sample-btn:hover {
188
  border-color: var(--accent);
189
  color: var(--accent);
@@ -220,6 +229,7 @@
220
  }
221
  .btn-main:hover { transform: translateY(-2px); box-shadow: 0 8px 25px var(--accent-glow); filter: brightness(1.1); }
222
  .btn-main:active { transform: translateY(0); }
 
223
 
224
  .meta-item { background: rgba(255, 255, 255, 0.02); padding: 14px; border-radius: 10px; border: 1px solid var(--border); }
225
  .meta-val { font-size: 14px; font-weight: 700; color: var(--accent); margin-top: 6px; font-family: 'JetBrains Mono'; }
@@ -402,7 +412,7 @@
402
  <a href="https://github.com/aclascollege/aegis-graph" target="_blank" style="font-size: 10px; color: var(--accent); text-decoration: none; margin-top: 12px; display: inline-block; font-weight: 700; letter-spacing: 0.5px;">PROTOCOL SOURCE ↗</a>
403
  </div>
404
  </div>
405
- <div class="bento"><p class="label" data-i18n="label_hub">Evidence Processing</p><div class="center-hub" style="height: 100%;"><div class="drop-zone" id="drop-zone"><div style="font-size: 32px; color: var(--accent); margin-bottom: 12px;">✧</div><p style="font-size: 15px; font-weight: 700; font-family: 'Outfit'; letter-spacing: 1px;" data-i18n="drop_text">INGEST CREDENTIAL</p><p style="font-size: 10px; color: var(--dim); margin-top: 4px;">PDF, JPG, PNG SUPPORTED</p><p style="font-size: 9px; color: #ffcc66; margin-top: 8px; max-width: 260px; line-height: 1.4;">DEMO MODE: local preview only. Final credential verification requires a server-signed audit.</p><input type="file" id="f-input" accept=".pdf,image/png,image/jpeg" style="display: none"></div><div class="sample-grid"><button class="sample-btn" data-sample="aclas_alumni_2025.png">VERIFIED<span>ACLAS</span></button><button class="sample-btn" data-sample="fake_degree_sample.png">FRAUD<span>Pacific Western</span></button><button class="sample-btn" data-sample="graham_university_transcript.pdf">BLACKLIST<span>Graham</span></button></div></div></div>
406
  <div class="bento" style="grid-row: span 2;"><p class="label" data-i18n="label_telemetry">Live Telemetry</p><div class="terminal" id="console"><div>[SYSTEM] Sovereign Node Active. Waiting for ingestion...</div></div></div>
407
  <div class="bento">
408
  <p class="label" data-i18n="label_metadata">Sovereign Analytics</p>
@@ -416,19 +426,215 @@
416
 
417
  <script>
418
  const i18n = {
419
- en: { btn_audit: "START AUDIT", label_agents: "System Agents", agent_v: "Vision Forensics", agent_g: "Graph Navigator", agent_l: "Logic Auditor", label_node_title: "Institutional Node", label_hub: "Evidence Processing", drop_text: "Ingest Credential", label_telemetry: "Live Telemetry", label_metadata: "Metadata Analytics", meta_issuer: "Issuer", meta_status: "Status", verdict_ok: "SERVER AUDIT REQUIRED", verdict_no: "AUDIT REJECTED" },
420
- cn: { btn_audit: "开始审计", label_agents: "系统代理", agent_v: "视觉法证", agent_g: "图谱导航", agent_l: "逻辑审计", label_node_title: "主权节点", label_hub: "证据处理中心", drop_text: "载入凭证", label_telemetry: "实时遥测", label_metadata: "元数据分析", meta_issuer: "签发机构", meta_status: "状态", verdict_ok: "需要服务端审计", verdict_no: "审计拒绝" },
421
- es: { btn_audit: "INICIAR AUDITORÍA", label_agents: "Agentes de Sistema", agent_v: "Forense Visual", agent_g: "Navegador de Grafos", agent_l: "Auditor Lógico", label_node_title: "Nodo Institucional", label_hub: "Procesamiento", drop_text: "Ingresar Credencial", label_telemetry: "Telemetría", label_metadata: "Metadatos", meta_issuer: "Emisor", meta_status: "Estado", verdict_ok: "AUDITORÍA REQUERIDA", verdict_no: "RECHAZADO" },
422
- fr: { btn_audit: "LANCER L'AUDIT", label_agents: "Agents Système", agent_v: "Forensique Visuelle", agent_g: "Navigateur", agent_l: "Auditeur Logique", label_node_title: "Nœud Institutionnel", label_hub: "Traitement", drop_text: "Ingérer", label_telemetry: "Télémétrie", label_metadata: "Métadonnées", meta_issuer: "Émetteur", meta_status: "Statut", verdict_ok: "AUDIT REQUIS", verdict_no: "REJETÉ" },
423
- de: { btn_audit: "AUDIT STARTEN", label_agents: "System-Agenten", agent_v: "Visuelle Forensik", agent_g: "Graph-Navigator", agent_l: "Logik-Auditor", label_node_title: "Institutioneller Knoten", label_hub: "Verarbeitung", drop_text: "Nachweis Einreichen", label_telemetry: "Telemetrie", label_metadata: "Metadaten", meta_issuer: "Aussteller", meta_status: "Status", verdict_ok: "SERVER-AUDIT ERFORDERLICH", verdict_no: "ABGELEHNT" },
424
- jp: { btn_audit: "監査開始", label_agents: "システムエージェント", agent_v: "視覚法医学", agent_g: "グラフナビ", agent_l: "ロジック監査", label_node_title: "機関ノード", label_hub: "証拠処理", drop_text: "資格情報を入れる", label_telemetry: "テレメトリ", label_metadata: "メタデータ", meta_issuer: "発行者", meta_status: "ステータス", verdict_ok: "サーバー監査が必要", verdict_no: "拒否" },
425
- kr: { btn_audit: "감사 시작", label_agents: "시스템 에이전트", agent_v: "시각적 법의학", agent_g: "그래프 탐색", agent_l: "논리 감사", label_node_title: "기관 노드", label_hub: "증거 처리", drop_text: "자격 증명 제출", label_telemetry: "원격 측정", label_metadata: "메타데이터", meta_issuer: "발행자", meta_status: "상태", verdict_ok: "서버 감사 필요", verdict_no: "거부" },
426
- pt: { btn_audit: "INICIAR AUDITORIA", label_agents: "Agentes de Sistema", agent_v: "Forense Visual", agent_g: "Navegador", agent_l: "Auditor Lógico", label_node_title: "Nó Institucional", label_hub: "Processamento", drop_text: "Inserir Credencial", label_telemetry: "Telemetria", label_metadata: "Metadados", meta_issuer: "Emissor", meta_status: "Estado", verdict_ok: "AUDITORIA REQUERIDA", verdict_no: "REJEITADO" }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
427
  };
428
 
429
  const dropdown = document.getElementById('lang-dropdown');
430
  const langCurrent = document.getElementById('lang-current');
431
  let currentLang = 'en';
 
432
 
433
  dropdown.onclick = (e) => {
434
  dropdown.classList.toggle('open');
@@ -445,6 +651,7 @@
445
  document.querySelectorAll('.dropdown-option').forEach(opt => {
446
  opt.onclick = (e) => {
447
  currentLang = opt.getAttribute('data-value');
 
448
  langCurrent.replaceChildren();
449
  langCurrent.appendChild(document.createTextNode(`${opt.innerText} `));
450
  const arrow = document.createElement('span');
@@ -455,6 +662,9 @@
455
  const key = el.getAttribute('data-i18n');
456
  if(i18n[currentLang][key]) el.innerText = i18n[currentLang][key];
457
  });
 
 
 
458
  e.stopPropagation();
459
  dropdown.classList.remove('open');
460
  };
@@ -464,6 +674,10 @@
464
  const cons = document.getElementById('console');
465
  let file = null;
466
 
 
 
 
 
467
  function log(msg) {
468
  const d = document.createElement('div');
469
  const prompt = document.createElement('span');
@@ -485,9 +699,9 @@
485
  function resetRunState() {
486
  btn.disabled = false;
487
  btn.innerText = i18n[currentLang].btn_audit;
488
- setAgent('v', 'IDLE', '0%', 'var(--dim)');
489
- setAgent('g', 'IDLE', '0%', 'var(--dim)');
490
- setAgent('l', 'IDLE', '0%', 'var(--dim)');
491
  document.getElementById('modal').style.display = 'none';
492
  }
493
 
@@ -516,7 +730,7 @@
516
  document.getElementById('f-input').onchange = (e) => {
517
  if(e.target.files.length) {
518
  file = e.target.files[0];
519
- showSelectedFile(file.name, 'Demo intake only; no browser-side approval will be issued.');
520
  log(`INGESTED: ${file.name}`);
521
  }
522
  };
@@ -526,7 +740,7 @@
526
  resetRunState();
527
  const mimeType = sampleName.endsWith('.pdf') ? 'application/pdf' : 'image/png';
528
  file = { name: sampleName, type: mimeType, demoSample: true };
529
- showSelectedFile(sampleName, 'Built-in demo fixture selected.');
530
  log(`SAMPLE LOADED: ${sampleName}`);
531
  setTimeout(() => btn.click(), 80);
532
  }
@@ -540,35 +754,36 @@
540
  });
541
 
542
  btn.onclick = async () => {
 
543
  if(!file) {
544
- log("SYSTEM: Select a PDF, PNG, or JPEG credential before starting the demo audit.");
545
  document.getElementById('drop-zone').style.borderColor = '#ffcc66';
546
  return;
547
  }
548
  btn.disabled = true;
549
- btn.innerText = 'AUDITING...';
550
- setAgent('v', 'IDLE', '0%', 'var(--dim)');
551
- setAgent('g', 'IDLE', '0%', 'var(--dim)');
552
- setAgent('l', 'IDLE', '0%', 'var(--dim)');
553
- log("INIT: Preparing local demo intake...");
554
 
555
  // Vision Agent
556
- setAgent('v', 'SCANNING', '100%');
557
- log("VISION: Demo animation only; document bytes are not authenticated in-browser...");
558
  await new Promise(r => setTimeout(r, 800));
559
- setAgent('v', 'DONE', '100%');
560
 
561
  // Graph Agent
562
- setAgent('g', 'RESOLVING', '100%');
563
- log("GRAPH: Skipping browser-side registry approval; server audit required...");
564
  await new Promise(r => setTimeout(r, 1500)); // Simulating a deeper search
565
- setAgent('g', 'DONE', '100%');
566
 
567
  // Logic Agent
568
- setAgent('l', 'AUDITING', '100%');
569
- log("LOGIC: Refusing automatic approval without signed server evidence...");
570
  await new Promise(r => setTimeout(r, 800));
571
- setAgent('l', 'DONE', '100%');
572
 
573
  await finish();
574
  };
@@ -596,7 +811,7 @@
596
 
597
  async function finish() {
598
  let verdict = 'NEEDS_REVIEW';
599
- let reason = 'This uploaded file is accepted by the demo intake, but this static browser page cannot read document contents or issue credential approval. Use the built-in samples to demonstrate verified and rejected outcomes, or connect a server-side audit API for real files.';
600
  let status_code = 'DEMO_ONLY';
601
  let issuer = 'UNVERIFIED';
602
  let ror_id = '--';
@@ -607,7 +822,7 @@
607
  if (!fileTypeKnown) {
608
  verdict = 'REJECTED';
609
  status_code = 'UNSUPPORTED_TYPE';
610
- reason = 'Unsupported file type. Upload a PDF, PNG, or JPEG credential for a production server-side audit.';
611
  log('SYSTEM: REJECTED - Unsupported file type for audit intake.');
612
  } else if (inferDemoOutcome(file)) {
613
  const fixture = inferDemoOutcome(file);
@@ -615,12 +830,12 @@
615
  if (fixture.verdict === 'REJECTED') {
616
  verdict = 'REJECTED';
617
  status_code = 'DEMO_BLACKLIST_MATCH';
618
- reason = `${fixture.issuer} matches the public demo blacklist fixture. This is a browser-side demo result only; production rejection still requires a server-signed audit trail.`;
619
  log(`SYSTEM: DEMO REJECTED - ${fixture.issuer} matched the local demo blacklist fixture.`);
620
  } else {
621
  verdict = 'APPROVED';
622
  status_code = 'DEMO_VERIFIED';
623
- reason = `${fixture.issuer} matches the built-in verified demo fixture. This browser-side status is only a UI demonstration, not a production credential approval.`;
624
  log(`SYSTEM: DEMO VERIFIED - ${fixture.issuer} matched the local verified fixture.`);
625
  }
626
  } else {
@@ -631,11 +846,13 @@
631
  const modal = document.getElementById('modal');
632
  modal.style.display = 'flex';
633
  const t = i18n[currentLang];
 
 
634
 
635
  document.getElementById('m-issuer').innerText = issuer;
636
  document.getElementById('m-status').innerText = status_code;
637
 
638
- const verdictTitle = verdict === 'REJECTED' ? t.verdict_no : (verdict === 'APPROVED' ? 'DEMO VERIFIED' : 'NEEDS REVIEW');
639
  const verdictColor = verdict === 'REJECTED' ? '#ff3366' : (verdict === 'APPROVED' ? '#00ffaa' : '#ffcc66');
640
 
641
  const report = document.getElementById('rep-body');
@@ -667,13 +884,13 @@
667
 
668
  const fraudSample = document.createElement('button');
669
  fraudSample.className = 'btn-main';
670
- fraudSample.innerText = 'RUN FRAUD SAMPLE';
671
  fraudSample.onclick = () => runSampleAudit('fake_degree_sample.png');
672
  sampleActions.appendChild(fraudSample);
673
 
674
  const verifiedSample = document.createElement('button');
675
  verifiedSample.className = 'btn-main';
676
- verifiedSample.innerText = 'RUN VERIFIED SAMPLE';
677
  verifiedSample.onclick = () => runSampleAudit('aclas_alumni_2025.png');
678
  sampleActions.appendChild(verifiedSample);
679
 
@@ -683,7 +900,7 @@
683
  const done = document.createElement('button');
684
  done.className = 'btn-main';
685
  done.style.width = '100%';
686
- done.innerText = 'RUN ANOTHER AUDIT';
687
  done.onclick = resetRunState;
688
  report.appendChild(done);
689
  }
 
184
  margin-top: 3px;
185
  }
186
 
187
+ .sample-btn span:first-child {
188
+ color: inherit;
189
+ font-size: 10px;
190
+ font-family: 'Outfit';
191
+ font-weight: 800;
192
+ letter-spacing: 0.5px;
193
+ margin-top: 0;
194
+ }
195
+
196
  .sample-btn:hover {
197
  border-color: var(--accent);
198
  color: var(--accent);
 
229
  }
230
  .btn-main:hover { transform: translateY(-2px); box-shadow: 0 8px 25px var(--accent-glow); filter: brightness(1.1); }
231
  .btn-main:active { transform: translateY(0); }
232
+ .btn-main:disabled { opacity: 0.65; cursor: wait; transform: none; }
233
 
234
  .meta-item { background: rgba(255, 255, 255, 0.02); padding: 14px; border-radius: 10px; border: 1px solid var(--border); }
235
  .meta-val { font-size: 14px; font-weight: 700; color: var(--accent); margin-top: 6px; font-family: 'JetBrains Mono'; }
 
412
  <a href="https://github.com/aclascollege/aegis-graph" target="_blank" style="font-size: 10px; color: var(--accent); text-decoration: none; margin-top: 12px; display: inline-block; font-weight: 700; letter-spacing: 0.5px;">PROTOCOL SOURCE ↗</a>
413
  </div>
414
  </div>
415
+ <div class="bento"><p class="label" data-i18n="label_hub">Evidence Processing</p><div class="center-hub" style="height: 100%;"><div class="drop-zone" id="drop-zone"><div style="font-size: 32px; color: var(--accent); margin-bottom: 12px;">✧</div><p style="font-size: 15px; font-weight: 700; font-family: 'Outfit'; letter-spacing: 1px;" data-i18n="drop_text">INGEST CREDENTIAL</p><p style="font-size: 10px; color: var(--dim); margin-top: 4px;" data-i18n="file_support">PDF, JPG, PNG SUPPORTED</p><p style="font-size: 9px; color: #ffcc66; margin-top: 8px; max-width: 260px; line-height: 1.4;" data-i18n="demo_disclaimer">DEMO MODE: local preview only. Final credential verification requires a server-signed audit.</p><input type="file" id="f-input" accept=".pdf,image/png,image/jpeg" style="display: none"></div><div class="sample-grid"><button class="sample-btn" data-sample="aclas_alumni_2025.png"><span data-i18n="sample_verified_main">VERIFIED</span><span data-i18n="sample_verified_sub">ACLAS</span></button><button class="sample-btn" data-sample="fake_degree_sample.png"><span data-i18n="sample_fraud_main">FRAUD</span><span data-i18n="sample_fraud_sub">Pacific Western</span></button><button class="sample-btn" data-sample="graham_university_transcript.pdf"><span data-i18n="sample_blacklist_main">BLACKLIST</span><span data-i18n="sample_blacklist_sub">Graham</span></button></div></div></div>
416
  <div class="bento" style="grid-row: span 2;"><p class="label" data-i18n="label_telemetry">Live Telemetry</p><div class="terminal" id="console"><div>[SYSTEM] Sovereign Node Active. Waiting for ingestion...</div></div></div>
417
  <div class="bento">
418
  <p class="label" data-i18n="label_metadata">Sovereign Analytics</p>
 
426
 
427
  <script>
428
  const i18n = {
429
+ en: { btn_audit: "START AUDIT", label_agents: "System Agents", agent_v: "Vision Forensics", agent_g: "Graph Navigator", agent_l: "Logic Auditor", label_node_title: "Institutional Node", label_hub: "Evidence Processing", drop_text: "Ingest Credential", file_support: "PDF, JPG, PNG SUPPORTED", demo_disclaimer: "DEMO MODE: local preview only. Final credential verification requires a server-signed audit.", label_telemetry: "Live Telemetry", label_metadata: "Metadata Analytics", meta_issuer: "Issuer", meta_status: "Status", sample_verified_main: "VERIFIED", sample_verified_sub: "ACLAS", sample_fraud_main: "FRAUD", sample_fraud_sub: "Pacific Western", sample_blacklist_main: "BLACKLIST", sample_blacklist_sub: "Graham", verdict_ok: "SERVER AUDIT REQUIRED", verdict_no: "AUDIT REJECTED" },
430
+ cn: { btn_audit: "开始审计", label_agents: "系统代理", agent_v: "视觉法证", agent_g: "图谱导航", agent_l: "逻辑审计", label_node_title: "主权节点", label_hub: "证据处理中心", drop_text: "载入凭证", file_support: "支持 PDF、JPG、PNG", demo_disclaimer: "演示模式:仅本地预览。正式凭证验证需要服务端签名审计。", label_telemetry: "实时遥测", label_metadata: "元数据分析", meta_issuer: "签发机构", meta_status: "状态", sample_verified_main: "通过样本", sample_verified_sub: "ACLAS", sample_fraud_main: "欺诈样本", sample_fraud_sub: "Pacific Western", sample_blacklist_main: "黑名单", sample_blacklist_sub: "Graham", verdict_ok: "需要服务端审计", verdict_no: "审计拒绝" },
431
+ es: { btn_audit: "INICIAR AUDITORÍA", label_agents: "Agentes de Sistema", agent_v: "Forense Visual", agent_g: "Navegador de Grafos", agent_l: "Auditor Lógico", label_node_title: "Nodo Institucional", label_hub: "Procesamiento", drop_text: "Ingresar Credencial", file_support: "PDF, JPG, PNG SOPORTADOS", demo_disclaimer: "MODO DEMO: vista previa local. La verificación final requiere auditoría firmada por servidor.", label_telemetry: "Telemetría", label_metadata: "Metadatos", meta_issuer: "Emisor", meta_status: "Estado", sample_verified_main: "VÁLIDA", sample_verified_sub: "ACLAS", sample_fraud_main: "FRAUDE", sample_fraud_sub: "Pacific Western", sample_blacklist_main: "LISTA NEGRA", sample_blacklist_sub: "Graham", verdict_ok: "AUDITORÍA REQUERIDA", verdict_no: "RECHAZADO" },
432
+ fr: { btn_audit: "LANCER L'AUDIT", label_agents: "Agents Système", agent_v: "Forensique Visuelle", agent_g: "Navigateur", agent_l: "Auditeur Logique", label_node_title: "Nœud Institutionnel", label_hub: "Traitement", drop_text: "Ingérer", file_support: "PDF, JPG, PNG PRIS EN CHARGE", demo_disclaimer: "MODE DÉMO: aperçu local. La vérification finale exige un audit signé serveur.", label_telemetry: "Télémétrie", label_metadata: "Métadonnées", meta_issuer: "Émetteur", meta_status: "Statut", sample_verified_main: "VÉRIFIÉ", sample_verified_sub: "ACLAS", sample_fraud_main: "FRAUDE", sample_fraud_sub: "Pacific Western", sample_blacklist_main: "LISTE NOIRE", sample_blacklist_sub: "Graham", verdict_ok: "AUDIT REQUIS", verdict_no: "REJETÉ" },
433
+ de: { btn_audit: "AUDIT STARTEN", label_agents: "System-Agenten", agent_v: "Visuelle Forensik", agent_g: "Graph-Navigator", agent_l: "Logik-Auditor", label_node_title: "Institutioneller Knoten", label_hub: "Verarbeitung", drop_text: "Nachweis Einreichen", file_support: "PDF, JPG, PNG UNTERSTÜTZT", demo_disclaimer: "DEMO-MODUS: lokale Vorschau. Finale Verifikation erfordert serverseitig signierten Audit.", label_telemetry: "Telemetrie", label_metadata: "Metadaten", meta_issuer: "Aussteller", meta_status: "Status", sample_verified_main: "VERIFIZIERT", sample_verified_sub: "ACLAS", sample_fraud_main: "BETRUG", sample_fraud_sub: "Pacific Western", sample_blacklist_main: "BLACKLIST", sample_blacklist_sub: "Graham", verdict_ok: "SERVER-AUDIT ERFORDERLICH", verdict_no: "ABGELEHNT" },
434
+ jp: { btn_audit: "監査開始", label_agents: "システムエージェント", agent_v: "視覚法医学", agent_g: "グラフナビ", agent_l: "ロジック監査", label_node_title: "機関ノード", label_hub: "証拠処理", drop_text: "資格情報を入れる", file_support: "PDF、JPG、PNG対応", demo_disclaimer: "デモモード: ローカルプレビューのみ。最終検証にはサーバー署名監査が必要です。", label_telemetry: "テレメトリ", label_metadata: "メタデータ", meta_issuer: "発行者", meta_status: "ステータス", sample_verified_main: "検証済み", sample_verified_sub: "ACLAS", sample_fraud_main: "不正", sample_fraud_sub: "Pacific Western", sample_blacklist_main: "ブラックリスト", sample_blacklist_sub: "Graham", verdict_ok: "サーバー監査が必要", verdict_no: "拒否" },
435
+ kr: { btn_audit: "감사 시작", label_agents: "시스템 에이전트", agent_v: "시각적 법의학", agent_g: "그래프 탐색", agent_l: "논리 감사", label_node_title: "기관 노드", label_hub: "증거 처리", drop_text: "자격 증명 제출", file_support: "PDF, JPG, PNG 지원", demo_disclaimer: "데모 모드: 로컬 미리보기 전용. 최종 검증에는 서버 서명 감사가 필요합니다.", label_telemetry: "원격 측정", label_metadata: "메타데이터", meta_issuer: "발행자", meta_status: "상태", sample_verified_main: "검증됨", sample_verified_sub: "ACLAS", sample_fraud_main: "사기", sample_fraud_sub: "Pacific Western", sample_blacklist_main: "블랙리스트", sample_blacklist_sub: "Graham", verdict_ok: "서버 감사 필요", verdict_no: "거부" },
436
+ pt: { btn_audit: "INICIAR AUDITORIA", label_agents: "Agentes de Sistema", agent_v: "Forense Visual", agent_g: "Navegador", agent_l: "Auditor Lógico", label_node_title: "Nó Institucional", label_hub: "Processamento", drop_text: "Inserir Credencial", file_support: "PDF, JPG, PNG SUPORTADOS", demo_disclaimer: "MODO DEMO: prévia local. A verificação final requer auditoria assinada pelo servidor.", label_telemetry: "Telemetria", label_metadata: "Metadados", meta_issuer: "Emissor", meta_status: "Estado", sample_verified_main: "VERIFICADO", sample_verified_sub: "ACLAS", sample_fraud_main: "FRAUDE", sample_fraud_sub: "Pacific Western", sample_blacklist_main: "LISTA NEGRA", sample_blacklist_sub: "Graham", verdict_ok: "AUDITORIA REQUERIDA", verdict_no: "REJEITADO" }
437
+ };
438
+
439
+ const extraText = {
440
+ en: {
441
+ auditing: "AUDITING...",
442
+ idle: "IDLE",
443
+ scanning: "SCANNING",
444
+ resolving: "RESOLVING",
445
+ done: "DONE",
446
+ logicAuditing: "AUDITING",
447
+ needsReview: "NEEDS REVIEW",
448
+ demoVerified: "DEMO VERIFIED",
449
+ runAnother: "RUN ANOTHER AUDIT",
450
+ runFraud: "RUN FRAUD SAMPLE",
451
+ runVerified: "RUN VERIFIED SAMPLE",
452
+ uploadNote: "Demo intake only; no browser-side approval will be issued.",
453
+ sampleNote: "Built-in demo fixture selected.",
454
+ noFile: "SYSTEM: Select a PDF, PNG, or JPEG credential before starting the demo audit.",
455
+ init: "INIT: Preparing local demo intake...",
456
+ vision: "VISION: Demo animation only; document bytes are not authenticated in-browser...",
457
+ graph: "GRAPH: Resolving institution evidence in local demo mode...",
458
+ logic: "LOGIC: Applying demo policy rules...",
459
+ unsupported: "Unsupported file type. Upload a PDF, PNG, or JPEG credential for a production server-side audit.",
460
+ unknownReason: "This uploaded file is accepted by the demo intake, but this static browser page cannot read document contents or issue credential approval. Use the built-in samples to demonstrate verified and rejected outcomes, or connect a server-side audit API for real files.",
461
+ rejectedReason: issuer => `${issuer} matches the public demo blacklist fixture. This is a browser-side demo result only; production rejection still requires a server-signed audit trail.`,
462
+ verifiedReason: issuer => `${issuer} matches the built-in verified demo fixture. This browser-side status is only a UI demonstration, not a production credential approval.`
463
+ },
464
+ cn: {
465
+ auditing: "审计中...",
466
+ idle: "待命",
467
+ scanning: "扫描中",
468
+ resolving: "解析中",
469
+ done: "完成",
470
+ logicAuditing: "审计中",
471
+ needsReview: "需要复核",
472
+ demoVerified: "演示验证通过",
473
+ runAnother: "继续测试",
474
+ runFraud: "运行欺诈样本",
475
+ runVerified: "运行通过样本",
476
+ uploadNote: "仅演示载入;浏览器端不会签发审核结论。",
477
+ sampleNote: "已选择内置演示样本。",
478
+ noFile: "系统:请先选择 PDF、PNG 或 JPEG 凭证。",
479
+ init: "初始化:准备本地演示审计...",
480
+ vision: "视觉:浏览器演示不会认证文件字节...",
481
+ graph: "图谱:正在本地演示模式解析机构证据...",
482
+ logic: "逻辑:正在应用演示规则...",
483
+ unsupported: "文件类型不支持。请上传 PDF、PNG 或 JPEG。",
484
+ unknownReason: "该文件已进入演示流程,但静态网页无法读取文件内容或签发凭证结论。请使用内置样本演示通过/拒绝结果,或接入服务端审计 API 处理真实文件。",
485
+ rejectedReason: issuer => `${issuer} 命中公开演示黑名单样本。该结果仅为浏览器端演示;正式拒绝仍需要服务端签名审计轨迹。`,
486
+ verifiedReason: issuer => `${issuer} 命中内置通过样本。该状态仅为界面演示,不代表正式凭证批准。`
487
+ },
488
+ es: {
489
+ auditing: "AUDITANDO...",
490
+ idle: "INACTIVO",
491
+ scanning: "ESCANEANDO",
492
+ resolving: "RESOLVIENDO",
493
+ done: "LISTO",
494
+ logicAuditing: "AUDITANDO",
495
+ needsReview: "REVISIÓN NECESARIA",
496
+ demoVerified: "DEMO VERIFICADA",
497
+ runAnother: "EJECUTAR OTRA AUDITORÍA",
498
+ runFraud: "EJECUTAR MUESTRA FRAUDE",
499
+ runVerified: "EJECUTAR MUESTRA VÁLIDA",
500
+ uploadNote: "Solo entrada demo; no se emite aprobación en el navegador.",
501
+ sampleNote: "Muestra demo integrada seleccionada.",
502
+ noFile: "SISTEMA: Seleccione PDF, PNG o JPEG antes de iniciar.",
503
+ init: "INIT: Preparando auditoría demo local...",
504
+ vision: "VISIÓN: La demo no autentica bytes del documento en navegador...",
505
+ graph: "GRAFO: Resolviendo evidencia institucional en modo demo...",
506
+ logic: "LÓGICA: Aplicando reglas demo...",
507
+ unsupported: "Tipo de archivo no compatible. Suba PDF, PNG o JPEG.",
508
+ unknownReason: "El archivo fue aceptado por la demo, pero esta página estática no puede leer contenidos ni emitir aprobación. Use las muestras integradas o conecte una API de auditoría.",
509
+ rejectedReason: issuer => `${issuer} coincide con la lista negra demo. Es solo un resultado de navegador; producción requiere auditoría firmada por servidor.`,
510
+ verifiedReason: issuer => `${issuer} coincide con la muestra verificada demo. Es solo demostración de UI, no aprobación real.`
511
+ },
512
+ fr: {
513
+ auditing: "AUDIT EN COURS...",
514
+ idle: "INACTIF",
515
+ scanning: "ANALYSE",
516
+ resolving: "RÉSOLUTION",
517
+ done: "TERMINÉ",
518
+ logicAuditing: "AUDIT",
519
+ needsReview: "REVUE REQUISE",
520
+ demoVerified: "DÉMO VÉRIFIÉE",
521
+ runAnother: "RELANCER UN AUDIT",
522
+ runFraud: "LANCER ÉCHANTILLON FRAUDE",
523
+ runVerified: "LANCER ÉCHANTILLON VALIDE",
524
+ uploadNote: "Entrée de démonstration uniquement; aucune approbation navigateur.",
525
+ sampleNote: "Échantillon démo intégré sélectionné.",
526
+ noFile: "SYSTÈME: Sélectionnez un PDF, PNG ou JPEG avant l'audit.",
527
+ init: "INIT: Préparation de l'audit démo local...",
528
+ vision: "VISION: La démo n'authentifie pas les octets du document...",
529
+ graph: "GRAPHE: Résolution des preuves institutionnelles en mode démo...",
530
+ logic: "LOGIQUE: Application des règles de démonstration...",
531
+ unsupported: "Type de fichier non pris en charge. Chargez un PDF, PNG ou JPEG.",
532
+ unknownReason: "Le fichier est accepté par la démo, mais cette page statique ne peut pas lire le contenu ni approuver un diplôme. Utilisez les exemples intégrés ou connectez une API serveur.",
533
+ rejectedReason: issuer => `${issuer} correspond à la liste noire de démonstration. Résultat navigateur uniquement; la production exige un audit signé serveur.`,
534
+ verifiedReason: issuer => `${issuer} correspond à l'exemple vérifié intégré. C'est une démonstration UI, pas une approbation réelle.`
535
+ },
536
+ de: {
537
+ auditing: "AUDIT LÄUFT...",
538
+ idle: "BEREIT",
539
+ scanning: "SCANNT",
540
+ resolving: "PRÜFT",
541
+ done: "FERTIG",
542
+ logicAuditing: "AUDIT",
543
+ needsReview: "PRÜFUNG NÖTIG",
544
+ demoVerified: "DEMO VERIFIZIERT",
545
+ runAnother: "WEITEREN AUDIT STARTEN",
546
+ runFraud: "BETRUGSBEISPIEL",
547
+ runVerified: "VERIFIZIERTES BEISPIEL",
548
+ uploadNote: "Nur Demo-Eingabe; keine Browser-Freigabe.",
549
+ sampleNote: "Integriertes Demo-Beispiel ausgewählt.",
550
+ noFile: "SYSTEM: Bitte PDF, PNG oder JPEG auswählen.",
551
+ init: "INIT: Lokalen Demo-Audit vorbereiten...",
552
+ vision: "VISION: Browser-Demo authentifiziert keine Dokumentbytes...",
553
+ graph: "GRAPH: Institutionelle Evidenz im Demo-Modus wird geprüft...",
554
+ logic: "LOGIK: Demo-Regeln werden angewendet...",
555
+ unsupported: "Nicht unterstützter Dateityp. PDF, PNG oder JPEG hochladen.",
556
+ unknownReason: "Die Datei wurde angenommen, aber diese statische Seite kann Inhalte nicht lesen oder genehmigen. Nutzen Sie die Beispiele oder verbinden Sie eine Server-API.",
557
+ rejectedReason: issuer => `${issuer} passt zum Demo-Blacklist-Beispiel. Nur Browser-Demo; Produktion braucht serverseitig signierten Audit.`,
558
+ verifiedReason: issuer => `${issuer} passt zum verifizierten Demo-Beispiel. Das ist UI-Demo, keine echte Freigabe.`
559
+ },
560
+ jp: {
561
+ auditing: "監査中...",
562
+ idle: "待機",
563
+ scanning: "解析中",
564
+ resolving: "照合中",
565
+ done: "完了",
566
+ logicAuditing: "監査中",
567
+ needsReview: "レビュー必要",
568
+ demoVerified: "デモ検証済み",
569
+ runAnother: "別の監査を実行",
570
+ runFraud: "不正サンプル実行",
571
+ runVerified: "検証サンプル実行",
572
+ uploadNote: "デモ取込のみ。ブラウザでは承認しません。",
573
+ sampleNote: "内蔵デモサンプルを選択しました。",
574
+ noFile: "システム: PDF、PNG、JPEGを選択してください。",
575
+ init: "INIT: ローカルデモ監査を準備中...",
576
+ vision: "VISION: ブラウザデモでは文書バイトを認証しません...",
577
+ graph: "GRAPH: デモモードで機関証拠を照合中...",
578
+ logic: "LOGIC: デモルールを適用中...",
579
+ unsupported: "非対応ファイルです。PDF、PNG、JPEGをアップロードしてください。",
580
+ unknownReason: "ファイルはデモに受理されましたが、静的ページでは内容読取や承認はできません。内蔵サンプルを使うか、サーバーAPIを接続してください。",
581
+ rejectedReason: issuer => `${issuer} はデモ用ブラックリストに一致しました。これはブラウザデモであり、本番拒否には署名済み監査が必要です。`,
582
+ verifiedReason: issuer => `${issuer} は検証済みデモサンプルに一致しました。これはUIデモであり正式承認ではありません。`
583
+ },
584
+ kr: {
585
+ auditing: "감사 중...",
586
+ idle: "대기",
587
+ scanning: "스캔 중",
588
+ resolving: "확인 중",
589
+ done: "완료",
590
+ logicAuditing: "감사 중",
591
+ needsReview: "검토 필요",
592
+ demoVerified: "데모 검증됨",
593
+ runAnother: "다른 감사 실행",
594
+ runFraud: "사기 샘플 실행",
595
+ runVerified: "검증 샘플 실행",
596
+ uploadNote: "데모 입력 전용이며 브라우저 승인은 발급되지 않습니다.",
597
+ sampleNote: "내장 데모 샘플이 선택되었습니다.",
598
+ noFile: "시스템: PDF, PNG 또는 JPEG를 먼저 선택하세요.",
599
+ init: "INIT: 로컬 데모 감사를 준비 중...",
600
+ vision: "VISION: 브라우저 데모는 문서 바이트를 인증하지 않습니다...",
601
+ graph: "GRAPH: 데모 모드에서 기관 증거를 확인 중...",
602
+ logic: "LOGIC: 데모 정책 규칙 적용 중...",
603
+ unsupported: "지원하지 않는 파일 형식입니다. PDF, PNG 또는 JPEG를 업로드하세요.",
604
+ unknownReason: "파일은 데모 입력으로 허용되었지만 정적 페이지는 내용을 읽거나 승인할 수 없습니다. 내장 샘플을 사용하거나 서버 감사 API를 연결하세요.",
605
+ rejectedReason: issuer => `${issuer} 이 데모 블랙리스트 샘플과 일치합니다. 브라우저 데모 결과이며 실제 거부에는 서버 서명 감사가 필요합니다.`,
606
+ verifiedReason: issuer => `${issuer} 이 내장 검증 샘플과 일치합니다. UI 데모일 뿐 실제 승인이 아닙니다.`
607
+ },
608
+ pt: {
609
+ auditing: "AUDITANDO...",
610
+ idle: "OCIOSO",
611
+ scanning: "ESCANEANDO",
612
+ resolving: "RESOLVENDO",
613
+ done: "CONCLUÍDO",
614
+ logicAuditing: "AUDITANDO",
615
+ needsReview: "REVISÃO NECESSÁRIA",
616
+ demoVerified: "DEMO VERIFICADA",
617
+ runAnother: "EXECUTAR OUTRA AUDITORIA",
618
+ runFraud: "EXECUTAR AMOSTRA FRAUDE",
619
+ runVerified: "EXECUTAR AMOSTRA VÁLIDA",
620
+ uploadNote: "Entrada de demo; nenhuma aprovação no navegador.",
621
+ sampleNote: "Amostra demo integrada selecionada.",
622
+ noFile: "SISTEMA: selecione PDF, PNG ou JPEG antes de iniciar.",
623
+ init: "INIT: Preparando auditoria demo local...",
624
+ vision: "VISÃO: a demo não autentica bytes do documento no navegador...",
625
+ graph: "GRAFO: resolvendo evidências institucionais em modo demo...",
626
+ logic: "LÓGICA: aplicando regras de demo...",
627
+ unsupported: "Tipo de arquivo não suportado. Envie PDF, PNG ou JPEG.",
628
+ unknownReason: "O arquivo foi aceito pela demo, mas esta página estática não lê conteúdo nem emite aprovação. Use as amostras integradas ou conecte uma API de auditoria.",
629
+ rejectedReason: issuer => `${issuer} corresponde à lista negra de demonstração. É apenas resultado no navegador; produção exige auditoria assinada pelo servidor.`,
630
+ verifiedReason: issuer => `${issuer} corresponde à amostra verificada integrada. É demonstração de UI, não aprovação real.`
631
+ }
632
  };
633
 
634
  const dropdown = document.getElementById('lang-dropdown');
635
  const langCurrent = document.getElementById('lang-current');
636
  let currentLang = 'en';
637
+ const htmlLangMap = { en: 'en', cn: 'zh-CN', es: 'es', fr: 'fr', de: 'de', jp: 'ja', kr: 'ko', pt: 'pt' };
638
 
639
  dropdown.onclick = (e) => {
640
  dropdown.classList.toggle('open');
 
651
  document.querySelectorAll('.dropdown-option').forEach(opt => {
652
  opt.onclick = (e) => {
653
  currentLang = opt.getAttribute('data-value');
654
+ document.documentElement.lang = htmlLangMap[currentLang] || 'en';
655
  langCurrent.replaceChildren();
656
  langCurrent.appendChild(document.createTextNode(`${opt.innerText} `));
657
  const arrow = document.createElement('span');
 
662
  const key = el.getAttribute('data-i18n');
663
  if(i18n[currentLang][key]) el.innerText = i18n[currentLang][key];
664
  });
665
+ if (!btn.disabled && document.getElementById('modal').style.display !== 'flex') {
666
+ resetRunState();
667
+ }
668
  e.stopPropagation();
669
  dropdown.classList.remove('open');
670
  };
 
674
  const cons = document.getElementById('console');
675
  let file = null;
676
 
677
+ function tx(key) {
678
+ return (extraText[currentLang] && extraText[currentLang][key]) || extraText.en[key];
679
+ }
680
+
681
  function log(msg) {
682
  const d = document.createElement('div');
683
  const prompt = document.createElement('span');
 
699
  function resetRunState() {
700
  btn.disabled = false;
701
  btn.innerText = i18n[currentLang].btn_audit;
702
+ setAgent('v', tx('idle'), '0%', 'var(--dim)');
703
+ setAgent('g', tx('idle'), '0%', 'var(--dim)');
704
+ setAgent('l', tx('idle'), '0%', 'var(--dim)');
705
  document.getElementById('modal').style.display = 'none';
706
  }
707
 
 
730
  document.getElementById('f-input').onchange = (e) => {
731
  if(e.target.files.length) {
732
  file = e.target.files[0];
733
+ showSelectedFile(file.name, tx('uploadNote'));
734
  log(`INGESTED: ${file.name}`);
735
  }
736
  };
 
740
  resetRunState();
741
  const mimeType = sampleName.endsWith('.pdf') ? 'application/pdf' : 'image/png';
742
  file = { name: sampleName, type: mimeType, demoSample: true };
743
+ showSelectedFile(sampleName, tx('sampleNote'));
744
  log(`SAMPLE LOADED: ${sampleName}`);
745
  setTimeout(() => btn.click(), 80);
746
  }
 
754
  });
755
 
756
  btn.onclick = async () => {
757
+ if (document.getElementById('modal').style.display === 'flex') return;
758
  if(!file) {
759
+ log(tx('noFile'));
760
  document.getElementById('drop-zone').style.borderColor = '#ffcc66';
761
  return;
762
  }
763
  btn.disabled = true;
764
+ btn.innerText = tx('auditing');
765
+ setAgent('v', tx('idle'), '0%', 'var(--dim)');
766
+ setAgent('g', tx('idle'), '0%', 'var(--dim)');
767
+ setAgent('l', tx('idle'), '0%', 'var(--dim)');
768
+ log(tx('init'));
769
 
770
  // Vision Agent
771
+ setAgent('v', tx('scanning'), '100%');
772
+ log(tx('vision'));
773
  await new Promise(r => setTimeout(r, 800));
774
+ setAgent('v', tx('done'), '100%');
775
 
776
  // Graph Agent
777
+ setAgent('g', tx('resolving'), '100%');
778
+ log(tx('graph'));
779
  await new Promise(r => setTimeout(r, 1500)); // Simulating a deeper search
780
+ setAgent('g', tx('done'), '100%');
781
 
782
  // Logic Agent
783
+ setAgent('l', tx('logicAuditing'), '100%');
784
+ log(tx('logic'));
785
  await new Promise(r => setTimeout(r, 800));
786
+ setAgent('l', tx('done'), '100%');
787
 
788
  await finish();
789
  };
 
811
 
812
  async function finish() {
813
  let verdict = 'NEEDS_REVIEW';
814
+ let reason = tx('unknownReason');
815
  let status_code = 'DEMO_ONLY';
816
  let issuer = 'UNVERIFIED';
817
  let ror_id = '--';
 
822
  if (!fileTypeKnown) {
823
  verdict = 'REJECTED';
824
  status_code = 'UNSUPPORTED_TYPE';
825
+ reason = tx('unsupported');
826
  log('SYSTEM: REJECTED - Unsupported file type for audit intake.');
827
  } else if (inferDemoOutcome(file)) {
828
  const fixture = inferDemoOutcome(file);
 
830
  if (fixture.verdict === 'REJECTED') {
831
  verdict = 'REJECTED';
832
  status_code = 'DEMO_BLACKLIST_MATCH';
833
+ reason = tx('rejectedReason')(fixture.issuer);
834
  log(`SYSTEM: DEMO REJECTED - ${fixture.issuer} matched the local demo blacklist fixture.`);
835
  } else {
836
  verdict = 'APPROVED';
837
  status_code = 'DEMO_VERIFIED';
838
+ reason = tx('verifiedReason')(fixture.issuer);
839
  log(`SYSTEM: DEMO VERIFIED - ${fixture.issuer} matched the local verified fixture.`);
840
  }
841
  } else {
 
846
  const modal = document.getElementById('modal');
847
  modal.style.display = 'flex';
848
  const t = i18n[currentLang];
849
+ btn.disabled = false;
850
+ btn.innerText = i18n[currentLang].btn_audit;
851
 
852
  document.getElementById('m-issuer').innerText = issuer;
853
  document.getElementById('m-status').innerText = status_code;
854
 
855
+ const verdictTitle = verdict === 'REJECTED' ? t.verdict_no : (verdict === 'APPROVED' ? tx('demoVerified') : tx('needsReview'));
856
  const verdictColor = verdict === 'REJECTED' ? '#ff3366' : (verdict === 'APPROVED' ? '#00ffaa' : '#ffcc66');
857
 
858
  const report = document.getElementById('rep-body');
 
884
 
885
  const fraudSample = document.createElement('button');
886
  fraudSample.className = 'btn-main';
887
+ fraudSample.innerText = tx('runFraud');
888
  fraudSample.onclick = () => runSampleAudit('fake_degree_sample.png');
889
  sampleActions.appendChild(fraudSample);
890
 
891
  const verifiedSample = document.createElement('button');
892
  verifiedSample.className = 'btn-main';
893
+ verifiedSample.innerText = tx('runVerified');
894
  verifiedSample.onclick = () => runSampleAudit('aclas_alumni_2025.png');
895
  sampleActions.appendChild(verifiedSample);
896
 
 
900
  const done = document.createElement('button');
901
  done.className = 'btn-main';
902
  done.style.width = '100%';
903
+ done.innerText = tx('runAnother');
904
  done.onclick = resetRunState;
905
  report.appendChild(done);
906
  }