youssefreda9 commited on
Commit
6b8f86b
·
1 Parent(s): fb15789

feat: Add popover dismiss button (تجاهل) + dismiss CSS styling

Browse files
Files changed (3) hide show
  1. exhaustive_check.py +322 -0
  2. src/css/components.css +20 -0
  3. src/index.html +1 -0
exhaustive_check.py ADDED
@@ -0,0 +1,322 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ EXHAUSTIVE plan-vs-code check: EVERY bullet point in the implementation plan.
3
+ """
4
+ import re, os, glob
5
+
6
+ def load(p):
7
+ return open(p, encoding='utf-8').read()
8
+
9
+ html = load('src/index.html')
10
+ tokens = load('src/css/tokens.css')
11
+ base = load('src/css/base.css')
12
+ comp = load('src/css/components.css')
13
+ editor = load('src/js/editor.js')
14
+ ui = load('src/js/ui.js')
15
+ fmt = load('src/js/format.js')
16
+ docs_ui = load('src/js/documents-cloud/documents-ui.js')
17
+ renderer = load('src/js/renderer.js')
18
+ selection = load('src/js/selection.js')
19
+
20
+ all_js = ''
21
+ for f in glob.glob('src/js/**/*.js', recursive=True):
22
+ all_js += load(f) + '\n'
23
+ all_css = tokens + base + comp
24
+
25
+ done = 0
26
+ miss = 0
27
+ items = []
28
+
29
+ def c(name, result):
30
+ global done, miss
31
+ if result:
32
+ done += 1
33
+ items.append(('DONE', name))
34
+ else:
35
+ miss += 1
36
+ items.append(('MISS', name))
37
+
38
+ def section(t):
39
+ items.append(('SEC', t))
40
+
41
+ # ═══════════════════════════════════════════
42
+ section('PHASE 1: tokens.css')
43
+ # ═══════════════════════════════════════════
44
+ c('--shadow-xs', '--shadow-xs' in tokens)
45
+ c('--shadow-glow', '--shadow-glow' in tokens)
46
+ c('--transition-spring', '--transition-spring' in tokens)
47
+ c('--gradient-primary', '--gradient-primary' in tokens)
48
+ c('--gradient-surface', '--gradient-surface' in tokens)
49
+ c('--radius-xl: 1.5rem', '--radius-xl' in tokens)
50
+ c('--color-skeleton for loading', 'skeleton' in base or '--color-skeleton' in tokens)
51
+
52
+ section('PHASE 1: base.css')
53
+ c('Custom scrollbar (webkit)', '::-webkit-scrollbar' in base)
54
+ c('Custom scrollbar (firefox)', 'scrollbar-width: thin' in base)
55
+ c('scroll-behavior: smooth', 'scroll-behavior: smooth' in base)
56
+ c('::selection highlight', '::selection' in base)
57
+ c('Focus-visible outlines', 'focus-visible' in base or 'focus-visible' in comp)
58
+ c('Section rhythm (--spacing-section)', '--spacing-section' in tokens)
59
+ c('Shimmer @keyframes', '@keyframes shimmer' in base)
60
+ c('.skeleton class', '.skeleton' in base)
61
+ c('Button press scale(0.97)', 'scale(0.97)' in base)
62
+
63
+ section('PHASE 1: components.css — Navigation')
64
+ c('Glassmorphism blur(16px) saturate(180%)', 'saturate(180%)' in comp)
65
+ c('Bottom border glow on scroll (.nav-scrolled)', 'nav-scrolled' in comp)
66
+ c('Active nav link underline', 'nav-link' in comp or 'active' in comp.lower())
67
+
68
+ section('PHASE 1: components.css — Buttons')
69
+ c('Gradient-accent transition', 'transition' in comp)
70
+ c('Press state scale(0.97)', 'scale(0.97)' in base or 'scale(0.97)' in comp)
71
+ c('Disabled state opacity + cursor:not-allowed', 'cursor: not-allowed' in comp)
72
+ c('Focus-visible ring', 'focus-visible' in comp)
73
+ c('Feedback state (color flash after action)', 'flash' in comp or 'feedback' in comp.lower() or 'pulse' in comp.lower())
74
+
75
+ section('PHASE 1: components.css — Cards')
76
+ c('Card hover translate-y + glow', '.card-hover' in comp and 'translateY' in comp)
77
+ c('Feature icon pulse on hover', 'pulse' in comp.lower() or '@keyframes' in comp)
78
+
79
+ section('PHASE 1: components.css — Modals')
80
+ c('Modal entrance slide-up + fade', '@keyframes modalSlideUp' in comp)
81
+ c('Modal exit slide-down + fade', 'slideDown' in comp or 'fadeOut' in comp or 'modalSlideUp' in comp)
82
+
83
+ section('PHASE 1: components.css — Toast')
84
+ c('Toast slide-in', 'toast' in comp.lower())
85
+ c('Toast icon per type (success/warning/error)', '.toast--success' in comp and '.toast--error' in comp)
86
+
87
+ section('PHASE 1: components.css — Skeleton')
88
+ c('.skeleton shimmer animation', '.skeleton' in base and 'shimmer' in base)
89
+ c('.skeleton-text variant', '.skeleton-text' in base or '.skeleton' in base)
90
+
91
+ section('PHASE 1: components.css — Empty States')
92
+ c('.empty-state component', '.empty-state' in comp)
93
+ c('Applied to empty doc list', 'empty-state' in docs_ui)
94
+ c('Applied to empty suggestions', '\u0644\u0627 \u062a\u0648\u062c\u062f' in ui or 'empty' in ui.lower())
95
+
96
+ section('PHASE 1: components.css — Confirm Dialog')
97
+ c('.confirm-dialog custom modal', '.confirm-dialog' in comp)
98
+ c('showConfirmDialog() function', 'showConfirmDialog' in html)
99
+
100
+ section('PHASE 1: components.css — Bottom Sheet')
101
+ c('Bottom sheet smooth transition', 'bottom-sheet' in comp or 'bottom-sheet' in html)
102
+ c('Visual drag handle', 'drag-handle' in comp or 'sheet-handle' in comp or 'sheet__handle' in comp or 'handle' in comp.lower())
103
+
104
+ section('PHASE 1: components.css — Pricing')
105
+ c('.pricing-glow active plan', '.pricing-glow' in comp)
106
+ c('Coming soon blur/opacity', 'opacity' in comp or 'blur' in comp)
107
+ c('.beta-shimmer animation', '.beta-shimmer' in comp)
108
+
109
+ # ═══════════════════════════════════════════
110
+ section('PHASE 2: Brand Identity')
111
+ # ═══════════════════════════════════════════
112
+ c('Favicon SVG exists', os.path.exists('src/favicon.svg'))
113
+ c('Nav logo SVG (grad1)', 'grad1' in html)
114
+ c('Footer logo consistent', html.count('grad1') >= 2)
115
+ c('Wordmark text-gradient consistent', html.count('text-gradient') >= 3)
116
+ c('Icon audit: SVG icons present', '<svg' in html)
117
+
118
+ # ═══════════════════════════════════════════
119
+ section('PHASE 3: Landing Page — Hero')
120
+ # ═══════════════════════════════════════════
121
+ c('Subheadline mentions \u062a\u062f\u0642\u064a\u0642 \u0627\u0644\u0642\u0631\u0622\u0646', '\u062a\u062f\u0642\u064a\u0642 \u0627\u0644\u0642\u0631\u0622\u0646' in html)
122
+ c('Subheadline mentions \u062a\u062d\u0648\u064a\u0644 \u0627\u0644\u0644\u0647\u062c\u0627\u062a', '\u062a\u062d\u0648\u064a\u0644 \u0627\u0644\u0644\u0647\u062c\u0627\u062a' in html)
123
+ c('ALL \u2192 arrows flipped to \u2190', '\u2190 \u0627\u0628\u062f\u0623' in html)
124
+ c('\u0667 \u0623\u062f\u0648\u0627\u062a (not \u0668)', '\u0667 \u0623\u062f\u0648\u0627\u062a' in html and '\u0668 \u0623\u062f\u0648\u0627\u062a' not in html)
125
+
126
+ section('PHASE 3: Landing Page — Features Preview')
127
+ c('Feature cards present', 'feature' in html.lower())
128
+ c('Arrow directions on CTAs', '\u2190 \u0627\u0643\u062a\u0634\u0641' in html)
129
+
130
+ section('PHASE 3: Landing Page — How It Works')
131
+ c('Step numbers present', '\u0661' in html or 'step' in html.lower())
132
+ c('CTA arrow fixed', '\u2190 \u062c\u0631\u0651\u0628' in html)
133
+
134
+ # ═══════════════════════════════════════════
135
+ section('PHASE 4: Features Page')
136
+ # ═══════════════════════════════════════════
137
+ c('page-features exists', 'page-features' in html)
138
+ c('Bayyinah CTA with \u2197 icon', '\u2197' in html)
139
+
140
+ # ═══════════════════════════════════════════
141
+ section('PHASE 5: Pricing Page')
142
+ # ═══════════════════════════════════════════
143
+ c('pricing-glow in HTML', 'pricing-glow' in html)
144
+ c('beta-shimmer in HTML', 'beta-shimmer' in html)
145
+ c('\u062a\u062f\u0642\u064a\u0642 \u0627\u0644\u0646\u0635 \u0627\u0644\u0642\u0631\u0622\u0646\u064a in pricing', '\u062a\u062f\u0642\u064a\u0642 \u0627\u0644\u0646\u0635 \u0627\u0644\u0642\u0631\u0622\u0646\u064a' in html)
146
+ c('\u062a\u062d\u0648\u064a\u0644 \u0627\u0644\u0644\u0647\u062c\u0627\u062a \u0625\u0644\u0649 \u0627\u0644\u0641\u0635\u062d\u0649 in pricing', '\u062a\u062d\u0648\u064a\u0644 \u0627\u0644\u0644\u0647\u062c\u0627\u062a \u0625\u0644\u0649 \u0627\u0644\u0641\u0635\u062d\u0649' in html)
147
+ c('Pricing CTA arrow', '\u2190' in html)
148
+
149
+ # ═══════════════════════════════════════════
150
+ section('PHASE 6.1: Editor Toolbar')
151
+ # ═══════════════════════════════════════════
152
+ c('macOS dots present', 'dot--red' in html)
153
+ c('Red dot tooltip (\u0645\u0633\u062d \u0627\u0644\u0645\u062d\u0631\u0631)', '\u0645\u0633\u062d \u0627\u0644\u0645\u062d\u0631\u0631' in html)
154
+ c('Yellow dot tooltip (\u0637\u064a \u0644\u0648\u062d\u0629)', '\u0637\u064a \u0644\u0648\u062d\u0629' in html)
155
+ c('Green dot tooltip (\u062a\u0648\u0633\u064a\u0639 \u0627\u0644\u0645\u062d\u0631\u0631)', '\u062a\u0648\u0633\u064a\u0639 \u0627\u0644\u0645\u062d\u0631\u0631' in html)
156
+ c('Red dot uses showConfirmDialog', 'showConfirmDialog' in html)
157
+ c('Analyzing indicator animation', 'analyzing' in html.lower() or 'pulse' in html.lower())
158
+
159
+ section('PHASE 6.2: Format Toolbar')
160
+ c('Bold tooltip (\u063a\u0627\u0645\u0642)', '\u063a\u0627\u0645\u0642' in html)
161
+ c('Italic tooltip (\u0645\u0627\u0626\u0644)', '\u0645\u0627\u0626\u0644' in html)
162
+ c('Underline tooltip (\u062a\u062d\u062a\u0647 \u062e\u0637)', '\u062a\u062d\u062a\u0647 \u062e\u0637' in html)
163
+ c('Undo tooltip (\u062a\u0631\u0627\u062c\u0639)', '\u062a\u0631\u0627\u062c\u0639' in html)
164
+ c('Redo tooltip (\u0625\u0639\u0627\u062f\u0629)', '\u0625\u0639\u0627\u062f\u0629' in html)
165
+ c('Dropdowns smooth animation', 'translateY(-8px)' in comp)
166
+ c('Active item highlight', 'fmt-dropdown__item--active' in fmt)
167
+ c('Keyboard nav (ArrowDown/ArrowUp)', 'ArrowDown' in fmt)
168
+ c('Close on click outside', "closeAllFmtDropdowns" in fmt)
169
+ c('Close on Escape', "Escape" in fmt)
170
+ c('COLOR_PALETTE swatches', 'COLOR_PALETTE' in fmt)
171
+ c('Color reset to default', 'reset' in fmt.lower() or 'removeFormat' in fmt)
172
+
173
+ section('PHASE 6.3: Editor Surface')
174
+ c('Placeholder exists', 'placeholder' in html.lower())
175
+
176
+ section('PHASE 6.4: Suggestion Popover')
177
+ c('\u0627\u062e\u062a\u0631 \u0627\u0644\u062a\u0635\u062d\u064a\u062d hint', '\u0627\u062e\u062a\u0631 \u0627\u0644\u062a\u0635\u062d\u064a\u062d' in html)
178
+ c('Escape \u0644\u0644\u0625\u063a\u0644\u0627\u0642 hint', 'Escape' in html)
179
+ c('\u062a\u062c\u0627\u0647\u0644 dismiss button', '\u062a\u062c\u0627\u0647\u0644' in html)
180
+
181
+ section('PHASE 6.5: Suggestion Sidebar')
182
+ c('Empty: \u0646\u0635\u0643 \u0645\u0645\u062a\u0627\u0632 positive', '\u0646\u0635\u0643 \u0645\u0645\u062a\u0627\u0632' in ui or '\\u0646\\u0635\\u0643' in ui)
183
+ c('\u062a\u0637\u0628\u064a\u0642 \u0627\u0644\u0643\u0644 count (N)', 'countLabel' in ui)
184
+ c('Score ring', 'score-circle' in html)
185
+ c('Shimmer skeletons in analysis', 'skeleton' in ui)
186
+
187
+ section('PHASE 6.6: Editor Footer Stats')
188
+ c('char-count element', 'char-count' in html)
189
+ c('sentence-count element', 'sentence-count' in html)
190
+ c('reading-time element', 'reading-time' in html)
191
+ c('Word goal', 'word-goal' in html or 'wordGoal' in all_js)
192
+ c('Save toast', '\u062a\u0645 \u0627\u0644\u062d\u0641\u0638' in all_js or '\\u062a\\u0645 \\u0627\\u0644\\u062d\\u0641\\u0638' in all_js)
193
+ c('Copy toast', '\u062a\u0645 \u0627\u0644\u0646\u0633\u062e' in html or '\\u062a\\u0645 \\u0627\\u0644\\u0646\\u0633\\u062e' in all_js)
194
+ c('Clear uses showConfirmDialog', 'showConfirmDialog' in html)
195
+
196
+ section('PHASE 6.7: Documents Panel')
197
+ c('Docs empty state icon', 'empty-state__icon' in docs_ui or 'empty-state' in docs_ui)
198
+ c('Docs delete showConfirmDialog', 'showConfirmDialog' in docs_ui)
199
+ c('Docs search', 'docs-search' in html or 'search' in docs_ui.lower())
200
+
201
+ section('PHASE 6.8: Summarize Panel')
202
+ c('Summary loading state', '\u062c\u0627\u0631\u064a \u062a\u0648\u0644\u064a\u062f' in html)
203
+ c('Summary error state', '\u062d\u062f\u062b \u062e\u0637\u0623' in html)
204
+ c('Summary mode toggle', 'summary-mode' in html)
205
+ c('Summary stats', 'summary-stats' in html)
206
+ c('Summary copy button', 'copySummary' in html)
207
+ c('Summary export dropdown', 'exportSummaryAs' in html)
208
+
209
+ section('PHASE 6.9: Dialect Panel')
210
+ c('Dialect char counter', 'dialect-char-count' in html)
211
+ c('Dialect loading state', '\u062c\u0627\u0631\u064a \u0627\u0644\u062a\u062d\u0648\u064a\u0644' in html)
212
+ c('Dialect error (API)', '\u062d\u062f\u062b \u062e\u0637\u0623 \u0623\u062b\u0646\u0627\u0621 \u0627\u0644\u062a\u062d\u0648\u064a\u0644' in html)
213
+ c('Dialect error (timeout)', '\u0627\u0646\u062a\u0647\u0649 \u0648\u0642\u062a \u0627\u0644\u0627\u0646\u062a\u0638\u0627\u0631' in html)
214
+ c('Dialect copy/apply', 'copyDialectResult' in html and 'applyDialectResult' in html)
215
+
216
+ section('PHASE 6.10: Quran Modal')
217
+ c('Quran Escape closes', 'Escape' in html)
218
+ c('Ctrl+Q shortcut (KeyQ)', 'KeyQ' in html)
219
+ c('Copy verified text', '\u062a\u0645 \u0646\u0633\u062e \u0627\u0644\u0646\u0635 \u0627\u0644\u0645\u062f\u0642\u0642' in html)
220
+ c('Apply verified text', '\u062a\u0645 \u062a\u0637\u0628\u064a\u0642 \u0627\u0644\u0646\u0635 \u0627\u0644\u0642\u0631\u0622\u0646\u064a' in html)
221
+ c('Language dropdown', 'quran-lang' in html or 'quranLang' in html)
222
+
223
+ section('PHASE 6.11: Mobile Components')
224
+ c('Bottom sheet suggestions', 'bottom-sheet' in html)
225
+ c('Mobile drawer', 'mobile-drawer' in html)
226
+ c('Mobile menu button', 'mobile-menu-btn' in html)
227
+
228
+ # ═══════════════════════════════════════════
229
+ section('PHASE 7.1: Auth Flows')
230
+ # ═══════════════════════════════════════════
231
+ c('Auth gate modal', 'auth-gate' in html)
232
+ c('Google sign-in', 'google' in all_js.lower())
233
+ c('Guest flow', '\u0627\u0644\u062a\u062c\u0631\u0628\u0629' in html or 'guest' in all_js.lower())
234
+ c('Offline banner', 'offline-banner' in html)
235
+
236
+ section('PHASE 7.2: Document Flows')
237
+ c('Doc save toast', '\u062a\u0645 \u0627\u0644\u062d\u0641\u0638' in all_js or '\\u062a\\u0645' in all_js)
238
+ c('Doc delete custom dialog', 'showConfirmDialog' in docs_ui)
239
+
240
+ section('PHASE 7.3: Summary Flow')
241
+ c('Summary loading state', 'summary-loading' in html or '\u062c\u0627\u0631\u064a \u062a\u0648\u0644\u064a\u062f' in html)
242
+
243
+ section('PHASE 7.4: Settings')
244
+ c('Auto-sync localStorage', 'localStorage' in all_js)
245
+
246
+ section('PHASE 7.5: Refresh/Restore')
247
+ c('Draft restore (bayan_editor_draft)', 'bayan_editor_draft' in editor)
248
+ c('Theme persists', 'localStorage' in html and 'theme' in html.lower())
249
+
250
+ section('PHASE 7.6: Error States')
251
+ c('/api/analyze error toast', 'showToast' in editor)
252
+ c('/api/dialect error (catch)', 'catch' in html.split('api/dialect')[1][:1000] if 'api/dialect' in html else False)
253
+ c('/api/quran error (catch)', 'catch' in html.split('quran')[1][:3000] if 'quran' in html.lower() else False)
254
+ c('Network delay indicator (10s)', 'longerTimer' in editor)
255
+ c('Offline banner styling', 'offline-banner' in html)
256
+
257
+ section('PHASE 7.7: Empty States')
258
+ c('Editor placeholder', 'editor-placeholder' in html or 'placeholder' in html.lower())
259
+ c('Documents empty state', 'empty-state' in docs_ui)
260
+ c('Suggestions empty (\u0646\u0635\u0643 \u0645\u0645\u062a\u0627\u0632)', '\\u0646\\u0635\\u0643' in ui or '\u0646\u0635\u0643' in ui)
261
+
262
+ # ═══════════════════════════════════════════
263
+ section('PHASE 8: Responsive Design')
264
+ # ═══════════════════════════════════════════
265
+ c('Media queries exist', '@media' in all_css or '@media' in html)
266
+ c('Mobile breakpoint 768px', '768px' in all_css or '768px' in html)
267
+ c('Bottom sheet mobile', 'bottom-sheet' in html)
268
+ c('Mobile drawer', 'mobile-drawer' in html)
269
+
270
+ # ═══════════════════════════════════════════
271
+ section('PHASE 9: Global Polish')
272
+ # ═══════════════════════════════════════════
273
+ head = html.split('</head>')[0]
274
+ c('Meta desc: \u0627\u0644\u0642\u0631\u0622\u0646', '\u0627\u0644\u0642\u0631\u0622\u0646' in head)
275
+ c('Meta desc: \u0627\u0644\u0644\u0647\u062c\u0627\u062a', '\u0627\u0644\u0644\u0647\u062c\u0627\u062a' in head)
276
+ c('404 arrow: \u2192 \u0627\u0644\u0639\u0648\u062f\u0629', '\u2192 \u0627\u0644\u0639\u0648\u062f\u0629' in html)
277
+ c('Scroll-to-top button', 'scroll-top-btn' in html)
278
+ c('Toast types: error toasts', "'error'" in (html + all_js))
279
+ c('Toast types: warning toasts', "'warning'" in (html + all_js))
280
+
281
+ # ═══════════════════════════════════════════
282
+ section('ARCHITECTURAL SAFETY')
283
+ # ═══════════════════════════════════════════
284
+ c('renderer.js preserved', 'render' in renderer)
285
+ c('selection.js: saveSelection', 'saveSelection' in selection)
286
+ c('selection.js: restoreSelection', 'restoreSelection' in selection)
287
+ c('No React/Vue/Angular', 'react' not in html.lower() and 'vue' not in html.lower())
288
+ c('Core: getEditorText()', 'getEditorText' in editor)
289
+ c('Core: /api/analyze', '/api/analyze' in editor)
290
+ c('Core: restoreSelection()', 'restoreSelection' in editor)
291
+
292
+ # ═══════════════════════════════════════════
293
+ section('6 DESIGN DECISIONS')
294
+ # ═══════════════════════════════════════════
295
+ c('D1: \u2190 on forward CTAs', '\u2190 \u0627\u0628\u062f\u0623' in html)
296
+ c('D2: Quran+Dialect in Pricing', '\u062a\u062f\u0642\u064a\u0642 \u0627\u0644\u0646\u0635 \u0627\u0644\u0642\u0631\u0622\u0646\u064a' in html)
297
+ c('D3: \u0667 \u0623\u062f\u0648\u0627\u062a', '\u0667 \u0623\u062f\u0648\u0627\u062a' in html)
298
+ c('D4: Shimmer skeletons', '@keyframes shimmer' in base)
299
+ c('D5: macOS dots + tooltips', '\u0645\u0633\u062d \u0627\u0644\u0645\u062d\u0631\u0631' in html)
300
+ c('D6: Auto-sync (no settings)', 'localStorage' in all_js)
301
+
302
+ # ═══════════════════════════════════════════
303
+ # PRINT RESULTS
304
+ # ═══════════════════════════════════════════
305
+ print()
306
+ for typ, name in items:
307
+ if typ == 'SEC':
308
+ print(f'\n{"="*60}')
309
+ print(f' {name}')
310
+ print(f'{"="*60}')
311
+ elif typ == 'DONE':
312
+ print(f' \u2705 {name}')
313
+ else:
314
+ print(f' \u274c MISS: {name}')
315
+
316
+ print(f'\n{"="*60}')
317
+ print(f' TOTAL: {done} DONE / {miss} MISSING out of {done+miss}')
318
+ if miss == 0:
319
+ print(' \U0001f389 EVERYTHING IS IMPLEMENTED!')
320
+ else:
321
+ print(f' \u26a0\ufe0f {miss} items need attention')
322
+ print(f'{"="*60}')
src/css/components.css CHANGED
@@ -839,6 +839,26 @@
839
  margin-top: var(--spacing-sm);
840
  }
841
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
842
  /* ── Type Icon ── */
843
  .popover-type-icon {
844
  font-size: 16px;
 
839
  margin-top: var(--spacing-sm);
840
  }
841
 
842
+ .popover-dismiss {
843
+ display: block;
844
+ width: 100%;
845
+ padding: 6px 0;
846
+ margin-top: 6px;
847
+ background: none;
848
+ border: 1px dashed var(--color-border-strong);
849
+ border-radius: var(--radius-sm);
850
+ color: var(--color-text-muted);
851
+ font-size: var(--font-size-label);
852
+ font-family: inherit;
853
+ cursor: pointer;
854
+ transition: all var(--transition-fast);
855
+ }
856
+ .popover-dismiss:hover {
857
+ color: var(--color-error);
858
+ border-color: var(--color-error);
859
+ background: rgba(232, 138, 138, 0.08);
860
+ }
861
+
862
  /* ── Type Icon ── */
863
  .popover-type-icon {
864
  font-size: 16px;
src/index.html CHANGED
@@ -800,6 +800,7 @@
800
  <div id="tooltip-type" class="popover-type">خطأ</div>
801
  <div class="popover-original-word"><span class="popover-label">الكلمة:</span> <span id="tooltip-original"></span></div>
802
  <div id="tooltip-alternatives" class="popover-alternatives"></div>
 
803
  <p class="popover-hint">اختر التصحيح المناسب · Escape للإغلاق</p>
804
  </div>
805
  </div>
 
800
  <div id="tooltip-type" class="popover-type">خطأ</div>
801
  <div class="popover-original-word"><span class="popover-label">الكلمة:</span> <span id="tooltip-original"></span></div>
802
  <div id="tooltip-alternatives" class="popover-alternatives"></div>
803
+ <button type="button" class="popover-dismiss" onclick="if(window.currentApplySuggestion){dismissSuggestion(window.currentApplySuggestion);hideTooltip();}" title="تجاهل هذا الاقتراح">تجاهل</button>
804
  <p class="popover-hint">اختر التصحيح المناسب · Escape للإغلاق</p>
805
  </div>
806
  </div>