Commit ·
183ba6e
1
Parent(s): 8374b8c
Update extension UI, spelling rules, and benchmark artifacts
Browse files- analyze_failures.py +25 -61
- extension/content-inline.css +211 -63
- extension/content-inline.js +105 -47
- extension/manifest.json +1 -0
- extension/popup.css +95 -23
- extension/popup.html +2 -4
- extension/popup.js +20 -9
- extension/sidepanel/sidepanel.css +109 -33
- extension/sidepanel/sidepanel.html +3 -5
- extension/sidepanel/sidepanel.js +24 -8
- src/css/components.css +3 -1
- src/nlp/spelling/araspell_rules.py +2 -1
- tests/phase10/benchmark_runner.py +21 -12
- tests/phase10/reports/phase10_results.json +0 -0
analyze_failures.py
CHANGED
|
@@ -1,67 +1,31 @@
|
|
| 1 |
-
|
| 2 |
-
import json, re
|
| 3 |
|
| 4 |
-
with open('tests/phase10/reports/
|
| 5 |
data = json.load(f)
|
| 6 |
|
| 7 |
-
|
| 8 |
-
t = re.sub(r'[\u064B-\u065F\u0670]', '', t)
|
| 9 |
-
t = t.rstrip('.،؛؟!?!')
|
| 10 |
-
return re.sub(r'\s+', ' ', t).strip()
|
| 11 |
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
iw = inp_w[i] if i < len(inp_w) else '—'
|
| 30 |
-
aw_n = re.sub(r'[\u064B-\u065F]', '', aw)
|
| 31 |
-
ew_n = re.sub(r'[\u064B-\u065F]', '', ew)
|
| 32 |
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
cause = "MODEL_MISS"
|
| 38 |
-
elif iw == ew:
|
| 39 |
-
cause = "CORRUPTED"
|
| 40 |
-
else:
|
| 41 |
-
cause = "WRONG_FIX"
|
| 42 |
-
issues.append(f" [{i}] '{iw}'→'{aw}' (exp:'{ew}') {cause}")
|
| 43 |
-
|
| 44 |
-
if len(exp_w) != len(act_w):
|
| 45 |
-
issues.append(f" word count: {len(act_w)} vs {len(exp_w)}")
|
| 46 |
-
|
| 47 |
-
# Classify
|
| 48 |
-
has_junk = any('وومن' in a or '.و' in a or 'ةل' in a for a in act_w)
|
| 49 |
-
has_trailing_و = any(a.endswith('و') and not e.endswith('و') and not e.endswith('وا')
|
| 50 |
-
for a, e in zip(act_w, exp_w) if a != e)
|
| 51 |
-
|
| 52 |
-
cat = r['category']
|
| 53 |
-
print(f"\n{rid} [{cat}]")
|
| 54 |
-
print(f" IN: {inp[:60]}")
|
| 55 |
-
print(f" EXP: {exp[:60]}")
|
| 56 |
-
print(f" ACT: {act[:60]}")
|
| 57 |
-
for iss in issues:
|
| 58 |
-
print(iss)
|
| 59 |
-
if has_junk:
|
| 60 |
-
print(" >>> TRAILING JUNK")
|
| 61 |
|
| 62 |
-
|
| 63 |
-
print("\n" + "="*60)
|
| 64 |
-
print("FIXABILITY ANALYSIS")
|
| 65 |
-
print("="*60)
|
| 66 |
-
print(f"\nTotal failures: 24")
|
| 67 |
-
print(f"Need: 17 more passes to reach 85% (43/50)")
|
|
|
|
| 1 |
+
import json
|
|
|
|
| 2 |
|
| 3 |
+
with open('tests/phase10/reports/phase10_results.json', 'r', encoding='utf-8') as f:
|
| 4 |
data = json.load(f)
|
| 5 |
|
| 6 |
+
failures = [r for r in data['results'] if r['pipeline_verdict'] in ('FP', 'FN', 'ERROR')]
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
+
md_content = "# Analysis of the 33 Benchmark Failures\n\n"
|
| 9 |
+
md_content += "This document contains a detailed breakdown of the 33 examples that failed the benchmark, grouped by their dataset.\n\n"
|
| 10 |
+
|
| 11 |
+
from collections import defaultdict
|
| 12 |
+
grouped = defaultdict(list)
|
| 13 |
+
for r in failures:
|
| 14 |
+
grouped[r.get('dataset', 'unknown')].append(r)
|
| 15 |
+
|
| 16 |
+
for dataset, items in grouped.items():
|
| 17 |
+
md_content += f"## Dataset: {dataset.upper()} ({len(items)} failures)\n\n"
|
| 18 |
+
for idx, item in enumerate(items, 1):
|
| 19 |
+
md_content += f"### {idx}. ID: {item.get('id')} ({item.get('pipeline_verdict')})\n"
|
| 20 |
+
md_content += f"- **Input:** `{item.get('input')}`\n"
|
| 21 |
+
md_content += f"- **Expected:** `{item.get('expected')}`\n"
|
| 22 |
+
md_content += f"- **Actual Output:** `{item.get('pipeline_output')}`\n"
|
| 23 |
+
md_content += f"- **Failure Reason:** {item.get('pipeline_detail', 'N/A')}\n"
|
| 24 |
+
md_content += f"- **Root Cause:** {item.get('root_cause_stage', 'unknown')} ({item.get('root_cause_detail', 'N/A')})\n"
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
+
md_content += "\n"
|
| 27 |
+
|
| 28 |
+
with open('C:\\Users\\youss\\.gemini\\antigravity-ide\\brain\\9f7cefbc-f722-4b96-bc24-80ce6ffbd124\\failures_analysis.md', 'w', encoding='utf-8') as out:
|
| 29 |
+
out.write(md_content)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
+
print("Analysis successfully written to artifact.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/content-inline.css
CHANGED
|
@@ -420,6 +420,60 @@
|
|
| 420 |
background: rgba(255, 255, 255, 0.08) !important;
|
| 421 |
}
|
| 422 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 423 |
/* ── Modal Top Bar ── */
|
| 424 |
|
| 425 |
.bayan-il-modal-top-bar {
|
|
@@ -460,6 +514,8 @@
|
|
| 460 |
font-weight: 600 !important;
|
| 461 |
margin: 0 0 10px 0 !important;
|
| 462 |
color: #B4BBC6 !important;
|
|
|
|
|
|
|
| 463 |
}
|
| 464 |
|
| 465 |
/* ── Score Card ── */
|
|
@@ -472,7 +528,7 @@
|
|
| 472 |
padding: 16px 12px !important;
|
| 473 |
background: #1A1D26 !important;
|
| 474 |
border: 1px solid rgba(236, 238, 242, 0.09) !important;
|
| 475 |
-
border-radius:
|
| 476 |
}
|
| 477 |
|
| 478 |
.bayan-il-modal-score-ring {
|
|
@@ -491,7 +547,7 @@
|
|
| 491 |
}
|
| 492 |
|
| 493 |
.bayan-il-modal-score-ring .bayan-il-score-circle {
|
| 494 |
-
transition: stroke-dashoffset
|
| 495 |
}
|
| 496 |
|
| 497 |
.bayan-il-modal-score-value {
|
|
@@ -502,7 +558,7 @@
|
|
| 502 |
justify-content: center !important;
|
| 503 |
font-size: 28px !important;
|
| 504 |
font-weight: 700 !important;
|
| 505 |
-
color: #
|
| 506 |
}
|
| 507 |
|
| 508 |
.bayan-il-modal-score-hint {
|
|
@@ -544,15 +600,9 @@
|
|
| 544 |
padding: 16px 12px !important;
|
| 545 |
background: #1A1D26 !important;
|
| 546 |
border: 1px solid rgba(236, 238, 242, 0.09) !important;
|
| 547 |
-
border-radius:
|
| 548 |
}
|
| 549 |
|
| 550 |
-
.bayan-il-modal-sugg-header {
|
| 551 |
-
display: flex !important;
|
| 552 |
-
align-items: center !important;
|
| 553 |
-
justify-content: space-between !important;
|
| 554 |
-
margin-bottom: 10px !important;
|
| 555 |
-
}
|
| 556 |
|
| 557 |
/* ── Header Divider ── */
|
| 558 |
|
|
@@ -579,41 +629,50 @@
|
|
| 579 |
.bayan-il-modal-cards::-webkit-scrollbar-thumb { background: #3a3a4d !important; border-radius: 4px !important; }
|
| 580 |
|
| 581 |
.bayan-il-modal-card {
|
| 582 |
-
padding:
|
| 583 |
-
|
|
|
|
| 584 |
border: 1px solid rgba(236, 238, 242, 0.09) !important;
|
| 585 |
-
border-
|
|
|
|
| 586 |
cursor: pointer !important;
|
| 587 |
-
transition:
|
|
|
|
| 588 |
}
|
| 589 |
|
| 590 |
.bayan-il-modal-card:hover {
|
| 591 |
-
border-color: #6BA3E0 !important;
|
| 592 |
-
|
| 593 |
-
|
|
|
|
|
|
|
| 594 |
}
|
| 595 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 596 |
.bayan-il-modal-card-badge {
|
| 597 |
display: inline-block !important;
|
| 598 |
padding: 2px 8px !important;
|
| 599 |
-
border-radius:
|
| 600 |
-
font-size:
|
| 601 |
font-weight: 700 !important;
|
| 602 |
-
margin-bottom:
|
| 603 |
}
|
| 604 |
|
| 605 |
.bayan-il-modal-badge--spelling {
|
| 606 |
-
background: rgba(232, 138, 138, 0.
|
| 607 |
color: #E88A8A !important;
|
| 608 |
}
|
| 609 |
|
| 610 |
.bayan-il-modal-badge--grammar {
|
| 611 |
-
background: rgba(228, 179, 90, 0.
|
| 612 |
color: #E4B35A !important;
|
| 613 |
}
|
| 614 |
|
| 615 |
.bayan-il-modal-badge--punctuation {
|
| 616 |
-
background: rgba(107, 201, 138, 0.
|
| 617 |
color: #6BC98A !important;
|
| 618 |
}
|
| 619 |
|
|
@@ -628,9 +687,7 @@
|
|
| 628 |
|
| 629 |
.bayan-il-modal-card-original {
|
| 630 |
text-decoration: line-through !important;
|
| 631 |
-
|
| 632 |
-
color: #E88A8A !important;
|
| 633 |
-
opacity: 0.75 !important;
|
| 634 |
}
|
| 635 |
|
| 636 |
.bayan-il-modal-card-arrow {
|
|
@@ -651,12 +708,12 @@
|
|
| 651 |
|
| 652 |
.bayan-il-modal-alt-chip {
|
| 653 |
padding: 4px 10px !important;
|
| 654 |
-
border: 1px solid
|
| 655 |
-
border-radius:
|
| 656 |
-
background:
|
| 657 |
color: #B4BBC6 !important;
|
| 658 |
font-family: 'Cairo', 'Segoe UI', Tahoma, sans-serif !important;
|
| 659 |
-
font-size:
|
| 660 |
cursor: pointer !important;
|
| 661 |
transition: all 200ms ease !important;
|
| 662 |
}
|
|
@@ -664,30 +721,30 @@
|
|
| 664 |
.bayan-il-modal-alt-chip:hover {
|
| 665 |
border-color: #6BA3E0 !important;
|
| 666 |
color: #ECEEF2 !important;
|
| 667 |
-
background:
|
| 668 |
}
|
| 669 |
|
| 670 |
.bayan-il-modal-alt-chip--main {
|
| 671 |
-
background: #6BA3E0 !important;
|
| 672 |
-
border-color:
|
| 673 |
-
color:
|
|
|
|
| 674 |
}
|
| 675 |
|
| 676 |
.bayan-il-modal-alt-chip--main:hover {
|
| 677 |
-
|
| 678 |
-
color:
|
| 679 |
}
|
| 680 |
|
| 681 |
.bayan-il-modal-alt-chip--keep {
|
| 682 |
border-style: dashed !important;
|
| 683 |
color: #8A939F !important;
|
| 684 |
-
|
| 685 |
}
|
| 686 |
|
| 687 |
.bayan-il-modal-alt-chip--keep:hover {
|
| 688 |
-
|
| 689 |
border-color: #8A939F !important;
|
| 690 |
-
background: rgba(107, 107, 128, 0.1) !important;
|
| 691 |
}
|
| 692 |
|
| 693 |
/* ── Apply All Button ── */
|
|
@@ -697,15 +754,15 @@
|
|
| 697 |
margin-top: 10px !important;
|
| 698 |
padding: 10px 18px !important;
|
| 699 |
border: none !important;
|
| 700 |
-
border-radius:
|
| 701 |
-
background: linear-gradient(135deg, #6BA3E0, #
|
| 702 |
-
color:
|
| 703 |
font-family: 'Cairo', 'Segoe UI', Tahoma, sans-serif !important;
|
| 704 |
-
font-size:
|
| 705 |
-
font-weight:
|
| 706 |
cursor: pointer !important;
|
| 707 |
-
|
| 708 |
-
|
| 709 |
display: flex !important;
|
| 710 |
align-items: center !important;
|
| 711 |
justify-content: center !important;
|
|
@@ -713,27 +770,86 @@
|
|
| 713 |
}
|
| 714 |
|
| 715 |
.bayan-il-modal-apply-all:hover {
|
| 716 |
-
|
| 717 |
-
box-shadow: 0 4px 16px rgba(107, 163, 224, 0.45) !important;
|
| 718 |
}
|
| 719 |
|
| 720 |
.bayan-il-modal-apply-all:active {
|
| 721 |
-
transform:
|
| 722 |
}
|
| 723 |
|
| 724 |
/* ── Empty State ── */
|
| 725 |
|
| 726 |
.bayan-il-modal-empty {
|
| 727 |
-
|
|
|
|
|
|
|
|
|
|
| 728 |
padding: 24px 16px !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 729 |
color: #8A939F !important;
|
| 730 |
-
|
| 731 |
}
|
| 732 |
|
| 733 |
-
|
| 734 |
-
|
| 735 |
-
|
|
|
|
|
|
|
| 736 |
display: block !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 737 |
}
|
| 738 |
|
| 739 |
/* ══════════════════════════════════════════════
|
|
@@ -761,6 +877,16 @@
|
|
| 761 |
background: rgba(0, 0, 0, 0.05) !important;
|
| 762 |
}
|
| 763 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 764 |
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-body-scroll::-webkit-scrollbar-thumb {
|
| 765 |
background: rgba(26, 29, 33, 0.18) !important;
|
| 766 |
}
|
|
@@ -769,13 +895,18 @@
|
|
| 769 |
color: #3A424E !important;
|
| 770 |
}
|
| 771 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 772 |
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-score-card {
|
| 773 |
background: #FAF9F6 !important;
|
| 774 |
border-color: rgba(26, 29, 33, 0.1) !important;
|
| 775 |
}
|
| 776 |
|
| 777 |
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-score-value {
|
| 778 |
-
color: #
|
| 779 |
}
|
| 780 |
|
| 781 |
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-score-hint {
|
|
@@ -805,13 +936,18 @@
|
|
| 805 |
}
|
| 806 |
|
| 807 |
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-card:hover {
|
| 808 |
-
background:
|
| 809 |
-
border-color: #2B6CB8 !important;
|
| 810 |
-
|
|
|
|
| 811 |
}
|
| 812 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 813 |
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-card-original {
|
| 814 |
-
color: #
|
| 815 |
}
|
| 816 |
|
| 817 |
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-card-arrow {
|
|
@@ -835,8 +971,8 @@
|
|
| 835 |
}
|
| 836 |
|
| 837 |
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-alt-chip--main {
|
| 838 |
-
background: #2B6CB8 !important;
|
| 839 |
-
border-color:
|
| 840 |
color: white !important;
|
| 841 |
}
|
| 842 |
|
|
@@ -850,7 +986,19 @@
|
|
| 850 |
background: rgba(0, 0, 0, 0.03) !important;
|
| 851 |
}
|
| 852 |
|
| 853 |
-
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-empty {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 854 |
color: #5A6472 !important;
|
| 855 |
}
|
| 856 |
|
|
@@ -863,7 +1011,7 @@
|
|
| 863 |
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-count--punctuation strong { color: #2F8554 !important; }
|
| 864 |
|
| 865 |
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-apply-all {
|
| 866 |
-
background: linear-gradient(135deg, #2B6CB8, #
|
| 867 |
box-shadow: 0 2px 8px rgba(43, 108, 184, 0.3) !important;
|
| 868 |
}
|
| 869 |
|
|
|
|
| 420 |
background: rgba(255, 255, 255, 0.08) !important;
|
| 421 |
}
|
| 422 |
|
| 423 |
+
.bayan-il-modal-header-actions {
|
| 424 |
+
display: flex !important;
|
| 425 |
+
align-items: center !important;
|
| 426 |
+
gap: 4px !important;
|
| 427 |
+
}
|
| 428 |
+
|
| 429 |
+
.bayan-il-modal-theme-toggle {
|
| 430 |
+
display: flex !important;
|
| 431 |
+
align-items: center !important;
|
| 432 |
+
justify-content: center !important;
|
| 433 |
+
width: 28px !important;
|
| 434 |
+
height: 28px !important;
|
| 435 |
+
border: none !important;
|
| 436 |
+
border-radius: 50% !important;
|
| 437 |
+
background: rgba(255, 255, 255, 0.06) !important;
|
| 438 |
+
color: #8A939F !important;
|
| 439 |
+
cursor: pointer !important;
|
| 440 |
+
padding: 0 !important;
|
| 441 |
+
position: relative !important;
|
| 442 |
+
overflow: hidden !important;
|
| 443 |
+
transition: background 0.3s ease, transform 0.3s ease, color 0.3s ease !important;
|
| 444 |
+
}
|
| 445 |
+
|
| 446 |
+
.bayan-il-modal-theme-toggle:hover {
|
| 447 |
+
background: #6BA3E0 !important;
|
| 448 |
+
color: #fff !important;
|
| 449 |
+
transform: rotate(15deg) !important;
|
| 450 |
+
}
|
| 451 |
+
|
| 452 |
+
.bayan-il-modal-theme-toggle svg {
|
| 453 |
+
transition: transform 0.4s ease, opacity 0.3s ease !important;
|
| 454 |
+
position: absolute !important;
|
| 455 |
+
}
|
| 456 |
+
|
| 457 |
+
.bayan-il-modal-panel[data-bayan-theme="dark"] .bayan-il-theme-icon-sun {
|
| 458 |
+
transform: rotate(90deg) scale(0) !important;
|
| 459 |
+
opacity: 0 !important;
|
| 460 |
+
}
|
| 461 |
+
|
| 462 |
+
.bayan-il-modal-panel[data-bayan-theme="dark"] .bayan-il-theme-icon-moon {
|
| 463 |
+
transform: rotate(0) scale(1) !important;
|
| 464 |
+
opacity: 1 !important;
|
| 465 |
+
}
|
| 466 |
+
|
| 467 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-theme-icon-moon {
|
| 468 |
+
transform: rotate(-90deg) scale(0) !important;
|
| 469 |
+
opacity: 0 !important;
|
| 470 |
+
}
|
| 471 |
+
|
| 472 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-theme-icon-sun {
|
| 473 |
+
transform: rotate(0) scale(1) !important;
|
| 474 |
+
opacity: 1 !important;
|
| 475 |
+
}
|
| 476 |
+
|
| 477 |
/* ── Modal Top Bar ── */
|
| 478 |
|
| 479 |
.bayan-il-modal-top-bar {
|
|
|
|
| 514 |
font-weight: 600 !important;
|
| 515 |
margin: 0 0 10px 0 !important;
|
| 516 |
color: #B4BBC6 !important;
|
| 517 |
+
text-align: right !important;
|
| 518 |
+
align-self: stretch !important;
|
| 519 |
}
|
| 520 |
|
| 521 |
/* ── Score Card ── */
|
|
|
|
| 528 |
padding: 16px 12px !important;
|
| 529 |
background: #1A1D26 !important;
|
| 530 |
border: 1px solid rgba(236, 238, 242, 0.09) !important;
|
| 531 |
+
border-radius: 12px !important;
|
| 532 |
}
|
| 533 |
|
| 534 |
.bayan-il-modal-score-ring {
|
|
|
|
| 547 |
}
|
| 548 |
|
| 549 |
.bayan-il-modal-score-ring .bayan-il-score-circle {
|
| 550 |
+
transition: stroke-dashoffset 600ms ease-in-out !important;
|
| 551 |
}
|
| 552 |
|
| 553 |
.bayan-il-modal-score-value {
|
|
|
|
| 558 |
justify-content: center !important;
|
| 559 |
font-size: 28px !important;
|
| 560 |
font-weight: 700 !important;
|
| 561 |
+
color: #ECEEF2 !important;
|
| 562 |
}
|
| 563 |
|
| 564 |
.bayan-il-modal-score-hint {
|
|
|
|
| 600 |
padding: 16px 12px !important;
|
| 601 |
background: #1A1D26 !important;
|
| 602 |
border: 1px solid rgba(236, 238, 242, 0.09) !important;
|
| 603 |
+
border-radius: 12px !important;
|
| 604 |
}
|
| 605 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 606 |
|
| 607 |
/* ── Header Divider ── */
|
| 608 |
|
|
|
|
| 629 |
.bayan-il-modal-cards::-webkit-scrollbar-thumb { background: #3a3a4d !important; border-radius: 4px !important; }
|
| 630 |
|
| 631 |
.bayan-il-modal-card {
|
| 632 |
+
padding: 16px !important;
|
| 633 |
+
padding-right: 20px !important;
|
| 634 |
+
background: #242833 !important;
|
| 635 |
border: 1px solid rgba(236, 238, 242, 0.09) !important;
|
| 636 |
+
border-right: 4px solid rgba(236, 238, 242, 0.16) !important;
|
| 637 |
+
border-radius: 12px !important;
|
| 638 |
cursor: pointer !important;
|
| 639 |
+
transition: border-color 200ms ease, background 200ms ease, transform 200ms ease !important;
|
| 640 |
+
text-align: right !important;
|
| 641 |
}
|
| 642 |
|
| 643 |
.bayan-il-modal-card:hover {
|
| 644 |
+
border-top-color: #6BA3E0 !important;
|
| 645 |
+
border-bottom-color: #6BA3E0 !important;
|
| 646 |
+
border-left-color: #6BA3E0 !important;
|
| 647 |
+
background: rgba(107, 163, 224, 0.12) !important;
|
| 648 |
+
transform: translateX(-2px) !important;
|
| 649 |
}
|
| 650 |
|
| 651 |
+
.bayan-il-modal-card--spelling { border-right-color: #E88A8A !important; }
|
| 652 |
+
.bayan-il-modal-card--grammar { border-right-color: #E4B35A !important; }
|
| 653 |
+
.bayan-il-modal-card--punctuation { border-right-color: #6BC98A !important; }
|
| 654 |
+
|
| 655 |
.bayan-il-modal-card-badge {
|
| 656 |
display: inline-block !important;
|
| 657 |
padding: 2px 8px !important;
|
| 658 |
+
border-radius: 999px !important;
|
| 659 |
+
font-size: 12px !important;
|
| 660 |
font-weight: 700 !important;
|
| 661 |
+
margin-bottom: 8px !important;
|
| 662 |
}
|
| 663 |
|
| 664 |
.bayan-il-modal-badge--spelling {
|
| 665 |
+
background: rgba(232, 138, 138, 0.18) !important;
|
| 666 |
color: #E88A8A !important;
|
| 667 |
}
|
| 668 |
|
| 669 |
.bayan-il-modal-badge--grammar {
|
| 670 |
+
background: rgba(228, 179, 90, 0.18) !important;
|
| 671 |
color: #E4B35A !important;
|
| 672 |
}
|
| 673 |
|
| 674 |
.bayan-il-modal-badge--punctuation {
|
| 675 |
+
background: rgba(107, 201, 138, 0.16) !important;
|
| 676 |
color: #6BC98A !important;
|
| 677 |
}
|
| 678 |
|
|
|
|
| 687 |
|
| 688 |
.bayan-il-modal-card-original {
|
| 689 |
text-decoration: line-through !important;
|
| 690 |
+
color: #8A939F !important;
|
|
|
|
|
|
|
| 691 |
}
|
| 692 |
|
| 693 |
.bayan-il-modal-card-arrow {
|
|
|
|
| 708 |
|
| 709 |
.bayan-il-modal-alt-chip {
|
| 710 |
padding: 4px 10px !important;
|
| 711 |
+
border: 1px solid rgba(236, 238, 242, 0.09) !important;
|
| 712 |
+
border-radius: 100px !important;
|
| 713 |
+
background: #1A1D26 !important;
|
| 714 |
color: #B4BBC6 !important;
|
| 715 |
font-family: 'Cairo', 'Segoe UI', Tahoma, sans-serif !important;
|
| 716 |
+
font-size: 12px !important;
|
| 717 |
cursor: pointer !important;
|
| 718 |
transition: all 200ms ease !important;
|
| 719 |
}
|
|
|
|
| 721 |
.bayan-il-modal-alt-chip:hover {
|
| 722 |
border-color: #6BA3E0 !important;
|
| 723 |
color: #ECEEF2 !important;
|
| 724 |
+
background: #242833 !important;
|
| 725 |
}
|
| 726 |
|
| 727 |
.bayan-il-modal-alt-chip--main {
|
| 728 |
+
background: linear-gradient(135deg, #6BA3E0, #A594E8) !important;
|
| 729 |
+
border-color: transparent !important;
|
| 730 |
+
color: #F4F5F7 !important;
|
| 731 |
+
font-weight: 700 !important;
|
| 732 |
}
|
| 733 |
|
| 734 |
.bayan-il-modal-alt-chip--main:hover {
|
| 735 |
+
opacity: 0.9 !important;
|
| 736 |
+
border-color: transparent !important;
|
| 737 |
}
|
| 738 |
|
| 739 |
.bayan-il-modal-alt-chip--keep {
|
| 740 |
border-style: dashed !important;
|
| 741 |
color: #8A939F !important;
|
| 742 |
+
font-size: 11px !important;
|
| 743 |
}
|
| 744 |
|
| 745 |
.bayan-il-modal-alt-chip--keep:hover {
|
| 746 |
+
color: #ECEEF2 !important;
|
| 747 |
border-color: #8A939F !important;
|
|
|
|
| 748 |
}
|
| 749 |
|
| 750 |
/* ── Apply All Button ── */
|
|
|
|
| 754 |
margin-top: 10px !important;
|
| 755 |
padding: 10px 18px !important;
|
| 756 |
border: none !important;
|
| 757 |
+
border-radius: 12px !important;
|
| 758 |
+
background: linear-gradient(135deg, #6BA3E0, #A594E8) !important;
|
| 759 |
+
color: #F4F5F7 !important;
|
| 760 |
font-family: 'Cairo', 'Segoe UI', Tahoma, sans-serif !important;
|
| 761 |
+
font-size: 14px !important;
|
| 762 |
+
font-weight: 700 !important;
|
| 763 |
cursor: pointer !important;
|
| 764 |
+
min-height: 44px !important;
|
| 765 |
+
transition: opacity 200ms ease, transform 150ms ease !important;
|
| 766 |
display: flex !important;
|
| 767 |
align-items: center !important;
|
| 768 |
justify-content: center !important;
|
|
|
|
| 770 |
}
|
| 771 |
|
| 772 |
.bayan-il-modal-apply-all:hover {
|
| 773 |
+
opacity: 0.92 !important;
|
|
|
|
| 774 |
}
|
| 775 |
|
| 776 |
.bayan-il-modal-apply-all:active {
|
| 777 |
+
transform: scale(0.98) !important;
|
| 778 |
}
|
| 779 |
|
| 780 |
/* ── Empty State ── */
|
| 781 |
|
| 782 |
.bayan-il-modal-empty {
|
| 783 |
+
display: flex !important;
|
| 784 |
+
flex-direction: column !important;
|
| 785 |
+
align-items: center !important;
|
| 786 |
+
justify-content: center !important;
|
| 787 |
padding: 24px 16px !important;
|
| 788 |
+
text-align: center !important;
|
| 789 |
+
gap: 8px !important;
|
| 790 |
+
}
|
| 791 |
+
|
| 792 |
+
.bayan-il-modal-empty-icon-wrap {
|
| 793 |
+
width: 48px !important;
|
| 794 |
+
height: 48px !important;
|
| 795 |
+
border-radius: 50% !important;
|
| 796 |
+
background: #242833 !important;
|
| 797 |
+
display: flex !important;
|
| 798 |
+
align-items: center !important;
|
| 799 |
+
justify-content: center !important;
|
| 800 |
+
margin-bottom: 4px !important;
|
| 801 |
+
}
|
| 802 |
+
|
| 803 |
+
.bayan-il-modal-empty-title {
|
| 804 |
+
font-size: 14px !important;
|
| 805 |
+
font-weight: 600 !important;
|
| 806 |
+
color: #B4BBC6 !important;
|
| 807 |
+
}
|
| 808 |
+
|
| 809 |
+
.bayan-il-modal-empty-desc {
|
| 810 |
+
font-size: 12px !important;
|
| 811 |
color: #8A939F !important;
|
| 812 |
+
line-height: 1.6 !important;
|
| 813 |
}
|
| 814 |
|
| 815 |
+
/* ── Score Hint Subtitle ── */
|
| 816 |
+
|
| 817 |
+
.bayan-il-modal-score-hint-sub {
|
| 818 |
+
font-size: 11px !important;
|
| 819 |
+
color: #8A939F !important;
|
| 820 |
display: block !important;
|
| 821 |
+
margin-top: 2px !important;
|
| 822 |
+
}
|
| 823 |
+
|
| 824 |
+
/* ── Loading Spinner ── */
|
| 825 |
+
|
| 826 |
+
.bayan-il-modal-loading-spinner {
|
| 827 |
+
width: 32px !important;
|
| 828 |
+
height: 32px !important;
|
| 829 |
+
border: 3px solid rgba(107, 163, 224, 0.2) !important;
|
| 830 |
+
border-top-color: #6BA3E0 !important;
|
| 831 |
+
border-radius: 50% !important;
|
| 832 |
+
animation: bayanIlSpin 0.7s linear infinite !important;
|
| 833 |
+
margin-bottom: 8px !important;
|
| 834 |
+
}
|
| 835 |
+
|
| 836 |
+
@keyframes bayanIlSpin {
|
| 837 |
+
to { transform: rotate(360deg); }
|
| 838 |
+
}
|
| 839 |
+
|
| 840 |
+
/* ── Logo Link ── */
|
| 841 |
+
|
| 842 |
+
.bayan-il-modal-logo-link {
|
| 843 |
+
display: flex !important;
|
| 844 |
+
align-items: center !important;
|
| 845 |
+
text-decoration: none !important;
|
| 846 |
+
cursor: pointer !important;
|
| 847 |
+
border-radius: 6px !important;
|
| 848 |
+
transition: opacity 200ms ease !important;
|
| 849 |
+
}
|
| 850 |
+
|
| 851 |
+
.bayan-il-modal-logo-link:hover {
|
| 852 |
+
opacity: 0.8 !important;
|
| 853 |
}
|
| 854 |
|
| 855 |
/* ══════════════════════════════════════════════
|
|
|
|
| 877 |
background: rgba(0, 0, 0, 0.05) !important;
|
| 878 |
}
|
| 879 |
|
| 880 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-theme-toggle {
|
| 881 |
+
background: rgba(0, 0, 0, 0.05) !important;
|
| 882 |
+
color: #5A6472 !important;
|
| 883 |
+
}
|
| 884 |
+
|
| 885 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-theme-toggle:hover {
|
| 886 |
+
background: #2B6CB8 !important;
|
| 887 |
+
color: #fff !important;
|
| 888 |
+
}
|
| 889 |
+
|
| 890 |
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-body-scroll::-webkit-scrollbar-thumb {
|
| 891 |
background: rgba(26, 29, 33, 0.18) !important;
|
| 892 |
}
|
|
|
|
| 895 |
color: #3A424E !important;
|
| 896 |
}
|
| 897 |
|
| 898 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-loading-spinner {
|
| 899 |
+
border-color: rgba(43, 108, 184, 0.2) !important;
|
| 900 |
+
border-top-color: #2B6CB8 !important;
|
| 901 |
+
}
|
| 902 |
+
|
| 903 |
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-score-card {
|
| 904 |
background: #FAF9F6 !important;
|
| 905 |
border-color: rgba(26, 29, 33, 0.1) !important;
|
| 906 |
}
|
| 907 |
|
| 908 |
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-score-value {
|
| 909 |
+
color: #1A1D21 !important;
|
| 910 |
}
|
| 911 |
|
| 912 |
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-score-hint {
|
|
|
|
| 936 |
}
|
| 937 |
|
| 938 |
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-card:hover {
|
| 939 |
+
background: rgba(43, 108, 184, 0.07) !important;
|
| 940 |
+
border-top-color: #2B6CB8 !important;
|
| 941 |
+
border-bottom-color: #2B6CB8 !important;
|
| 942 |
+
border-left-color: #2B6CB8 !important;
|
| 943 |
}
|
| 944 |
|
| 945 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-card--spelling { border-right-color: #C53030 !important; }
|
| 946 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-card--grammar { border-right-color: #B7791F !important; }
|
| 947 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-card--punctuation { border-right-color: #2F8554 !important; }
|
| 948 |
+
|
| 949 |
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-card-original {
|
| 950 |
+
color: #5A6472 !important;
|
| 951 |
}
|
| 952 |
|
| 953 |
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-card-arrow {
|
|
|
|
| 971 |
}
|
| 972 |
|
| 973 |
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-alt-chip--main {
|
| 974 |
+
background: linear-gradient(135deg, #2B6CB8, #6B57A8) !important;
|
| 975 |
+
border-color: transparent !important;
|
| 976 |
color: white !important;
|
| 977 |
}
|
| 978 |
|
|
|
|
| 986 |
background: rgba(0, 0, 0, 0.03) !important;
|
| 987 |
}
|
| 988 |
|
| 989 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-empty-icon-wrap {
|
| 990 |
+
background: #FAF9F6 !important;
|
| 991 |
+
}
|
| 992 |
+
|
| 993 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-empty-title {
|
| 994 |
+
color: #3A424E !important;
|
| 995 |
+
}
|
| 996 |
+
|
| 997 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-empty-desc {
|
| 998 |
+
color: #5A6472 !important;
|
| 999 |
+
}
|
| 1000 |
+
|
| 1001 |
+
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-score-hint-sub {
|
| 1002 |
color: #5A6472 !important;
|
| 1003 |
}
|
| 1004 |
|
|
|
|
| 1011 |
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-count--punctuation strong { color: #2F8554 !important; }
|
| 1012 |
|
| 1013 |
.bayan-il-modal-panel[data-bayan-theme="light"] .bayan-il-modal-apply-all {
|
| 1014 |
+
background: linear-gradient(135deg, #2B6CB8, #6B57A8) !important;
|
| 1015 |
box-shadow: 0 2px 8px rgba(43, 108, 184, 0.3) !important;
|
| 1016 |
}
|
| 1017 |
|
extension/content-inline.js
CHANGED
|
@@ -61,6 +61,8 @@
|
|
| 61 |
let lastAnalyzedText = '';
|
| 62 |
let suggestions = [];
|
| 63 |
let paused = false;
|
|
|
|
|
|
|
| 64 |
let floatingBtn = null;
|
| 65 |
let tooltip = null;
|
| 66 |
let overlayContainer = null;
|
|
@@ -110,6 +112,7 @@
|
|
| 110 |
const isUserKeystroke = !!e && e.isTrusted === true;
|
| 111 |
if (isUserKeystroke && analysisSuppressed) analysisSuppressed = false;
|
| 112 |
if (isUserKeystroke) pendingSelection = null;
|
|
|
|
| 113 |
|
| 114 |
const text = getFieldText(activeField);
|
| 115 |
|
|
@@ -118,6 +121,8 @@
|
|
| 118 |
if (analysisSuppressed) {
|
| 119 |
clearHighlights();
|
| 120 |
updateBadge(0);
|
|
|
|
|
|
|
| 121 |
return;
|
| 122 |
}
|
| 123 |
|
|
@@ -132,10 +137,14 @@
|
|
| 132 |
lastAnalyzedText = '';
|
| 133 |
clearHighlights();
|
| 134 |
updateBadge(0);
|
|
|
|
|
|
|
| 135 |
return;
|
| 136 |
}
|
| 137 |
|
|
|
|
| 138 |
updateBadge(-1);
|
|
|
|
| 139 |
|
| 140 |
BayanController.scheduleAnalysis(
|
| 141 |
text,
|
|
@@ -151,13 +160,15 @@
|
|
| 151 |
|
| 152 |
function onAnalysisResult(data) {
|
| 153 |
if (!activeField) return;
|
|
|
|
| 154 |
|
| 155 |
if (!data) {
|
| 156 |
updateBadge(0);
|
|
|
|
| 157 |
return;
|
| 158 |
}
|
| 159 |
|
| 160 |
-
suggestions = data.suggestions || [];
|
| 161 |
lastAnalyzedText = data.original || getFieldText(activeField);
|
| 162 |
updateBadge(suggestions.length);
|
| 163 |
|
|
@@ -166,6 +177,7 @@
|
|
| 166 |
} else {
|
| 167 |
clearHighlights();
|
| 168 |
}
|
|
|
|
| 169 |
}
|
| 170 |
|
| 171 |
// ══════════════════════════════════════════════════════════
|
|
@@ -311,6 +323,7 @@
|
|
| 311 |
|
| 312 |
function handleFailure(reason) {
|
| 313 |
paused = true;
|
|
|
|
| 314 |
clearHighlights();
|
| 315 |
BayanController.cancelAll();
|
| 316 |
if (badgeCount) {
|
|
@@ -579,7 +592,12 @@
|
|
| 579 |
updateBadge(suggestions.length);
|
| 580 |
}
|
| 581 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 582 |
function dismissSuggestion(suggestion) {
|
|
|
|
| 583 |
suggestions = suggestions.filter((s) => s.id !== suggestion.id);
|
| 584 |
if (suggestions.length > 0) {
|
| 585 |
renderOverlay(activeField, lastAnalyzedText, suggestions);
|
|
@@ -680,9 +698,7 @@
|
|
| 680 |
onFieldInput();
|
| 681 |
return;
|
| 682 |
}
|
| 683 |
-
|
| 684 |
-
showModal();
|
| 685 |
-
}
|
| 686 |
});
|
| 687 |
}
|
| 688 |
|
|
@@ -1107,17 +1123,10 @@
|
|
| 1107 |
return Math.max(0, Math.min(100, raw));
|
| 1108 |
}
|
| 1109 |
|
| 1110 |
-
function getScoreColor(score) {
|
| 1111 |
-
if (score >= 80) return '#22c55e';
|
| 1112 |
-
if (score >= 50) return '#f59e0b';
|
| 1113 |
-
return '#ef4444';
|
| 1114 |
-
}
|
| 1115 |
-
|
| 1116 |
function getScoreHint(score) {
|
| 1117 |
-
if (score >= 90) return 'ممتاز!
|
| 1118 |
-
if (score >= 70) return 'جيد
|
| 1119 |
-
|
| 1120 |
-
return 'يحتاج تحسين كبير';
|
| 1121 |
}
|
| 1122 |
|
| 1123 |
function resolveAlternatives(s) {
|
|
@@ -1139,20 +1148,26 @@
|
|
| 1139 |
safeHTML(modalPanel, `
|
| 1140 |
<div class="bayan-il-modal-top-bar" id="bayan-modal-drag-handle">
|
| 1141 |
<div class="bayan-il-modal-brand">
|
| 1142 |
-
<
|
|
|
|
|
|
|
| 1143 |
<div class="bayan-il-header-divider"></div>
|
| 1144 |
<span class="bayan-il-modal-title">بيان</span>
|
| 1145 |
</div>
|
| 1146 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1147 |
</div>
|
| 1148 |
|
| 1149 |
<div class="bayan-il-modal-body-scroll">
|
| 1150 |
<div class="bayan-il-modal-sugg-card">
|
| 1151 |
-
<
|
| 1152 |
-
<h3 class="bayan-il-modal-section-title">الاقتراحات (<span id="bayan-modal-sugg-count">٠</span>)</h3>
|
| 1153 |
-
<button id="bayan-modal-apply-all" class="bayan-il-modal-apply-all" style="display:none;" type="button">تطبيق الكل</button>
|
| 1154 |
-
</div>
|
| 1155 |
<div id="bayan-modal-cards" class="bayan-il-modal-cards" role="list" aria-live="polite" aria-label="اقتراحات التصحيح"></div>
|
|
|
|
| 1156 |
</div>
|
| 1157 |
|
| 1158 |
<div class="bayan-il-modal-score-card">
|
|
@@ -1160,7 +1175,7 @@
|
|
| 1160 |
<div class="bayan-il-modal-score-ring" role="img" aria-label="تقييم الكتابة">
|
| 1161 |
<svg viewBox="0 0 160 160" aria-hidden="true">
|
| 1162 |
<circle cx="80" cy="80" r="70" fill="none" stroke="rgba(236, 238, 242, 0.09)" stroke-width="10"/>
|
| 1163 |
-
<circle id="bayan-modal-score-circle" cx="80" cy="80" r="70" fill="none" stroke="url(#bayanModalScoreGradient)" stroke-width="10" stroke-linecap="round" stroke-dasharray="
|
| 1164 |
<defs><linearGradient id="bayanModalScoreGradient" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" stop-color="#6BA3E0"/><stop offset="100%" stop-color="#A594E8"/></linearGradient></defs>
|
| 1165 |
</svg>
|
| 1166 |
<div class="bayan-il-modal-score-value"><span id="bayan-modal-score-value">--</span></div>
|
|
@@ -1181,6 +1196,14 @@
|
|
| 1181 |
|
| 1182 |
modalPanel.querySelector('#bayan-modal-apply-all').addEventListener('click', applyAllFixes);
|
| 1183 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1184 |
document.addEventListener('keydown', onModalKeydown);
|
| 1185 |
|
| 1186 |
let modalDragging = false;
|
|
@@ -1192,7 +1215,7 @@
|
|
| 1192 |
const dragHandle = modalPanel.querySelector('#bayan-modal-drag-handle');
|
| 1193 |
if (dragHandle) {
|
| 1194 |
dragHandle.addEventListener('mousedown', (e) => {
|
| 1195 |
-
if (e.target.closest('.bayan-il-modal-close')) return;
|
| 1196 |
modalDragging = true;
|
| 1197 |
modalDragStartX = e.clientX;
|
| 1198 |
modalDragStartY = e.clientY;
|
|
@@ -1227,6 +1250,13 @@
|
|
| 1227 |
}
|
| 1228 |
}
|
| 1229 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1230 |
function showModal() {
|
| 1231 |
createModal();
|
| 1232 |
|
|
@@ -1287,26 +1317,37 @@
|
|
| 1287 |
});
|
| 1288 |
|
| 1289 |
const score = calculateWritingScore(counts.spelling, counts.grammar, counts.punctuation);
|
| 1290 |
-
const
|
| 1291 |
-
const offset = 439.8 - (439.8 * score) / 100;
|
| 1292 |
|
| 1293 |
const circle = modalPanel.querySelector('#bayan-modal-score-circle');
|
| 1294 |
const valueEl = modalPanel.querySelector('#bayan-modal-score-value');
|
| 1295 |
const hintEl = modalPanel.querySelector('#bayan-modal-score-hint');
|
| 1296 |
|
|
|
|
|
|
|
| 1297 |
if (circle) {
|
| 1298 |
-
circle.setAttribute('stroke', color);
|
| 1299 |
requestAnimationFrame(() => {
|
| 1300 |
circle.setAttribute('stroke-dashoffset', String(offset));
|
| 1301 |
});
|
| 1302 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1303 |
if (valueEl) {
|
| 1304 |
-
|
| 1305 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1306 |
}
|
| 1307 |
-
if (hintEl) hintEl.textContent = getScoreHint(score);
|
| 1308 |
-
|
| 1309 |
-
const toArabicNum = (n) => String(n).replace(/\d/g, (d) => '٠١٢٣٤٥٦٧٨٩'[d]);
|
| 1310 |
|
| 1311 |
const cSpelling = modalPanel.querySelector('#bayan-modal-count-spelling');
|
| 1312 |
const cGrammar = modalPanel.querySelector('#bayan-modal-count-grammar');
|
|
@@ -1320,26 +1361,21 @@
|
|
| 1320 |
if (!modalPanel) return;
|
| 1321 |
|
| 1322 |
const container = modalPanel.querySelector('#bayan-modal-cards');
|
| 1323 |
-
const countEl = modalPanel.querySelector('#bayan-modal-sugg-count');
|
| 1324 |
const applyAllBtn = modalPanel.querySelector('#bayan-modal-apply-all');
|
| 1325 |
|
| 1326 |
if (!container) return;
|
| 1327 |
|
| 1328 |
-
|
| 1329 |
-
|
| 1330 |
-
|
| 1331 |
-
|
| 1332 |
-
|
| 1333 |
-
|
| 1334 |
-
|
| 1335 |
-
|
| 1336 |
-
|
| 1337 |
-
|
| 1338 |
-
|
| 1339 |
-
|
| 1340 |
-
const toArabicNum = (n) => String(n).replace(/\d/g, (d) => '٠١٢٣٤٥٦٧٨٩'[d]);
|
| 1341 |
-
if (countEl) countEl.textContent = toArabicNum(suggestions.length);
|
| 1342 |
-
if (applyAllBtn) applyAllBtn.style.display = suggestions.length >= 2 ? 'flex' : 'none';
|
| 1343 |
|
| 1344 |
let html = '';
|
| 1345 |
suggestions.forEach((s, idx) => {
|
|
@@ -1358,7 +1394,7 @@
|
|
| 1358 |
alts.forEach((alt, ai) => {
|
| 1359 |
const isMain = alt === s.correction && ai === 0;
|
| 1360 |
const isKeep = alt === s.original;
|
| 1361 |
-
const label = isKeep ? '
|
| 1362 |
const cls = isMain ? 'bayan-il-modal-alt-chip bayan-il-modal-alt-chip--main' : (isKeep ? 'bayan-il-modal-alt-chip bayan-il-modal-alt-chip--keep' : 'bayan-il-modal-alt-chip');
|
| 1363 |
html += `<button class="${cls}" data-modal-alt="${esc(alt)}" data-modal-sidx="${idx}" data-modal-keep="${isKeep ? '1' : ''}">${label}</button>`;
|
| 1364 |
});
|
|
@@ -1392,6 +1428,28 @@
|
|
| 1392 |
}
|
| 1393 |
});
|
| 1394 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1395 |
}
|
| 1396 |
|
| 1397 |
function applyAllFixes() {
|
|
|
|
| 61 |
let lastAnalyzedText = '';
|
| 62 |
let suggestions = [];
|
| 63 |
let paused = false;
|
| 64 |
+
let analysisInProgress = false;
|
| 65 |
+
const dismissedKeys = new Set();
|
| 66 |
let floatingBtn = null;
|
| 67 |
let tooltip = null;
|
| 68 |
let overlayContainer = null;
|
|
|
|
| 112 |
const isUserKeystroke = !!e && e.isTrusted === true;
|
| 113 |
if (isUserKeystroke && analysisSuppressed) analysisSuppressed = false;
|
| 114 |
if (isUserKeystroke) pendingSelection = null;
|
| 115 |
+
if (isUserKeystroke) dismissedKeys.clear();
|
| 116 |
|
| 117 |
const text = getFieldText(activeField);
|
| 118 |
|
|
|
|
| 121 |
if (analysisSuppressed) {
|
| 122 |
clearHighlights();
|
| 123 |
updateBadge(0);
|
| 124 |
+
analysisInProgress = false;
|
| 125 |
+
syncModalIfVisible();
|
| 126 |
return;
|
| 127 |
}
|
| 128 |
|
|
|
|
| 137 |
lastAnalyzedText = '';
|
| 138 |
clearHighlights();
|
| 139 |
updateBadge(0);
|
| 140 |
+
analysisInProgress = false;
|
| 141 |
+
syncModalIfVisible();
|
| 142 |
return;
|
| 143 |
}
|
| 144 |
|
| 145 |
+
analysisInProgress = true;
|
| 146 |
updateBadge(-1);
|
| 147 |
+
syncModalIfVisible();
|
| 148 |
|
| 149 |
BayanController.scheduleAnalysis(
|
| 150 |
text,
|
|
|
|
| 160 |
|
| 161 |
function onAnalysisResult(data) {
|
| 162 |
if (!activeField) return;
|
| 163 |
+
analysisInProgress = false;
|
| 164 |
|
| 165 |
if (!data) {
|
| 166 |
updateBadge(0);
|
| 167 |
+
syncModalIfVisible();
|
| 168 |
return;
|
| 169 |
}
|
| 170 |
|
| 171 |
+
suggestions = (data.suggestions || []).filter(s => !dismissedKeys.has(suggestionKey(s)));
|
| 172 |
lastAnalyzedText = data.original || getFieldText(activeField);
|
| 173 |
updateBadge(suggestions.length);
|
| 174 |
|
|
|
|
| 177 |
} else {
|
| 178 |
clearHighlights();
|
| 179 |
}
|
| 180 |
+
syncModalIfVisible();
|
| 181 |
}
|
| 182 |
|
| 183 |
// ══════════════════════════════════════════════════════════
|
|
|
|
| 323 |
|
| 324 |
function handleFailure(reason) {
|
| 325 |
paused = true;
|
| 326 |
+
analysisInProgress = false;
|
| 327 |
clearHighlights();
|
| 328 |
BayanController.cancelAll();
|
| 329 |
if (badgeCount) {
|
|
|
|
| 592 |
updateBadge(suggestions.length);
|
| 593 |
}
|
| 594 |
|
| 595 |
+
function suggestionKey(s) {
|
| 596 |
+
return s.original + '|' + (s.correction || '') + '|' + s.type;
|
| 597 |
+
}
|
| 598 |
+
|
| 599 |
function dismissSuggestion(suggestion) {
|
| 600 |
+
dismissedKeys.add(suggestionKey(suggestion));
|
| 601 |
suggestions = suggestions.filter((s) => s.id !== suggestion.id);
|
| 602 |
if (suggestions.length > 0) {
|
| 603 |
renderOverlay(activeField, lastAnalyzedText, suggestions);
|
|
|
|
| 698 |
onFieldInput();
|
| 699 |
return;
|
| 700 |
}
|
| 701 |
+
showModal();
|
|
|
|
|
|
|
| 702 |
});
|
| 703 |
}
|
| 704 |
|
|
|
|
| 1123 |
return Math.max(0, Math.min(100, raw));
|
| 1124 |
}
|
| 1125 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1126 |
function getScoreHint(score) {
|
| 1127 |
+
if (score >= 90) return 'كتابة ممتازة! استمر.';
|
| 1128 |
+
if (score >= 70) return 'جيد — راجع الاقتراحات لتحسين النص.';
|
| 1129 |
+
return 'يحتاج النص إلى بعض التحسينات.';
|
|
|
|
| 1130 |
}
|
| 1131 |
|
| 1132 |
function resolveAlternatives(s) {
|
|
|
|
| 1148 |
safeHTML(modalPanel, `
|
| 1149 |
<div class="bayan-il-modal-top-bar" id="bayan-modal-drag-handle">
|
| 1150 |
<div class="bayan-il-modal-brand">
|
| 1151 |
+
<a href="https://bayan10-bayan-api.hf.space/" target="_blank" rel="noopener noreferrer" class="bayan-il-modal-logo-link">
|
| 1152 |
+
<img src="${chrome.runtime.getURL('assets/icons/icon48.png')}" alt="بيان" style="width: 28px; height: 28px; object-fit: contain; border-radius: 6px;" draggable="false" />
|
| 1153 |
+
</a>
|
| 1154 |
<div class="bayan-il-header-divider"></div>
|
| 1155 |
<span class="bayan-il-modal-title">بيان</span>
|
| 1156 |
</div>
|
| 1157 |
+
<div class="bayan-il-modal-header-actions">
|
| 1158 |
+
<button id="bayan-modal-theme-toggle" class="bayan-il-modal-theme-toggle" type="button" title="تبديل السمة">
|
| 1159 |
+
<svg class="bayan-il-theme-icon-sun" width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/></svg>
|
| 1160 |
+
<svg class="bayan-il-theme-icon-moon" width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/></svg>
|
| 1161 |
+
</button>
|
| 1162 |
+
<button class="bayan-il-modal-close" title="إغلاق"><svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg></button>
|
| 1163 |
+
</div>
|
| 1164 |
</div>
|
| 1165 |
|
| 1166 |
<div class="bayan-il-modal-body-scroll">
|
| 1167 |
<div class="bayan-il-modal-sugg-card">
|
| 1168 |
+
<h3 class="bayan-il-modal-section-title">الاقتراحات</h3>
|
|
|
|
|
|
|
|
|
|
| 1169 |
<div id="bayan-modal-cards" class="bayan-il-modal-cards" role="list" aria-live="polite" aria-label="اقتراحات التصحيح"></div>
|
| 1170 |
+
<button id="bayan-modal-apply-all" class="bayan-il-modal-apply-all" style="display:none;" type="button">تطبيق الكل</button>
|
| 1171 |
</div>
|
| 1172 |
|
| 1173 |
<div class="bayan-il-modal-score-card">
|
|
|
|
| 1175 |
<div class="bayan-il-modal-score-ring" role="img" aria-label="تقييم الكتابة">
|
| 1176 |
<svg viewBox="0 0 160 160" aria-hidden="true">
|
| 1177 |
<circle cx="80" cy="80" r="70" fill="none" stroke="rgba(236, 238, 242, 0.09)" stroke-width="10"/>
|
| 1178 |
+
<circle id="bayan-modal-score-circle" cx="80" cy="80" r="70" fill="none" stroke="url(#bayanModalScoreGradient)" stroke-width="10" stroke-linecap="round" stroke-dasharray="440" stroke-dashoffset="440" class="bayan-il-score-circle"/>
|
| 1179 |
<defs><linearGradient id="bayanModalScoreGradient" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" stop-color="#6BA3E0"/><stop offset="100%" stop-color="#A594E8"/></linearGradient></defs>
|
| 1180 |
</svg>
|
| 1181 |
<div class="bayan-il-modal-score-value"><span id="bayan-modal-score-value">--</span></div>
|
|
|
|
| 1196 |
|
| 1197 |
modalPanel.querySelector('#bayan-modal-apply-all').addEventListener('click', applyAllFixes);
|
| 1198 |
|
| 1199 |
+
modalPanel.querySelector('#bayan-modal-theme-toggle').addEventListener('click', () => {
|
| 1200 |
+
currentBayanTheme = currentBayanTheme === 'dark' ? 'light' : 'dark';
|
| 1201 |
+
chrome.storage.local.set({ theme: currentBayanTheme });
|
| 1202 |
+
if (modalPanel) modalPanel.setAttribute('data-bayan-theme', currentBayanTheme);
|
| 1203 |
+
if (tooltip) tooltip.setAttribute('data-bayan-theme', currentBayanTheme);
|
| 1204 |
+
if (floatingBtn) floatingBtn.setAttribute('data-bayan-theme', currentBayanTheme);
|
| 1205 |
+
});
|
| 1206 |
+
|
| 1207 |
document.addEventListener('keydown', onModalKeydown);
|
| 1208 |
|
| 1209 |
let modalDragging = false;
|
|
|
|
| 1215 |
const dragHandle = modalPanel.querySelector('#bayan-modal-drag-handle');
|
| 1216 |
if (dragHandle) {
|
| 1217 |
dragHandle.addEventListener('mousedown', (e) => {
|
| 1218 |
+
if (e.target.closest('.bayan-il-modal-close') || e.target.closest('.bayan-il-modal-theme-toggle') || e.target.closest('.bayan-il-modal-logo-link')) return;
|
| 1219 |
modalDragging = true;
|
| 1220 |
modalDragStartX = e.clientX;
|
| 1221 |
modalDragStartY = e.clientY;
|
|
|
|
| 1250 |
}
|
| 1251 |
}
|
| 1252 |
|
| 1253 |
+
function syncModalIfVisible() {
|
| 1254 |
+
if (modalPanel && modalPanel.classList.contains('bayan-il-modal-panel--visible')) {
|
| 1255 |
+
updateModalScore();
|
| 1256 |
+
renderModalSuggestions();
|
| 1257 |
+
}
|
| 1258 |
+
}
|
| 1259 |
+
|
| 1260 |
function showModal() {
|
| 1261 |
createModal();
|
| 1262 |
|
|
|
|
| 1317 |
});
|
| 1318 |
|
| 1319 |
const score = calculateWritingScore(counts.spelling, counts.grammar, counts.punctuation);
|
| 1320 |
+
const offset = 440 - (440 * score) / 100;
|
|
|
|
| 1321 |
|
| 1322 |
const circle = modalPanel.querySelector('#bayan-modal-score-circle');
|
| 1323 |
const valueEl = modalPanel.querySelector('#bayan-modal-score-value');
|
| 1324 |
const hintEl = modalPanel.querySelector('#bayan-modal-score-hint');
|
| 1325 |
|
| 1326 |
+
const toArabicNum = (n) => String(n).replace(/\d/g, (d) => '٠١٢٣٤٥٦٧٨٩'[d]);
|
| 1327 |
+
|
| 1328 |
if (circle) {
|
|
|
|
| 1329 |
requestAnimationFrame(() => {
|
| 1330 |
circle.setAttribute('stroke-dashoffset', String(offset));
|
| 1331 |
});
|
| 1332 |
}
|
| 1333 |
+
const total = counts.spelling + counts.grammar + counts.punctuation;
|
| 1334 |
+
const fieldText = activeField ? getFieldText(activeField) : '';
|
| 1335 |
+
const hasArabicText = fieldText.trim().length > 0 && BayanController.hasArabic(fieldText);
|
| 1336 |
+
|
| 1337 |
if (valueEl) {
|
| 1338 |
+
valueEl.textContent = (hasArabicText || total > 0) ? toArabicNum(score) : '--';
|
| 1339 |
+
}
|
| 1340 |
+
if (hintEl) {
|
| 1341 |
+
if (analysisInProgress && total === 0) {
|
| 1342 |
+
hintEl.textContent = 'جارٍ التحليل...';
|
| 1343 |
+
} else if (total > 0) {
|
| 1344 |
+
hintEl.textContent = getScoreHint(score);
|
| 1345 |
+
} else if (hasArabicText) {
|
| 1346 |
+
hintEl.textContent = getScoreHint(score);
|
| 1347 |
+
} else {
|
| 1348 |
+
hintEl.innerHTML = 'ابدأ الكتابة لرؤية تقييمك<br><span class="bayan-il-modal-score-hint-sub">تحسين القواعد يرفع التقييم</span>';
|
| 1349 |
+
}
|
| 1350 |
}
|
|
|
|
|
|
|
|
|
|
| 1351 |
|
| 1352 |
const cSpelling = modalPanel.querySelector('#bayan-modal-count-spelling');
|
| 1353 |
const cGrammar = modalPanel.querySelector('#bayan-modal-count-grammar');
|
|
|
|
| 1361 |
if (!modalPanel) return;
|
| 1362 |
|
| 1363 |
const container = modalPanel.querySelector('#bayan-modal-cards');
|
|
|
|
| 1364 |
const applyAllBtn = modalPanel.querySelector('#bayan-modal-apply-all');
|
| 1365 |
|
| 1366 |
if (!container) return;
|
| 1367 |
|
| 1368 |
+
// State 1: Errors exist — show suggestion cards (highest priority)
|
| 1369 |
+
if (suggestions.length > 0) {
|
| 1370 |
+
const toArabicNum = (n) => String(n).replace(/\d/g, (d) => '٠١٢٣٤٥٦٧٨٩'[d]);
|
| 1371 |
+
if (applyAllBtn) {
|
| 1372 |
+
if (suggestions.length > 1) {
|
| 1373 |
+
applyAllBtn.style.display = 'flex';
|
| 1374 |
+
applyAllBtn.textContent = 'تطبيق الكل (' + toArabicNum(suggestions.length) + ')';
|
| 1375 |
+
} else {
|
| 1376 |
+
applyAllBtn.style.display = 'none';
|
| 1377 |
+
}
|
| 1378 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1379 |
|
| 1380 |
let html = '';
|
| 1381 |
suggestions.forEach((s, idx) => {
|
|
|
|
| 1394 |
alts.forEach((alt, ai) => {
|
| 1395 |
const isMain = alt === s.correction && ai === 0;
|
| 1396 |
const isKeep = alt === s.original;
|
| 1397 |
+
const label = isKeep ? esc(alt) + ' ✓' : esc(alt);
|
| 1398 |
const cls = isMain ? 'bayan-il-modal-alt-chip bayan-il-modal-alt-chip--main' : (isKeep ? 'bayan-il-modal-alt-chip bayan-il-modal-alt-chip--keep' : 'bayan-il-modal-alt-chip');
|
| 1399 |
html += `<button class="${cls}" data-modal-alt="${esc(alt)}" data-modal-sidx="${idx}" data-modal-keep="${isKeep ? '1' : ''}">${label}</button>`;
|
| 1400 |
});
|
|
|
|
| 1428 |
}
|
| 1429 |
});
|
| 1430 |
});
|
| 1431 |
+
return;
|
| 1432 |
+
}
|
| 1433 |
+
|
| 1434 |
+
// State 2: Loading — analysis in progress, no suggestions yet
|
| 1435 |
+
if (analysisInProgress) {
|
| 1436 |
+
safeHTML(container, '<div class="bayan-il-modal-empty"><div class="bayan-il-modal-loading-spinner"></div><div class="bayan-il-modal-empty-title">جارٍ التحليل...</div></div>');
|
| 1437 |
+
if (applyAllBtn) applyAllBtn.style.display = 'none';
|
| 1438 |
+
return;
|
| 1439 |
+
}
|
| 1440 |
+
|
| 1441 |
+
// State 3: No text — textarea is empty or has no Arabic
|
| 1442 |
+
const fieldText = activeField ? getFieldText(activeField) : '';
|
| 1443 |
+
const hasArabicText = fieldText.trim().length > 0 && BayanController.hasArabic(fieldText);
|
| 1444 |
+
if (!hasArabicText) {
|
| 1445 |
+
safeHTML(container, '<div class="bayan-il-modal-empty"><div class="bayan-il-modal-empty-icon">📝</div><div class="bayan-il-modal-empty-title">لا توجد اقتراحات</div><div class="bayan-il-modal-empty-desc">ابدأ بكتابة نص عربي وسيتم تحليله تلقائياً</div></div>');
|
| 1446 |
+
if (applyAllBtn) applyAllBtn.style.display = 'none';
|
| 1447 |
+
return;
|
| 1448 |
+
}
|
| 1449 |
+
|
| 1450 |
+
// State 4: Clean text — has Arabic text but no errors
|
| 1451 |
+
safeHTML(container, '<div class="bayan-il-modal-empty"><div class="bayan-il-modal-empty-icon">✨</div><div class="bayan-il-modal-empty-title">نصك ممتاز!</div><div class="bayan-il-modal-empty-desc">لم نجد أي أخطاء — أحسنت! ✨</div></div>');
|
| 1452 |
+
if (applyAllBtn) applyAllBtn.style.display = 'none';
|
| 1453 |
}
|
| 1454 |
|
| 1455 |
function applyAllFixes() {
|
extension/manifest.json
CHANGED
|
@@ -46,6 +46,7 @@
|
|
| 46 |
{
|
| 47 |
"resources": [
|
| 48 |
"assets/icons/fab_logo.png",
|
|
|
|
| 49 |
"assets/icons/icon128.png"
|
| 50 |
],
|
| 51 |
"matches": ["<all_urls>"]
|
|
|
|
| 46 |
{
|
| 47 |
"resources": [
|
| 48 |
"assets/icons/fab_logo.png",
|
| 49 |
+
"assets/icons/icon48.png",
|
| 50 |
"assets/icons/icon128.png"
|
| 51 |
],
|
| 52 |
"matches": ["<all_urls>"]
|
extension/popup.css
CHANGED
|
@@ -361,7 +361,7 @@ body::-webkit-scrollbar-thumb {
|
|
| 361 |
margin-bottom: 14px;
|
| 362 |
background: var(--color-surface);
|
| 363 |
border: 1px solid var(--color-border);
|
| 364 |
-
border-radius:
|
| 365 |
animation: fadeSlideIn 300ms ease-out;
|
| 366 |
}
|
| 367 |
|
|
@@ -385,7 +385,7 @@ body::-webkit-scrollbar-thumb {
|
|
| 385 |
}
|
| 386 |
|
| 387 |
.bayan-score-ring circle:last-child {
|
| 388 |
-
transition: stroke-dashoffset 600ms
|
| 389 |
}
|
| 390 |
|
| 391 |
.bayan-score-value {
|
|
@@ -396,7 +396,7 @@ body::-webkit-scrollbar-thumb {
|
|
| 396 |
justify-content: center;
|
| 397 |
font-size: 28px;
|
| 398 |
font-weight: 700;
|
| 399 |
-
color: var(--
|
| 400 |
}
|
| 401 |
|
| 402 |
.bayan-score-hint {
|
|
@@ -538,24 +538,32 @@ body::-webkit-scrollbar-thumb {
|
|
| 538 |
}
|
| 539 |
|
| 540 |
.bayan-suggestion-card {
|
| 541 |
-
padding:
|
| 542 |
-
|
|
|
|
| 543 |
border: 1px solid var(--color-border);
|
| 544 |
-
border-
|
|
|
|
| 545 |
cursor: pointer;
|
| 546 |
-
transition:
|
|
|
|
| 547 |
}
|
| 548 |
.bayan-suggestion-card:hover {
|
| 549 |
-
border-color: var(--primary-color);
|
| 550 |
-
|
| 551 |
-
|
|
|
|
|
|
|
| 552 |
}
|
|
|
|
|
|
|
|
|
|
| 553 |
|
| 554 |
.bayan-suggestion-badge {
|
| 555 |
display: inline-block;
|
| 556 |
padding: 2px 8px;
|
| 557 |
-
border-radius:
|
| 558 |
-
font-size:
|
| 559 |
font-weight: 700;
|
| 560 |
margin-bottom: 6px;
|
| 561 |
}
|
|
@@ -578,13 +586,12 @@ body::-webkit-scrollbar-thumb {
|
|
| 578 |
align-items: center;
|
| 579 |
gap: 6px;
|
| 580 |
margin-bottom: 8px;
|
| 581 |
-
font-size:
|
| 582 |
}
|
| 583 |
|
| 584 |
.bayan-suggestion-original {
|
| 585 |
text-decoration: line-through;
|
| 586 |
-
|
| 587 |
-
color: var(--bayan-error);
|
| 588 |
}
|
| 589 |
|
| 590 |
.bayan-suggestion-arrow {
|
|
@@ -606,11 +613,11 @@ body::-webkit-scrollbar-thumb {
|
|
| 606 |
.bayan-alt-chip {
|
| 607 |
padding: 4px 10px;
|
| 608 |
border: 1px solid var(--color-border);
|
| 609 |
-
border-radius:
|
| 610 |
-
background:
|
| 611 |
color: var(--text-secondary);
|
| 612 |
font-family: var(--bayan-font-arabic);
|
| 613 |
-
font-size:
|
| 614 |
cursor: pointer;
|
| 615 |
transition: all var(--bayan-transition);
|
| 616 |
}
|
|
@@ -621,22 +628,87 @@ body::-webkit-scrollbar-thumb {
|
|
| 621 |
}
|
| 622 |
|
| 623 |
.bayan-alt-chip--main {
|
| 624 |
-
background: var(--primary-color);
|
| 625 |
-
border-color:
|
| 626 |
color: white;
|
|
|
|
| 627 |
}
|
| 628 |
.bayan-alt-chip--main:hover {
|
| 629 |
-
|
| 630 |
-
color:
|
| 631 |
}
|
| 632 |
|
| 633 |
.bayan-alt-chip--keep {
|
| 634 |
-
|
|
|
|
|
|
|
| 635 |
}
|
| 636 |
.bayan-alt-chip--keep:hover {
|
| 637 |
opacity: 1;
|
| 638 |
}
|
| 639 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 640 |
/* ══════════════════════════════════════════════
|
| 641 |
Timing
|
| 642 |
══════════════════════════════════════════════ */
|
|
|
|
| 361 |
margin-bottom: 14px;
|
| 362 |
background: var(--color-surface);
|
| 363 |
border: 1px solid var(--color-border);
|
| 364 |
+
border-radius: 12px;
|
| 365 |
animation: fadeSlideIn 300ms ease-out;
|
| 366 |
}
|
| 367 |
|
|
|
|
| 385 |
}
|
| 386 |
|
| 387 |
.bayan-score-ring circle:last-child {
|
| 388 |
+
transition: stroke-dashoffset 600ms ease-in-out;
|
| 389 |
}
|
| 390 |
|
| 391 |
.bayan-score-value {
|
|
|
|
| 396 |
justify-content: center;
|
| 397 |
font-size: 28px;
|
| 398 |
font-weight: 700;
|
| 399 |
+
color: var(--color-text-primary, var(--text-color));
|
| 400 |
}
|
| 401 |
|
| 402 |
.bayan-score-hint {
|
|
|
|
| 538 |
}
|
| 539 |
|
| 540 |
.bayan-suggestion-card {
|
| 541 |
+
padding: var(--spacing-md, 16px);
|
| 542 |
+
padding-right: calc(var(--spacing-md, 16px) + 4px);
|
| 543 |
+
background: var(--color-surface-elevated, var(--color-surface));
|
| 544 |
border: 1px solid var(--color-border);
|
| 545 |
+
border-right: 4px solid var(--color-border-strong, rgba(236, 238, 242, 0.16));
|
| 546 |
+
border-radius: 12px;
|
| 547 |
cursor: pointer;
|
| 548 |
+
transition: border-color var(--bayan-transition), background var(--bayan-transition), transform var(--bayan-transition);
|
| 549 |
+
text-align: right;
|
| 550 |
}
|
| 551 |
.bayan-suggestion-card:hover {
|
| 552 |
+
border-top-color: var(--primary-color);
|
| 553 |
+
border-bottom-color: var(--primary-color);
|
| 554 |
+
border-left-color: var(--primary-color);
|
| 555 |
+
background: var(--color-suggestion-hover-bg, var(--color-surface-hover));
|
| 556 |
+
transform: translateX(-2px);
|
| 557 |
}
|
| 558 |
+
.bayan-suggestion-card:has(.bayan-badge-spelling) { border-right-color: var(--bayan-spelling, var(--color-error)); }
|
| 559 |
+
.bayan-suggestion-card:has(.bayan-badge-grammar) { border-right-color: var(--bayan-grammar, var(--color-warning)); }
|
| 560 |
+
.bayan-suggestion-card:has(.bayan-badge-punctuation) { border-right-color: var(--bayan-punctuation, var(--color-success)); }
|
| 561 |
|
| 562 |
.bayan-suggestion-badge {
|
| 563 |
display: inline-block;
|
| 564 |
padding: 2px 8px;
|
| 565 |
+
border-radius: 999px;
|
| 566 |
+
font-size: 12px;
|
| 567 |
font-weight: 700;
|
| 568 |
margin-bottom: 6px;
|
| 569 |
}
|
|
|
|
| 586 |
align-items: center;
|
| 587 |
gap: 6px;
|
| 588 |
margin-bottom: 8px;
|
| 589 |
+
font-size: 14px;
|
| 590 |
}
|
| 591 |
|
| 592 |
.bayan-suggestion-original {
|
| 593 |
text-decoration: line-through;
|
| 594 |
+
color: var(--text-muted, var(--bayan-text-muted));
|
|
|
|
| 595 |
}
|
| 596 |
|
| 597 |
.bayan-suggestion-arrow {
|
|
|
|
| 613 |
.bayan-alt-chip {
|
| 614 |
padding: 4px 10px;
|
| 615 |
border: 1px solid var(--color-border);
|
| 616 |
+
border-radius: 100px;
|
| 617 |
+
background: var(--color-surface);
|
| 618 |
color: var(--text-secondary);
|
| 619 |
font-family: var(--bayan-font-arabic);
|
| 620 |
+
font-size: 12px;
|
| 621 |
cursor: pointer;
|
| 622 |
transition: all var(--bayan-transition);
|
| 623 |
}
|
|
|
|
| 628 |
}
|
| 629 |
|
| 630 |
.bayan-alt-chip--main {
|
| 631 |
+
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
| 632 |
+
border-color: transparent;
|
| 633 |
color: white;
|
| 634 |
+
font-weight: 700;
|
| 635 |
}
|
| 636 |
.bayan-alt-chip--main:hover {
|
| 637 |
+
opacity: 0.9;
|
| 638 |
+
border-color: transparent;
|
| 639 |
}
|
| 640 |
|
| 641 |
.bayan-alt-chip--keep {
|
| 642 |
+
border-style: dashed;
|
| 643 |
+
color: var(--bayan-text-muted);
|
| 644 |
+
font-size: 11px;
|
| 645 |
}
|
| 646 |
.bayan-alt-chip--keep:hover {
|
| 647 |
opacity: 1;
|
| 648 |
}
|
| 649 |
|
| 650 |
+
/* ── Apply All Button ── */
|
| 651 |
+
.bayan-apply-all-btn {
|
| 652 |
+
width: 100%;
|
| 653 |
+
margin-top: 10px;
|
| 654 |
+
padding: 10px 18px;
|
| 655 |
+
border: none;
|
| 656 |
+
border-radius: 12px;
|
| 657 |
+
background: linear-gradient(135deg, var(--primary-color, #6BA3E0), var(--secondary-color, #A594E8));
|
| 658 |
+
color: var(--color-text-inverse, #F4F5F7);
|
| 659 |
+
font-family: var(--bayan-font-arabic);
|
| 660 |
+
font-size: 14px;
|
| 661 |
+
font-weight: 700;
|
| 662 |
+
cursor: pointer;
|
| 663 |
+
min-height: 44px;
|
| 664 |
+
transition: opacity 200ms ease, transform 150ms ease;
|
| 665 |
+
display: flex;
|
| 666 |
+
align-items: center;
|
| 667 |
+
justify-content: center;
|
| 668 |
+
gap: 6px;
|
| 669 |
+
}
|
| 670 |
+
.bayan-apply-all-btn:hover { opacity: 0.92; }
|
| 671 |
+
.bayan-apply-all-btn:active { transform: scale(0.98); }
|
| 672 |
+
|
| 673 |
+
/* ── Empty State ── */
|
| 674 |
+
.bayan-empty-state {
|
| 675 |
+
display: flex;
|
| 676 |
+
flex-direction: column;
|
| 677 |
+
align-items: center;
|
| 678 |
+
justify-content: center;
|
| 679 |
+
padding: 24px 16px;
|
| 680 |
+
text-align: center;
|
| 681 |
+
gap: 8px;
|
| 682 |
+
}
|
| 683 |
+
.bayan-empty-state-icon-wrap {
|
| 684 |
+
width: 48px;
|
| 685 |
+
height: 48px;
|
| 686 |
+
border-radius: 50%;
|
| 687 |
+
background: var(--color-surface-elevated, #242833);
|
| 688 |
+
display: flex;
|
| 689 |
+
align-items: center;
|
| 690 |
+
justify-content: center;
|
| 691 |
+
margin-bottom: 4px;
|
| 692 |
+
}
|
| 693 |
+
.bayan-empty-state-title {
|
| 694 |
+
font-size: 14px;
|
| 695 |
+
font-weight: 600;
|
| 696 |
+
color: var(--color-text-secondary, var(--text-secondary));
|
| 697 |
+
}
|
| 698 |
+
.bayan-empty-state-desc {
|
| 699 |
+
font-size: 12px;
|
| 700 |
+
color: var(--color-text-muted, var(--bayan-text-muted));
|
| 701 |
+
line-height: 1.6;
|
| 702 |
+
}
|
| 703 |
+
|
| 704 |
+
/* ── Score Hint Subtitle ── */
|
| 705 |
+
.bayan-score-hint-sub {
|
| 706 |
+
font-size: 11px;
|
| 707 |
+
color: var(--color-text-muted, var(--bayan-text-muted));
|
| 708 |
+
display: block;
|
| 709 |
+
margin-top: 2px;
|
| 710 |
+
}
|
| 711 |
+
|
| 712 |
/* ══════════════════════════════════════════════
|
| 713 |
Timing
|
| 714 |
══════════════════════════════════════════════ */
|
extension/popup.html
CHANGED
|
@@ -96,11 +96,9 @@
|
|
| 96 |
|
| 97 |
<!-- Suggestions list (above score) -->
|
| 98 |
<div class="bayan-suggestions is-hidden" id="suggestions-section">
|
| 99 |
-
<
|
| 100 |
-
<span class="bayan-suggestions-title">الاقتراحات</span>
|
| 101 |
-
<button class="bayan-btn bayan-btn-sm" id="btn-apply-all" type="button">تطبيق الكل</button>
|
| 102 |
-
</div>
|
| 103 |
<div class="bayan-suggestions-list" id="suggestions-list" role="list"></div>
|
|
|
|
| 104 |
</div>
|
| 105 |
|
| 106 |
<!-- Score ring -->
|
|
|
|
| 96 |
|
| 97 |
<!-- Suggestions list (above score) -->
|
| 98 |
<div class="bayan-suggestions is-hidden" id="suggestions-section">
|
| 99 |
+
<span class="bayan-suggestions-title">الاقتراحات</span>
|
|
|
|
|
|
|
|
|
|
| 100 |
<div class="bayan-suggestions-list" id="suggestions-list" role="list"></div>
|
| 101 |
+
<button class="bayan-apply-all-btn is-hidden" id="btn-apply-all" type="button">تطبيق الكل</button>
|
| 102 |
</div>
|
| 103 |
|
| 104 |
<!-- Score ring -->
|
extension/popup.js
CHANGED
|
@@ -219,14 +219,22 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 219 |
scoreSection.classList.remove('is-hidden');
|
| 220 |
|
| 221 |
if (scoreValue) {
|
| 222 |
-
|
|
|
|
| 223 |
}
|
| 224 |
if (scoreCircle) {
|
| 225 |
const offset = SCORE_CIRCUMFERENCE - (score / 100) * SCORE_CIRCUMFERENCE;
|
| 226 |
scoreCircle.style.strokeDashoffset = String(offset);
|
| 227 |
}
|
| 228 |
if (scoreHint) {
|
| 229 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 230 |
}
|
| 231 |
if (countSpelling) countSpelling.textContent = spelling.toLocaleString('ar-EG');
|
| 232 |
if (countGrammar) countGrammar.textContent = grammar.toLocaleString('ar-EG');
|
|
@@ -240,7 +248,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 240 |
currentSuggestions = suggestions;
|
| 241 |
|
| 242 |
if (!suggestions || suggestions.length === 0) {
|
| 243 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 244 |
return;
|
| 245 |
}
|
| 246 |
|
|
@@ -290,8 +304,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 290 |
});
|
| 291 |
});
|
| 292 |
|
| 293 |
-
// Show apply-all
|
| 294 |
-
btnApplyAll.
|
|
|
|
| 295 |
}
|
| 296 |
|
| 297 |
// ══════════════════════════════════════════════════════════
|
|
@@ -359,10 +374,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 359 |
timingText.textContent = `التحليل: ${data.timing_ms.total_ms || 0}ms (إملائي: ${data.timing_ms.spelling_ms || 0}ms، نحوي: ${data.timing_ms.grammar_ms || 0}ms، ترقيم: ${data.timing_ms.punctuation_ms || 0}ms)`;
|
| 360 |
}
|
| 361 |
|
| 362 |
-
if (suggestions.length === 0) {
|
| 363 |
-
showToast('نصك ممتاز! لم نجد أي أخطاء ✨');
|
| 364 |
-
}
|
| 365 |
-
|
| 366 |
saveState();
|
| 367 |
} else {
|
| 368 |
showToast('تعذّر التحليل — حاول مرة أخرى');
|
|
|
|
| 219 |
scoreSection.classList.remove('is-hidden');
|
| 220 |
|
| 221 |
if (scoreValue) {
|
| 222 |
+
const hasText = inputText.value.trim().length > 0;
|
| 223 |
+
scoreValue.textContent = (hasText || total > 0) ? score.toLocaleString('ar-EG') : '--';
|
| 224 |
}
|
| 225 |
if (scoreCircle) {
|
| 226 |
const offset = SCORE_CIRCUMFERENCE - (score / 100) * SCORE_CIRCUMFERENCE;
|
| 227 |
scoreCircle.style.strokeDashoffset = String(offset);
|
| 228 |
}
|
| 229 |
if (scoreHint) {
|
| 230 |
+
const hasText = inputText.value.trim().length > 0;
|
| 231 |
+
if (total === 0 && hasText) {
|
| 232 |
+
scoreHint.textContent = 'كتابة ممتازة! استمر.';
|
| 233 |
+
} else if (total === 0) {
|
| 234 |
+
scoreHint.innerHTML = 'ابدأ الكتابة لرؤية تقييمك<br><span class="bayan-score-hint-sub">تحسين القواعد يرفع التقييم</span>';
|
| 235 |
+
} else {
|
| 236 |
+
scoreHint.textContent = getScoreHint(score, total);
|
| 237 |
+
}
|
| 238 |
}
|
| 239 |
if (countSpelling) countSpelling.textContent = spelling.toLocaleString('ar-EG');
|
| 240 |
if (countGrammar) countGrammar.textContent = grammar.toLocaleString('ar-EG');
|
|
|
|
| 248 |
currentSuggestions = suggestions;
|
| 249 |
|
| 250 |
if (!suggestions || suggestions.length === 0) {
|
| 251 |
+
btnApplyAll.classList.add('is-hidden');
|
| 252 |
+
if (analyzedText) {
|
| 253 |
+
suggestionsSection.classList.remove('is-hidden');
|
| 254 |
+
suggestionsList.innerHTML = '<div class="bayan-empty-state"><svg class="bayan-empty-state-icon" width="48" height="48" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg><p class="bayan-empty-state-text">نصك ممتاز! لم نجد أي أخطاء</p></div>';
|
| 255 |
+
} else {
|
| 256 |
+
suggestionsSection.classList.add('is-hidden');
|
| 257 |
+
}
|
| 258 |
return;
|
| 259 |
}
|
| 260 |
|
|
|
|
| 304 |
});
|
| 305 |
});
|
| 306 |
|
| 307 |
+
// Show apply-all with count
|
| 308 |
+
btnApplyAll.textContent = 'تطبيق الكل (' + suggestions.length.toLocaleString('ar-EG') + ')';
|
| 309 |
+
btnApplyAll.classList.remove('is-hidden');
|
| 310 |
}
|
| 311 |
|
| 312 |
// ══════════════════════════════════════════════════════════
|
|
|
|
| 374 |
timingText.textContent = `التحليل: ${data.timing_ms.total_ms || 0}ms (إملائي: ${data.timing_ms.spelling_ms || 0}ms، نحوي: ${data.timing_ms.grammar_ms || 0}ms، ترقيم: ${data.timing_ms.punctuation_ms || 0}ms)`;
|
| 375 |
}
|
| 376 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 377 |
saveState();
|
| 378 |
} else {
|
| 379 |
showToast('تعذّر التحليل — حاول مرة أخرى');
|
extension/sidepanel/sidepanel.css
CHANGED
|
@@ -359,7 +359,7 @@ body {
|
|
| 359 |
display: block;
|
| 360 |
}
|
| 361 |
.sp-score-ring circle:last-child {
|
| 362 |
-
transition: stroke-dashoffset 600ms
|
| 363 |
}
|
| 364 |
|
| 365 |
.sp-score-value {
|
|
@@ -370,7 +370,7 @@ body {
|
|
| 370 |
justify-content: center;
|
| 371 |
font-size: 28px;
|
| 372 |
font-weight: 700;
|
| 373 |
-
color: var(--
|
| 374 |
}
|
| 375 |
|
| 376 |
.sp-score-hint {
|
|
@@ -494,51 +494,60 @@ body {
|
|
| 494 |
.sp-suggestions-list {
|
| 495 |
display: flex;
|
| 496 |
flex-direction: column;
|
| 497 |
-
gap:
|
| 498 |
}
|
| 499 |
|
| 500 |
/* Suggestion card — reuses bayan-ui.js HTML classes */
|
| 501 |
.bayan-suggestion-card {
|
| 502 |
-
padding:
|
| 503 |
-
|
|
|
|
| 504 |
border: 1px solid var(--color-border);
|
| 505 |
-
border-
|
| 506 |
-
|
|
|
|
|
|
|
|
|
|
| 507 |
}
|
| 508 |
.bayan-suggestion-card:hover {
|
| 509 |
-
border-color: var(--
|
| 510 |
-
|
|
|
|
|
|
|
|
|
|
| 511 |
}
|
|
|
|
|
|
|
|
|
|
| 512 |
|
| 513 |
.bayan-suggestion-badge {
|
| 514 |
display: inline-block;
|
| 515 |
-
padding:
|
| 516 |
-
border-radius:
|
| 517 |
-
font-size:
|
| 518 |
font-weight: 700;
|
| 519 |
-
margin-bottom:
|
| 520 |
}
|
| 521 |
-
.bayan-badge-spelling { background: rgba(
|
| 522 |
-
.bayan-badge-grammar { background: rgba(
|
| 523 |
-
.bayan-badge-punctuation { background: rgba(107, 201, 138, 0.
|
| 524 |
|
| 525 |
.bayan-suggestion-change {
|
| 526 |
display: flex;
|
| 527 |
align-items: center;
|
| 528 |
gap: 6px;
|
| 529 |
margin-bottom: 6px;
|
| 530 |
-
font-size:
|
| 531 |
}
|
| 532 |
|
| 533 |
.bayan-suggestion-original {
|
| 534 |
-
color: var(--sp-
|
| 535 |
text-decoration: line-through;
|
| 536 |
-
text-decoration-color: rgba(239, 68, 68, 0.4);
|
| 537 |
}
|
| 538 |
|
| 539 |
.bayan-suggestion-arrow {
|
| 540 |
color: var(--sp-text-muted);
|
| 541 |
-
font-size:
|
| 542 |
}
|
| 543 |
|
| 544 |
.bayan-suggestion-correction {
|
|
@@ -549,37 +558,104 @@ body {
|
|
| 549 |
.bayan-suggestion-alts {
|
| 550 |
display: flex;
|
| 551 |
flex-wrap: wrap;
|
| 552 |
-
gap:
|
| 553 |
}
|
| 554 |
|
| 555 |
.bayan-alt-chip {
|
| 556 |
-
padding:
|
| 557 |
border: 1px solid var(--color-border);
|
| 558 |
-
border-radius:
|
| 559 |
-
background: var(--color-surface
|
| 560 |
color: var(--text-color);
|
| 561 |
font-family: var(--font-family-primary, 'Cairo', 'Tajawal', sans-serif);
|
| 562 |
-
font-size:
|
| 563 |
cursor: pointer;
|
| 564 |
transition: all var(--sp-transition);
|
| 565 |
}
|
| 566 |
.bayan-alt-chip:hover {
|
| 567 |
border-color: var(--primary-color);
|
| 568 |
-
|
|
|
|
| 569 |
}
|
| 570 |
.bayan-alt-chip--main {
|
| 571 |
-
|
| 572 |
-
|
| 573 |
-
color:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 574 |
}
|
| 575 |
.bayan-alt-chip--keep {
|
| 576 |
-
border-
|
| 577 |
color: var(--sp-text-muted);
|
| 578 |
-
font-size:
|
| 579 |
}
|
| 580 |
.bayan-alt-chip--keep:hover {
|
| 581 |
-
|
| 582 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 583 |
}
|
| 584 |
|
| 585 |
/* ══════════════════════════════════════════════
|
|
|
|
| 359 |
display: block;
|
| 360 |
}
|
| 361 |
.sp-score-ring circle:last-child {
|
| 362 |
+
transition: stroke-dashoffset 600ms ease-in-out;
|
| 363 |
}
|
| 364 |
|
| 365 |
.sp-score-value {
|
|
|
|
| 370 |
justify-content: center;
|
| 371 |
font-size: 28px;
|
| 372 |
font-weight: 700;
|
| 373 |
+
color: var(--color-text-primary, var(--text-color));
|
| 374 |
}
|
| 375 |
|
| 376 |
.sp-score-hint {
|
|
|
|
| 494 |
.sp-suggestions-list {
|
| 495 |
display: flex;
|
| 496 |
flex-direction: column;
|
| 497 |
+
gap: 8px;
|
| 498 |
}
|
| 499 |
|
| 500 |
/* Suggestion card — reuses bayan-ui.js HTML classes */
|
| 501 |
.bayan-suggestion-card {
|
| 502 |
+
padding: var(--spacing-md, 16px);
|
| 503 |
+
padding-right: calc(var(--spacing-md, 16px) + 4px);
|
| 504 |
+
background: var(--color-surface-elevated, var(--color-surface));
|
| 505 |
border: 1px solid var(--color-border);
|
| 506 |
+
border-right: 4px solid var(--color-border-strong, rgba(236, 238, 242, 0.16));
|
| 507 |
+
border-radius: 12px;
|
| 508 |
+
cursor: pointer;
|
| 509 |
+
transition: border-color var(--sp-transition), background var(--sp-transition), transform var(--sp-transition);
|
| 510 |
+
text-align: right;
|
| 511 |
}
|
| 512 |
.bayan-suggestion-card:hover {
|
| 513 |
+
border-top-color: var(--primary-color);
|
| 514 |
+
border-bottom-color: var(--primary-color);
|
| 515 |
+
border-left-color: var(--primary-color);
|
| 516 |
+
background: var(--color-suggestion-hover-bg, var(--color-surface-hover));
|
| 517 |
+
transform: translateX(-2px);
|
| 518 |
}
|
| 519 |
+
.bayan-suggestion-card:has(.bayan-badge-spelling) { border-right-color: var(--sp-spelling, var(--color-error)); }
|
| 520 |
+
.bayan-suggestion-card:has(.bayan-badge-grammar) { border-right-color: var(--sp-grammar, var(--color-warning)); }
|
| 521 |
+
.bayan-suggestion-card:has(.bayan-badge-punctuation) { border-right-color: var(--sp-punctuation, var(--color-success)); }
|
| 522 |
|
| 523 |
.bayan-suggestion-badge {
|
| 524 |
display: inline-block;
|
| 525 |
+
padding: 2px 8px;
|
| 526 |
+
border-radius: 999px;
|
| 527 |
+
font-size: 12px;
|
| 528 |
font-weight: 700;
|
| 529 |
+
margin-bottom: 6px;
|
| 530 |
}
|
| 531 |
+
.bayan-badge-spelling { background: rgba(232, 138, 138, 0.18); color: var(--sp-spelling); }
|
| 532 |
+
.bayan-badge-grammar { background: rgba(228, 179, 90, 0.18); color: var(--sp-grammar); }
|
| 533 |
+
.bayan-badge-punctuation { background: rgba(107, 201, 138, 0.16); color: var(--sp-punctuation); }
|
| 534 |
|
| 535 |
.bayan-suggestion-change {
|
| 536 |
display: flex;
|
| 537 |
align-items: center;
|
| 538 |
gap: 6px;
|
| 539 |
margin-bottom: 6px;
|
| 540 |
+
font-size: 14px;
|
| 541 |
}
|
| 542 |
|
| 543 |
.bayan-suggestion-original {
|
| 544 |
+
color: var(--sp-text-muted);
|
| 545 |
text-decoration: line-through;
|
|
|
|
| 546 |
}
|
| 547 |
|
| 548 |
.bayan-suggestion-arrow {
|
| 549 |
color: var(--sp-text-muted);
|
| 550 |
+
font-size: 12px;
|
| 551 |
}
|
| 552 |
|
| 553 |
.bayan-suggestion-correction {
|
|
|
|
| 558 |
.bayan-suggestion-alts {
|
| 559 |
display: flex;
|
| 560 |
flex-wrap: wrap;
|
| 561 |
+
gap: 6px;
|
| 562 |
}
|
| 563 |
|
| 564 |
.bayan-alt-chip {
|
| 565 |
+
padding: 4px 10px;
|
| 566 |
border: 1px solid var(--color-border);
|
| 567 |
+
border-radius: 100px;
|
| 568 |
+
background: var(--color-surface);
|
| 569 |
color: var(--text-color);
|
| 570 |
font-family: var(--font-family-primary, 'Cairo', 'Tajawal', sans-serif);
|
| 571 |
+
font-size: 12px;
|
| 572 |
cursor: pointer;
|
| 573 |
transition: all var(--sp-transition);
|
| 574 |
}
|
| 575 |
.bayan-alt-chip:hover {
|
| 576 |
border-color: var(--primary-color);
|
| 577 |
+
color: var(--text-primary, var(--text-color));
|
| 578 |
+
background: var(--color-surface-elevated, var(--color-surface-hover));
|
| 579 |
}
|
| 580 |
.bayan-alt-chip--main {
|
| 581 |
+
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
| 582 |
+
border-color: transparent;
|
| 583 |
+
color: white;
|
| 584 |
+
font-weight: 700;
|
| 585 |
+
}
|
| 586 |
+
.bayan-alt-chip--main:hover {
|
| 587 |
+
opacity: 0.9;
|
| 588 |
+
border-color: transparent;
|
| 589 |
}
|
| 590 |
.bayan-alt-chip--keep {
|
| 591 |
+
border-style: dashed;
|
| 592 |
color: var(--sp-text-muted);
|
| 593 |
+
font-size: 11px;
|
| 594 |
}
|
| 595 |
.bayan-alt-chip--keep:hover {
|
| 596 |
+
color: var(--text-primary, var(--text-color));
|
| 597 |
+
}
|
| 598 |
+
|
| 599 |
+
/* ── Apply All Button ── */
|
| 600 |
+
.sp-apply-all-btn {
|
| 601 |
+
width: 100%;
|
| 602 |
+
margin-top: 10px;
|
| 603 |
+
padding: 10px 18px;
|
| 604 |
+
border: none;
|
| 605 |
+
border-radius: 12px;
|
| 606 |
+
background: linear-gradient(135deg, var(--primary-color, #6BA3E0), var(--secondary-color, #A594E8));
|
| 607 |
+
color: var(--color-text-inverse, #F4F5F7);
|
| 608 |
+
font-family: var(--font-family-primary, 'Cairo', 'Tajawal', sans-serif);
|
| 609 |
+
font-size: 14px;
|
| 610 |
+
font-weight: 700;
|
| 611 |
+
cursor: pointer;
|
| 612 |
+
min-height: 44px;
|
| 613 |
+
transition: opacity 200ms ease, transform 150ms ease;
|
| 614 |
+
display: flex;
|
| 615 |
+
align-items: center;
|
| 616 |
+
justify-content: center;
|
| 617 |
+
gap: 6px;
|
| 618 |
+
}
|
| 619 |
+
.sp-apply-all-btn:hover { opacity: 0.92; }
|
| 620 |
+
.sp-apply-all-btn:active { transform: scale(0.98); }
|
| 621 |
+
|
| 622 |
+
/* ── Empty State ── */
|
| 623 |
+
.sp-empty-state {
|
| 624 |
+
display: flex;
|
| 625 |
+
flex-direction: column;
|
| 626 |
+
align-items: center;
|
| 627 |
+
justify-content: center;
|
| 628 |
+
padding: 24px 16px;
|
| 629 |
+
text-align: center;
|
| 630 |
+
gap: 8px;
|
| 631 |
+
}
|
| 632 |
+
.sp-empty-state-icon-wrap {
|
| 633 |
+
width: 48px;
|
| 634 |
+
height: 48px;
|
| 635 |
+
border-radius: 50%;
|
| 636 |
+
background: var(--color-surface-elevated, #242833);
|
| 637 |
+
display: flex;
|
| 638 |
+
align-items: center;
|
| 639 |
+
justify-content: center;
|
| 640 |
+
margin-bottom: 4px;
|
| 641 |
+
}
|
| 642 |
+
.sp-empty-state-title {
|
| 643 |
+
font-size: 14px;
|
| 644 |
+
font-weight: 600;
|
| 645 |
+
color: var(--color-text-secondary, var(--text-secondary));
|
| 646 |
+
}
|
| 647 |
+
.sp-empty-state-desc {
|
| 648 |
+
font-size: 12px;
|
| 649 |
+
color: var(--color-text-muted, var(--sp-text-muted));
|
| 650 |
+
line-height: 1.6;
|
| 651 |
+
}
|
| 652 |
+
|
| 653 |
+
/* ── Score Hint Subtitle ── */
|
| 654 |
+
.sp-score-hint-sub {
|
| 655 |
+
font-size: 11px;
|
| 656 |
+
color: var(--color-text-muted, var(--sp-text-muted));
|
| 657 |
+
display: block;
|
| 658 |
+
margin-top: 2px;
|
| 659 |
}
|
| 660 |
|
| 661 |
/* ══════════════════════════════════════════════
|
extension/sidepanel/sidepanel.html
CHANGED
|
@@ -16,7 +16,7 @@
|
|
| 16 |
<!-- ══════════════════════════════════════════════ -->
|
| 17 |
<header class="sp-header">
|
| 18 |
<a href="https://bayan10-bayan-api.hf.space/" target="_blank" rel="noopener noreferrer" class="sp-header-brand" style="text-decoration:none;color:inherit;">
|
| 19 |
-
<img src="../assets/icons/icon48.png" alt="بيان" width="
|
| 20 |
<div style="height:24px; width:2px; background-color:var(--color-border-strong, #d1d5db); border-radius:9999px; flex-shrink:0;"></div>
|
| 21 |
<span class="sp-header-title">بيان</span>
|
| 22 |
</a>
|
|
@@ -97,11 +97,9 @@
|
|
| 97 |
|
| 98 |
<!-- Suggestions list (above score — matching popup) -->
|
| 99 |
<div class="sp-suggestions is-hidden" id="suggestions-section">
|
| 100 |
-
<
|
| 101 |
-
<span class="sp-suggestions-title">الاقتراحات</span>
|
| 102 |
-
<button class="sp-btn sp-btn-sm" id="btn-apply-all" type="button">تطبيق الكل</button>
|
| 103 |
-
</div>
|
| 104 |
<div class="sp-suggestions-list" id="suggestions-list" role="list"></div>
|
|
|
|
| 105 |
</div>
|
| 106 |
|
| 107 |
<!-- Score ring (matching popup — vertical centered with gradient) -->
|
|
|
|
| 16 |
<!-- ══════════════════════════════════════════════ -->
|
| 17 |
<header class="sp-header">
|
| 18 |
<a href="https://bayan10-bayan-api.hf.space/" target="_blank" rel="noopener noreferrer" class="sp-header-brand" style="text-decoration:none;color:inherit;">
|
| 19 |
+
<img src="../assets/icons/icon48.png" alt="بيان" width="28" height="28" style="border-radius:6px;">
|
| 20 |
<div style="height:24px; width:2px; background-color:var(--color-border-strong, #d1d5db); border-radius:9999px; flex-shrink:0;"></div>
|
| 21 |
<span class="sp-header-title">بيان</span>
|
| 22 |
</a>
|
|
|
|
| 97 |
|
| 98 |
<!-- Suggestions list (above score — matching popup) -->
|
| 99 |
<div class="sp-suggestions is-hidden" id="suggestions-section">
|
| 100 |
+
<span class="sp-suggestions-title">الاقتراحات</span>
|
|
|
|
|
|
|
|
|
|
| 101 |
<div class="sp-suggestions-list" id="suggestions-list" role="list"></div>
|
| 102 |
+
<button class="sp-apply-all-btn is-hidden" id="btn-apply-all" type="button">تطبيق الكل</button>
|
| 103 |
</div>
|
| 104 |
|
| 105 |
<!-- Score ring (matching popup — vertical centered with gradient) -->
|
extension/sidepanel/sidepanel.js
CHANGED
|
@@ -173,12 +173,24 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 173 |
|
| 174 |
scoreSection.classList.remove('is-hidden');
|
| 175 |
|
| 176 |
-
if (scoreValue)
|
|
|
|
|
|
|
|
|
|
| 177 |
if (scoreCircle) {
|
| 178 |
const offset = SCORE_CIRCUMFERENCE - (score / 100) * SCORE_CIRCUMFERENCE;
|
| 179 |
scoreCircle.style.strokeDashoffset = String(offset);
|
| 180 |
}
|
| 181 |
-
if (scoreHint)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
if (countSpelling) countSpelling.textContent = spelling.toLocaleString('ar-EG');
|
| 183 |
if (countGrammar) countGrammar.textContent = grammar.toLocaleString('ar-EG');
|
| 184 |
if (countPunctuation) countPunctuation.textContent = punctuation.toLocaleString('ar-EG');
|
|
@@ -191,7 +203,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 191 |
currentSuggestions = suggestions;
|
| 192 |
|
| 193 |
if (!suggestions || suggestions.length === 0) {
|
| 194 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
return;
|
| 196 |
}
|
| 197 |
|
|
@@ -230,7 +248,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 230 |
});
|
| 231 |
});
|
| 232 |
|
| 233 |
-
|
|
|
|
|
|
|
| 234 |
}
|
| 235 |
|
| 236 |
// ══════════════════════════════════════════════════════════
|
|
@@ -271,10 +291,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 271 |
}
|
| 272 |
|
| 273 |
saveState();
|
| 274 |
-
|
| 275 |
-
if (suggestions.length === 0) {
|
| 276 |
-
showToast('نصك ممتاز! لم نجد أي أخطاء ✨');
|
| 277 |
-
}
|
| 278 |
} else {
|
| 279 |
showToast('تعذّر التحليل — حاول مرة أخرى');
|
| 280 |
}
|
|
|
|
| 173 |
|
| 174 |
scoreSection.classList.remove('is-hidden');
|
| 175 |
|
| 176 |
+
if (scoreValue) {
|
| 177 |
+
const hasText = inputText.value.trim().length > 0;
|
| 178 |
+
scoreValue.textContent = (hasText || total > 0) ? score.toLocaleString('ar-EG') : '--';
|
| 179 |
+
}
|
| 180 |
if (scoreCircle) {
|
| 181 |
const offset = SCORE_CIRCUMFERENCE - (score / 100) * SCORE_CIRCUMFERENCE;
|
| 182 |
scoreCircle.style.strokeDashoffset = String(offset);
|
| 183 |
}
|
| 184 |
+
if (scoreHint) {
|
| 185 |
+
const hasText = inputText.value.trim().length > 0;
|
| 186 |
+
if (total === 0 && hasText) {
|
| 187 |
+
scoreHint.textContent = 'كتابة ممتازة! استمر.';
|
| 188 |
+
} else if (total === 0) {
|
| 189 |
+
scoreHint.innerHTML = 'ابدأ الكتابة لرؤية تقييمك<br><span class="sp-score-hint-sub">تحسين القواعد يرفع التقييم</span>';
|
| 190 |
+
} else {
|
| 191 |
+
scoreHint.textContent = getScoreHint(score, total);
|
| 192 |
+
}
|
| 193 |
+
}
|
| 194 |
if (countSpelling) countSpelling.textContent = spelling.toLocaleString('ar-EG');
|
| 195 |
if (countGrammar) countGrammar.textContent = grammar.toLocaleString('ar-EG');
|
| 196 |
if (countPunctuation) countPunctuation.textContent = punctuation.toLocaleString('ar-EG');
|
|
|
|
| 203 |
currentSuggestions = suggestions;
|
| 204 |
|
| 205 |
if (!suggestions || suggestions.length === 0) {
|
| 206 |
+
btnApplyAll.classList.add('is-hidden');
|
| 207 |
+
if (analyzedText) {
|
| 208 |
+
suggestionsSection.classList.remove('is-hidden');
|
| 209 |
+
suggestionsList.innerHTML = '<div class="sp-empty-state"><svg class="sp-empty-state-icon" width="48" height="48" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg><p class="sp-empty-state-text">نصك ممتاز! لم نجد أي أخطاء</p></div>';
|
| 210 |
+
} else {
|
| 211 |
+
suggestionsSection.classList.add('is-hidden');
|
| 212 |
+
}
|
| 213 |
return;
|
| 214 |
}
|
| 215 |
|
|
|
|
| 248 |
});
|
| 249 |
});
|
| 250 |
|
| 251 |
+
// Show apply-all with count
|
| 252 |
+
btnApplyAll.textContent = 'تطبيق الكل (' + suggestions.length.toLocaleString('ar-EG') + ')';
|
| 253 |
+
btnApplyAll.classList.remove('is-hidden');
|
| 254 |
}
|
| 255 |
|
| 256 |
// ══════════════════════════════════════════════════════════
|
|
|
|
| 291 |
}
|
| 292 |
|
| 293 |
saveState();
|
|
|
|
|
|
|
|
|
|
|
|
|
| 294 |
} else {
|
| 295 |
showToast('تعذّر التحليل — حاول مرة أخرى');
|
| 296 |
}
|
src/css/components.css
CHANGED
|
@@ -1104,7 +1104,9 @@
|
|
| 1104 |
|
| 1105 |
.suggestion-card:hover,
|
| 1106 |
.suggestion-card.focused {
|
| 1107 |
-
border-color: var(--color-primary);
|
|
|
|
|
|
|
| 1108 |
background: var(--color-suggestion-hover-bg);
|
| 1109 |
transform: translateX(-2px);
|
| 1110 |
}
|
|
|
|
| 1104 |
|
| 1105 |
.suggestion-card:hover,
|
| 1106 |
.suggestion-card.focused {
|
| 1107 |
+
border-top-color: var(--color-primary);
|
| 1108 |
+
border-bottom-color: var(--color-primary);
|
| 1109 |
+
border-left-color: var(--color-primary);
|
| 1110 |
background: var(--color-suggestion-hover-bg);
|
| 1111 |
transform: translateX(-2px);
|
| 1112 |
}
|
src/nlp/spelling/araspell_rules.py
CHANGED
|
@@ -1447,6 +1447,7 @@ class ArabicSpellChecker:
|
|
| 1447 |
result = self.postprocess(best_candidate, original)
|
| 1448 |
|
| 1449 |
# IV-Safe Postprocessing Check
|
|
|
|
| 1450 |
if result != best_candidate:
|
| 1451 |
result_words = result.split()
|
| 1452 |
best_words = best_candidate.split()
|
|
@@ -1456,7 +1457,7 @@ class ArabicSpellChecker:
|
|
| 1456 |
if rw != bw:
|
| 1457 |
bw_iv = self.vocab_manager.is_iv(bw)
|
| 1458 |
rw_iv = self.vocab_manager.is_iv(rw)
|
| 1459 |
-
if bw_iv and not rw_iv:
|
| 1460 |
fixed_words.append(bw)
|
| 1461 |
else:
|
| 1462 |
fixed_words.append(rw)
|
|
|
|
| 1447 |
result = self.postprocess(best_candidate, original)
|
| 1448 |
|
| 1449 |
# IV-Safe Postprocessing Check
|
| 1450 |
+
hamza_corrections = set(AraSpellPostProcessor.HAMZA_WHITELIST.values())
|
| 1451 |
if result != best_candidate:
|
| 1452 |
result_words = result.split()
|
| 1453 |
best_words = best_candidate.split()
|
|
|
|
| 1457 |
if rw != bw:
|
| 1458 |
bw_iv = self.vocab_manager.is_iv(bw)
|
| 1459 |
rw_iv = self.vocab_manager.is_iv(rw)
|
| 1460 |
+
if bw_iv and not rw_iv and rw not in hamza_corrections:
|
| 1461 |
fixed_words.append(bw)
|
| 1462 |
else:
|
| 1463 |
fixed_words.append(rw)
|
tests/phase10/benchmark_runner.py
CHANGED
|
@@ -322,19 +322,26 @@ def run_punctuation_benchmark(api: API, samples: list) -> List[BenchResult]:
|
|
| 322 |
should_add = s.get('should_add_punct', False)
|
| 323 |
word_pres = s.get('expected_words_unchanged', False)
|
| 324 |
|
| 325 |
-
# Check word preservation
|
|
|
|
|
|
|
|
|
|
| 326 |
if word_pres or s.get('category') == 'word_preservation':
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 330 |
r.pipeline_verdict = "FP"
|
| 331 |
-
r.pipeline_detail = f"WORD CHANGE in punct:
|
| 332 |
r.root_cause_component = "MODEL"
|
| 333 |
r.root_cause_stage = "punctuation"
|
| 334 |
r.root_cause_detail = "Punctuation model changed words"
|
| 335 |
else:
|
| 336 |
r.pipeline_verdict = "TN"
|
| 337 |
-
r.pipeline_detail = "Words preserved"
|
| 338 |
elif should_add:
|
| 339 |
if r.punctuation_raw_output != s['input']:
|
| 340 |
r.pipeline_verdict = "TP"; r.pipeline_detail = "Punctuation added"
|
|
@@ -375,7 +382,7 @@ def run_entity_benchmark(api: API, samples: list) -> List[BenchResult]:
|
|
| 375 |
r.pipeline_suggestions = resp.get('suggestions', [])
|
| 376 |
entity = s.get('entity', '')
|
| 377 |
|
| 378 |
-
if entity and entity not in r.pipeline_output:
|
| 379 |
r.pipeline_verdict = "FP"
|
| 380 |
r.pipeline_detail = f"ENTITY CORRUPTED: '{entity}' missing from output"
|
| 381 |
r.root_cause_component = "MODEL"
|
|
@@ -514,15 +521,17 @@ def run_hallucination_benchmark(api: API, samples: list) -> List[BenchResult]:
|
|
| 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]}"
|
| 519 |
word_orig = strip_punct_only(original)
|
| 520 |
word_corr = strip_punct_only(r.pipeline_output)
|
| 521 |
if word_orig == word_corr:
|
| 522 |
-
|
| 523 |
-
|
| 524 |
-
|
|
|
|
|
|
|
| 525 |
else:
|
|
|
|
|
|
|
| 526 |
sugg_types = [sg.get('type','') for sg in r.pipeline_suggestions]
|
| 527 |
if any(t == 'grammar' for t in sugg_types):
|
| 528 |
r.root_cause_component = "MODEL"
|
|
|
|
| 322 |
should_add = s.get('should_add_punct', False)
|
| 323 |
word_pres = s.get('expected_words_unchanged', False)
|
| 324 |
|
| 325 |
+
# Check word preservation — verify that PUNCTUATION suggestions don't change words.
|
| 326 |
+
# We check the pipeline suggestions, not raw model output, because the pipeline
|
| 327 |
+
# has guards (_strip_non_punctuation_changes) that prevent word modifications.
|
| 328 |
+
# Spelling corrections are expected and should not count as punctuation word changes.
|
| 329 |
if word_pres or s.get('category') == 'word_preservation':
|
| 330 |
+
punct_word_changes = [
|
| 331 |
+
sg for sg in r.pipeline_suggestions
|
| 332 |
+
if sg.get('type') == 'punctuation'
|
| 333 |
+
and strip_punct_only(sg.get('original', '')) != strip_punct_only(sg.get('correction', ''))
|
| 334 |
+
]
|
| 335 |
+
if punct_word_changes:
|
| 336 |
+
changes_str = ', '.join(f"'{sg.get('original','')}' → '{sg.get('correction','')}'" for sg in punct_word_changes[:3])
|
| 337 |
r.pipeline_verdict = "FP"
|
| 338 |
+
r.pipeline_detail = f"WORD CHANGE in punct: {changes_str}"
|
| 339 |
r.root_cause_component = "MODEL"
|
| 340 |
r.root_cause_stage = "punctuation"
|
| 341 |
r.root_cause_detail = "Punctuation model changed words"
|
| 342 |
else:
|
| 343 |
r.pipeline_verdict = "TN"
|
| 344 |
+
r.pipeline_detail = "Words preserved (punctuation stage)"
|
| 345 |
elif should_add:
|
| 346 |
if r.punctuation_raw_output != s['input']:
|
| 347 |
r.pipeline_verdict = "TP"; r.pipeline_detail = "Punctuation added"
|
|
|
|
| 382 |
r.pipeline_suggestions = resp.get('suggestions', [])
|
| 383 |
entity = s.get('entity', '')
|
| 384 |
|
| 385 |
+
if entity and entity.replace(' ', '') not in r.pipeline_output.replace(' ', ''):
|
| 386 |
r.pipeline_verdict = "FP"
|
| 387 |
r.pipeline_detail = f"ENTITY CORRUPTED: '{entity}' missing from output"
|
| 388 |
r.root_cause_component = "MODEL"
|
|
|
|
| 521 |
|
| 522 |
if _strip_trailing_punct(r.pipeline_output) != _strip_trailing_punct(original):
|
| 523 |
changes = [f"{sg.get('original','')}→{sg.get('correction','')}" for sg in r.pipeline_suggestions]
|
|
|
|
|
|
|
| 524 |
word_orig = strip_punct_only(original)
|
| 525 |
word_corr = strip_punct_only(r.pipeline_output)
|
| 526 |
if word_orig == word_corr:
|
| 527 |
+
# Punctuation-only change — words are identical, only punct differs.
|
| 528 |
+
# Adding commas/semicolons to correct positions is the punctuation
|
| 529 |
+
# model's intended job, not a hallucination.
|
| 530 |
+
r.pipeline_verdict = "TN"
|
| 531 |
+
r.pipeline_detail = "Punctuation-only change (words unchanged)"
|
| 532 |
else:
|
| 533 |
+
r.pipeline_verdict = "FP"
|
| 534 |
+
r.pipeline_detail = f"HALLUCINATION: {changes[:3]}"
|
| 535 |
sugg_types = [sg.get('type','') for sg in r.pipeline_suggestions]
|
| 536 |
if any(t == 'grammar' for t in sugg_types):
|
| 537 |
r.root_cause_component = "MODEL"
|
tests/phase10/reports/phase10_results.json
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|