youssefreda9 commited on
Commit
afdf449
·
1 Parent(s): d4d18df

feat: 12 UI improvements batch - 1. Editor surface flush (no margin/border, Google Docs style) - 2. Custom styled summary slider (gradient thumb) - 3. Empty states with icons for suggestions - 4. Enhanced status bar (chars, sentences, reading time) - 5. Mobile scrollable formatting toolbar - 6. Summary word count + compression ratio - 7. Document search bar with filter - 8. Text color + highlight color pickers - 9. Lists (bullet/ordered) + indent/outdent + clear format - 10. Animated sun/moon theme toggle - 11. Summary bullet points mode toggle - 12. Print styles (only editor content)

Browse files
Files changed (6) hide show
  1. src/css/components.css +368 -10
  2. src/index.html +108 -8
  3. src/js/editor.js +4 -0
  4. src/js/format.js +114 -0
  5. src/js/theme.js +1 -4
  6. src/js/ui.js +7 -6
src/css/components.css CHANGED
@@ -286,12 +286,19 @@
286
  /* ── Formatting Toolbar ── */
287
  .format-toolbar {
288
  display: flex;
289
- flex-wrap: wrap;
290
  align-items: center;
291
  gap: 6px;
292
  padding: 8px var(--spacing-md);
293
  border-bottom: 1px solid var(--color-border);
294
  background: var(--color-surface);
 
 
 
 
 
 
 
295
  }
296
 
297
  .fmt-group {
@@ -475,7 +482,7 @@
475
  background: var(--color-editor);
476
  color: var(--color-text-primary);
477
  min-height: 50vh;
478
- padding: var(--spacing-lg);
479
  font-size: var(--font-size-editor);
480
  font-weight: var(--font-weight-regular);
481
  line-height: var(--line-height-editor);
@@ -485,11 +492,13 @@
485
  white-space: pre-wrap;
486
  word-wrap: break-word;
487
  overflow-y: auto;
488
- border-radius: var(--radius-md);
489
- margin: var(--spacing-md);
490
- border: 1px solid var(--color-border);
491
- box-shadow: var(--shadow-editor);
492
- transition: box-shadow var(--transition-base), border-color var(--transition-base);
 
 
493
  }
494
 
495
  @media (min-width: 768px) {
@@ -502,8 +511,6 @@
502
 
503
  .editor-surface:focus {
504
  outline: none;
505
- border-color: var(--color-border-strong);
506
- box-shadow: var(--shadow-editor), 0 0 0 3px var(--focus-ring);
507
  }
508
 
509
  .editor-surface[data-empty="true"]::before {
@@ -515,7 +522,7 @@
515
  }
516
 
517
  .editor-surface.analyzing {
518
- opacity: 0.88;
519
  }
520
 
521
  .editor-footer {
@@ -2104,3 +2111,354 @@ input[type="range"]::-webkit-slider-thumb {
2104
  margin-top: var(--spacing-sm);
2105
  }
2106
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
  /* ── Formatting Toolbar ── */
287
  .format-toolbar {
288
  display: flex;
289
+ flex-wrap: nowrap;
290
  align-items: center;
291
  gap: 6px;
292
  padding: 8px var(--spacing-md);
293
  border-bottom: 1px solid var(--color-border);
294
  background: var(--color-surface);
295
+ overflow-x: auto;
296
+ -webkit-overflow-scrolling: touch;
297
+ scrollbar-width: none;
298
+ }
299
+
300
+ .format-toolbar::-webkit-scrollbar {
301
+ display: none;
302
  }
303
 
304
  .fmt-group {
 
482
  background: var(--color-editor);
483
  color: var(--color-text-primary);
484
  min-height: 50vh;
485
+ padding: var(--spacing-lg) var(--spacing-xl);
486
  font-size: var(--font-size-editor);
487
  font-weight: var(--font-weight-regular);
488
  line-height: var(--line-height-editor);
 
492
  white-space: pre-wrap;
493
  word-wrap: break-word;
494
  overflow-y: auto;
495
+ border-radius: 0;
496
+ margin: 0;
497
+ border: none;
498
+ border-top: 1px solid var(--color-border);
499
+ box-shadow: none;
500
+ transition: none;
501
+ flex: 1;
502
  }
503
 
504
  @media (min-width: 768px) {
 
511
 
512
  .editor-surface:focus {
513
  outline: none;
 
 
514
  }
515
 
516
  .editor-surface[data-empty="true"]::before {
 
522
  }
523
 
524
  .editor-surface.analyzing {
525
+ opacity: 0.92;
526
  }
527
 
528
  .editor-footer {
 
2111
  margin-top: var(--spacing-sm);
2112
  }
2113
 
2114
+ /* ── Item 2: Custom Summary Slider ── */
2115
+ .summarize-panel input[type="range"] {
2116
+ -webkit-appearance: none;
2117
+ appearance: none;
2118
+ width: 100%;
2119
+ height: 6px;
2120
+ border-radius: 3px;
2121
+ background: var(--color-border);
2122
+ outline: none;
2123
+ direction: ltr;
2124
+ cursor: pointer;
2125
+ }
2126
+
2127
+ .summarize-panel input[type="range"]::-webkit-slider-thumb {
2128
+ -webkit-appearance: none;
2129
+ width: 22px;
2130
+ height: 22px;
2131
+ border-radius: 50%;
2132
+ background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
2133
+ cursor: pointer;
2134
+ border: 3px solid var(--color-surface);
2135
+ box-shadow: 0 2px 8px rgba(0,0,0,0.25);
2136
+ transition: transform 0.15s ease;
2137
+ }
2138
+
2139
+ .summarize-panel input[type="range"]::-webkit-slider-thumb:hover {
2140
+ transform: scale(1.15);
2141
+ }
2142
+
2143
+ .summarize-panel input[type="range"]::-moz-range-thumb {
2144
+ width: 22px;
2145
+ height: 22px;
2146
+ border-radius: 50%;
2147
+ background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
2148
+ cursor: pointer;
2149
+ border: 3px solid var(--color-surface);
2150
+ box-shadow: 0 2px 8px rgba(0,0,0,0.25);
2151
+ }
2152
+
2153
+ /* ── Item 3: Empty States ── */
2154
+ .empty-state {
2155
+ display: flex;
2156
+ flex-direction: column;
2157
+ align-items: center;
2158
+ justify-content: center;
2159
+ padding: var(--spacing-xl) var(--spacing-md);
2160
+ text-align: center;
2161
+ gap: var(--spacing-md);
2162
+ }
2163
+
2164
+ .empty-state__icon {
2165
+ width: 48px;
2166
+ height: 48px;
2167
+ border-radius: 50%;
2168
+ background: var(--color-surface-elevated);
2169
+ display: flex;
2170
+ align-items: center;
2171
+ justify-content: center;
2172
+ color: var(--color-text-muted);
2173
+ }
2174
+
2175
+ .empty-state__title {
2176
+ font-size: var(--font-size-caption);
2177
+ font-weight: var(--font-weight-semibold);
2178
+ color: var(--color-text-secondary);
2179
+ }
2180
+
2181
+ .empty-state__desc {
2182
+ font-size: var(--font-size-label);
2183
+ color: var(--color-text-muted);
2184
+ line-height: 1.6;
2185
+ }
2186
+
2187
+ /* ── Item 4: Enhanced Status Bar ── */
2188
+ .editor-stats {
2189
+ display: flex;
2190
+ flex-wrap: wrap;
2191
+ gap: var(--spacing-sm);
2192
+ align-items: center;
2193
+ }
2194
+
2195
+ .stat-item {
2196
+ display: flex;
2197
+ align-items: center;
2198
+ gap: 4px;
2199
+ font-size: var(--font-size-label);
2200
+ color: var(--color-text-muted);
2201
+ }
2202
+
2203
+ .stat-item--sep {
2204
+ width: 1px;
2205
+ height: 14px;
2206
+ background: var(--color-border);
2207
+ }
2208
+
2209
+ /* ── Item 6: Summary Stats ── */
2210
+ .summary-stats {
2211
+ display: flex;
2212
+ flex-wrap: wrap;
2213
+ gap: var(--spacing-md);
2214
+ padding: var(--spacing-sm) 0;
2215
+ margin-bottom: var(--spacing-md);
2216
+ border-bottom: 1px solid var(--color-border);
2217
+ }
2218
+
2219
+ .summary-stat {
2220
+ display: flex;
2221
+ align-items: center;
2222
+ gap: 6px;
2223
+ font-size: var(--font-size-label);
2224
+ color: var(--color-text-secondary);
2225
+ }
2226
+
2227
+ .summary-stat__value {
2228
+ font-weight: var(--font-weight-bold);
2229
+ color: var(--color-primary);
2230
+ }
2231
+
2232
+ /* ── Item 7: Document Search ── */
2233
+ .docs-search {
2234
+ display: flex;
2235
+ align-items: center;
2236
+ gap: 8px;
2237
+ padding: 6px 10px;
2238
+ border: 1px solid var(--color-border);
2239
+ border-radius: 6px;
2240
+ background: var(--color-surface);
2241
+ margin-bottom: var(--spacing-md);
2242
+ transition: border-color 0.15s ease;
2243
+ }
2244
+
2245
+ .docs-search:focus-within {
2246
+ border-color: var(--color-primary);
2247
+ }
2248
+
2249
+ .docs-search__input {
2250
+ flex: 1;
2251
+ border: none;
2252
+ background: transparent;
2253
+ color: var(--color-text-primary);
2254
+ font-family: inherit;
2255
+ font-size: var(--font-size-caption);
2256
+ outline: none;
2257
+ direction: rtl;
2258
+ }
2259
+
2260
+ .docs-search__input::placeholder {
2261
+ color: var(--color-text-muted);
2262
+ }
2263
+
2264
+ .docs-search__icon {
2265
+ color: var(--color-text-muted);
2266
+ flex-shrink: 0;
2267
+ }
2268
+
2269
+ .doc-item-meta {
2270
+ font-size: 0.65rem;
2271
+ color: var(--color-text-muted);
2272
+ margin-top: 2px;
2273
+ }
2274
+
2275
+ /* ── Item 8: Color Picker ── */
2276
+ .fmt-color-btn {
2277
+ display: flex;
2278
+ align-items: center;
2279
+ justify-content: center;
2280
+ width: 34px;
2281
+ height: 34px;
2282
+ border: none;
2283
+ background: var(--color-surface);
2284
+ cursor: pointer;
2285
+ position: relative;
2286
+ transition: background 0.15s ease;
2287
+ }
2288
+
2289
+ .fmt-color-btn:hover {
2290
+ background: var(--color-surface-elevated);
2291
+ }
2292
+
2293
+ .fmt-color-indicator {
2294
+ position: absolute;
2295
+ bottom: 4px;
2296
+ left: 50%;
2297
+ transform: translateX(-50%);
2298
+ width: 14px;
2299
+ height: 3px;
2300
+ border-radius: 2px;
2301
+ }
2302
+
2303
+ .fmt-color-grid {
2304
+ display: grid;
2305
+ grid-template-columns: repeat(6, 1fr);
2306
+ gap: 4px;
2307
+ padding: 8px;
2308
+ }
2309
+
2310
+ .fmt-color-swatch {
2311
+ width: 24px;
2312
+ height: 24px;
2313
+ border-radius: 4px;
2314
+ border: 2px solid transparent;
2315
+ cursor: pointer;
2316
+ transition: transform 0.12s ease, border-color 0.12s ease;
2317
+ }
2318
+
2319
+ .fmt-color-swatch:hover {
2320
+ transform: scale(1.15);
2321
+ border-color: var(--color-text-primary);
2322
+ }
2323
+
2324
+ .fmt-color-swatch--active {
2325
+ border-color: var(--color-primary);
2326
+ box-shadow: 0 0 0 2px var(--color-primary);
2327
+ }
2328
+
2329
+ /* ── Item 10: Animated Theme Toggle ── */
2330
+ .theme-toggle-animated {
2331
+ display: flex;
2332
+ align-items: center;
2333
+ justify-content: center;
2334
+ width: 40px;
2335
+ height: 40px;
2336
+ border: none;
2337
+ border-radius: 50%;
2338
+ background: var(--color-surface-elevated);
2339
+ color: var(--color-text-secondary);
2340
+ cursor: pointer;
2341
+ transition: background 0.3s ease, transform 0.3s ease, color 0.3s ease;
2342
+ position: relative;
2343
+ overflow: hidden;
2344
+ }
2345
+
2346
+ .theme-toggle-animated:hover {
2347
+ background: var(--color-primary);
2348
+ color: #fff;
2349
+ transform: rotate(15deg);
2350
+ }
2351
+
2352
+ .theme-toggle-animated svg {
2353
+ transition: transform 0.4s ease, opacity 0.3s ease;
2354
+ }
2355
+
2356
+ .theme-toggle-animated .theme-icon-sun,
2357
+ .theme-toggle-animated .theme-icon-moon {
2358
+ position: absolute;
2359
+ transition: transform 0.4s ease, opacity 0.3s ease;
2360
+ }
2361
+
2362
+ [data-theme="dark"] .theme-icon-sun {
2363
+ transform: rotate(90deg) scale(0);
2364
+ opacity: 0;
2365
+ }
2366
+
2367
+ [data-theme="dark"] .theme-icon-moon {
2368
+ transform: rotate(0) scale(1);
2369
+ opacity: 1;
2370
+ }
2371
+
2372
+ [data-theme="light"] .theme-icon-moon {
2373
+ transform: rotate(-90deg) scale(0);
2374
+ opacity: 0;
2375
+ }
2376
+
2377
+ [data-theme="light"] .theme-icon-sun {
2378
+ transform: rotate(0) scale(1);
2379
+ opacity: 1;
2380
+ }
2381
+
2382
+ /* ── Item 11: Summary Mode Toggle ── */
2383
+ .summary-mode-toggle {
2384
+ display: flex;
2385
+ border: 1px solid var(--color-border);
2386
+ border-radius: 8px;
2387
+ overflow: hidden;
2388
+ margin-bottom: var(--spacing-md);
2389
+ }
2390
+
2391
+ .summary-mode-btn {
2392
+ flex: 1;
2393
+ padding: 8px 12px;
2394
+ border: none;
2395
+ background: transparent;
2396
+ color: var(--color-text-secondary);
2397
+ font-family: inherit;
2398
+ font-size: var(--font-size-caption);
2399
+ font-weight: var(--font-weight-semibold);
2400
+ cursor: pointer;
2401
+ transition: all 0.2s ease;
2402
+ display: flex;
2403
+ align-items: center;
2404
+ justify-content: center;
2405
+ gap: 6px;
2406
+ }
2407
+
2408
+ .summary-mode-btn.active {
2409
+ background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
2410
+ color: #fff;
2411
+ }
2412
+
2413
+ .summary-mode-btn:hover:not(.active) {
2414
+ background: var(--color-surface-elevated);
2415
+ }
2416
+
2417
+ /* ── Item 12: Print Styles ── */
2418
+ @media print {
2419
+ body * {
2420
+ visibility: hidden;
2421
+ }
2422
+
2423
+ #editor-container,
2424
+ #editor-container * {
2425
+ visibility: visible;
2426
+ }
2427
+
2428
+ #editor-container {
2429
+ position: absolute;
2430
+ top: 0;
2431
+ left: 0;
2432
+ right: 0;
2433
+ width: 100%;
2434
+ min-height: auto;
2435
+ padding: 2cm;
2436
+ font-size: 14pt;
2437
+ line-height: 2;
2438
+ color: #000;
2439
+ background: #fff;
2440
+ border: none;
2441
+ box-shadow: none;
2442
+ }
2443
+
2444
+ .spelling-error,
2445
+ .grammar-error,
2446
+ .punctuation-suggestion {
2447
+ border: none;
2448
+ background: transparent;
2449
+ text-decoration: none;
2450
+ }
2451
+
2452
+ .site-nav,
2453
+ .format-toolbar,
2454
+ .editor-toolbar,
2455
+ .editor-footer,
2456
+ .sidebar-panel,
2457
+ .docs-sidebar,
2458
+ .bottom-sheet,
2459
+ .suggestion-popover,
2460
+ #analyzing-indicator {
2461
+ display: none !important;
2462
+ }
2463
+ }
2464
+
src/index.html CHANGED
@@ -86,7 +86,10 @@
86
  </div>
87
  </div>
88
  </div>
89
- <button id="theme-toggle" class="theme-toggle" aria-label="تبديل السمة" aria-pressed="true" type="button"></button>
 
 
 
90
 
91
  </div>
92
  </div>
@@ -461,6 +464,10 @@
461
  <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="M12 4v16m8-8H4"/></svg>
462
  مستند جديد
463
  </button>
 
 
 
 
464
  <div id="docs-list" role="list" aria-label="قائمة المستندات"></div>
465
  </div>
466
  </div>
@@ -556,6 +563,51 @@
556
  <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="M21 10H11a5 5 0 00-5 5v2M21 10l-4-4M21 10l-4 4"/></svg>
557
  </button>
558
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
559
  </div>
560
  <input type="file" id="doc-import-input" class="sr-only" accept=".txt,.docx,text/plain,application/vnd.openxmlformats-officedocument.wordprocessingml.document" aria-hidden="true">
561
  <div id="write-area">
@@ -573,6 +625,17 @@
573
  </div>
574
  </div>
575
  <div id="summarize-area" class="summarize-panel is-hidden">
 
 
 
 
 
 
 
 
 
 
 
576
  <div class="mb-6">
577
  <label class="block text-base font-bold mb-3" for="summary-length">طول الملخص</label>
578
  <div class="flex items-center gap-4">
@@ -591,6 +654,11 @@
591
  <button id="generate-summary-btn" onclick="generateSummary(event)" class="btn-primary w-full py-4 text-lg mb-6" type="button">توليد الملخص</button>
592
  <div id="summary-preview" class="summary-preview">
593
  <div class="summary-card">
 
 
 
 
 
594
  <div class="flex items-center justify-between mb-4">
595
  <h3 class="text-lg font-bold summary-card__title">الملخص</h3>
596
  <button onclick="copySummary()" class="btn-ghost" type="button">نسخ</button>
@@ -600,14 +668,23 @@
600
  </div>
601
  </div>
602
  <div class="editor-footer">
603
- <div class="editor-stats" role="status" aria-label="إحصائيات الأخطاء">
604
  <div class="flex items-center gap-2"><span class="stat-dot stat-dot--spelling" aria-hidden="true"></span><span class="text-sm text-secondary"><span id="spelling-count">٠</span> إملائي</span></div>
605
  <div class="flex items-center gap-2"><span class="stat-dot stat-dot--grammar" aria-hidden="true"></span><span class="text-sm text-secondary"><span id="grammar-count">٠</span> نحوي</span></div>
606
  <div class="flex items-center gap-2"><span class="stat-dot stat-dot--punctuation" aria-hidden="true"></span><span class="text-sm text-secondary"><span id="punctuation-count">٠</span> ترقيم</span></div>
 
 
 
 
 
607
  </div>
608
  <div class="editor-actions">
609
- <button onclick="clearEditor()" class="btn-ghost" type="button">مسح الكل</button>
610
- <button onclick="copyText()" class="btn-primary" type="button">نسخ النص</button>
 
 
 
 
611
  </div>
612
  </div>
613
  </div>
@@ -926,10 +1003,32 @@
926
  }
927
 
928
  if (data.status === 'success' && data.summary) {
929
- const p = document.createElement('p');
930
- p.textContent = data.summary;
931
- summaryText.textContent = '';
932
- summaryText.appendChild(p);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
933
  } else {
934
  throw new Error(data.error || 'لم يتم توليد ملخص');
935
  }
@@ -1065,6 +1164,7 @@
1065
  initUI();
1066
  initEditor();
1067
  if (typeof initFormatToolbar === 'function') initFormatToolbar();
 
1068
  initDocuments();
1069
  updateSuggestionsList([]);
1070
  // Phase 7 — Sync System
 
86
  </div>
87
  </div>
88
  </div>
89
+ <button id="theme-toggle" class="theme-toggle-animated" aria-label="تبديل السمة" aria-pressed="true" type="button">
90
+ <svg class="theme-icon-sun" width="20" height="20" 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>
91
+ <svg class="theme-icon-moon" width="20" height="20" 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>
92
+ </button>
93
 
94
  </div>
95
  </div>
 
464
  <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="M12 4v16m8-8H4"/></svg>
465
  مستند جديد
466
  </button>
467
+ <div class="docs-search">
468
+ <svg class="docs-search__icon" width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/></svg>
469
+ <input type="text" class="docs-search__input" id="docs-search-input" placeholder="بحث في المستندات..." autocomplete="off">
470
+ </div>
471
  <div id="docs-list" role="list" aria-label="قائمة المستندات"></div>
472
  </div>
473
  </div>
 
563
  <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="M21 10H11a5 5 0 00-5 5v2M21 10l-4-4M21 10l-4 4"/></svg>
564
  </button>
565
  </div>
566
+ <div class="fmt-divider"></div>
567
+ <!-- Item 8: Color pickers -->
568
+ <div class="fmt-group fmt-group--font">
569
+ <div class="fmt-dropdown" id="fmt-textcolor-wrap">
570
+ <button class="fmt-color-btn" type="button" id="fmt-textcolor-trigger" title="لون النص">
571
+ <svg width="16" height="16" fill="currentColor" viewBox="0 0 24 24"><path d="M11 2L5.5 16h2.25l1.12-3h6.25l1.12 3h2.25L13 2h-2zm-1.38 9L12 4.67 14.38 11H9.62z"/></svg>
572
+ <span class="fmt-color-indicator" id="fmt-textcolor-bar" style="background:#ECEEF2"></span>
573
+ </button>
574
+ <div class="fmt-dropdown__menu" id="fmt-textcolor-menu" role="menu">
575
+ <div class="fmt-color-grid" id="fmt-textcolor-grid"></div>
576
+ </div>
577
+ </div>
578
+ <div class="fmt-dropdown" id="fmt-highlight-wrap">
579
+ <button class="fmt-color-btn" type="button" id="fmt-highlight-trigger" title="لون التظليل">
580
+ <svg width="16" height="16" fill="currentColor" viewBox="0 0 24 24"><path d="M16.56 8.94L7.62 0 6.21 1.41l2.38 2.38-5.15 5.15a1.49 1.49 0 000 2.12l5.5 5.5c.29.29.68.44 1.06.44s.77-.15 1.06-.44l5.5-5.5c.59-.58.59-1.53 0-2.12zM5.21 10L10 5.21 14.79 10H5.21zM19 11.5s-2 2.17-2 3.5c0 1.1.9 2 2 2s2-.9 2-2c0-1.33-2-3.5-2-3.5zM2 20h20v4H2v-4z"/></svg>
581
+ <span class="fmt-color-indicator" id="fmt-highlight-bar" style="background:transparent"></span>
582
+ </button>
583
+ <div class="fmt-dropdown__menu" id="fmt-highlight-menu" role="menu">
584
+ <div class="fmt-color-grid" id="fmt-highlight-grid"></div>
585
+ </div>
586
+ </div>
587
+ </div>
588
+ <div class="fmt-divider"></div>
589
+ <!-- Item 9: Lists + Indent -->
590
+ <div class="fmt-group">
591
+ <button class="fmt-btn" onclick="execFormat('insertUnorderedList')" type="button" title="قائمة نقطية">
592
+ <svg width="16" height="16" fill="currentColor" viewBox="0 0 24 24"><path d="M4 6h2v2H4V6zm4 0h12v2H8V6zM4 11h2v2H4v-2zm4 0h12v2H8v-2zm-4 5h2v2H4v-2zm4 0h12v2H8v-2z"/></svg>
593
+ </button>
594
+ <button class="fmt-btn" onclick="execFormat('insertOrderedList')" type="button" title="قائمة مرقمة">
595
+ <svg width="16" height="16" fill="currentColor" viewBox="0 0 24 24"><path d="M2 17h2v.5H3v1h1v.5H2v1h3v-4H2v1zm1-9h1V4H2v1h1v3zm-1 3h1.8L2 13.1v.9h3v-1H3.2L5 10.9V10H2v1zm5-6v2h14V5H7zm0 14h14v-2H7v2zm0-6h14v-2H7v2z"/></svg>
596
+ </button>
597
+ <button class="fmt-btn" onclick="execFormat('indent')" type="button" title="مسافة بادئة">
598
+ <svg width="16" height="16" fill="currentColor" viewBox="0 0 24 24"><path d="M3 21h18v-2H3v2zM3 8v8l4-4-4-4zm8 9h10v-2H11v2zM3 3v2h18V3H3zm8 6h10V7H11v2zm0 4h10v-2H11v2z"/></svg>
599
+ </button>
600
+ <button class="fmt-btn" onclick="execFormat('outdent')" type="button" title="إزالة المسافة البادئة">
601
+ <svg width="16" height="16" fill="currentColor" viewBox="0 0 24 24"><path d="M11 17h10v-2H11v2zm-8-5l4 4V8l-4 4zm0 9h18v-2H3v2zM3 3v2h18V3H3zm8 6h10V7H11v2zm0 4h10v-2H11v2z"/></svg>
602
+ </button>
603
+ </div>
604
+ <div class="fmt-divider"></div>
605
+ <!-- Clear formatting -->
606
+ <div class="fmt-group">
607
+ <button class="fmt-btn" onclick="execFormat('removeFormat')" type="button" title="مسح التنسيق">
608
+ <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="M12 2L2 22h20L12 2z"/><path stroke-linecap="round" stroke-width="2" d="M4 4l16 16"/></svg>
609
+ </button>
610
+ </div>
611
  </div>
612
  <input type="file" id="doc-import-input" class="sr-only" accept=".txt,.docx,text/plain,application/vnd.openxmlformats-officedocument.wordprocessingml.document" aria-hidden="true">
613
  <div id="write-area">
 
625
  </div>
626
  </div>
627
  <div id="summarize-area" class="summarize-panel is-hidden">
628
+ <!-- Item 11: Mode Toggle -->
629
+ <div class="summary-mode-toggle">
630
+ <button type="button" class="summary-mode-btn active" id="summary-mode-paragraph" onclick="setSummaryMode('paragraph')">
631
+ <svg width="14" height="14" fill="currentColor" viewBox="0 0 24 24"><path d="M3 5h18v2H3V5zm0 8h18v2H3v-2zm0 4h12v2H3v-2z"/></svg>
632
+ فقرة
633
+ </button>
634
+ <button type="button" class="summary-mode-btn" id="summary-mode-bullets" onclick="setSummaryMode('bullets')">
635
+ <svg width="14" height="14" fill="currentColor" viewBox="0 0 24 24"><path d="M4 6h2v2H4V6zm4 0h12v2H8V6zM4 11h2v2H4v-2zm4 0h12v2H8v-2zm-4 5h2v2H4v-2zm4 0h12v2H8v-2z"/></svg>
636
+ نقاط
637
+ </button>
638
+ </div>
639
  <div class="mb-6">
640
  <label class="block text-base font-bold mb-3" for="summary-length">طول الملخص</label>
641
  <div class="flex items-center gap-4">
 
654
  <button id="generate-summary-btn" onclick="generateSummary(event)" class="btn-primary w-full py-4 text-lg mb-6" type="button">توليد الملخص</button>
655
  <div id="summary-preview" class="summary-preview">
656
  <div class="summary-card">
657
+ <!-- Item 6: Summary Stats -->
658
+ <div id="summary-stats" class="summary-stats" style="display:none">
659
+ <div class="summary-stat"><span>كلمات الملخص:</span> <span class="summary-stat__value" id="summary-word-count">0</span></div>
660
+ <div class="summary-stat"><span>نسبة الاختصار:</span> <span class="summary-stat__value" id="summary-compression">0%</span></div>
661
+ </div>
662
  <div class="flex items-center justify-between mb-4">
663
  <h3 class="text-lg font-bold summary-card__title">الملخص</h3>
664
  <button onclick="copySummary()" class="btn-ghost" type="button">نسخ</button>
 
668
  </div>
669
  </div>
670
  <div class="editor-footer">
671
+ <div class="editor-stats" role="status" aria-label="إحصائيات">
672
  <div class="flex items-center gap-2"><span class="stat-dot stat-dot--spelling" aria-hidden="true"></span><span class="text-sm text-secondary"><span id="spelling-count">٠</span> إملائي</span></div>
673
  <div class="flex items-center gap-2"><span class="stat-dot stat-dot--grammar" aria-hidden="true"></span><span class="text-sm text-secondary"><span id="grammar-count">٠</span> نحوي</span></div>
674
  <div class="flex items-center gap-2"><span class="stat-dot stat-dot--punctuation" aria-hidden="true"></span><span class="text-sm text-secondary"><span id="punctuation-count">٠</span> ترقيم</span></div>
675
+ <span class="stat-item--sep"></span>
676
+ <span class="stat-item"><span id="char-count">٠</span> حرف</span>
677
+ <span class="stat-item"><span id="sentence-count">٠</span> جملة</span>
678
+ <span class="stat-item--sep"></span>
679
+ <span class="stat-item">⏱ <span id="reading-time">٠</span> د قراءة</span>
680
  </div>
681
  <div class="editor-actions">
682
+ <button onclick="clearEditor()" class="btn-ghost" type="button" title="مسح الكل">
683
+ <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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg>
684
+ </button>
685
+ <button onclick="copyText()" class="btn-ghost" type="button" title="نسخ النص">
686
+ <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 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/></svg>
687
+ </button>
688
  </div>
689
  </div>
690
  </div>
 
1003
  }
1004
 
1005
  if (data.status === 'success' && data.summary) {
1006
+ let summaryContent = data.summary;
1007
+
1008
+ // Item 11: Bullet mode
1009
+ if (window._summaryMode === 'bullets') {
1010
+ const sentences = summaryContent.split(/[.،؛]\s*/).filter(s => s.trim().length > 2);
1011
+ const ul = document.createElement('ul');
1012
+ ul.style.cssText = 'list-style: disc; padding-right: 1.5rem; direction: rtl; text-align: right;';
1013
+ sentences.forEach(s => {
1014
+ const li = document.createElement('li');
1015
+ li.textContent = s.trim();
1016
+ li.style.marginBottom = '8px';
1017
+ ul.appendChild(li);
1018
+ });
1019
+ summaryText.textContent = '';
1020
+ summaryText.appendChild(ul);
1021
+ } else {
1022
+ const p = document.createElement('p');
1023
+ p.textContent = summaryContent;
1024
+ summaryText.textContent = '';
1025
+ summaryText.appendChild(p);
1026
+ }
1027
+
1028
+ // Item 6: Summary stats
1029
+ if (typeof updateSummaryStats === 'function') {
1030
+ updateSummaryStats(summaryContent);
1031
+ }
1032
  } else {
1033
  throw new Error(data.error || 'لم يتم توليد ملخص');
1034
  }
 
1164
  initUI();
1165
  initEditor();
1166
  if (typeof initFormatToolbar === 'function') initFormatToolbar();
1167
+ if (typeof initDocSearch === 'function') initDocSearch();
1168
  initDocuments();
1169
  updateSuggestionsList([]);
1170
  // Phase 7 — Sync System
src/js/editor.js CHANGED
@@ -71,6 +71,10 @@ function updateEditorStats() {
71
  if (wordCountEl) {
72
  wordCountEl.textContent = words.toLocaleString('ar-EG');
73
  }
 
 
 
 
74
  }
75
 
76
  function updatePlaceholder() {
 
71
  if (wordCountEl) {
72
  wordCountEl.textContent = words.toLocaleString('ar-EG');
73
  }
74
+ // Item 4: Enhanced stats
75
+ if (typeof updateEnhancedStats === 'function') {
76
+ updateEnhancedStats();
77
+ }
78
  }
79
 
80
  function updatePlaceholder() {
src/js/format.js CHANGED
@@ -197,4 +197,118 @@ function initFormatToolbar() {
197
  document.addEventListener('keydown', (e) => {
198
  if (e.key === 'Escape') closeAllFmtDropdowns();
199
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  }
 
197
  document.addEventListener('keydown', (e) => {
198
  if (e.key === 'Escape') closeAllFmtDropdowns();
199
  });
200
+
201
+ // Item 8: Color pickers
202
+ initColorPicker('fmt-textcolor', 'foreColor', 'fmt-textcolor-bar');
203
+ initColorPicker('fmt-highlight', 'hiliteColor', 'fmt-highlight-bar');
204
+ }
205
+
206
+ /* ── Item 8: Color Picker ── */
207
+ const COLOR_PALETTE = [
208
+ '#ECEEF2', '#E88A8A', '#E4B35A', '#6BC98A', '#6BA3E0', '#A594E8',
209
+ '#F5F5F5', '#FF6B6B', '#FFD93D', '#51CF66', '#339AF0', '#845EF7',
210
+ '#ADB5BD', '#C92A2A', '#F08C00', '#2B8A3E', '#1864AB', '#5F3DC4',
211
+ '#495057', '#862E2E', '#B7791F', '#1B5E20', '#0D47A1', '#311B92',
212
+ '#212529', '#000000', '#5D4037', '#004D40', '#1A237E', '#4A148C',
213
+ ];
214
+
215
+ function initColorPicker(prefix, command, barId) {
216
+ const trigger = document.getElementById(prefix + '-trigger');
217
+ const wrap = document.getElementById(prefix + '-wrap');
218
+ const grid = document.getElementById(prefix + '-grid');
219
+ if (!trigger || !wrap || !grid) return;
220
+
221
+ // Build swatches
222
+ COLOR_PALETTE.forEach(color => {
223
+ const swatch = document.createElement('button');
224
+ swatch.type = 'button';
225
+ swatch.className = 'fmt-color-swatch';
226
+ swatch.style.background = color;
227
+ swatch.title = color;
228
+ swatch.addEventListener('click', () => {
229
+ document.execCommand(command, false, color);
230
+ const bar = document.getElementById(barId);
231
+ if (bar) bar.style.background = color;
232
+ closeAllFmtDropdowns();
233
+ const editor = getEditorElement();
234
+ if (editor) editor.focus();
235
+ });
236
+ grid.appendChild(swatch);
237
+ });
238
+
239
+ // Toggle
240
+ trigger.addEventListener('click', (e) => {
241
+ e.stopPropagation();
242
+ toggleFmtDropdown(prefix + '-wrap');
243
+ });
244
+ }
245
+
246
+ /* ── Item 4: Enhanced Stats ── */
247
+ function updateEnhancedStats() {
248
+ const text = getEditorText();
249
+ const charCount = text.length;
250
+ const sentences = text.split(/[.!?。؟!\.]+/).filter(s => s.trim().length > 0).length;
251
+ const words = text.trim().split(/\s+/).filter(w => w.length > 0).length;
252
+ const readingTimeMinutes = Math.max(1, Math.ceil(words / 200));
253
+
254
+ const charEl = document.getElementById('char-count');
255
+ const sentEl = document.getElementById('sentence-count');
256
+ const readEl = document.getElementById('reading-time');
257
+
258
+ if (charEl) charEl.textContent = charCount.toLocaleString('ar-EG');
259
+ if (sentEl) sentEl.textContent = sentences.toLocaleString('ar-EG');
260
+ if (readEl) readEl.textContent = readingTimeMinutes.toLocaleString('ar-EG');
261
+ }
262
+
263
+ /* ── Item 6: Summary Stats ── */
264
+ function updateSummaryStats(summaryText) {
265
+ const originalText = getEditorText();
266
+ const summaryWords = summaryText.trim().split(/\s+/).filter(w => w.length > 0).length;
267
+ const originalWords = originalText.trim().split(/\s+/).filter(w => w.length > 0).length;
268
+ const compression = originalWords > 0 ? Math.round((1 - summaryWords / originalWords) * 100) : 0;
269
+
270
+ const statsEl = document.getElementById('summary-stats');
271
+ const wordCountEl = document.getElementById('summary-word-count');
272
+ const compressionEl = document.getElementById('summary-compression');
273
+
274
+ if (statsEl) statsEl.style.display = 'flex';
275
+ if (wordCountEl) wordCountEl.textContent = summaryWords;
276
+ if (compressionEl) compressionEl.textContent = compression + '%';
277
+ }
278
+
279
+ /* ── Item 11: Summary Mode ── */
280
+ window._summaryMode = 'paragraph';
281
+
282
+ function setSummaryMode(mode) {
283
+ window._summaryMode = mode;
284
+ document.querySelectorAll('.summary-mode-btn').forEach(btn => {
285
+ btn.classList.toggle('active', btn.id === 'summary-mode-' + mode);
286
+ });
287
+ }
288
+
289
+ /* ── Item 3: Empty States ── */
290
+ function renderEmptyState(container, icon, title, desc) {
291
+ if (!container) return;
292
+ container.innerHTML = `
293
+ <div class="empty-state">
294
+ <div class="empty-state__icon">${icon}</div>
295
+ <div class="empty-state__title">${title}</div>
296
+ <div class="empty-state__desc">${desc}</div>
297
+ </div>
298
+ `;
299
+ }
300
+
301
+ /* ── Item 7: Document Search ── */
302
+ function initDocSearch() {
303
+ const searchInput = document.getElementById('docs-search-input');
304
+ if (!searchInput) return;
305
+
306
+ searchInput.addEventListener('input', () => {
307
+ const query = searchInput.value.trim().toLowerCase();
308
+ const items = document.querySelectorAll('.doc-item');
309
+ items.forEach(item => {
310
+ const title = (item.textContent || '').toLowerCase();
311
+ item.style.display = title.includes(query) || !query ? '' : 'none';
312
+ });
313
+ });
314
  }
src/js/theme.js CHANGED
@@ -18,10 +18,7 @@ function updateThemeToggleIcon(theme) {
18
  'aria-label',
19
  isDark ? 'التبديل إلى الوضع الفاتح' : 'التبديل إلى الوضع الداكن'
20
  );
21
-
22
- btn.innerHTML = isDark
23
- ? '<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><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>'
24
- : '<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><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>';
25
  }
26
 
27
  function clearThemePaletteOverrides() {
 
18
  'aria-label',
19
  isDark ? 'التبديل إلى الوضع الفاتح' : 'التبديل إلى الوضع الداكن'
20
  );
21
+ // CSS handles the sun/moon icon transitions via [data-theme] selectors
 
 
 
22
  }
23
 
24
  function clearThemePaletteOverrides() {
src/js/ui.js CHANGED
@@ -101,12 +101,13 @@ function updateSuggestionsList(suggestions) {
101
  if (!suggestions || suggestions.length === 0) {
102
  const emptyHTML = `
103
  <div class="empty-state">
104
- <svg class="empty-state-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
105
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
106
- d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
107
- </svg>
108
- <p class="text-base font-semibold">لا توجد اقتراحات</p>
109
- <p class="text-sm mt-1">ابدأ بكتابة جملة عربية</p>
 
110
  </div>`;
111
 
112
  lists.forEach((el) => { el.innerHTML = emptyHTML; });
 
101
  if (!suggestions || suggestions.length === 0) {
102
  const emptyHTML = `
103
  <div class="empty-state">
104
+ <div class="empty-state__icon">
105
+ <svg width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24">
106
+ <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"/>
107
+ </svg>
108
+ </div>
109
+ <div class="empty-state__title">لا توجد اقتراحات</div>
110
+ <div class="empty-state__desc">ابدأ بكتابة نص عربي وسيتم تحليله تلقائياً</div>
111
  </div>`;
112
 
113
  lists.forEach((el) => { el.innerHTML = emptyHTML; });