Commit ·
ed2cae2
1
Parent(s): 5447041
audit-v5: Complete ALL remaining green items - #8: Nav logo uses favicon.svg + clickable to home - #9: CTA button arrow indicator - #12: Word count goal (localStorage bayan_word_goal) - #13: Export summary as .txt file - #14: Character limit warning (already existed) - #15: Auto-save indicator in toolbar - #16: 404 page with Arabic numerals - #17: Loading skeleton animation for suggestions
Browse files- src/css/components.css +41 -0
- src/index.html +44 -5
- src/js/documents-cloud/documents-ui.js +2 -0
- src/js/editor.js +17 -0
- src/js/ui.js +15 -0
src/css/components.css
CHANGED
|
@@ -2595,3 +2595,44 @@ input[type="range"]::-moz-range-thumb {
|
|
| 2595 |
transform: translateY(-2px);
|
| 2596 |
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
|
| 2597 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2595 |
transform: translateY(-2px);
|
| 2596 |
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
|
| 2597 |
}
|
| 2598 |
+
|
| 2599 |
+
/* ── Loading Skeleton ── */
|
| 2600 |
+
.skeleton {
|
| 2601 |
+
background: linear-gradient(90deg, var(--color-border) 25%, rgba(255,255,255,0.08) 50%, var(--color-border) 75%);
|
| 2602 |
+
background-size: 200% 100%;
|
| 2603 |
+
animation: skeleton-shimmer 1.5s ease infinite;
|
| 2604 |
+
border-radius: 8px;
|
| 2605 |
+
}
|
| 2606 |
+
|
| 2607 |
+
.skeleton-line {
|
| 2608 |
+
height: 14px;
|
| 2609 |
+
margin-bottom: 10px;
|
| 2610 |
+
border-radius: 6px;
|
| 2611 |
+
}
|
| 2612 |
+
|
| 2613 |
+
.skeleton-line:last-child {
|
| 2614 |
+
width: 60%;
|
| 2615 |
+
}
|
| 2616 |
+
|
| 2617 |
+
@keyframes skeleton-shimmer {
|
| 2618 |
+
0% { background-position: 200% 0; }
|
| 2619 |
+
100% { background-position: -200% 0; }
|
| 2620 |
+
}
|
| 2621 |
+
|
| 2622 |
+
/* ── Word Count Goal ── */
|
| 2623 |
+
.word-goal-badge {
|
| 2624 |
+
display: inline-block;
|
| 2625 |
+
margin-right: 6px;
|
| 2626 |
+
padding: 1px 8px;
|
| 2627 |
+
border-radius: 12px;
|
| 2628 |
+
font-size: 11px;
|
| 2629 |
+
font-weight: 700;
|
| 2630 |
+
background: rgba(107, 163, 224, 0.15);
|
| 2631 |
+
color: var(--primary-color);
|
| 2632 |
+
transition: all 0.3s;
|
| 2633 |
+
}
|
| 2634 |
+
|
| 2635 |
+
.word-goal-badge.goal-reached {
|
| 2636 |
+
background: rgba(34, 197, 94, 0.15);
|
| 2637 |
+
color: #22c55e;
|
| 2638 |
+
}
|
src/index.html
CHANGED
|
@@ -58,8 +58,7 @@
|
|
| 58 |
<button id="mobile-menu-btn" class="mobile-menu-btn md:hidden" aria-label="فتح القائمة" aria-expanded="false" aria-controls="mobile-drawer">
|
| 59 |
<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg>
|
| 60 |
</button>
|
| 61 |
-
<
|
| 62 |
-
<span id="nav-brand" class="text-xl md:text-2xl font-bold text-gradient">بيان</span>
|
| 63 |
</div>
|
| 64 |
<div class="hidden md:flex items-center gap-8">
|
| 65 |
<button onclick="showPage('home')" class="nav-link active text-base font-medium" data-page="home">الرئيسية</button>
|
|
@@ -164,7 +163,7 @@
|
|
| 164 |
</div>
|
| 165 |
<h1 id="hero-headline" class="text-5xl lg:text-7xl font-bold leading-tight mb-6" style="line-height: 1.3;">اكتب العربية<br><span class="text-gradient">بثقة واحتراف</span></h1>
|
| 166 |
<p id="hero-subheadline" class="text-xl lg:text-2xl leading-relaxed mb-10" style="color: var(--text-secondary); line-height: 1.9;">مساعد ذكي يصحح الإملاء والنحو وعلامات الترقيم ويُلخّص النصوص العربية بدقة عالية.</p>
|
| 167 |
-
<div class="flex flex-col sm:flex-row gap-4 justify-center lg:justify-start mb-12"><button id="hero-cta-primary" onclick="showPage('editor')" class="px-8 py-4 rounded-2xl text-lg font-bold text-white gradient-accent transition-all hover:scale-105 hover:shadow-2xl"> جربه الآن مجاناً </button> <button onclick="showPage('features')" class="px-8 py-4 rounded-2xl text-lg font-bold transition-all hover:scale-105 border-2" style="border-color: rgba(255, 255, 255, 0.2); color: var(--text-color);"> استكشف الميزات
|
| 168 |
</div><!-- Stats -->
|
| 169 |
<div class="flex items-center gap-8 justify-center lg:justify-start">
|
| 170 |
<div class="text-center">
|
|
@@ -489,8 +488,9 @@
|
|
| 489 |
<span class="analyzing-spinner" aria-hidden="true"></span>
|
| 490 |
<span>جاري التحليل...</span>
|
| 491 |
</div>
|
| 492 |
-
<span class="text-sm text-secondary">عدد الكلمات: <span id="word-count" class="font-bold">٠</span></span>
|
| 493 |
-
<
|
|
|
|
| 494 |
<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="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4"/></svg>
|
| 495 |
</button>
|
| 496 |
</div>
|
|
@@ -644,6 +644,7 @@
|
|
| 644 |
<div class="flex items-center justify-between mb-4">
|
| 645 |
<h3 class="text-lg font-bold summary-card__title">الملخص</h3>
|
| 646 |
<button onclick="copySummary()" class="btn-ghost" type="button">نسخ</button>
|
|
|
|
| 647 |
</div>
|
| 648 |
<div id="summary-text" class="text-right text-lg editor-content" dir="rtl"></div>
|
| 649 |
</div>
|
|
@@ -855,6 +856,16 @@
|
|
| 855 |
</div>
|
| 856 |
</div>
|
| 857 |
</section>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 858 |
</div><!-- Footer -->
|
| 859 |
<footer class="py-16 border-t footer-bar">
|
| 860 |
<div class="max-w-7xl mx-auto px-6">
|
|
@@ -1103,6 +1114,34 @@
|
|
| 1103 |
});
|
| 1104 |
}
|
| 1105 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1106 |
// Element SDK Integration
|
| 1107 |
async function onConfigChange(cfg) {
|
| 1108 |
config = { ...defaultConfig, ...cfg };
|
|
|
|
| 58 |
<button id="mobile-menu-btn" class="mobile-menu-btn md:hidden" aria-label="فتح القائمة" aria-expanded="false" aria-controls="mobile-drawer">
|
| 59 |
<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg>
|
| 60 |
</button>
|
| 61 |
+
<button onclick="showPage('home')" class="flex items-center gap-3" style="background:none;border:none;cursor:pointer;" aria-label="الرئيسية"><img src="/favicon.svg" alt="بيان" width="40" height="40" class="rounded-xl"><span id="nav-brand" class="text-xl md:text-2xl font-bold text-gradient">بيان</span></button>
|
|
|
|
| 62 |
</div>
|
| 63 |
<div class="hidden md:flex items-center gap-8">
|
| 64 |
<button onclick="showPage('home')" class="nav-link active text-base font-medium" data-page="home">الرئيسية</button>
|
|
|
|
| 163 |
</div>
|
| 164 |
<h1 id="hero-headline" class="text-5xl lg:text-7xl font-bold leading-tight mb-6" style="line-height: 1.3;">اكتب العربية<br><span class="text-gradient">بثقة واحتراف</span></h1>
|
| 165 |
<p id="hero-subheadline" class="text-xl lg:text-2xl leading-relaxed mb-10" style="color: var(--text-secondary); line-height: 1.9;">مساعد ذكي يصحح الإملاء والنحو وعلامات الترقيم ويُلخّص النصوص العربية بدقة عالية.</p>
|
| 166 |
+
<div class="flex flex-col sm:flex-row gap-4 justify-center lg:justify-start mb-12"><button id="hero-cta-primary" onclick="showPage('editor')" class="px-8 py-4 rounded-2xl text-lg font-bold text-white gradient-accent transition-all hover:scale-105 hover:shadow-2xl"> جربه الآن مجاناً ←</button> <button onclick="showPage('features')" class="px-8 py-4 rounded-2xl text-lg font-bold transition-all hover:scale-105 border-2" style="border-color: rgba(255, 255, 255, 0.2); color: var(--text-color);"> استكشف الميزات</button>
|
| 167 |
</div><!-- Stats -->
|
| 168 |
<div class="flex items-center gap-8 justify-center lg:justify-start">
|
| 169 |
<div class="text-center">
|
|
|
|
| 488 |
<span class="analyzing-spinner" aria-hidden="true"></span>
|
| 489 |
<span>جاري التحليل...</span>
|
| 490 |
</div>
|
| 491 |
+
<span class="text-sm text-secondary">عدد الكلمات: <span id="word-count" class="font-bold">٠</span><span id="word-goal-indicator" class="word-goal-badge" style="display:none"></span></span>
|
| 492 |
+
<span id="auto-save-status" class="text-xs text-secondary" style="opacity:0;transition:opacity 0.3s;"></span>
|
| 493 |
+
<button id="doc-save-btn" class="doc-save-btn-icon btn-ghost" type="button" aria-label="حفظ المستند" title="حفظ">
|
| 494 |
<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="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4"/></svg>
|
| 495 |
</button>
|
| 496 |
</div>
|
|
|
|
| 644 |
<div class="flex items-center justify-between mb-4">
|
| 645 |
<h3 class="text-lg font-bold summary-card__title">الملخص</h3>
|
| 646 |
<button onclick="copySummary()" class="btn-ghost" type="button">نسخ</button>
|
| 647 |
+
<button onclick="exportSummaryAsTxt()" class="btn-ghost" type="button">تصدير</button>
|
| 648 |
</div>
|
| 649 |
<div id="summary-text" class="text-right text-lg editor-content" dir="rtl"></div>
|
| 650 |
</div>
|
|
|
|
| 856 |
</div>
|
| 857 |
</div>
|
| 858 |
</section>
|
| 859 |
+
</div><!-- 404 Page -->
|
| 860 |
+
<div id="page-404" class="page">
|
| 861 |
+
<section class="gradient-bg min-h-screen pt-28 pb-20 flex items-center justify-center">
|
| 862 |
+
<div class="text-center">
|
| 863 |
+
<div class="text-8xl font-bold text-gradient mb-6">٤٠٤</div>
|
| 864 |
+
<h2 class="text-3xl font-bold mb-4">الصفحة غير موجودة</h2>
|
| 865 |
+
<p class="text-lg mb-8" style="color: var(--text-secondary);">يبدو أن الصفحة التي تبحث عنها غير متاحة.</p>
|
| 866 |
+
<button onclick="showPage('home')" class="px-8 py-4 rounded-2xl text-lg font-bold text-white gradient-accent transition-all hover:scale-105">العودة للرئيسية ←</button>
|
| 867 |
+
</div>
|
| 868 |
+
</section>
|
| 869 |
</div><!-- Footer -->
|
| 870 |
<footer class="py-16 border-t footer-bar">
|
| 871 |
<div class="max-w-7xl mx-auto px-6">
|
|
|
|
| 1114 |
});
|
| 1115 |
}
|
| 1116 |
|
| 1117 |
+
function exportSummaryAsTxt() {
|
| 1118 |
+
const text = document.getElementById('summary-text')?.innerText;
|
| 1119 |
+
if (!text || !text.trim()) {
|
| 1120 |
+
if (typeof showToast === 'function') showToast('لا يوجد ملخص للتصدير', 'error');
|
| 1121 |
+
return;
|
| 1122 |
+
}
|
| 1123 |
+
const blob = new Blob([text], { type: 'text/plain;charset=utf-8' });
|
| 1124 |
+
const url = URL.createObjectURL(blob);
|
| 1125 |
+
const a = document.createElement('a');
|
| 1126 |
+
a.href = url;
|
| 1127 |
+
a.download = 'ملخص-بيان.txt';
|
| 1128 |
+
document.body.appendChild(a);
|
| 1129 |
+
a.click();
|
| 1130 |
+
document.body.removeChild(a);
|
| 1131 |
+
URL.revokeObjectURL(url);
|
| 1132 |
+
if (typeof showToast === 'function') showToast('✓ تم تصدير الملخص');
|
| 1133 |
+
}
|
| 1134 |
+
|
| 1135 |
+
// Auto-save indicator
|
| 1136 |
+
function showAutoSaveStatus(msg) {
|
| 1137 |
+
const el = document.getElementById('auto-save-status');
|
| 1138 |
+
if (!el) return;
|
| 1139 |
+
el.textContent = msg;
|
| 1140 |
+
el.style.opacity = '1';
|
| 1141 |
+
clearTimeout(el._timer);
|
| 1142 |
+
el._timer = setTimeout(() => { el.style.opacity = '0'; }, 3000);
|
| 1143 |
+
}
|
| 1144 |
+
|
| 1145 |
// Element SDK Integration
|
| 1146 |
async function onConfigChange(cfg) {
|
| 1147 |
config = { ...defaultConfig, ...cfg };
|
src/js/documents-cloud/documents-ui.js
CHANGED
|
@@ -36,8 +36,10 @@ function initDocumentsCloud() {
|
|
| 36 |
if (state === 'saving') {
|
| 37 |
saveBtn.title = 'جاري الحفظ...';
|
| 38 |
saveBtn.classList.add('is-saving');
|
|
|
|
| 39 |
} else if (state === 'saved') {
|
| 40 |
saveBtn.title = 'تم الحفظ';
|
|
|
|
| 41 |
saveBtn.classList.remove('is-saving', 'doc-save-btn--dirty');
|
| 42 |
saveBtn.classList.add('is-saved');
|
| 43 |
setTimeout(() => {
|
|
|
|
| 36 |
if (state === 'saving') {
|
| 37 |
saveBtn.title = 'جاري الحفظ...';
|
| 38 |
saveBtn.classList.add('is-saving');
|
| 39 |
+
if (typeof showAutoSaveStatus === 'function') showAutoSaveStatus('جاري الحفظ...');
|
| 40 |
} else if (state === 'saved') {
|
| 41 |
saveBtn.title = 'تم الحفظ';
|
| 42 |
+
if (typeof showAutoSaveStatus === 'function') showAutoSaveStatus('✓ تم الحفظ');
|
| 43 |
saveBtn.classList.remove('is-saving', 'doc-save-btn--dirty');
|
| 44 |
saveBtn.classList.add('is-saved');
|
| 45 |
setTimeout(() => {
|
src/js/editor.js
CHANGED
|
@@ -82,6 +82,23 @@ function updateEditorStats() {
|
|
| 82 |
if (wordCountEl) {
|
| 83 |
wordCountEl.textContent = words.toLocaleString('ar-EG');
|
| 84 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
// Item 4: Enhanced stats
|
| 86 |
if (typeof updateEnhancedStats === 'function') {
|
| 87 |
updateEnhancedStats();
|
|
|
|
| 82 |
if (wordCountEl) {
|
| 83 |
wordCountEl.textContent = words.toLocaleString('ar-EG');
|
| 84 |
}
|
| 85 |
+
|
| 86 |
+
// Word count goal
|
| 87 |
+
const goalEl = document.getElementById('word-goal-indicator');
|
| 88 |
+
if (goalEl) {
|
| 89 |
+
try {
|
| 90 |
+
const goal = parseInt(localStorage.getItem('bayan_word_goal') || '0', 10);
|
| 91 |
+
if (goal > 0) {
|
| 92 |
+
const pct = Math.min(Math.round((words / goal) * 100), 100);
|
| 93 |
+
goalEl.style.display = 'inline-block';
|
| 94 |
+
goalEl.textContent = `${pct}% من ${goal.toLocaleString('ar-EG')}`;
|
| 95 |
+
goalEl.classList.toggle('goal-reached', pct >= 100);
|
| 96 |
+
} else {
|
| 97 |
+
goalEl.style.display = 'none';
|
| 98 |
+
}
|
| 99 |
+
} catch(e) { goalEl.style.display = 'none'; }
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
// Item 4: Enhanced stats
|
| 103 |
if (typeof updateEnhancedStats === 'function') {
|
| 104 |
updateEnhancedStats();
|
src/js/ui.js
CHANGED
|
@@ -200,6 +200,21 @@ function setAnalyzingState(isAnalyzing) {
|
|
| 200 |
if (indicator) {
|
| 201 |
indicator.classList.toggle('active', isAnalyzing);
|
| 202 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 203 |
}
|
| 204 |
|
| 205 |
/* ── Mobile navigation ── */
|
|
|
|
| 200 |
if (indicator) {
|
| 201 |
indicator.classList.toggle('active', isAnalyzing);
|
| 202 |
}
|
| 203 |
+
|
| 204 |
+
// Show skeleton loading in suggestions panel
|
| 205 |
+
if (isAnalyzing) {
|
| 206 |
+
const lists = [
|
| 207 |
+
document.getElementById('suggestions-list'),
|
| 208 |
+
document.getElementById('bottom-sheet-list')
|
| 209 |
+
].filter(Boolean);
|
| 210 |
+
const skeletonHTML = `
|
| 211 |
+
<div style="padding: 16px;">
|
| 212 |
+
<div class="skeleton skeleton-line" style="width:80%"></div>
|
| 213 |
+
<div class="skeleton skeleton-line" style="width:100%"></div>
|
| 214 |
+
<div class="skeleton skeleton-line" style="width:65%"></div>
|
| 215 |
+
</div>`;
|
| 216 |
+
lists.forEach(el => { el.innerHTML = skeletonHTML; });
|
| 217 |
+
}
|
| 218 |
}
|
| 219 |
|
| 220 |
/* ── Mobile navigation ── */
|