youssefreda9 commited on
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 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
- <div class="w-10 h-10 rounded-xl flex items-center justify-center gradient-accent" aria-hidden="true"><span class="text-white text-xl font-bold">ب</span></div>
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);"> استكشف الميزات </button>
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
- <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,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 ── */