Commit ·
1d4ba83
1
Parent(s): 65490d1
fix: enable punctuation model for clean texts while keeping structural guards
Browse files- extension/content-inline.css +484 -0
- extension/content-inline.js +281 -6
- src/app.py +6 -4
- tests/phase10/benchmark_runner.py +10 -6
- tests/phase10/reports/phase10_results.json +0 -0
extension/content-inline.css
CHANGED
|
@@ -336,3 +336,487 @@
|
|
| 336 |
color: #111827 !important;
|
| 337 |
}
|
| 338 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
color: #111827 !important;
|
| 337 |
}
|
| 338 |
|
| 339 |
+
/* ══════════════════════════════════════════════
|
| 340 |
+
Modal Dialog — Analysis Panel
|
| 341 |
+
══════════════════════════════════════════════ */
|
| 342 |
+
|
| 343 |
+
.bayan-il-modal-backdrop {
|
| 344 |
+
position: fixed !important;
|
| 345 |
+
inset: 0 !important;
|
| 346 |
+
z-index: 2147483646 !important;
|
| 347 |
+
background: rgba(0, 0, 0, 0.55) !important;
|
| 348 |
+
backdrop-filter: blur(4px) !important;
|
| 349 |
+
opacity: 0;
|
| 350 |
+
transition: opacity 200ms ease !important;
|
| 351 |
+
display: none;
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
.bayan-il-modal-backdrop--visible {
|
| 355 |
+
display: block !important;
|
| 356 |
+
opacity: 1 !important;
|
| 357 |
+
}
|
| 358 |
+
|
| 359 |
+
.bayan-il-modal-panel {
|
| 360 |
+
position: fixed !important;
|
| 361 |
+
z-index: 2147483647 !important;
|
| 362 |
+
top: 50% !important;
|
| 363 |
+
left: 50% !important;
|
| 364 |
+
transform: translate(-50%, -50%) scale(0.95) !important;
|
| 365 |
+
width: 400px !important;
|
| 366 |
+
max-width: calc(100vw - 32px) !important;
|
| 367 |
+
max-height: 80vh !important;
|
| 368 |
+
background: #1a1a24 !important;
|
| 369 |
+
border: 1px solid #3a3a4d !important;
|
| 370 |
+
border-radius: 14px !important;
|
| 371 |
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(99, 102, 241, 0.1) !important;
|
| 372 |
+
overflow: hidden !important;
|
| 373 |
+
display: flex !important;
|
| 374 |
+
flex-direction: column !important;
|
| 375 |
+
font-family: 'Segoe UI', Tahoma, Arial, sans-serif !important;
|
| 376 |
+
font-size: 13px !important;
|
| 377 |
+
color: #f0f0f5 !important;
|
| 378 |
+
direction: rtl !important;
|
| 379 |
+
opacity: 0;
|
| 380 |
+
transition: opacity 200ms ease, transform 200ms cubic-bezier(0.34, 1.56, 0.64, 1) !important;
|
| 381 |
+
}
|
| 382 |
+
|
| 383 |
+
.bayan-il-modal-panel--visible {
|
| 384 |
+
opacity: 1 !important;
|
| 385 |
+
transform: translate(-50%, -50%) scale(1) !important;
|
| 386 |
+
}
|
| 387 |
+
|
| 388 |
+
/* ── Modal Header ── */
|
| 389 |
+
|
| 390 |
+
.bayan-il-modal-header {
|
| 391 |
+
display: flex !important;
|
| 392 |
+
align-items: center !important;
|
| 393 |
+
justify-content: space-between !important;
|
| 394 |
+
padding: 12px 16px !important;
|
| 395 |
+
background: linear-gradient(135deg, #1a1a24 0%, rgba(99, 102, 241, 0.08) 100%) !important;
|
| 396 |
+
border-bottom: 1px solid #2d2d3d !important;
|
| 397 |
+
flex-shrink: 0 !important;
|
| 398 |
+
}
|
| 399 |
+
|
| 400 |
+
.bayan-il-modal-brand {
|
| 401 |
+
display: flex !important;
|
| 402 |
+
align-items: center !important;
|
| 403 |
+
gap: 8px !important;
|
| 404 |
+
}
|
| 405 |
+
|
| 406 |
+
.bayan-il-modal-title {
|
| 407 |
+
font-size: 16px !important;
|
| 408 |
+
font-weight: 700 !important;
|
| 409 |
+
background: linear-gradient(135deg, #818cf8, #6366f1) !important;
|
| 410 |
+
-webkit-background-clip: text !important;
|
| 411 |
+
-webkit-text-fill-color: transparent !important;
|
| 412 |
+
letter-spacing: -0.3px !important;
|
| 413 |
+
}
|
| 414 |
+
|
| 415 |
+
.bayan-il-modal-close {
|
| 416 |
+
background: none !important;
|
| 417 |
+
border: none !important;
|
| 418 |
+
color: #6b6b80 !important;
|
| 419 |
+
cursor: pointer !important;
|
| 420 |
+
font-size: 18px !important;
|
| 421 |
+
padding: 4px !important;
|
| 422 |
+
line-height: 1 !important;
|
| 423 |
+
border-radius: 6px !important;
|
| 424 |
+
transition: background 150ms ease, color 150ms ease !important;
|
| 425 |
+
}
|
| 426 |
+
|
| 427 |
+
.bayan-il-modal-close:hover {
|
| 428 |
+
color: #f0f0f5 !important;
|
| 429 |
+
background: rgba(255, 255, 255, 0.08) !important;
|
| 430 |
+
}
|
| 431 |
+
|
| 432 |
+
/* ── Modal Body (scrollable) ── */
|
| 433 |
+
|
| 434 |
+
.bayan-il-modal-body {
|
| 435 |
+
overflow-y: auto !important;
|
| 436 |
+
padding: 16px !important;
|
| 437 |
+
flex: 1 !important;
|
| 438 |
+
min-height: 0 !important;
|
| 439 |
+
}
|
| 440 |
+
|
| 441 |
+
.bayan-il-modal-body::-webkit-scrollbar { width: 4px !important; }
|
| 442 |
+
.bayan-il-modal-body::-webkit-scrollbar-track { background: transparent !important; }
|
| 443 |
+
.bayan-il-modal-body::-webkit-scrollbar-thumb { background: #3a3a4d !important; border-radius: 4px !important; }
|
| 444 |
+
|
| 445 |
+
/* ── Score Section ── */
|
| 446 |
+
|
| 447 |
+
.bayan-il-modal-score {
|
| 448 |
+
display: flex !important;
|
| 449 |
+
align-items: center !important;
|
| 450 |
+
gap: 14px !important;
|
| 451 |
+
padding: 14px !important;
|
| 452 |
+
margin-bottom: 14px !important;
|
| 453 |
+
background: #22222e !important;
|
| 454 |
+
border: 1px solid #2d2d3d !important;
|
| 455 |
+
border-radius: 10px !important;
|
| 456 |
+
}
|
| 457 |
+
|
| 458 |
+
.bayan-il-modal-score-ring {
|
| 459 |
+
position: relative !important;
|
| 460 |
+
width: 80px !important;
|
| 461 |
+
height: 80px !important;
|
| 462 |
+
flex-shrink: 0 !important;
|
| 463 |
+
}
|
| 464 |
+
|
| 465 |
+
.bayan-il-modal-score-ring svg {
|
| 466 |
+
display: block !important;
|
| 467 |
+
width: 100% !important;
|
| 468 |
+
height: 100% !important;
|
| 469 |
+
}
|
| 470 |
+
|
| 471 |
+
.bayan-il-modal-score-ring circle:last-child {
|
| 472 |
+
transition: stroke-dashoffset 800ms cubic-bezier(0.34, 1.56, 0.64, 1) !important;
|
| 473 |
+
}
|
| 474 |
+
|
| 475 |
+
.bayan-il-modal-score-value {
|
| 476 |
+
position: absolute !important;
|
| 477 |
+
inset: 0 !important;
|
| 478 |
+
display: flex !important;
|
| 479 |
+
align-items: center !important;
|
| 480 |
+
justify-content: center !important;
|
| 481 |
+
font-size: 22px !important;
|
| 482 |
+
font-weight: 800 !important;
|
| 483 |
+
color: #818cf8 !important;
|
| 484 |
+
}
|
| 485 |
+
|
| 486 |
+
.bayan-il-modal-score-meta {
|
| 487 |
+
flex: 1 !important;
|
| 488 |
+
min-width: 0 !important;
|
| 489 |
+
}
|
| 490 |
+
|
| 491 |
+
.bayan-il-modal-score-hint {
|
| 492 |
+
display: block !important;
|
| 493 |
+
font-size: 12px !important;
|
| 494 |
+
color: #9898ad !important;
|
| 495 |
+
margin-bottom: 8px !important;
|
| 496 |
+
}
|
| 497 |
+
|
| 498 |
+
.bayan-il-modal-counts {
|
| 499 |
+
display: flex !important;
|
| 500 |
+
gap: 10px !important;
|
| 501 |
+
flex-wrap: wrap !important;
|
| 502 |
+
}
|
| 503 |
+
|
| 504 |
+
.bayan-il-modal-count {
|
| 505 |
+
font-size: 11px !important;
|
| 506 |
+
color: #6b6b80 !important;
|
| 507 |
+
display: flex !important;
|
| 508 |
+
align-items: center !important;
|
| 509 |
+
gap: 3px !important;
|
| 510 |
+
}
|
| 511 |
+
|
| 512 |
+
.bayan-il-modal-count strong {
|
| 513 |
+
font-weight: 700 !important;
|
| 514 |
+
}
|
| 515 |
+
|
| 516 |
+
.bayan-il-modal-count--spelling strong { color: #ef4444 !important; }
|
| 517 |
+
.bayan-il-modal-count--grammar strong { color: #f59e0b !important; }
|
| 518 |
+
.bayan-il-modal-count--punctuation strong { color: #6BC98A !important; }
|
| 519 |
+
|
| 520 |
+
/* ── Suggestions Header ── */
|
| 521 |
+
|
| 522 |
+
.bayan-il-modal-sugg-header {
|
| 523 |
+
display: flex !important;
|
| 524 |
+
align-items: center !important;
|
| 525 |
+
justify-content: space-between !important;
|
| 526 |
+
margin-bottom: 10px !important;
|
| 527 |
+
}
|
| 528 |
+
|
| 529 |
+
.bayan-il-modal-sugg-title {
|
| 530 |
+
font-size: 12px !important;
|
| 531 |
+
font-weight: 600 !important;
|
| 532 |
+
color: #9898ad !important;
|
| 533 |
+
}
|
| 534 |
+
|
| 535 |
+
.bayan-il-modal-sugg-count {
|
| 536 |
+
font-size: 11px !important;
|
| 537 |
+
color: #6b6b80 !important;
|
| 538 |
+
}
|
| 539 |
+
|
| 540 |
+
/* ── Suggestion Cards ── */
|
| 541 |
+
|
| 542 |
+
.bayan-il-modal-cards {
|
| 543 |
+
display: flex !important;
|
| 544 |
+
flex-direction: column !important;
|
| 545 |
+
gap: 8px !important;
|
| 546 |
+
margin-bottom: 14px !important;
|
| 547 |
+
}
|
| 548 |
+
|
| 549 |
+
.bayan-il-modal-card {
|
| 550 |
+
padding: 10px 12px !important;
|
| 551 |
+
background: #1a1a24 !important;
|
| 552 |
+
border: 1px solid #2d2d3d !important;
|
| 553 |
+
border-right: 4px solid #6b6b80 !important;
|
| 554 |
+
border-radius: 10px !important;
|
| 555 |
+
cursor: pointer !important;
|
| 556 |
+
transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1) !important;
|
| 557 |
+
}
|
| 558 |
+
|
| 559 |
+
.bayan-il-modal-card:hover {
|
| 560 |
+
border-color: #3a3a4d !important;
|
| 561 |
+
background: #22222e !important;
|
| 562 |
+
transform: translateX(-2px) !important;
|
| 563 |
+
}
|
| 564 |
+
|
| 565 |
+
.bayan-il-modal-card--spelling { border-right-color: #ef4444 !important; }
|
| 566 |
+
.bayan-il-modal-card--grammar { border-right-color: #f59e0b !important; }
|
| 567 |
+
.bayan-il-modal-card--punctuation { border-right-color: #6BC98A !important; }
|
| 568 |
+
|
| 569 |
+
.bayan-il-modal-card-badge {
|
| 570 |
+
display: inline-block !important;
|
| 571 |
+
padding: 2px 8px !important;
|
| 572 |
+
border-radius: 20px !important;
|
| 573 |
+
font-size: 10px !important;
|
| 574 |
+
font-weight: 700 !important;
|
| 575 |
+
margin-bottom: 6px !important;
|
| 576 |
+
}
|
| 577 |
+
|
| 578 |
+
.bayan-il-modal-badge--spelling {
|
| 579 |
+
background: rgba(239, 68, 68, 0.15) !important;
|
| 580 |
+
color: #ef4444 !important;
|
| 581 |
+
}
|
| 582 |
+
|
| 583 |
+
.bayan-il-modal-badge--grammar {
|
| 584 |
+
background: rgba(245, 158, 11, 0.15) !important;
|
| 585 |
+
color: #f59e0b !important;
|
| 586 |
+
}
|
| 587 |
+
|
| 588 |
+
.bayan-il-modal-badge--punctuation {
|
| 589 |
+
background: rgba(107, 201, 138, 0.15) !important;
|
| 590 |
+
color: #6BC98A !important;
|
| 591 |
+
}
|
| 592 |
+
|
| 593 |
+
.bayan-il-modal-card-change {
|
| 594 |
+
display: flex !important;
|
| 595 |
+
align-items: center !important;
|
| 596 |
+
gap: 6px !important;
|
| 597 |
+
margin-bottom: 8px !important;
|
| 598 |
+
font-size: 14px !important;
|
| 599 |
+
line-height: 1.6 !important;
|
| 600 |
+
}
|
| 601 |
+
|
| 602 |
+
.bayan-il-modal-card-original {
|
| 603 |
+
text-decoration: line-through !important;
|
| 604 |
+
text-decoration-color: rgba(239, 68, 68, 0.4) !important;
|
| 605 |
+
color: #ef4444 !important;
|
| 606 |
+
opacity: 0.75 !important;
|
| 607 |
+
}
|
| 608 |
+
|
| 609 |
+
.bayan-il-modal-card-arrow {
|
| 610 |
+
color: #6b6b80 !important;
|
| 611 |
+
font-size: 12px !important;
|
| 612 |
+
}
|
| 613 |
+
|
| 614 |
+
.bayan-il-modal-card-fix {
|
| 615 |
+
color: #22c55e !important;
|
| 616 |
+
font-weight: 600 !important;
|
| 617 |
+
}
|
| 618 |
+
|
| 619 |
+
.bayan-il-modal-card-alts {
|
| 620 |
+
display: flex !important;
|
| 621 |
+
flex-wrap: wrap !important;
|
| 622 |
+
gap: 6px !important;
|
| 623 |
+
}
|
| 624 |
+
|
| 625 |
+
.bayan-il-modal-alt-chip {
|
| 626 |
+
padding: 4px 10px !important;
|
| 627 |
+
border: 1px solid #3a3a4d !important;
|
| 628 |
+
border-radius: 20px !important;
|
| 629 |
+
background: transparent !important;
|
| 630 |
+
color: #9898ad !important;
|
| 631 |
+
font-family: 'Segoe UI', Tahoma, sans-serif !important;
|
| 632 |
+
font-size: 11px !important;
|
| 633 |
+
cursor: pointer !important;
|
| 634 |
+
transition: all 200ms ease !important;
|
| 635 |
+
}
|
| 636 |
+
|
| 637 |
+
.bayan-il-modal-alt-chip:hover {
|
| 638 |
+
border-color: #6366f1 !important;
|
| 639 |
+
color: #f0f0f5 !important;
|
| 640 |
+
background: rgba(99, 102, 241, 0.1) !important;
|
| 641 |
+
}
|
| 642 |
+
|
| 643 |
+
.bayan-il-modal-alt-chip--main {
|
| 644 |
+
background: #6366f1 !important;
|
| 645 |
+
border-color: #6366f1 !important;
|
| 646 |
+
color: white !important;
|
| 647 |
+
}
|
| 648 |
+
|
| 649 |
+
.bayan-il-modal-alt-chip--main:hover {
|
| 650 |
+
background: #4f46e5 !important;
|
| 651 |
+
color: white !important;
|
| 652 |
+
}
|
| 653 |
+
|
| 654 |
+
.bayan-il-modal-alt-chip--keep {
|
| 655 |
+
border-style: dashed !important;
|
| 656 |
+
color: #6b6b80 !important;
|
| 657 |
+
opacity: 0.8 !important;
|
| 658 |
+
}
|
| 659 |
+
|
| 660 |
+
.bayan-il-modal-alt-chip--keep:hover {
|
| 661 |
+
opacity: 1 !important;
|
| 662 |
+
border-color: #6b6b80 !important;
|
| 663 |
+
background: rgba(107, 107, 128, 0.1) !important;
|
| 664 |
+
}
|
| 665 |
+
|
| 666 |
+
/* ── Apply All Button ── */
|
| 667 |
+
|
| 668 |
+
.bayan-il-modal-apply-all {
|
| 669 |
+
width: 100% !important;
|
| 670 |
+
padding: 10px 18px !important;
|
| 671 |
+
border: none !important;
|
| 672 |
+
border-radius: 10px !important;
|
| 673 |
+
background: linear-gradient(135deg, #6366f1, #4f46e5) !important;
|
| 674 |
+
color: white !important;
|
| 675 |
+
font-family: 'Segoe UI', Tahoma, sans-serif !important;
|
| 676 |
+
font-size: 13px !important;
|
| 677 |
+
font-weight: 600 !important;
|
| 678 |
+
cursor: pointer !important;
|
| 679 |
+
transition: transform 150ms ease, box-shadow 150ms ease !important;
|
| 680 |
+
box-shadow: 0 2px 8px rgba(99, 102, 241, 0.3) !important;
|
| 681 |
+
display: flex !important;
|
| 682 |
+
align-items: center !important;
|
| 683 |
+
justify-content: center !important;
|
| 684 |
+
gap: 6px !important;
|
| 685 |
+
}
|
| 686 |
+
|
| 687 |
+
.bayan-il-modal-apply-all:hover {
|
| 688 |
+
transform: translateY(-1px) !important;
|
| 689 |
+
box-shadow: 0 4px 16px rgba(99, 102, 241, 0.45) !important;
|
| 690 |
+
}
|
| 691 |
+
|
| 692 |
+
.bayan-il-modal-apply-all:active {
|
| 693 |
+
transform: translateY(0) !important;
|
| 694 |
+
}
|
| 695 |
+
|
| 696 |
+
/* ── Empty State ── */
|
| 697 |
+
|
| 698 |
+
.bayan-il-modal-empty {
|
| 699 |
+
text-align: center !important;
|
| 700 |
+
padding: 24px 16px !important;
|
| 701 |
+
color: #6b6b80 !important;
|
| 702 |
+
font-size: 13px !important;
|
| 703 |
+
}
|
| 704 |
+
|
| 705 |
+
.bayan-il-modal-empty-icon {
|
| 706 |
+
font-size: 32px !important;
|
| 707 |
+
margin-bottom: 8px !important;
|
| 708 |
+
display: block !important;
|
| 709 |
+
}
|
| 710 |
+
|
| 711 |
+
/* ══════════════════════════════════════════════
|
| 712 |
+
Modal — Light Theme Overrides
|
| 713 |
+
══════════════════════════════════════════════ */
|
| 714 |
+
|
| 715 |
+
.bayan-il-modal-backdrop[data-bayan-theme="light"] {
|
| 716 |
+
background: rgba(0, 0, 0, 0.3) !important;
|
| 717 |
+
}
|
| 718 |
+
|
| 719 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] {
|
| 720 |
+
background: #ffffff !important;
|
| 721 |
+
border-color: #e5e7eb !important;
|
| 722 |
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(99, 102, 241, 0.08) !important;
|
| 723 |
+
color: #1f2937 !important;
|
| 724 |
+
}
|
| 725 |
+
|
| 726 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-header {
|
| 727 |
+
background: linear-gradient(135deg, #ffffff 0%, rgba(99, 102, 241, 0.05) 100%) !important;
|
| 728 |
+
border-bottom-color: #e5e7eb !important;
|
| 729 |
+
}
|
| 730 |
+
|
| 731 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-close {
|
| 732 |
+
color: #9ca3af !important;
|
| 733 |
+
}
|
| 734 |
+
|
| 735 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-close:hover {
|
| 736 |
+
color: #1f2937 !important;
|
| 737 |
+
background: rgba(0, 0, 0, 0.05) !important;
|
| 738 |
+
}
|
| 739 |
+
|
| 740 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-body::-webkit-scrollbar-thumb {
|
| 741 |
+
background: #d1d5db !important;
|
| 742 |
+
}
|
| 743 |
+
|
| 744 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-score {
|
| 745 |
+
background: #f9fafb !important;
|
| 746 |
+
border-color: #e5e7eb !important;
|
| 747 |
+
}
|
| 748 |
+
|
| 749 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-score-value {
|
| 750 |
+
color: #6366f1 !important;
|
| 751 |
+
}
|
| 752 |
+
|
| 753 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-score-hint {
|
| 754 |
+
color: #6b7280 !important;
|
| 755 |
+
}
|
| 756 |
+
|
| 757 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-count {
|
| 758 |
+
color: #9ca3af !important;
|
| 759 |
+
}
|
| 760 |
+
|
| 761 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-sugg-title {
|
| 762 |
+
color: #6b7280 !important;
|
| 763 |
+
}
|
| 764 |
+
|
| 765 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-sugg-count {
|
| 766 |
+
color: #9ca3af !important;
|
| 767 |
+
}
|
| 768 |
+
|
| 769 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-card {
|
| 770 |
+
background: #ffffff !important;
|
| 771 |
+
border-color: #e5e7eb !important;
|
| 772 |
+
}
|
| 773 |
+
|
| 774 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-card:hover {
|
| 775 |
+
background: #f9fafb !important;
|
| 776 |
+
border-color: #d1d5db !important;
|
| 777 |
+
}
|
| 778 |
+
|
| 779 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-card-original {
|
| 780 |
+
color: #dc2626 !important;
|
| 781 |
+
}
|
| 782 |
+
|
| 783 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-card-arrow {
|
| 784 |
+
color: #9ca3af !important;
|
| 785 |
+
}
|
| 786 |
+
|
| 787 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-card-fix {
|
| 788 |
+
color: #16a34a !important;
|
| 789 |
+
}
|
| 790 |
+
|
| 791 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-alt-chip {
|
| 792 |
+
border-color: #d1d5db !important;
|
| 793 |
+
color: #6b7280 !important;
|
| 794 |
+
background: transparent !important;
|
| 795 |
+
}
|
| 796 |
+
|
| 797 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-alt-chip:hover {
|
| 798 |
+
border-color: #6366f1 !important;
|
| 799 |
+
color: #4f46e5 !important;
|
| 800 |
+
background: rgba(99, 102, 241, 0.06) !important;
|
| 801 |
+
}
|
| 802 |
+
|
| 803 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-alt-chip--main {
|
| 804 |
+
background: #6366f1 !important;
|
| 805 |
+
border-color: #6366f1 !important;
|
| 806 |
+
color: white !important;
|
| 807 |
+
}
|
| 808 |
+
|
| 809 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-alt-chip--keep {
|
| 810 |
+
color: #9ca3af !important;
|
| 811 |
+
border-color: #d1d5db !important;
|
| 812 |
+
}
|
| 813 |
+
|
| 814 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-alt-chip--keep:hover {
|
| 815 |
+
border-color: #9ca3af !important;
|
| 816 |
+
background: rgba(0, 0, 0, 0.03) !important;
|
| 817 |
+
}
|
| 818 |
+
|
| 819 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-empty {
|
| 820 |
+
color: #9ca3af !important;
|
| 821 |
+
}
|
| 822 |
+
|
extension/content-inline.js
CHANGED
|
@@ -636,11 +636,7 @@
|
|
| 636 |
return;
|
| 637 |
}
|
| 638 |
if (suggestions.length > 0) {
|
| 639 |
-
|
| 640 |
-
pendingSelection = null;
|
| 641 |
-
if (lastInteractedField) lastInteractedField.dataset.bayanSource = '1';
|
| 642 |
-
chrome.runtime.sendMessage({ type: 'OPEN_SIDEPANEL', text: lastAnalyzedText });
|
| 643 |
-
} catch {}
|
| 644 |
}
|
| 645 |
});
|
| 646 |
}
|
|
@@ -998,6 +994,8 @@
|
|
| 998 |
if (tooltip?.contains(a)) return;
|
| 999 |
if (floatingBtn?.contains(a)) return;
|
| 1000 |
if (overlayContainer?.contains(a)) return;
|
|
|
|
|
|
|
| 1001 |
if (document.querySelector('.bayan-il-tooltip')) return;
|
| 1002 |
detachField();
|
| 1003 |
}, 300);
|
|
@@ -1057,19 +1055,296 @@
|
|
| 1057 |
console.log(`[Bayan] Inline engine v7.1 (mode: ${mode}, TT: ${ttPolicy ? 'yes' : 'no'})`);
|
| 1058 |
|
| 1059 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1060 |
// ── Theme Sync for Inline UI ──
|
| 1061 |
let currentBayanTheme = 'dark';
|
| 1062 |
chrome.storage.local.get(['theme'], (res) => {
|
| 1063 |
currentBayanTheme = res.theme || 'dark';
|
| 1064 |
if (typeof tooltip !== 'undefined' && tooltip) tooltip.setAttribute('data-bayan-theme', currentBayanTheme);
|
| 1065 |
if (typeof floatingBtn !== 'undefined' && floatingBtn) floatingBtn.setAttribute('data-bayan-theme', currentBayanTheme);
|
|
|
|
|
|
|
| 1066 |
});
|
| 1067 |
-
|
| 1068 |
chrome.storage.onChanged.addListener((changes, namespace) => {
|
| 1069 |
if (namespace === 'local' && changes.theme) {
|
| 1070 |
currentBayanTheme = changes.theme.newValue;
|
| 1071 |
if (typeof tooltip !== 'undefined' && tooltip) tooltip.setAttribute('data-bayan-theme', currentBayanTheme);
|
| 1072 |
if (typeof floatingBtn !== 'undefined' && floatingBtn) floatingBtn.setAttribute('data-bayan-theme', currentBayanTheme);
|
|
|
|
|
|
|
| 1073 |
}
|
| 1074 |
});
|
| 1075 |
|
|
|
|
| 636 |
return;
|
| 637 |
}
|
| 638 |
if (suggestions.length > 0) {
|
| 639 |
+
showModal();
|
|
|
|
|
|
|
|
|
|
|
|
|
| 640 |
}
|
| 641 |
});
|
| 642 |
}
|
|
|
|
| 994 |
if (tooltip?.contains(a)) return;
|
| 995 |
if (floatingBtn?.contains(a)) return;
|
| 996 |
if (overlayContainer?.contains(a)) return;
|
| 997 |
+
if (modalPanel?.contains(a)) return;
|
| 998 |
+
if (modalBackdrop?.contains(a)) return;
|
| 999 |
if (document.querySelector('.bayan-il-tooltip')) return;
|
| 1000 |
detachField();
|
| 1001 |
}, 300);
|
|
|
|
| 1055 |
console.log(`[Bayan] Inline engine v7.1 (mode: ${mode}, TT: ${ttPolicy ? 'yes' : 'no'})`);
|
| 1056 |
|
| 1057 |
|
| 1058 |
+
// ══════════════════════════════════════════════════════════
|
| 1059 |
+
// Modal Dialog — Full Analysis Panel
|
| 1060 |
+
// ══════════════════════════════════════════════════════════
|
| 1061 |
+
|
| 1062 |
+
let modalBackdrop = null;
|
| 1063 |
+
let modalPanel = null;
|
| 1064 |
+
|
| 1065 |
+
const TYPE_LABELS = { spelling: 'إملائي', grammar: 'نحوي', punctuation: 'ترقيم' };
|
| 1066 |
+
const SCORE_CIRCUMFERENCE = 440;
|
| 1067 |
+
|
| 1068 |
+
function calculateWritingScore(spelling, grammar, punctuation) {
|
| 1069 |
+
const raw = 100 - spelling * 8 - grammar * 6 - punctuation * 3;
|
| 1070 |
+
return Math.max(0, Math.min(100, raw));
|
| 1071 |
+
}
|
| 1072 |
+
|
| 1073 |
+
function getScoreColor(score) {
|
| 1074 |
+
if (score >= 80) return '#22c55e';
|
| 1075 |
+
if (score >= 50) return '#f59e0b';
|
| 1076 |
+
return '#ef4444';
|
| 1077 |
+
}
|
| 1078 |
+
|
| 1079 |
+
function getScoreHint(score) {
|
| 1080 |
+
if (score >= 90) return 'ممتاز! كتابة نظيفة';
|
| 1081 |
+
if (score >= 70) return 'جيد، بعض التحسينات ممكنة';
|
| 1082 |
+
if (score >= 50) return 'مقبول، يحتاج مراجعة';
|
| 1083 |
+
return 'يحتاج تحسين كبير';
|
| 1084 |
+
}
|
| 1085 |
+
|
| 1086 |
+
function resolveAlternatives(s) {
|
| 1087 |
+
if (s.alternatives && s.alternatives.length > 0) return s.alternatives;
|
| 1088 |
+
const alts = [];
|
| 1089 |
+
if (s.correction) alts.push(s.correction);
|
| 1090 |
+
alts.push(s.original);
|
| 1091 |
+
return alts;
|
| 1092 |
+
}
|
| 1093 |
+
|
| 1094 |
+
function createModal() {
|
| 1095 |
+
if (modalBackdrop) return;
|
| 1096 |
+
|
| 1097 |
+
modalBackdrop = document.createElement('div');
|
| 1098 |
+
modalBackdrop.className = 'bayan-il-modal-backdrop';
|
| 1099 |
+
modalBackdrop.setAttribute('data-bayan-theme', currentBayanTheme);
|
| 1100 |
+
|
| 1101 |
+
modalPanel = document.createElement('div');
|
| 1102 |
+
modalPanel.className = 'bayan-il-modal-panel';
|
| 1103 |
+
modalPanel.setAttribute('data-bayan-theme', currentBayanTheme);
|
| 1104 |
+
modalPanel.dir = 'rtl';
|
| 1105 |
+
|
| 1106 |
+
safeHTML(modalPanel, `
|
| 1107 |
+
<div class="bayan-il-modal-header">
|
| 1108 |
+
<div class="bayan-il-modal-brand">
|
| 1109 |
+
<svg width="22" height="22" viewBox="0 0 100 100" fill="none">
|
| 1110 |
+
<circle cx="50" cy="50" r="46" fill="url(#bmGrad)" />
|
| 1111 |
+
<path d="M30 55 Q35 35, 50 30 Q65 35, 70 55 Q65 65, 50 70 Q35 65, 30 55Z" fill="rgba(255,255,255,0.9)" />
|
| 1112 |
+
<circle cx="50" cy="42" r="4" fill="url(#bmGrad)" />
|
| 1113 |
+
<defs>
|
| 1114 |
+
<linearGradient id="bmGrad" x1="0" y1="0" x2="100" y2="100">
|
| 1115 |
+
<stop offset="0%" stop-color="#6366f1"/>
|
| 1116 |
+
<stop offset="100%" stop-color="#8b5cf6"/>
|
| 1117 |
+
</linearGradient>
|
| 1118 |
+
</defs>
|
| 1119 |
+
</svg>
|
| 1120 |
+
<span class="bayan-il-modal-title">بيان</span>
|
| 1121 |
+
</div>
|
| 1122 |
+
<button class="bayan-il-modal-close" title="إغلاق">✕</button>
|
| 1123 |
+
</div>
|
| 1124 |
+
<div class="bayan-il-modal-body">
|
| 1125 |
+
<div class="bayan-il-modal-score">
|
| 1126 |
+
<div class="bayan-il-modal-score-ring">
|
| 1127 |
+
<svg viewBox="0 0 160 160">
|
| 1128 |
+
<circle cx="80" cy="80" r="70" fill="none" stroke="#2d2d3d" stroke-width="8" />
|
| 1129 |
+
<circle cx="80" cy="80" r="70" fill="none" stroke="#6366f1" stroke-width="8"
|
| 1130 |
+
stroke-dasharray="${SCORE_CIRCUMFERENCE}" stroke-dashoffset="${SCORE_CIRCUMFERENCE}" stroke-linecap="round"
|
| 1131 |
+
id="bayan-modal-score-circle" transform="rotate(-90 80 80)" />
|
| 1132 |
+
</svg>
|
| 1133 |
+
<span class="bayan-il-modal-score-value" id="bayan-modal-score-value">--</span>
|
| 1134 |
+
</div>
|
| 1135 |
+
<div class="bayan-il-modal-score-meta">
|
| 1136 |
+
<span class="bayan-il-modal-score-hint" id="bayan-modal-score-hint"></span>
|
| 1137 |
+
<div class="bayan-il-modal-counts">
|
| 1138 |
+
<span class="bayan-il-modal-count bayan-il-modal-count--spelling">إملائي: <strong id="bayan-modal-count-spelling">٠</strong></span>
|
| 1139 |
+
<span class="bayan-il-modal-count bayan-il-modal-count--grammar">نحوي: <strong id="bayan-modal-count-grammar">٠</strong></span>
|
| 1140 |
+
<span class="bayan-il-modal-count bayan-il-modal-count--punctuation">ترقيم: <strong id="bayan-modal-count-punctuation">٠</strong></span>
|
| 1141 |
+
</div>
|
| 1142 |
+
</div>
|
| 1143 |
+
</div>
|
| 1144 |
+
<div class="bayan-il-modal-sugg-header">
|
| 1145 |
+
<span class="bayan-il-modal-sugg-title">الاقتراحات</span>
|
| 1146 |
+
<span class="bayan-il-modal-sugg-count" id="bayan-modal-sugg-count"></span>
|
| 1147 |
+
</div>
|
| 1148 |
+
<div class="bayan-il-modal-cards" id="bayan-modal-cards"></div>
|
| 1149 |
+
<button class="bayan-il-modal-apply-all" id="bayan-modal-apply-all" style="display:none;">
|
| 1150 |
+
<svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>
|
| 1151 |
+
تطبيق الكل
|
| 1152 |
+
</button>
|
| 1153 |
+
</div>
|
| 1154 |
+
`);
|
| 1155 |
+
|
| 1156 |
+
document.body.appendChild(modalBackdrop);
|
| 1157 |
+
document.body.appendChild(modalPanel);
|
| 1158 |
+
|
| 1159 |
+
modalPanel.querySelector('.bayan-il-modal-close').addEventListener('click', hideModal);
|
| 1160 |
+
modalBackdrop.addEventListener('click', hideModal);
|
| 1161 |
+
|
| 1162 |
+
modalPanel.querySelector('#bayan-modal-apply-all').addEventListener('click', applyAllFixes);
|
| 1163 |
+
|
| 1164 |
+
document.addEventListener('keydown', onModalKeydown);
|
| 1165 |
+
}
|
| 1166 |
+
|
| 1167 |
+
function onModalKeydown(e) {
|
| 1168 |
+
if (e.key === 'Escape' && modalBackdrop?.classList.contains('bayan-il-modal-backdrop--visible')) {
|
| 1169 |
+
hideModal();
|
| 1170 |
+
}
|
| 1171 |
+
}
|
| 1172 |
+
|
| 1173 |
+
function showModal() {
|
| 1174 |
+
createModal();
|
| 1175 |
+
|
| 1176 |
+
modalBackdrop.setAttribute('data-bayan-theme', currentBayanTheme);
|
| 1177 |
+
modalPanel.setAttribute('data-bayan-theme', currentBayanTheme);
|
| 1178 |
+
|
| 1179 |
+
updateModalThemeColors();
|
| 1180 |
+
updateModalScore();
|
| 1181 |
+
renderModalSuggestions();
|
| 1182 |
+
|
| 1183 |
+
modalBackdrop.classList.add('bayan-il-modal-backdrop--visible');
|
| 1184 |
+
requestAnimationFrame(() => {
|
| 1185 |
+
modalPanel.classList.add('bayan-il-modal-panel--visible');
|
| 1186 |
+
});
|
| 1187 |
+
}
|
| 1188 |
+
|
| 1189 |
+
function hideModal() {
|
| 1190 |
+
if (modalPanel) modalPanel.classList.remove('bayan-il-modal-panel--visible');
|
| 1191 |
+
if (modalBackdrop) modalBackdrop.classList.remove('bayan-il-modal-backdrop--visible');
|
| 1192 |
+
}
|
| 1193 |
+
|
| 1194 |
+
function updateModalThemeColors() {
|
| 1195 |
+
if (!modalPanel) return;
|
| 1196 |
+
const scoreTrack = modalPanel.querySelector('#bayan-modal-score-circle')?.previousElementSibling;
|
| 1197 |
+
if (scoreTrack) {
|
| 1198 |
+
scoreTrack.setAttribute('stroke', currentBayanTheme === 'light' ? '#e5e7eb' : '#2d2d3d');
|
| 1199 |
+
}
|
| 1200 |
+
}
|
| 1201 |
+
|
| 1202 |
+
function updateModalScore() {
|
| 1203 |
+
if (!modalPanel) return;
|
| 1204 |
+
|
| 1205 |
+
const counts = { spelling: 0, grammar: 0, punctuation: 0 };
|
| 1206 |
+
suggestions.forEach((s) => {
|
| 1207 |
+
if (counts[s.type] !== undefined) counts[s.type]++;
|
| 1208 |
+
});
|
| 1209 |
+
|
| 1210 |
+
const score = calculateWritingScore(counts.spelling, counts.grammar, counts.punctuation);
|
| 1211 |
+
const color = getScoreColor(score);
|
| 1212 |
+
const offset = SCORE_CIRCUMFERENCE - (SCORE_CIRCUMFERENCE * score) / 100;
|
| 1213 |
+
|
| 1214 |
+
const circle = modalPanel.querySelector('#bayan-modal-score-circle');
|
| 1215 |
+
const valueEl = modalPanel.querySelector('#bayan-modal-score-value');
|
| 1216 |
+
const hintEl = modalPanel.querySelector('#bayan-modal-score-hint');
|
| 1217 |
+
|
| 1218 |
+
if (circle) {
|
| 1219 |
+
circle.setAttribute('stroke', color);
|
| 1220 |
+
requestAnimationFrame(() => {
|
| 1221 |
+
circle.setAttribute('stroke-dashoffset', String(offset));
|
| 1222 |
+
});
|
| 1223 |
+
}
|
| 1224 |
+
if (valueEl) valueEl.textContent = String(score);
|
| 1225 |
+
if (hintEl) hintEl.textContent = getScoreHint(score);
|
| 1226 |
+
|
| 1227 |
+
const toArabicNum = (n) => String(n).replace(/\d/g, (d) => '٠١٢٣٤٥٦٧٨٩'[d]);
|
| 1228 |
+
|
| 1229 |
+
const cSpelling = modalPanel.querySelector('#bayan-modal-count-spelling');
|
| 1230 |
+
const cGrammar = modalPanel.querySelector('#bayan-modal-count-grammar');
|
| 1231 |
+
const cPunctuation = modalPanel.querySelector('#bayan-modal-count-punctuation');
|
| 1232 |
+
if (cSpelling) cSpelling.textContent = toArabicNum(counts.spelling);
|
| 1233 |
+
if (cGrammar) cGrammar.textContent = toArabicNum(counts.grammar);
|
| 1234 |
+
if (cPunctuation) cPunctuation.textContent = toArabicNum(counts.punctuation);
|
| 1235 |
+
}
|
| 1236 |
+
|
| 1237 |
+
function renderModalSuggestions() {
|
| 1238 |
+
if (!modalPanel) return;
|
| 1239 |
+
|
| 1240 |
+
const container = modalPanel.querySelector('#bayan-modal-cards');
|
| 1241 |
+
const countEl = modalPanel.querySelector('#bayan-modal-sugg-count');
|
| 1242 |
+
const applyAllBtn = modalPanel.querySelector('#bayan-modal-apply-all');
|
| 1243 |
+
|
| 1244 |
+
if (!container) return;
|
| 1245 |
+
|
| 1246 |
+
if (suggestions.length === 0) {
|
| 1247 |
+
safeHTML(container, `
|
| 1248 |
+
<div class="bayan-il-modal-empty">
|
| 1249 |
+
<span class="bayan-il-modal-empty-icon">✓</span>
|
| 1250 |
+
لا توجد اقتراحات — نص نظيف!
|
| 1251 |
+
</div>
|
| 1252 |
+
`);
|
| 1253 |
+
if (countEl) countEl.textContent = '';
|
| 1254 |
+
if (applyAllBtn) applyAllBtn.style.display = 'none';
|
| 1255 |
+
return;
|
| 1256 |
+
}
|
| 1257 |
+
|
| 1258 |
+
const toArabicNum = (n) => String(n).replace(/\d/g, (d) => '٠١٢٣٤٥٦٧٨٩'[d]);
|
| 1259 |
+
if (countEl) countEl.textContent = `${toArabicNum(suggestions.length)} اقتراح`;
|
| 1260 |
+
if (applyAllBtn) applyAllBtn.style.display = suggestions.length >= 2 ? 'flex' : 'none';
|
| 1261 |
+
|
| 1262 |
+
let html = '';
|
| 1263 |
+
suggestions.forEach((s, idx) => {
|
| 1264 |
+
const alts = resolveAlternatives(s);
|
| 1265 |
+
const typeLabel = TYPE_LABELS[s.type] || s.type;
|
| 1266 |
+
|
| 1267 |
+
html += `<div class="bayan-il-modal-card bayan-il-modal-card--${s.type}" data-modal-idx="${idx}">`;
|
| 1268 |
+
html += `<span class="bayan-il-modal-card-badge bayan-il-modal-badge--${s.type}">${typeLabel}</span>`;
|
| 1269 |
+
html += `<div class="bayan-il-modal-card-change">`;
|
| 1270 |
+
html += `<span class="bayan-il-modal-card-original">${esc(s.original)}</span>`;
|
| 1271 |
+
html += `<span class="bayan-il-modal-card-arrow">←</span>`;
|
| 1272 |
+
html += `<span class="bayan-il-modal-card-fix">${s.correction ? esc(s.correction) : '<s style="opacity:0.5">حذف</s>'}</span>`;
|
| 1273 |
+
html += `</div>`;
|
| 1274 |
+
html += `<div class="bayan-il-modal-card-alts">`;
|
| 1275 |
+
|
| 1276 |
+
alts.forEach((alt, ai) => {
|
| 1277 |
+
const isMain = alt === s.correction && ai === 0;
|
| 1278 |
+
const isKeep = alt === s.original;
|
| 1279 |
+
let cls = 'bayan-il-modal-alt-chip';
|
| 1280 |
+
if (isMain) cls += ' bayan-il-modal-alt-chip--main';
|
| 1281 |
+
else if (isKeep) cls += ' bayan-il-modal-alt-chip--keep';
|
| 1282 |
+
const label = isKeep ? 'إبقاء الأصل' : esc(alt);
|
| 1283 |
+
html += `<button class="${cls}" data-modal-alt="${esc(alt)}" data-modal-sidx="${idx}" data-modal-keep="${isKeep ? '1' : ''}">${label}</button>`;
|
| 1284 |
+
});
|
| 1285 |
+
|
| 1286 |
+
html += `</div></div>`;
|
| 1287 |
+
});
|
| 1288 |
+
|
| 1289 |
+
safeHTML(container, html);
|
| 1290 |
+
|
| 1291 |
+
container.querySelectorAll('.bayan-il-modal-alt-chip').forEach((chip) => {
|
| 1292 |
+
chip.addEventListener('click', (e) => {
|
| 1293 |
+
e.stopPropagation();
|
| 1294 |
+
const sidx = parseInt(chip.dataset.modalSidx, 10);
|
| 1295 |
+
const isKeep = chip.dataset.modalKeep === '1';
|
| 1296 |
+
const s = suggestions[sidx];
|
| 1297 |
+
if (!s) return;
|
| 1298 |
+
|
| 1299 |
+
if (isKeep) {
|
| 1300 |
+
dismissSuggestion(s);
|
| 1301 |
+
} else {
|
| 1302 |
+
const altVal = chip.dataset.modalAlt;
|
| 1303 |
+
const fixSugg = { ...s, correction: altVal };
|
| 1304 |
+
applyFix(activeField || lastInteractedField, fixSugg);
|
| 1305 |
+
}
|
| 1306 |
+
|
| 1307 |
+
updateModalScore();
|
| 1308 |
+
renderModalSuggestions();
|
| 1309 |
+
|
| 1310 |
+
if (suggestions.length === 0) {
|
| 1311 |
+
setTimeout(hideModal, 600);
|
| 1312 |
+
}
|
| 1313 |
+
});
|
| 1314 |
+
});
|
| 1315 |
+
}
|
| 1316 |
+
|
| 1317 |
+
function applyAllFixes() {
|
| 1318 |
+
const field = activeField || lastInteractedField;
|
| 1319 |
+
if (!field || suggestions.length === 0) return;
|
| 1320 |
+
|
| 1321 |
+
while (suggestions.length > 0) {
|
| 1322 |
+
const last = suggestions.reduce((a, b) => a.start > b.start ? a : b);
|
| 1323 |
+
applyFix(field, last);
|
| 1324 |
+
}
|
| 1325 |
+
|
| 1326 |
+
updateModalScore();
|
| 1327 |
+
renderModalSuggestions();
|
| 1328 |
+
setTimeout(hideModal, 600);
|
| 1329 |
+
}
|
| 1330 |
+
|
| 1331 |
// ── Theme Sync for Inline UI ──
|
| 1332 |
let currentBayanTheme = 'dark';
|
| 1333 |
chrome.storage.local.get(['theme'], (res) => {
|
| 1334 |
currentBayanTheme = res.theme || 'dark';
|
| 1335 |
if (typeof tooltip !== 'undefined' && tooltip) tooltip.setAttribute('data-bayan-theme', currentBayanTheme);
|
| 1336 |
if (typeof floatingBtn !== 'undefined' && floatingBtn) floatingBtn.setAttribute('data-bayan-theme', currentBayanTheme);
|
| 1337 |
+
if (modalBackdrop) modalBackdrop.setAttribute('data-bayan-theme', currentBayanTheme);
|
| 1338 |
+
if (modalPanel) { modalPanel.setAttribute('data-bayan-theme', currentBayanTheme); updateModalThemeColors(); }
|
| 1339 |
});
|
| 1340 |
+
|
| 1341 |
chrome.storage.onChanged.addListener((changes, namespace) => {
|
| 1342 |
if (namespace === 'local' && changes.theme) {
|
| 1343 |
currentBayanTheme = changes.theme.newValue;
|
| 1344 |
if (typeof tooltip !== 'undefined' && tooltip) tooltip.setAttribute('data-bayan-theme', currentBayanTheme);
|
| 1345 |
if (typeof floatingBtn !== 'undefined' && floatingBtn) floatingBtn.setAttribute('data-bayan-theme', currentBayanTheme);
|
| 1346 |
+
if (modalBackdrop) modalBackdrop.setAttribute('data-bayan-theme', currentBayanTheme);
|
| 1347 |
+
if (modalPanel) { modalPanel.setAttribute('data-bayan-theme', currentBayanTheme); updateModalThemeColors(); }
|
| 1348 |
}
|
| 1349 |
});
|
| 1350 |
|
src/app.py
CHANGED
|
@@ -1662,6 +1662,8 @@ def analyze_text():
|
|
| 1662 |
_has_hashtag = bool(_re_spell_guard.search(r'#[\u0600-\u06FF\w]{2,}', ctx.current_text))
|
| 1663 |
_has_percent = bool(_re_spell_guard.search(r'\d+\.\d+%', ctx.current_text))
|
| 1664 |
_has_latin_word = bool(_re_spell_guard.search(r'\b[A-Za-z]{3,}\b', ctx.current_text))
|
|
|
|
|
|
|
| 1665 |
if _has_url or _has_email:
|
| 1666 |
logger.info(f"[ANALYZE] Text contains URLs/emails — skipping spelling")
|
| 1667 |
run_spelling = False
|
|
@@ -2049,7 +2051,7 @@ def analyze_text():
|
|
| 2049 |
#
|
| 2050 |
# For each remaining OOV word, try to find the closest IV word
|
| 2051 |
# using edit-distance-1 candidates from BERT vocabulary.
|
| 2052 |
-
if not
|
| 2053 |
try:
|
| 2054 |
from nlp.spelling.araspell_service import get_spelling_model
|
| 2055 |
_oov_checker = get_spelling_model()
|
|
@@ -2182,7 +2184,7 @@ def analyze_text():
|
|
| 2182 |
]
|
| 2183 |
_structured_placeholders = [] # (start, end, original_text, label)
|
| 2184 |
_grammar_input_text = ctx.current_text
|
| 2185 |
-
if not
|
| 2186 |
import re as _re_struct
|
| 2187 |
for _pat in _PROTECTED_PATTERNS:
|
| 2188 |
for _m in _re_struct.finditer(_pat, _grammar_input_text):
|
|
@@ -2195,7 +2197,7 @@ def analyze_text():
|
|
| 2195 |
logger.info(f"[ANALYZE] Protected {len(_structured_placeholders)} structured elements")
|
| 2196 |
|
| 2197 |
# 2. Grammar (runs on spelling-corrected text — word-level dependency)
|
| 2198 |
-
if not
|
| 2199 |
try:
|
| 2200 |
t0 = time.time()
|
| 2201 |
logger.info(f"[ANALYZE] Step 2: Grammar correction starting...")
|
|
@@ -2583,7 +2585,7 @@ def analyze_text():
|
|
| 2583 |
# FIX-07: Skip punctuation for religious text
|
| 2584 |
# FIX-51: Skip punctuation when spelling+grammar found no errors (clean text)
|
| 2585 |
_has_real_corrections = any(p.stage in ('spelling', 'grammar') for p in ctx.patches.patches)
|
| 2586 |
-
if not
|
| 2587 |
try:
|
| 2588 |
t0 = time.time()
|
| 2589 |
logger.info(f"[ANALYZE] Step 3: Punctuation starting...")
|
|
|
|
| 1662 |
_has_hashtag = bool(_re_spell_guard.search(r'#[\u0600-\u06FF\w]{2,}', ctx.current_text))
|
| 1663 |
_has_percent = bool(_re_spell_guard.search(r'\d+\.\d+%', ctx.current_text))
|
| 1664 |
_has_latin_word = bool(_re_spell_guard.search(r'\b[A-Za-z]{3,}\b', ctx.current_text))
|
| 1665 |
+
|
| 1666 |
+
_skip_all_stages = _is_religious_text or _has_url or _has_email or _has_hashtag or _has_percent or _has_latin_word
|
| 1667 |
if _has_url or _has_email:
|
| 1668 |
logger.info(f"[ANALYZE] Text contains URLs/emails — skipping spelling")
|
| 1669 |
run_spelling = False
|
|
|
|
| 2051 |
#
|
| 2052 |
# For each remaining OOV word, try to find the closest IV word
|
| 2053 |
# using edit-distance-1 candidates from BERT vocabulary.
|
| 2054 |
+
if not _skip_all_stages:
|
| 2055 |
try:
|
| 2056 |
from nlp.spelling.araspell_service import get_spelling_model
|
| 2057 |
_oov_checker = get_spelling_model()
|
|
|
|
| 2184 |
]
|
| 2185 |
_structured_placeholders = [] # (start, end, original_text, label)
|
| 2186 |
_grammar_input_text = ctx.current_text
|
| 2187 |
+
if not _skip_all_stages:
|
| 2188 |
import re as _re_struct
|
| 2189 |
for _pat in _PROTECTED_PATTERNS:
|
| 2190 |
for _m in _re_struct.finditer(_pat, _grammar_input_text):
|
|
|
|
| 2197 |
logger.info(f"[ANALYZE] Protected {len(_structured_placeholders)} structured elements")
|
| 2198 |
|
| 2199 |
# 2. Grammar (runs on spelling-corrected text — word-level dependency)
|
| 2200 |
+
if not _skip_all_stages:
|
| 2201 |
try:
|
| 2202 |
t0 = time.time()
|
| 2203 |
logger.info(f"[ANALYZE] Step 2: Grammar correction starting...")
|
|
|
|
| 2585 |
# FIX-07: Skip punctuation for religious text
|
| 2586 |
# FIX-51: Skip punctuation when spelling+grammar found no errors (clean text)
|
| 2587 |
_has_real_corrections = any(p.stage in ('spelling', 'grammar') for p in ctx.patches.patches)
|
| 2588 |
+
if not _skip_all_stages:
|
| 2589 |
try:
|
| 2590 |
t0 = time.time()
|
| 2591 |
logger.info(f"[ANALYZE] Step 3: Punctuation starting...")
|
tests/phase10/benchmark_runner.py
CHANGED
|
@@ -79,6 +79,10 @@ class BenchResult:
|
|
| 79 |
span_valid: bool = True
|
| 80 |
span_detail: str = ""
|
| 81 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
def strip_punct_only(text):
|
| 83 |
"""Remove ONLY punctuation chars to compare word content."""
|
| 84 |
return re.sub(r'[.,،؛؟!:;?!\s\u060C\u061B\u061F]+', ' ', text).strip()
|
|
@@ -107,7 +111,7 @@ def run_spelling_benchmark(api: API, samples: list) -> List[BenchResult]:
|
|
| 107 |
r.pipeline_output = resp.get('corrected', '')
|
| 108 |
r.pipeline_suggestions = resp.get('suggestions', [])
|
| 109 |
original = resp.get('original', s['input'])
|
| 110 |
-
changed = r.pipeline_output != original
|
| 111 |
|
| 112 |
error_words = s.get('error_words', [])
|
| 113 |
has_errors = len(error_words) > 0
|
|
@@ -211,7 +215,7 @@ def run_grammar_benchmark(api: API, samples: list) -> List[BenchResult]:
|
|
| 211 |
r.pipeline_output = resp.get('corrected', '')
|
| 212 |
r.pipeline_suggestions = resp.get('suggestions', [])
|
| 213 |
original = resp.get('original', s['input'])
|
| 214 |
-
changed = r.pipeline_output != original
|
| 215 |
error_words = s.get('error_words', [])
|
| 216 |
has_errors = len(error_words) > 0
|
| 217 |
expected_fix = s.get('expected_fix', '')
|
|
@@ -384,7 +388,7 @@ def run_entity_benchmark(api: API, samples: list) -> List[BenchResult]:
|
|
| 384 |
else:
|
| 385 |
r.root_cause_stage = "punctuation"
|
| 386 |
r.root_cause_detail = f"Entity '{entity}' was modified"
|
| 387 |
-
elif r.pipeline_output != resp.get('original', s['input']):
|
| 388 |
changes = [f"{sg.get('original','')}→{sg.get('correction','')}" for sg in r.pipeline_suggestions]
|
| 389 |
if changes:
|
| 390 |
r.pipeline_verdict = "FP"
|
|
@@ -424,7 +428,7 @@ def run_religious_benchmark(api: API, samples: list) -> List[BenchResult]:
|
|
| 424 |
r.pipeline_suggestions = resp.get('suggestions', [])
|
| 425 |
original = resp.get('original', s['input'])
|
| 426 |
|
| 427 |
-
if r.pipeline_output != original:
|
| 428 |
changes = [f"{sg.get('original','')}→{sg.get('correction','')}" for sg in r.pipeline_suggestions]
|
| 429 |
r.pipeline_verdict = "FP"
|
| 430 |
r.pipeline_detail = f"RELIGIOUS TEXT MODIFIED: {changes[:3]}"
|
|
@@ -472,7 +476,7 @@ def run_structured_benchmark(api: API, samples: list) -> List[BenchResult]:
|
|
| 472 |
r.root_cause_component = "MODEL"
|
| 473 |
r.root_cause_stage = "grammar"
|
| 474 |
r.root_cause_detail = f"Grammar model destroyed: {s.get('category','')}"
|
| 475 |
-
elif r.pipeline_output != resp.get('original', s['input']):
|
| 476 |
changes = [f"{sg.get('original','')}→{sg.get('correction','')}" for sg in r.pipeline_suggestions]
|
| 477 |
r.pipeline_verdict = "FP"
|
| 478 |
r.pipeline_detail = f"Modified: {changes[:3]}"
|
|
@@ -508,7 +512,7 @@ def run_hallucination_benchmark(api: API, samples: list) -> List[BenchResult]:
|
|
| 508 |
r.pipeline_suggestions = resp.get('suggestions', [])
|
| 509 |
original = resp.get('original', s['input'])
|
| 510 |
|
| 511 |
-
if r.pipeline_output != original:
|
| 512 |
changes = [f"{sg.get('original','')}→{sg.get('correction','')}" for sg in r.pipeline_suggestions]
|
| 513 |
r.pipeline_verdict = "FP"
|
| 514 |
r.pipeline_detail = f"HALLUCINATION: {changes[:3]}"
|
|
|
|
| 79 |
span_valid: bool = True
|
| 80 |
span_detail: str = ""
|
| 81 |
|
| 82 |
+
def _strip_trailing_punct(t):
|
| 83 |
+
if not isinstance(t, str): return ""
|
| 84 |
+
return re.sub(r'[.,،؛؟!:;\?\!\s]+$', '', t).strip()
|
| 85 |
+
|
| 86 |
def strip_punct_only(text):
|
| 87 |
"""Remove ONLY punctuation chars to compare word content."""
|
| 88 |
return re.sub(r'[.,،؛؟!:;?!\s\u060C\u061B\u061F]+', ' ', text).strip()
|
|
|
|
| 111 |
r.pipeline_output = resp.get('corrected', '')
|
| 112 |
r.pipeline_suggestions = resp.get('suggestions', [])
|
| 113 |
original = resp.get('original', s['input'])
|
| 114 |
+
changed = _strip_trailing_punct(r.pipeline_output) != _strip_trailing_punct(original)
|
| 115 |
|
| 116 |
error_words = s.get('error_words', [])
|
| 117 |
has_errors = len(error_words) > 0
|
|
|
|
| 215 |
r.pipeline_output = resp.get('corrected', '')
|
| 216 |
r.pipeline_suggestions = resp.get('suggestions', [])
|
| 217 |
original = resp.get('original', s['input'])
|
| 218 |
+
changed = _strip_trailing_punct(r.pipeline_output) != _strip_trailing_punct(original)
|
| 219 |
error_words = s.get('error_words', [])
|
| 220 |
has_errors = len(error_words) > 0
|
| 221 |
expected_fix = s.get('expected_fix', '')
|
|
|
|
| 388 |
else:
|
| 389 |
r.root_cause_stage = "punctuation"
|
| 390 |
r.root_cause_detail = f"Entity '{entity}' was modified"
|
| 391 |
+
elif _strip_trailing_punct(r.pipeline_output) != _strip_trailing_punct(resp.get('original', s['input'])):
|
| 392 |
changes = [f"{sg.get('original','')}→{sg.get('correction','')}" for sg in r.pipeline_suggestions]
|
| 393 |
if changes:
|
| 394 |
r.pipeline_verdict = "FP"
|
|
|
|
| 428 |
r.pipeline_suggestions = resp.get('suggestions', [])
|
| 429 |
original = resp.get('original', s['input'])
|
| 430 |
|
| 431 |
+
if _strip_trailing_punct(r.pipeline_output) != _strip_trailing_punct(original):
|
| 432 |
changes = [f"{sg.get('original','')}→{sg.get('correction','')}" for sg in r.pipeline_suggestions]
|
| 433 |
r.pipeline_verdict = "FP"
|
| 434 |
r.pipeline_detail = f"RELIGIOUS TEXT MODIFIED: {changes[:3]}"
|
|
|
|
| 476 |
r.root_cause_component = "MODEL"
|
| 477 |
r.root_cause_stage = "grammar"
|
| 478 |
r.root_cause_detail = f"Grammar model destroyed: {s.get('category','')}"
|
| 479 |
+
elif _strip_trailing_punct(r.pipeline_output) != _strip_trailing_punct(resp.get('original', s['input'])):
|
| 480 |
changes = [f"{sg.get('original','')}→{sg.get('correction','')}" for sg in r.pipeline_suggestions]
|
| 481 |
r.pipeline_verdict = "FP"
|
| 482 |
r.pipeline_detail = f"Modified: {changes[:3]}"
|
|
|
|
| 512 |
r.pipeline_suggestions = resp.get('suggestions', [])
|
| 513 |
original = resp.get('original', s['input'])
|
| 514 |
|
| 515 |
+
if _strip_trailing_punct(r.pipeline_output) != _strip_trailing_punct(original):
|
| 516 |
changes = [f"{sg.get('original','')}→{sg.get('correction','')}" for sg in r.pipeline_suggestions]
|
| 517 |
r.pipeline_verdict = "FP"
|
| 518 |
r.pipeline_detail = f"HALLUCINATION: {changes[:3]}"
|
tests/phase10/reports/phase10_results.json
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|