Commit ·
42357d1
1
Parent(s): c081da8
feat: enhance extension UI and improve Smart Colon Guard
Browse files- Added visual divider lines to Chrome extension logos (Popup, Sidepanel, Inline)
- Synchronized light/dark theme toggle across all extension views via chrome.storage.local
- Fixed sidepanel theme variables to ensure responsive toggle
- Re-ordered z-index for login overlay to ensure it covers the editor
- Improved punctuation rules with a Smart Colon Guard that checks 6 words back for speech cues and perfectly retains colons even for long subjects
- LOGOS/icon128.png +3 -0
- LOGOS/icon16.png +3 -0
- LOGOS/icon32.png +3 -0
- LOGOS/icon48.png +3 -0
- extension/assets/icons/icon128.png +2 -2
- extension/assets/icons/icon16.png +2 -2
- extension/assets/icons/icon32.png +2 -2
- extension/assets/icons/icon48.png +2 -2
- extension/content-inline.css +50 -0
- extension/content-inline.js +20 -0
- extension/popup.css +66 -0
- extension/popup.html +9 -1
- extension/popup.js +29 -0
- extension/sidepanel/sidepanel.css +83 -0
- extension/sidepanel/sidepanel.html +9 -1
- extension/sidepanel/sidepanel.js +29 -0
- generate_icons.py +3 -3
- src/nlp/punctuation/punctuation_rules.py +30 -11
- tests/phase10/reports/phase10_results.json +0 -0
LOGOS/icon128.png
ADDED
|
|
Git LFS Details
|
LOGOS/icon16.png
ADDED
|
|
Git LFS Details
|
LOGOS/icon32.png
ADDED
|
|
Git LFS Details
|
LOGOS/icon48.png
ADDED
|
|
Git LFS Details
|
extension/assets/icons/icon128.png
CHANGED
|
|
Git LFS Details
|
|
|
Git LFS Details
|
extension/assets/icons/icon16.png
CHANGED
|
|
Git LFS Details
|
|
|
Git LFS Details
|
extension/assets/icons/icon32.png
CHANGED
|
|
Git LFS Details
|
|
|
Git LFS Details
|
extension/assets/icons/icon48.png
CHANGED
|
|
Git LFS Details
|
|
|
Git LFS Details
|
extension/content-inline.css
CHANGED
|
@@ -286,3 +286,53 @@
|
|
| 286 |
opacity: 0.85 !important;
|
| 287 |
transition: none !important;
|
| 288 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 286 |
opacity: 0.85 !important;
|
| 287 |
transition: none !important;
|
| 288 |
}
|
| 289 |
+
|
| 290 |
+
|
| 291 |
+
/* ── Light Theme Overrides for Inline UI ── */
|
| 292 |
+
.bayan-il-fab[data-bayan-theme="light"] {
|
| 293 |
+
background: #ffffff !important;
|
| 294 |
+
border: 1px solid #e5e7eb !important;
|
| 295 |
+
}
|
| 296 |
+
|
| 297 |
+
.bayan-il-fab[data-bayan-theme="light"] svg path {
|
| 298 |
+
fill: #6366f1 !important;
|
| 299 |
+
}
|
| 300 |
+
|
| 301 |
+
.bayan-il-tooltip[data-bayan-theme="light"] {
|
| 302 |
+
background: #ffffff !important;
|
| 303 |
+
border: 1px solid #e5e7eb !important;
|
| 304 |
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(99, 102, 241, 0.1) !important;
|
| 305 |
+
}
|
| 306 |
+
|
| 307 |
+
.bayan-il-tooltip[data-bayan-theme="light"] .bayan-il-tooltip-header {
|
| 308 |
+
background: #f9fafb !important;
|
| 309 |
+
border-bottom: 1px solid #e5e7eb !important;
|
| 310 |
+
color: #4b5563 !important;
|
| 311 |
+
}
|
| 312 |
+
|
| 313 |
+
.bayan-il-tooltip[data-bayan-theme="light"] .bayan-il-tooltip-body {
|
| 314 |
+
background: #ffffff !important;
|
| 315 |
+
}
|
| 316 |
+
|
| 317 |
+
.bayan-il-tooltip[data-bayan-theme="light"] .bayan-il-tooltip-original {
|
| 318 |
+
color: #4b5563 !important;
|
| 319 |
+
}
|
| 320 |
+
|
| 321 |
+
.bayan-il-tooltip[data-bayan-theme="light"] .bayan-il-tooltip-correction {
|
| 322 |
+
color: #111827 !important;
|
| 323 |
+
}
|
| 324 |
+
|
| 325 |
+
.bayan-il-tooltip[data-bayan-theme="light"] .bayan-il-tooltip-actions {
|
| 326 |
+
border-top: 1px solid #e5e7eb !important;
|
| 327 |
+
background: #f9fafb !important;
|
| 328 |
+
}
|
| 329 |
+
|
| 330 |
+
.bayan-il-tooltip[data-bayan-theme="light"] .bayan-il-tooltip-ignore {
|
| 331 |
+
color: #4b5563 !important;
|
| 332 |
+
}
|
| 333 |
+
|
| 334 |
+
.bayan-il-tooltip[data-bayan-theme="light"] .bayan-il-tooltip-ignore:hover {
|
| 335 |
+
background: #e5e7eb !important;
|
| 336 |
+
color: #111827 !important;
|
| 337 |
+
}
|
| 338 |
+
|
extension/content-inline.js
CHANGED
|
@@ -419,6 +419,7 @@
|
|
| 419 |
const typeLabels = { spelling: 'إملائي', grammar: 'نحوي', punctuation: 'ترقيم' };
|
| 420 |
|
| 421 |
tooltip = document.createElement('div');
|
|
|
|
| 422 |
tooltip.className = 'bayan-il-tooltip';
|
| 423 |
tooltip.dir = 'rtl';
|
| 424 |
|
|
@@ -566,6 +567,7 @@
|
|
| 566 |
loadFabPosition();
|
| 567 |
|
| 568 |
floatingBtn = document.createElement('div');
|
|
|
|
| 569 |
floatingBtn.className = 'bayan-il-fab';
|
| 570 |
safeHTML(floatingBtn, `
|
| 571 |
<svg width="18" height="18" viewBox="0 0 100 100" fill="none">
|
|
@@ -1053,4 +1055,22 @@
|
|
| 1053 |
// ── Log ──
|
| 1054 |
const mode = IS_PROTECTED ? 'protected' : 'full';
|
| 1055 |
console.log(`[Bayan] Inline engine v7.1 (mode: ${mode}, TT: ${ttPolicy ? 'yes' : 'no'})`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1056 |
})();
|
|
|
|
| 419 |
const typeLabels = { spelling: 'إملائي', grammar: 'نحوي', punctuation: 'ترقيم' };
|
| 420 |
|
| 421 |
tooltip = document.createElement('div');
|
| 422 |
+
tooltip.setAttribute('data-bayan-theme', currentBayanTheme);
|
| 423 |
tooltip.className = 'bayan-il-tooltip';
|
| 424 |
tooltip.dir = 'rtl';
|
| 425 |
|
|
|
|
| 567 |
loadFabPosition();
|
| 568 |
|
| 569 |
floatingBtn = document.createElement('div');
|
| 570 |
+
floatingBtn.setAttribute('data-bayan-theme', currentBayanTheme);
|
| 571 |
floatingBtn.className = 'bayan-il-fab';
|
| 572 |
safeHTML(floatingBtn, `
|
| 573 |
<svg width="18" height="18" viewBox="0 0 100 100" fill="none">
|
|
|
|
| 1055 |
// ── Log ──
|
| 1056 |
const mode = IS_PROTECTED ? 'protected' : 'full';
|
| 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 |
+
|
| 1076 |
})();
|
extension/popup.css
CHANGED
|
@@ -767,3 +767,69 @@ body::-webkit-scrollbar-thumb {
|
|
| 767 |
.is-hidden {
|
| 768 |
display: none !important;
|
| 769 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 767 |
.is-hidden {
|
| 768 |
display: none !important;
|
| 769 |
}
|
| 770 |
+
|
| 771 |
+
|
| 772 |
+
/* Light Theme Variables */
|
| 773 |
+
[data-theme="light"] {
|
| 774 |
+
--bayan-bg: #f9fafb;
|
| 775 |
+
--bayan-surface: #ffffff;
|
| 776 |
+
--bayan-surface-hover: #f3f4f6;
|
| 777 |
+
--bayan-surface-active: #e5e7eb;
|
| 778 |
+
--bayan-border: #e5e7eb;
|
| 779 |
+
--bayan-border-light: #d1d5db;
|
| 780 |
+
--bayan-text: #111827;
|
| 781 |
+
--bayan-text-secondary: #4b5563;
|
| 782 |
+
--bayan-text-muted: #9ca3af;
|
| 783 |
+
--bayan-success: #16a34a;
|
| 784 |
+
--bayan-warning: #d97706;
|
| 785 |
+
}
|
| 786 |
+
|
| 787 |
+
/* Theme Toggle Button Styles */
|
| 788 |
+
.theme-toggle-animated {
|
| 789 |
+
display: flex;
|
| 790 |
+
align-items: center;
|
| 791 |
+
justify-content: center;
|
| 792 |
+
width: 32px;
|
| 793 |
+
height: 32px;
|
| 794 |
+
border: none;
|
| 795 |
+
border-radius: 50%;
|
| 796 |
+
background: var(--bayan-surface-hover);
|
| 797 |
+
color: var(--bayan-text-secondary);
|
| 798 |
+
cursor: pointer;
|
| 799 |
+
transition: background 0.3s ease, transform 0.3s ease, color 0.3s ease;
|
| 800 |
+
position: relative;
|
| 801 |
+
overflow: hidden;
|
| 802 |
+
margin-right: 8px;
|
| 803 |
+
}
|
| 804 |
+
|
| 805 |
+
.theme-toggle-animated:hover {
|
| 806 |
+
background: var(--bayan-primary);
|
| 807 |
+
color: #fff;
|
| 808 |
+
transform: rotate(15deg);
|
| 809 |
+
}
|
| 810 |
+
|
| 811 |
+
.theme-toggle-animated svg {
|
| 812 |
+
transition: transform 0.4s ease, opacity 0.3s ease;
|
| 813 |
+
position: absolute;
|
| 814 |
+
}
|
| 815 |
+
|
| 816 |
+
[data-theme="dark"] .theme-icon-sun {
|
| 817 |
+
transform: rotate(90deg) scale(0);
|
| 818 |
+
opacity: 0;
|
| 819 |
+
}
|
| 820 |
+
|
| 821 |
+
[data-theme="dark"] .theme-icon-moon {
|
| 822 |
+
transform: rotate(0) scale(1);
|
| 823 |
+
opacity: 1;
|
| 824 |
+
}
|
| 825 |
+
|
| 826 |
+
[data-theme="light"] .theme-icon-moon {
|
| 827 |
+
transform: rotate(-90deg) scale(0);
|
| 828 |
+
opacity: 0;
|
| 829 |
+
}
|
| 830 |
+
|
| 831 |
+
[data-theme="light"] .theme-icon-sun {
|
| 832 |
+
transform: rotate(0) scale(1);
|
| 833 |
+
opacity: 1;
|
| 834 |
+
}
|
| 835 |
+
|
extension/popup.html
CHANGED
|
@@ -13,12 +13,20 @@
|
|
| 13 |
<header class="bayan-header">
|
| 14 |
<div class="bayan-header-brand">
|
| 15 |
<img src="assets/icons/icon48.png" alt="بيان" width="28" height="28" style="border-radius:6px;">
|
|
|
|
| 16 |
<span class="bayan-header-title">بيان</span>
|
| 17 |
</div>
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
<span class="bayan-status-dot"></span>
|
| 20 |
<span class="bayan-status-text" id="status-text">جاهز</span>
|
| 21 |
</div>
|
|
|
|
| 22 |
</header>
|
| 23 |
|
| 24 |
<!-- ══════════════════════════════════════════════ -->
|
|
|
|
| 13 |
<header class="bayan-header">
|
| 14 |
<div class="bayan-header-brand">
|
| 15 |
<img src="assets/icons/icon48.png" alt="بيان" width="28" height="28" style="border-radius:6px;">
|
| 16 |
+
<div style="height:24px; width:2px; background-color:#d1d5db; border-radius:9999px; flex-shrink:0;"></div>
|
| 17 |
<span class="bayan-header-title">بيان</span>
|
| 18 |
</div>
|
| 19 |
+
|
| 20 |
+
<div style="display: flex; align-items: center; gap: 12px;">
|
| 21 |
+
<button id="ext-theme-toggle" class="theme-toggle-animated" aria-label="تبديل السمة" type="button">
|
| 22 |
+
<svg class="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>
|
| 23 |
+
<svg class="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>
|
| 24 |
+
</button>
|
| 25 |
+
<div class="bayan-header-status" id="status-indicator">
|
| 26 |
<span class="bayan-status-dot"></span>
|
| 27 |
<span class="bayan-status-text" id="status-text">جاهز</span>
|
| 28 |
</div>
|
| 29 |
+
</div>
|
| 30 |
</header>
|
| 31 |
|
| 32 |
<!-- ══════════════════════════════════════════════ -->
|
extension/popup.js
CHANGED
|
@@ -680,3 +680,32 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 680 |
})();
|
| 681 |
});
|
| 682 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 680 |
})();
|
| 681 |
});
|
| 682 |
|
| 683 |
+
|
| 684 |
+
|
| 685 |
+
// ── Theme Toggle Logic ──
|
| 686 |
+
(function initBayanThemeToggle() {
|
| 687 |
+
const toggleBtn = document.getElementById('ext-theme-toggle');
|
| 688 |
+
|
| 689 |
+
// Load theme from storage
|
| 690 |
+
chrome.storage.local.get(['theme'], (result) => {
|
| 691 |
+
const currentTheme = result.theme || 'dark'; // default to dark
|
| 692 |
+
document.documentElement.setAttribute('data-theme', currentTheme);
|
| 693 |
+
});
|
| 694 |
+
|
| 695 |
+
// Sync theme changes instantly across all views
|
| 696 |
+
chrome.storage.onChanged.addListener((changes, namespace) => {
|
| 697 |
+
if (namespace === 'local' && changes.theme) {
|
| 698 |
+
document.documentElement.setAttribute('data-theme', changes.theme.newValue);
|
| 699 |
+
}
|
| 700 |
+
});
|
| 701 |
+
|
| 702 |
+
|
| 703 |
+
if (toggleBtn) {
|
| 704 |
+
toggleBtn.addEventListener('click', () => {
|
| 705 |
+
let theme = document.documentElement.getAttribute('data-theme') || 'dark';
|
| 706 |
+
let targetTheme = theme === 'dark' ? 'light' : 'dark';
|
| 707 |
+
document.documentElement.setAttribute('data-theme', targetTheme);
|
| 708 |
+
chrome.storage.local.set({ theme: targetTheme });
|
| 709 |
+
});
|
| 710 |
+
}
|
| 711 |
+
})();
|
extension/sidepanel/sidepanel.css
CHANGED
|
@@ -678,3 +678,86 @@ body {
|
|
| 678 |
.is-hidden {
|
| 679 |
display: none !important;
|
| 680 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 678 |
.is-hidden {
|
| 679 |
display: none !important;
|
| 680 |
}
|
| 681 |
+
|
| 682 |
+
|
| 683 |
+
/* Light Theme Variables */
|
| 684 |
+
[data-theme="light"] {
|
| 685 |
+
--bayan-bg: #f9fafb;
|
| 686 |
+
--bayan-surface: #ffffff;
|
| 687 |
+
--bayan-surface-hover: #f3f4f6;
|
| 688 |
+
--bayan-surface-active: #e5e7eb;
|
| 689 |
+
--bayan-border: #e5e7eb;
|
| 690 |
+
--bayan-border-light: #d1d5db;
|
| 691 |
+
--bayan-text: #111827;
|
| 692 |
+
--bayan-text-secondary: #4b5563;
|
| 693 |
+
--bayan-text-muted: #9ca3af;
|
| 694 |
+
--bayan-success: #16a34a;
|
| 695 |
+
--bayan-warning: #d97706;
|
| 696 |
+
}
|
| 697 |
+
|
| 698 |
+
/* Theme Toggle Button Styles */
|
| 699 |
+
.theme-toggle-animated {
|
| 700 |
+
display: flex;
|
| 701 |
+
align-items: center;
|
| 702 |
+
justify-content: center;
|
| 703 |
+
width: 32px;
|
| 704 |
+
height: 32px;
|
| 705 |
+
border: none;
|
| 706 |
+
border-radius: 50%;
|
| 707 |
+
background: var(--sp-surface-hover);
|
| 708 |
+
color: var(--sp-text-secondary);
|
| 709 |
+
cursor: pointer;
|
| 710 |
+
transition: background 0.3s ease, transform 0.3s ease, color 0.3s ease;
|
| 711 |
+
position: relative;
|
| 712 |
+
overflow: hidden;
|
| 713 |
+
margin-right: 8px;
|
| 714 |
+
}
|
| 715 |
+
|
| 716 |
+
.theme-toggle-animated:hover {
|
| 717 |
+
background: var(--sp-primary);
|
| 718 |
+
color: #fff;
|
| 719 |
+
transform: rotate(15deg);
|
| 720 |
+
}
|
| 721 |
+
|
| 722 |
+
.theme-toggle-animated svg {
|
| 723 |
+
transition: transform 0.4s ease, opacity 0.3s ease;
|
| 724 |
+
position: absolute;
|
| 725 |
+
}
|
| 726 |
+
|
| 727 |
+
[data-theme="dark"] .theme-icon-sun {
|
| 728 |
+
transform: rotate(90deg) scale(0);
|
| 729 |
+
opacity: 0;
|
| 730 |
+
}
|
| 731 |
+
|
| 732 |
+
[data-theme="dark"] .theme-icon-moon {
|
| 733 |
+
transform: rotate(0) scale(1);
|
| 734 |
+
opacity: 1;
|
| 735 |
+
}
|
| 736 |
+
|
| 737 |
+
[data-theme="light"] .theme-icon-moon {
|
| 738 |
+
transform: rotate(-90deg) scale(0);
|
| 739 |
+
opacity: 0;
|
| 740 |
+
}
|
| 741 |
+
|
| 742 |
+
[data-theme="light"] .theme-icon-sun {
|
| 743 |
+
transform: rotate(0) scale(1);
|
| 744 |
+
opacity: 1;
|
| 745 |
+
}
|
| 746 |
+
|
| 747 |
+
|
| 748 |
+
|
| 749 |
+
/* Light Theme Variables for Side Panel */
|
| 750 |
+
[data-theme="light"] {
|
| 751 |
+
--sp-bg: #f9fafb;
|
| 752 |
+
--sp-surface: #ffffff;
|
| 753 |
+
--sp-surface-hover: #f3f4f6;
|
| 754 |
+
--sp-surface-active: #e5e7eb;
|
| 755 |
+
--sp-border: #e5e7eb;
|
| 756 |
+
--sp-border-light: #d1d5db;
|
| 757 |
+
--sp-text: #111827;
|
| 758 |
+
--sp-text-secondary: #4b5563;
|
| 759 |
+
--sp-text-muted: #9ca3af;
|
| 760 |
+
--sp-success: #16a34a;
|
| 761 |
+
--sp-warning: #d97706;
|
| 762 |
+
}
|
| 763 |
+
|
extension/sidepanel/sidepanel.html
CHANGED
|
@@ -13,12 +13,20 @@
|
|
| 13 |
<header class="sp-header">
|
| 14 |
<div class="sp-header-brand">
|
| 15 |
<img src="../assets/icons/icon48.png" alt="بيان" width="24" height="24" style="border-radius:5px;">
|
|
|
|
| 16 |
<span class="sp-header-title">بيان</span>
|
| 17 |
</div>
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
<span class="sp-status-dot"></span>
|
| 20 |
<span class="sp-status-text" id="status-text">جاري الاتصال...</span>
|
| 21 |
</div>
|
|
|
|
| 22 |
</header>
|
| 23 |
|
| 24 |
<!-- ══════════════════════════════════════════════ -->
|
|
|
|
| 13 |
<header class="sp-header">
|
| 14 |
<div class="sp-header-brand">
|
| 15 |
<img src="../assets/icons/icon48.png" alt="بيان" width="24" height="24" style="border-radius:5px;">
|
| 16 |
+
<div style="height:24px; width:2px; background-color:#d1d5db; border-radius:9999px; flex-shrink:0;"></div>
|
| 17 |
<span class="sp-header-title">بيان</span>
|
| 18 |
</div>
|
| 19 |
+
|
| 20 |
+
<div style="display: flex; align-items: center; gap: 12px;">
|
| 21 |
+
<button id="ext-theme-toggle" class="theme-toggle-animated" aria-label="تبديل السمة" type="button">
|
| 22 |
+
<svg class="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>
|
| 23 |
+
<svg class="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>
|
| 24 |
+
</button>
|
| 25 |
+
<div class="sp-header-status" id="status-indicator">
|
| 26 |
<span class="sp-status-dot"></span>
|
| 27 |
<span class="sp-status-text" id="status-text">جاري الاتصال...</span>
|
| 28 |
</div>
|
| 29 |
+
</div>
|
| 30 |
</header>
|
| 31 |
|
| 32 |
<!-- ══════════════════════════════════════════════ -->
|
extension/sidepanel/sidepanel.js
CHANGED
|
@@ -846,3 +846,32 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 846 |
});
|
| 847 |
}
|
| 848 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 846 |
});
|
| 847 |
}
|
| 848 |
});
|
| 849 |
+
|
| 850 |
+
|
| 851 |
+
// ── Theme Toggle Logic ──
|
| 852 |
+
(function initBayanThemeToggle() {
|
| 853 |
+
const toggleBtn = document.getElementById('ext-theme-toggle');
|
| 854 |
+
|
| 855 |
+
// Load theme from storage
|
| 856 |
+
chrome.storage.local.get(['theme'], (result) => {
|
| 857 |
+
const currentTheme = result.theme || 'dark'; // default to dark
|
| 858 |
+
document.documentElement.setAttribute('data-theme', currentTheme);
|
| 859 |
+
});
|
| 860 |
+
|
| 861 |
+
// Sync theme changes instantly across all views
|
| 862 |
+
chrome.storage.onChanged.addListener((changes, namespace) => {
|
| 863 |
+
if (namespace === 'local' && changes.theme) {
|
| 864 |
+
document.documentElement.setAttribute('data-theme', changes.theme.newValue);
|
| 865 |
+
}
|
| 866 |
+
});
|
| 867 |
+
|
| 868 |
+
|
| 869 |
+
if (toggleBtn) {
|
| 870 |
+
toggleBtn.addEventListener('click', () => {
|
| 871 |
+
let theme = document.documentElement.getAttribute('data-theme') || 'dark';
|
| 872 |
+
let targetTheme = theme === 'dark' ? 'light' : 'dark';
|
| 873 |
+
document.documentElement.setAttribute('data-theme', targetTheme);
|
| 874 |
+
chrome.storage.local.set({ theme: targetTheme });
|
| 875 |
+
});
|
| 876 |
+
}
|
| 877 |
+
})();
|
generate_icons.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
| 1 |
"""Generate extension icons from LOGOS/1.png (gradient bg for best small-size visibility)."""
|
| 2 |
from PIL import Image
|
| 3 |
|
| 4 |
-
img = Image.open("LOGOS/
|
| 5 |
for size in [16, 32, 48, 128]:
|
| 6 |
-
img.resize((size, size), Image.LANCZOS).save(f"
|
| 7 |
-
print(f"Generated icon{size}.png ({size}x{size}) from LOGOS/
|
| 8 |
print("Done!")
|
|
|
|
| 1 |
"""Generate extension icons from LOGOS/1.png (gradient bg for best small-size visibility)."""
|
| 2 |
from PIL import Image
|
| 3 |
|
| 4 |
+
img = Image.open("LOGOS/2.png")
|
| 5 |
for size in [16, 32, 48, 128]:
|
| 6 |
+
img.resize((size, size), Image.LANCZOS).save(f"LOGOS/icon{size}.png")
|
| 7 |
+
print(f"Generated icon{size}.png ({size}x{size}) from LOGOS/2.png")
|
| 8 |
print("Done!")
|
src/nlp/punctuation/punctuation_rules.py
CHANGED
|
@@ -56,23 +56,42 @@ def arabic_postprocessing(text: str) -> str:
|
|
| 56 |
# Remove colons/semicolons before relative pronouns
|
| 57 |
text = re.sub(r'[؛:]\s*(التي|الذي|الذين|اللتان|اللذان|اللاتي|اللواتي)', r' \1', text)
|
| 58 |
|
| 59 |
-
#
|
| 60 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
|
| 62 |
def _colon_guard(match):
|
| 63 |
-
|
| 64 |
-
|
|
|
|
|
|
|
|
|
|
| 65 |
return match.group(0)
|
| 66 |
-
|
| 67 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
if prev_word.startswith(('ال', 'لل', 'بال', 'فال', 'وال', 'كال')):
|
| 69 |
-
return
|
|
|
|
| 70 |
return match.group(0)
|
| 71 |
|
| 72 |
-
text = re.sub(r'([
|
| 73 |
-
|
| 74 |
-
# Fix misplaced colons for saying verbs (e.g. قال: المعلم -> قال المعلم:)
|
| 75 |
-
text = re.sub(r'\b(قال|يقول|قالت|تقول|أجاب|أجابت|سأل|سألت|أخبر|أخبرت|صرح|صرحت|أضاف|أضافت|أردف|أردفت):?\s+(ال[أ-ي]+|أحمد|محمد|محمود|علي|عمر|خالد|فاطمة|مريم|عائشة|خديجة)\b:?', r'\1 \2:', text)
|
| 76 |
|
| 77 |
# Remove colons after specific non-speech verbs (fallback for verbs without ال)
|
| 78 |
text = re.sub(r'\b(يقدر|يستطيع|يمكن|يجب|ينبغي|يعتبر|يعد|يرى|يعتقد)\s*:', r'\1 ', text)
|
|
|
|
| 56 |
# Remove colons/semicolons before relative pronouns
|
| 57 |
text = re.sub(r'[؛:]\s*(التي|الذي|الذين|اللتان|اللذان|اللاتي|اللواتي)', r' \1', text)
|
| 58 |
|
| 59 |
+
# 1. Fix misplaced colons (e.g. قال: المعلم -> قال المعلم:)
|
| 60 |
+
# Only applies if a colon is actually present on the verb or the name
|
| 61 |
+
def _fix_misplaced(m):
|
| 62 |
+
verb, col1, name, col2 = m.groups()
|
| 63 |
+
if col1 == ':' or col2 == ':':
|
| 64 |
+
return f"{verb} {name}:"
|
| 65 |
+
return m.group(0)
|
| 66 |
+
|
| 67 |
+
text = re.sub(
|
| 68 |
+
r'\b([وفلس]?(?:قال|يقول|قالت|تقول|أجاب|أجابت|سأل|سألت|أخبر|أخبرت|صرح|صرحت|أضاف|أضافت|أردف|أردفت))(:?)\s+(ال[أ-ي]+|أحمد|محمد|محمود|علي|عمر|خالد|فاطمة|مريم|عائشة|خديجة)\b(:?)',
|
| 69 |
+
_fix_misplaced, text
|
| 70 |
+
)
|
| 71 |
+
|
| 72 |
+
# 2. Smart Colon Guard (looks up to 6 words back)
|
| 73 |
+
_ALLOWED_COLON_CUES = r'^[وفلس]?(قال|يقول|قالت|تقول|أجاب|أجابت|سأل|سألت|أخبر|أخبرت|صرح|صرحت|أضاف|أضافت|أردف|أردفت|وضح|وضحت|أوضح|أوضحت|رد|ردت|التالي|الآتي|مثال|ملاحظة|تنبيه|تحذير|قائلا|قائلة|اسم|العمر|تاريخ|رقم|عاجل|الآتية|التالية)$'
|
| 74 |
|
| 75 |
def _colon_guard(match):
|
| 76 |
+
context = match.group(1)
|
| 77 |
+
colon = match.group(2)
|
| 78 |
+
|
| 79 |
+
words = re.findall(r'[\u0600-\u06FFa-zA-Z]+', context)
|
| 80 |
+
if not words:
|
| 81 |
return match.group(0)
|
| 82 |
+
|
| 83 |
+
prev_word = words[-1]
|
| 84 |
+
last_6_words = words[-6:]
|
| 85 |
+
|
| 86 |
+
if any(re.match(_ALLOWED_COLON_CUES, w) for w in last_6_words):
|
| 87 |
+
return match.group(0)
|
| 88 |
+
|
| 89 |
if prev_word.startswith(('ال', 'لل', 'بال', 'فال', 'وال', 'كال')):
|
| 90 |
+
return context + " "
|
| 91 |
+
|
| 92 |
return match.group(0)
|
| 93 |
|
| 94 |
+
text = re.sub(r'([^:]+)(:)', _colon_guard, text)
|
|
|
|
|
|
|
|
|
|
| 95 |
|
| 96 |
# Remove colons after specific non-speech verbs (fallback for verbs without ال)
|
| 97 |
text = re.sub(r'\b(يقدر|يستطيع|يمكن|يجب|ينبغي|يعتبر|يعد|يرى|يعتقد)\s*:', r'\1 ', text)
|
tests/phase10/reports/phase10_results.json
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|