sondakika 2.0.0 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/generate-index.js DELETED
@@ -1,1136 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- const emoji = {
5
- newspaper: '\uD83D\uDCF0',
6
- refresh: '\uD83D\uDD04',
7
- empty: '\uD83D\uDCFD',
8
- clock: '\uD83D\uDD52',
9
- link: '\uD83D\uDD17',
10
- leftArrow: '\u2190',
11
- rightArrow: '\u2192',
12
- upArrow: '\u2191',
13
- downArrow: '\u2193',
14
- laquo: '\u00AB',
15
- raquo: '\u00BB',
16
- lsquo: '\u2039',
17
- rsquo: '\u203A',
18
- bullet: '\u2022',
19
- check: '\u2713'
20
- };
21
-
22
- const html = `<!DOCTYPE html>
23
- <html lang="tr">
24
- <head>
25
- <meta charset="UTF-8">
26
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
27
- <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;">
28
- <title>Sondakika - Haber Okuyucu</title>
29
- <link rel="preconnect" href="https://fonts.googleapis.com">
30
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
31
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
32
- <style>
33
- * { margin:0; padding:0; box-sizing: border-box; }
34
-
35
- :root {
36
- --bg-primary: #0f0f1a;
37
- --bg-secondary: #1a1a2e;
38
- --bg-card: #252542;
39
- --bg-elevated: #2d2d4a;
40
- --accent-primary: #6366f1;
41
- --accent-secondary: #818cf8;
42
- --accent-hover: #4f46e5;
43
- --text-primary: #f1f5f9;
44
- --text-secondary: #94a3b8;
45
- --text-muted: #64748b;
46
- --border-color: #334155;
47
- --success: #10b981;
48
- --warning: #f59e0b;
49
- --error: #ef4444;
50
- --breaking: #ef4444;
51
- --selected-bg: rgba(99, 102, 241, 0.25);
52
- --selected-border: #6366f1;
53
- }
54
-
55
- .theme-light {
56
- --bg-primary: #f8fafc;
57
- --bg-secondary: #ffffff;
58
- --bg-card: #f1f5f9;
59
- --bg-elevated: #e2e8f0;
60
- --accent-primary: #6366f1;
61
- --accent-secondary: #818cf8;
62
- --accent-hover: #4f46e5;
63
- --text-primary: #0f172a;
64
- --text-secondary: #475569;
65
- --text-muted: #94a3b8;
66
- --border-color: #e2e8f0;
67
- --success: #10b981;
68
- --warning: #f59e0b;
69
- --error: #ef4444;
70
- --breaking: #dc2626;
71
- --selected-bg: rgba(99, 102, 241, 0.15);
72
- --selected-border: #6366f1;
73
- }
74
-
75
- body {
76
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
77
- background: var(--bg-primary);
78
- color: var(--text-primary);
79
- min-height: 100vh;
80
- overflow-x: hidden;
81
- }
82
-
83
- .app-container {
84
- display: flex;
85
- min-height: 100vh;
86
- }
87
-
88
- .sidebar {
89
- width: 320px;
90
- background: var(--bg-secondary);
91
- border-right: 1px solid var(--border-color);
92
- display: flex;
93
- flex-direction: column;
94
- position: fixed;
95
- height: 100vh;
96
- overflow-y: auto;
97
- }
98
-
99
- .sidebar-header {
100
- padding: 24px 20px;
101
- border-bottom: 1px solid var(--border-color);
102
- }
103
-
104
- .logo {
105
- display: flex;
106
- align-items: center;
107
- gap: 12px;
108
- }
109
-
110
- .logo-icon {
111
- width: 42px;
112
- height: 42px;
113
- background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
114
- border-radius: 12px;
115
- display: flex;
116
- align-items: center;
117
- justify-content: center;
118
- font-size: 22px;
119
- }
120
-
121
- .logo-text {
122
- font-size: 20px;
123
- font-weight: 700;
124
- background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
125
- -webkit-background-clip: text;
126
- -webkit-text-fill-color: transparent;
127
- }
128
-
129
- .sidebar-section {
130
- padding: 20px;
131
- border-bottom: 1px solid var(--border-color);
132
- }
133
-
134
- .section-title {
135
- font-size: 11px;
136
- font-weight: 600;
137
- text-transform: uppercase;
138
- letter-spacing: 1.2px;
139
- color: var(--text-muted);
140
- margin-bottom: 16px;
141
- }
142
-
143
- .sources-list {
144
- display: flex;
145
- flex-direction: column;
146
- gap: 8px;
147
- }
148
-
149
- .source-item {
150
- display: flex;
151
- align-items: center;
152
- gap: 12px;
153
- padding: 12px 14px;
154
- background: var(--bg-card);
155
- border-radius: 10px;
156
- cursor: pointer;
157
- transition: all 0.2s ease;
158
- border: 1px solid transparent;
159
- }
160
-
161
- .source-item:hover {
162
- background: var(--bg-elevated);
163
- border-color: var(--border-color);
164
- }
165
-
166
- .source-item.selected {
167
- background: rgba(99, 102, 241, 0.15);
168
- border-color: var(--accent-primary);
169
- }
170
-
171
- .source-item input[type="checkbox"] {
172
- width: 18px;
173
- height: 18px;
174
- border-radius: 5px;
175
- border: 2px solid var(--border-color);
176
- background: transparent;
177
- cursor: pointer;
178
- appearance: none;
179
- display: flex;
180
- align-items: center;
181
- justify-content: center;
182
- transition: all 0.2s ease;
183
- flex-shrink: 0;
184
- }
185
-
186
- .source-item input[type="checkbox"]:checked {
187
- background: var(--accent-primary);
188
- border-color: var(--accent-primary);
189
- }
190
-
191
- .source-item input[type="checkbox"]:checked::after {
192
- content: '${emoji.check}';
193
- color: white;
194
- font-size: 12px;
195
- font-weight: bold;
196
- }
197
-
198
- .source-name {
199
- font-size: 14px;
200
- font-weight: 500;
201
- }
202
-
203
- .source-badge {
204
- margin-left: auto;
205
- padding: 3px 8px;
206
- background: rgba(239, 68, 68, 0.15);
207
- color: var(--breaking);
208
- font-size: 10px;
209
- font-weight: 600;
210
- border-radius: 4px;
211
- text-transform: uppercase;
212
- }
213
-
214
- .options-grid {
215
- display: grid;
216
- gap: 12px;
217
- }
218
-
219
- .option-item {
220
- display: flex;
221
- align-items: center;
222
- justify-content: space-between;
223
- padding: 14px 16px;
224
- background: var(--bg-card);
225
- border-radius: 10px;
226
- border: 1px solid transparent;
227
- transition: all 0.2s ease;
228
- }
229
-
230
- .option-item:hover {
231
- border-color: var(--border-color);
232
- }
233
-
234
- .option-label {
235
- font-size: 14px;
236
- font-weight: 500;
237
- }
238
-
239
- .option-select {
240
- background: var(--bg-elevated);
241
- border: 1px solid var(--border-color);
242
- color: var(--text-primary);
243
- padding: 8px 14px;
244
- border-radius: 8px;
245
- font-size: 13px;
246
- font-family: inherit;
247
- cursor: pointer;
248
- outline: none;
249
- }
250
-
251
- .option-select:focus {
252
- border-color: var(--accent-primary);
253
- }
254
-
255
- .font-size-controls {
256
- display: flex;
257
- align-items: center;
258
- gap: 8px;
259
- }
260
-
261
- .font-btn {
262
- width: 32px;
263
- height: 32px;
264
- background: var(--bg-elevated);
265
- border: 1px solid var(--border-color);
266
- border-radius: 6px;
267
- color: var(--text-primary);
268
- font-size: 12px;
269
- font-weight: 600;
270
- cursor: pointer;
271
- transition: all 0.2s ease;
272
- }
273
-
274
- .font-btn:hover {
275
- background: var(--accent-primary);
276
- border-color: var(--accent-primary);
277
- }
278
-
279
- .font-size-value {
280
- font-size: 14px;
281
- font-weight: 600;
282
- min-width: 40px;
283
- text-align: center;
284
- }
285
-
286
- .main-content {
287
- flex: 1;
288
- margin-left: 320px;
289
- padding: 32px 40px;
290
- min-height: 100vh;
291
- }
292
-
293
- .content-header {
294
- display: flex;
295
- align-items: center;
296
- justify-content: space-between;
297
- margin-bottom: 32px;
298
- }
299
-
300
- .header-left {
301
- display: flex;
302
- flex-direction: column;
303
- gap: 6px;
304
- }
305
-
306
- .page-title {
307
- font-size: 28px;
308
- font-weight: 700;
309
- }
310
-
311
- .page-subtitle {
312
- font-size: 14px;
313
- color: var(--text-secondary);
314
- }
315
-
316
- .header-actions {
317
- display: flex;
318
- align-items: center;
319
- gap: 16px;
320
- }
321
-
322
- .refresh-btn {
323
- display: flex;
324
- align-items: center;
325
- gap: 8px;
326
- padding: 12px 20px;
327
- background: var(--accent-primary);
328
- color: white;
329
- border: none;
330
- border-radius: 10px;
331
- font-size: 14px;
332
- font-weight: 600;
333
- cursor: pointer;
334
- transition: all 0.2s ease;
335
- font-family: inherit;
336
- }
337
-
338
- .refresh-btn:hover {
339
- background: var(--accent-hover);
340
- transform: translateY(-1px);
341
- }
342
-
343
- .refresh-btn:disabled {
344
- opacity: 0.6;
345
- cursor: not-allowed;
346
- transform: none;
347
- }
348
-
349
- .status-badge {
350
- display: flex;
351
- align-items: center;
352
- gap: 8px;
353
- padding: 10px 16px;
354
- background: var(--bg-card);
355
- border-radius: 8px;
356
- font-size: 13px;
357
- }
358
-
359
- .status-dot {
360
- width: 8px;
361
- height: 8px;
362
- border-radius: 50%;
363
- background: var(--success);
364
- }
365
-
366
- .status-dot.loading {
367
- background: var(--warning);
368
- animation: pulse 1s infinite;
369
- }
370
-
371
- @keyframes pulse {
372
- 0%, 100% { opacity: 1; }
373
- 50% { opacity: 0.4; }
374
- }
375
-
376
- .news-grid {
377
- display: grid;
378
- gap: 20px;
379
- }
380
-
381
- .news-card {
382
- background: var(--bg-card);
383
- border-radius: 16px;
384
- padding: 24px;
385
- border: 2px solid var(--border-color);
386
- transition: all 0.25s ease;
387
- cursor: pointer;
388
- }
389
-
390
- .news-card:hover {
391
- transform: translateY(-4px);
392
- border-color: var(--accent-primary);
393
- box-shadow: 0 12px 40px rgba(99, 102, 241, 0.15);
394
- }
395
-
396
- .news-card.selected {
397
- background: var(--selected-bg);
398
- border-color: var(--selected-border);
399
- box-shadow: 0 0 20px rgba(99, 102, 241, 0.3);
400
- }
401
-
402
- .news-card-header {
403
- display: flex;
404
- align-items: center;
405
- justify-content: space-between;
406
- margin-bottom: 14px;
407
- }
408
-
409
- .news-source {
410
- display: flex;
411
- align-items: center;
412
- gap: 10px;
413
- }
414
-
415
- .source-tag {
416
- padding: 5px 12px;
417
- background: var(--bg-elevated);
418
- border-radius: 6px;
419
- font-size: 12px;
420
- font-weight: 600;
421
- color: var(--text-secondary);
422
- }
423
-
424
- .source-tag.breaking {
425
- background: rgba(239, 68, 68, 0.15);
426
- color: var(--breaking);
427
- }
428
-
429
- .news-date-badge {
430
- background: rgba(99, 102, 241, 0.15);
431
- color: var(--accent-secondary);
432
- padding: 6px 12px;
433
- border-radius: 8px;
434
- font-weight: 600;
435
- font-size: 12px;
436
- display: flex;
437
- align-items: center;
438
- gap: 6px;
439
- }
440
-
441
- .external-link-btn {
442
- display: flex;
443
- align-items: center;
444
- gap: 6px;
445
- padding: 8px 14px;
446
- background: var(--bg-elevated);
447
- border: 1px solid var(--border-color);
448
- border-radius: 8px;
449
- color: var(--text-secondary);
450
- font-size: 12px;
451
- font-weight: 500;
452
- cursor: pointer;
453
- transition: all 0.2s ease;
454
- text-decoration: none;
455
- }
456
-
457
- .external-link-btn:hover {
458
- background: var(--accent-primary);
459
- color: white;
460
- border-color: var(--accent-primary);
461
- }
462
-
463
- .news-title {
464
- font-size: 17px;
465
- font-weight: 600;
466
- line-height: 1.5;
467
- margin-bottom: 12px;
468
- color: var(--text-primary);
469
- }
470
-
471
- .news-summary {
472
- font-size: 14px;
473
- line-height: 1.7;
474
- color: var(--text-secondary);
475
- display: -webkit-box;
476
- -webkit-line-clamp: 3;
477
- -webkit-box-orient: vertical;
478
- overflow: hidden;
479
- }
480
-
481
- .news-card-footer {
482
- display: flex;
483
- align-items: center;
484
- justify-content: space-between;
485
- margin-top: 18px;
486
- padding-top: 16px;
487
- border-top: 1px solid var(--border-color);
488
- }
489
-
490
- .read-more {
491
- font-size: 13px;
492
- font-weight: 600;
493
- color: var(--accent-primary);
494
- display: flex;
495
- align-items: center;
496
- gap: 6px;
497
- }
498
-
499
- .pagination {
500
- display: flex;
501
- align-items: center;
502
- justify-content: center;
503
- gap: 10px;
504
- margin-top: 40px;
505
- }
506
-
507
- .page-btn {
508
- display: flex;
509
- align-items: center;
510
- justify-content: center;
511
- width: 44px;
512
- height: 44px;
513
- background: var(--bg-card);
514
- border: 1px solid var(--border-color);
515
- border-radius: 10px;
516
- color: var(--text-primary);
517
- font-size: 14px;
518
- font-weight: 600;
519
- cursor: pointer;
520
- transition: all 0.2s ease;
521
- }
522
-
523
- .page-btn:hover:not(:disabled) {
524
- background: var(--bg-elevated);
525
- border-color: var(--accent-primary);
526
- }
527
-
528
- .page-btn:disabled {
529
- opacity: 0.4;
530
- cursor: not-allowed;
531
- }
532
-
533
- .page-btn.active {
534
- background: var(--accent-primary);
535
- border-color: var(--accent-primary);
536
- }
537
-
538
- .empty-state {
539
- display: flex;
540
- flex-direction: column;
541
- align-items: center;
542
- justify-content: center;
543
- padding: 80px 40px;
544
- text-align: center;
545
- }
546
-
547
- .empty-icon {
548
- width: 100px;
549
- height: 100px;
550
- background: var(--bg-card);
551
- border-radius: 24px;
552
- display: flex;
553
- align-items: center;
554
- justify-content: center;
555
- font-size: 48px;
556
- margin-bottom: 24px;
557
- }
558
-
559
- .empty-title {
560
- font-size: 22px;
561
- font-weight: 600;
562
- margin-bottom: 10px;
563
- }
564
-
565
- .empty-text {
566
- font-size: 15px;
567
- color: var(--text-secondary);
568
- max-width: 360px;
569
- }
570
-
571
- .loading-grid {
572
- display: grid;
573
- gap: 20px;
574
- }
575
-
576
- .skeleton {
577
- background: linear-gradient(90deg, var(--bg-card) 25%, var(--bg-elevated) 50%, var(--bg-card) 75%);
578
- background-size: 200% 100%;
579
- animation: shimmer 1.5s infinite;
580
- border-radius: 8px;
581
- }
582
-
583
- .skeleton-card {
584
- height: 180px;
585
- border-radius: 16px;
586
- }
587
-
588
- @keyframes shimmer {
589
- 0% { background-position: 200% 0; }
590
- 100% { background-position: -200% 0; }
591
- }
592
-
593
- .keys-hint {
594
- position: fixed;
595
- bottom: 20px;
596
- right: 20px;
597
- background: var(--bg-card);
598
- border: 1px solid var(--border-color);
599
- border-radius: 10px;
600
- padding: 12px 16px;
601
- font-size: 11px;
602
- color: var(--text-muted);
603
- display: flex;
604
- gap: 12px;
605
- z-index: 100;
606
- }
607
-
608
- .keys-hint kbd {
609
- display: inline-block;
610
- padding: 2px 6px;
611
- background: var(--bg-elevated);
612
- border: 1px solid var(--border-color);
613
- border-radius: 4px;
614
- font-family: inherit;
615
- font-size: 10px;
616
- margin: 0 2px;
617
- }
618
-
619
- ::-webkit-scrollbar { width: 8px; }
620
- ::-webkit-scrollbar-track { background: var(--bg-secondary); }
621
- ::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 4px; }
622
- ::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }
623
- </style>
624
- </head>
625
- <body>
626
- <div class="app-container">
627
- <aside class="sidebar">
628
- <div class="sidebar-header">
629
- <div class="logo">
630
- <div class="logo-icon">${emoji.newspaper}</div>
631
- <span class="logo-text">Sondakika</span>
632
- </div>
633
- </div>
634
-
635
- <div class="sidebar-section">
636
- <div class="section-title">Haber Kaynakları</div>
637
- <div class="sources-list" id="sourcesList"></div>
638
- </div>
639
-
640
- <div class="sidebar-section">
641
- <div class="section-title">Seçenekler</div>
642
- <div class="options-grid">
643
- <div class="option-item">
644
- <span class="option-label">Sırala</span>
645
- <select class="option-select" id="sortOrder">
646
- <option value="desc">En Yeni (Önce)</option>
647
- <option value="asc">En Eski (Önce)</option>
648
- </select>
649
- </div>
650
- <div class="option-item">
651
- <span class="option-label">Sayfa Başına</span>
652
- <select class="option-select" id="itemsPerPage">
653
- <option value="5">5 Haber</option>
654
- <option value="10" selected>10 Haber</option>
655
- <option value="15">15 Haber</option>
656
- <option value="20">20 Haber</option>
657
- </select>
658
- </div>
659
- <div class="option-item">
660
- <span class="option-label">Yazı Büyüklüğü</span>
661
- <div class="font-size-controls">
662
- <button class="font-btn" id="fontDecrease" onclick="changeFontSize(-2)">A-</button>
663
- <span class="font-size-value" id="fontSizeValue">16px</span>
664
- <button class="font-btn" id="fontIncrease" onclick="changeFontSize(2)">A+</button>
665
- </div>
666
- </div>
667
- <div class="option-item">
668
- <span class="option-label">Tema</span>
669
- <select class="option-select" id="themeSelect" onchange="changeTheme(this.value)">
670
- <option value="dark">Karanlık</option>
671
- <option value="light">Açık</option>
672
- </select>
673
- </div>
674
- </div>
675
- </div>
676
- </aside>
677
-
678
- <main class="main-content">
679
- <div class="content-header">
680
- <div class="header-left">
681
- <h1 class="page-title">Haberler</h1>
682
- <p class="page-subtitle" id="newsCount">Yükleniyor...</p>
683
- </div>
684
- <div class="header-actions">
685
- <div class="status-badge">
686
- <span class="status-dot" id="statusDot"></span>
687
- <span id="statusText">Hazır</span>
688
- </div>
689
- <button class="refresh-btn" id="refreshBtn">${emoji.refresh} Yenile</button>
690
- </div>
691
- </div>
692
-
693
- <div id="newsContainer">
694
- <div class="loading-grid" id="loadingGrid"></div>
695
- <div class="news-grid hidden" id="newsGrid"></div>
696
- <div class="empty-state hidden" id="emptyState">
697
- <div class="empty-icon">${emoji.empty}</div>
698
- <h2 class="empty-title">Haber Kaynağı Seçin</h2>
699
- <p class="empty-text">Lütfen en az bir haber kaynağı seçiniz ve yenile butonuna basınız.</p>
700
- </div>
701
- </div>
702
-
703
- <div class="pagination hidden" id="pagination"></div>
704
- </main>
705
- </div>
706
-
707
- <div class="keys-hint">
708
- <span><kbd>${emoji.upArrow}</kbd><kbd>${emoji.downArrow}</kbd> Seç</span>
709
- <span><kbd>${emoji.leftArrow}</kbd><kbd>${emoji.rightArrow}</kbd> Sayfa</span>
710
- <span><kbd>Enter</kbd> Görüntüle</span>
711
- </div>
712
-
713
- <script>
714
- let sources = {};
715
- let currentState = {
716
- enabledSources: [],
717
- sortOrder: 'desc',
718
- itemsPerPage: 10,
719
- currentPage: 1,
720
- fontSize: 16,
721
- theme: 'dark'
722
- };
723
- let allNews = [];
724
- let isLoading = false;
725
- let selectedIndex = -1;
726
-
727
- async function init() {
728
- sources = await window.electronAPI.getSources();
729
- const state = await window.electronAPI.getState();
730
- currentState = Object.assign({}, currentState, state);
731
-
732
- renderSources();
733
- setupEventListeners();
734
- applyState();
735
-
736
- if (currentState.enabledSources.length > 0) {
737
- await fetchNews();
738
- } else {
739
- showEmptyState();
740
- }
741
-
742
- setupKeyboardNavigation();
743
- }
744
-
745
- function renderSources() {
746
- const container = document.getElementById('sourcesList');
747
- container.innerHTML = '';
748
-
749
- Object.entries(sources).forEach(function(entry) {
750
- var key = entry[0];
751
- var source = entry[1];
752
- const item = document.createElement('div');
753
- item.className = 'source-item';
754
- item.dataset.key = key;
755
-
756
- const checkbox = document.createElement('input');
757
- checkbox.type = 'checkbox';
758
- checkbox.checked = currentState.enabledSources.indexOf(key) !== -1;
759
-
760
- const name = document.createElement('span');
761
- name.className = 'source-name';
762
- name.textContent = source.name;
763
-
764
- item.appendChild(checkbox);
765
- item.appendChild(name);
766
-
767
- if (source.isSondakika) {
768
- const badge = document.createElement('span');
769
- badge.className = 'source-badge';
770
- badge.textContent = 'SON DAKİKA';
771
- item.appendChild(badge);
772
- }
773
-
774
- container.appendChild(item);
775
- });
776
- }
777
-
778
- function setupEventListeners() {
779
- document.getElementById('sourcesList').addEventListener('change', async function(e) {
780
- if (e.target.type === 'checkbox') {
781
- const key = e.target.closest('.source-item').dataset.key;
782
- if (e.target.checked) {
783
- if (currentState.enabledSources.indexOf(key) === -1) {
784
- currentState.enabledSources.push(key);
785
- }
786
- } else {
787
- currentState.enabledSources = currentState.enabledSources.filter(function(s) { return s !== key; });
788
- }
789
- await saveState();
790
- if (currentState.enabledSources.length > 0) {
791
- await fetchNews();
792
- } else {
793
- showEmptyState();
794
- }
795
- }
796
- });
797
-
798
- document.getElementById('sortOrder').addEventListener('change', async function(e) {
799
- currentState.sortOrder = e.target.value;
800
- await saveState();
801
- if (allNews.length > 0) {
802
- await fetchNews();
803
- }
804
- });
805
-
806
- document.getElementById('itemsPerPage').addEventListener('change', async function(e) {
807
- currentState.itemsPerPage = parseInt(e.target.value);
808
- currentState.currentPage = 1;
809
- await saveState();
810
- renderNews();
811
- });
812
-
813
- document.getElementById('refreshBtn').addEventListener('click', async function() {
814
- await fetchNews();
815
- });
816
- }
817
-
818
- function setupKeyboardNavigation() {
819
- document.addEventListener('keydown', function(e) {
820
- if (allNews.length === 0) return;
821
-
822
- var totalPages = Math.ceil(allNews.length / currentState.itemsPerPage);
823
- var startIdx = (currentState.currentPage - 1) * currentState.itemsPerPage;
824
- var endIdx = Math.min(startIdx + currentState.itemsPerPage, allNews.length);
825
- var itemsOnPage = endIdx - startIdx;
826
-
827
- switch (e.key) {
828
- case 'ArrowUp':
829
- e.preventDefault();
830
- if (selectedIndex > 0) {
831
- selectNewsItem(selectedIndex - 1);
832
- }
833
- break;
834
-
835
- case 'ArrowDown':
836
- e.preventDefault();
837
- if (selectedIndex < itemsOnPage - 1) {
838
- selectNewsItem(selectedIndex + 1);
839
- }
840
- break;
841
-
842
- case 'ArrowLeft':
843
- e.preventDefault();
844
- if (currentState.currentPage > 1) {
845
- currentState.currentPage--;
846
- selectedIndex = 0;
847
- renderNews();
848
- saveState();
849
- }
850
- break;
851
-
852
- case 'ArrowRight':
853
- e.preventDefault();
854
- if (currentState.currentPage < totalPages) {
855
- currentState.currentPage++;
856
- selectedIndex = 0;
857
- renderNews();
858
- saveState();
859
- }
860
- break;
861
-
862
- case 'Enter':
863
- e.preventDefault();
864
- if (selectedIndex >= 0) {
865
- var globalIdx = startIdx + selectedIndex;
866
- openArticleView(globalIdx);
867
- }
868
- break;
869
- }
870
- });
871
- }
872
-
873
- function selectNewsItem(index) {
874
- selectedIndex = index;
875
- updateSelectedCard();
876
- }
877
-
878
- function updateSelectedCard() {
879
- var cards = document.querySelectorAll('.news-card');
880
- cards.forEach(function(card, idx) {
881
- if (idx === selectedIndex) {
882
- card.classList.add('selected');
883
- card.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
884
- } else {
885
- card.classList.remove('selected');
886
- }
887
- });
888
- }
889
-
890
- function applyState() {
891
- document.getElementById('sortOrder').value = currentState.sortOrder;
892
- document.getElementById('itemsPerPage').value = currentState.itemsPerPage;
893
- document.getElementById('fontSizeValue').textContent = currentState.fontSize + 'px';
894
- document.getElementById('themeSelect').value = currentState.theme || 'dark';
895
- applyFontSize(currentState.fontSize);
896
- applyTheme(currentState.theme || 'dark');
897
- }
898
-
899
- async function saveState() {
900
- await window.electronAPI.saveState(currentState);
901
- }
902
-
903
- function applyFontSize(size) {
904
- document.body.style.fontSize = size + 'px';
905
- }
906
-
907
- function applyTheme(theme) {
908
- document.body.classList.remove('theme-light');
909
- if (theme === 'light') {
910
- document.body.classList.add('theme-light');
911
- }
912
- }
913
-
914
- async function changeTheme(theme) {
915
- currentState.theme = theme;
916
- applyTheme(theme);
917
- await window.electronAPI.setTheme(theme);
918
- }
919
-
920
- async function changeFontSize(delta) {
921
- var newSize = (currentState.fontSize || 16) + delta;
922
- newSize = Math.max(12, Math.min(28, newSize));
923
- currentState.fontSize = newSize;
924
- document.getElementById('fontSizeValue').textContent = newSize + 'px';
925
- applyFontSize(newSize);
926
- await window.electronAPI.setFontSize(newSize);
927
- }
928
-
929
- async function fetchNews() {
930
- if (isLoading) return;
931
-
932
- isLoading = true;
933
- setLoading(true);
934
-
935
- try {
936
- allNews = await window.electronAPI.fetchNews(
937
- currentState.enabledSources,
938
- currentState.sortOrder
939
- );
940
- currentState.currentPage = 1;
941
- selectedIndex = -1;
942
- await saveState();
943
- renderNews();
944
- } catch (err) {
945
- console.error('Haberler alınırken hata:', err);
946
- } finally {
947
- isLoading = false;
948
- setLoading(false);
949
- }
950
- }
951
-
952
- function setLoading(loading) {
953
- var dot = document.getElementById('statusDot');
954
- var text = document.getElementById('statusText');
955
- var btn = document.getElementById('refreshBtn');
956
-
957
- if (loading) {
958
- dot.classList.add('loading');
959
- text.textContent = 'Yükleniyor...';
960
- btn.disabled = true;
961
- } else {
962
- dot.classList.remove('loading');
963
- text.textContent = 'Hazır';
964
- btn.disabled = false;
965
- }
966
- }
967
-
968
- function renderNews() {
969
- var grid = document.getElementById('newsGrid');
970
- var empty = document.getElementById('emptyState');
971
- var loading = document.getElementById('loadingGrid');
972
- var pag = document.getElementById('pagination');
973
-
974
- if (allNews.length === 0) {
975
- grid.classList.add('hidden');
976
- pag.classList.add('hidden');
977
- empty.classList.remove('hidden');
978
- document.getElementById('newsCount').textContent = 'Kaynak seçilmedi';
979
- return;
980
- }
981
-
982
- empty.classList.add('hidden');
983
- loading.classList.add('hidden');
984
- grid.classList.remove('hidden');
985
-
986
- var start = (currentState.currentPage - 1) * currentState.itemsPerPage;
987
- var end = start + currentState.itemsPerPage;
988
- var pageNews = allNews.slice(start, end);
989
- var totalPages = Math.ceil(allNews.length / currentState.itemsPerPage);
990
-
991
- document.getElementById('newsCount').textContent = allNews.length + ' haber \\u2022 Sayfa ' + currentState.currentPage + '/' + totalPages;
992
-
993
- var html = '';
994
- pageNews.forEach(function(news, index) {
995
- var date = formatDate(news.pubDate || news.isoDate);
996
- var summary = news.summary || news.contentSnippet || news.description || '';
997
- summary = summary.replace(/<[^>]*>/g, '').replace(/&nbsp;/g, ' ').replace(/\\s+/g, ' ').trim();
998
- var newsIndex = start + index;
999
-
1000
- var breakingClass = news.isSondakika ? ' breaking' : '';
1001
- var escapedTitle = escapeHtml(news.title);
1002
- var escapedSummary = escapeHtml(summary);
1003
-
1004
- html += '<article class="news-card' + (index === selectedIndex ? ' selected' : '') + '" data-index="' + newsIndex + '" onclick="window.selectAndOpen(' + newsIndex + ')">' +
1005
- '<div class="news-card-header">' +
1006
- '<div class="news-source">' +
1007
- '<span class="source-tag' + breakingClass + '">' + news.sourceName + '</span>' +
1008
- '</div>' +
1009
- '<span class="news-date-badge">${emoji.clock} ' + date + '</span>' +
1010
- '</div>' +
1011
- '<h2 class="news-title">' + escapedTitle + '</h2>' +
1012
- '<p class="news-summary">' + escapedSummary + '</p>' +
1013
- '<div class="news-card-footer">' +
1014
- '<span class="read-more" onclick="event.stopPropagation(); window.openArticleView(' + newsIndex + ')">Görüntüle</span>' +
1015
- '<span class="external-link-btn" onclick="event.stopPropagation(); window.electronAPI.openExternal(\"' + news.link + '\")">${emoji.link} Tarayıcıda aç</span>' +
1016
- '</div>' +
1017
- '</article>';
1018
- });
1019
-
1020
- grid.innerHTML = html;
1021
-
1022
- renderPagination(totalPages);
1023
-
1024
- var itemsOnPage = Math.min(currentState.itemsPerPage, allNews.length - start);
1025
- if (selectedIndex >= itemsOnPage) {
1026
- selectedIndex = itemsOnPage - 1;
1027
- }
1028
- if (selectedIndex >= 0) {
1029
- updateSelectedCard();
1030
- }
1031
- }
1032
-
1033
- function renderPagination(totalPages) {
1034
- var pag = document.getElementById('pagination');
1035
-
1036
- if (totalPages <= 1) {
1037
- pag.classList.add('hidden');
1038
- return;
1039
- }
1040
-
1041
- pag.classList.remove('hidden');
1042
- var html = '';
1043
-
1044
- html += '<button class="page-btn" onclick="window.changePage(1)"' + (currentState.currentPage === 1 ? ' disabled' : '') + '>${emoji.laquo}</button>';
1045
- html += '<button class="page-btn" onclick="window.changePage(' + (currentState.currentPage - 1) + ')"' + (currentState.currentPage === 1 ? ' disabled' : '') + '>${emoji.lsquo}</button>';
1046
-
1047
- for (var i = 1; i <= totalPages; i++) {
1048
- if (i === 1 || i === totalPages || (i >= currentState.currentPage - 1 && i <= currentState.currentPage + 1)) {
1049
- html += '<button class="page-btn' + (i === currentState.currentPage ? ' active' : '') + '" onclick="window.changePage(' + i + ')">' + i + '</button>';
1050
- } else if (i === currentState.currentPage - 2 || i === currentState.currentPage + 2) {
1051
- html += '<span style="color: var(--text-muted);">...</span>';
1052
- }
1053
- }
1054
-
1055
- html += '<button class="page-btn" onclick="window.changePage(' + (currentState.currentPage + 1) + ')"' + (currentState.currentPage === totalPages ? ' disabled' : '') + '>${emoji.rsquo}</button>';
1056
- html += '<button class="page-btn" onclick="window.changePage(' + totalPages + ')"' + (currentState.currentPage === totalPages ? ' disabled' : '') + '>${emoji.raquo}</button>';
1057
-
1058
- pag.innerHTML = html;
1059
- }
1060
-
1061
- window.changePage = async function(page) {
1062
- currentState.currentPage = page;
1063
- selectedIndex = 0;
1064
- renderNews();
1065
- await saveState();
1066
- };
1067
-
1068
- function formatDate(dateStr) {
1069
- if (!dateStr) return '';
1070
- var date = new Date(dateStr);
1071
- if (isNaN(date.getTime())) return '';
1072
-
1073
- return date.toLocaleDateString('tr-TR', {
1074
- day: '2-digit',
1075
- month: '2-digit',
1076
- year: 'numeric',
1077
- hour: '2-digit',
1078
- minute: '2-digit'
1079
- });
1080
- }
1081
-
1082
- function escapeHtml(text) {
1083
- if (!text) return '';
1084
- var div = document.createElement('div');
1085
- div.textContent = text;
1086
- return div.innerHTML;
1087
- }
1088
-
1089
- function showEmptyState() {
1090
- document.getElementById('loadingGrid').classList.add('hidden');
1091
- document.getElementById('newsGrid').classList.add('hidden');
1092
- document.getElementById('pagination').classList.add('hidden');
1093
- document.getElementById('emptyState').classList.remove('hidden');
1094
- document.getElementById('newsCount').textContent = 'Kaynak seçilmedi';
1095
- }
1096
-
1097
- function showLoading() {
1098
- var loading = document.getElementById('loadingGrid');
1099
- loading.classList.remove('hidden');
1100
- var skeletons = '';
1101
- for (var i = 0; i < 5; i++) {
1102
- skeletons += '<div class="skeleton skeleton-card"></div>';
1103
- }
1104
- loading.innerHTML = skeletons;
1105
- }
1106
-
1107
- window.selectAndOpen = function(index) {
1108
- selectedIndex = index - (currentState.currentPage - 1) * currentState.itemsPerPage;
1109
- openArticleView(index);
1110
- };
1111
-
1112
- window.openArticleView = function(newsIndex) {
1113
- if (newsIndex < 0 || newsIndex >= allNews.length) return;
1114
- window.electronAPI.openArticleView(allNews, newsIndex);
1115
- };
1116
-
1117
- window.electronAPI.onArticleViewClosed(function(returnedIndex) {
1118
- if (returnedIndex >= 0 && allNews && allNews[returnedIndex]) {
1119
- var newPage = Math.floor(returnedIndex / currentState.itemsPerPage) + 1;
1120
- if (newPage !== currentState.currentPage) {
1121
- currentState.currentPage = newPage;
1122
- renderNews();
1123
- }
1124
- selectedIndex = returnedIndex - (currentState.currentPage - 1) * currentState.itemsPerPage;
1125
- updateSelectedCard();
1126
- }
1127
- });
1128
-
1129
- showLoading();
1130
- init();
1131
- </script>
1132
- </body>
1133
- </html>`;
1134
-
1135
- fs.writeFileSync(path.join(__dirname, 'src', 'renderer', 'index.html'), html, 'utf8');
1136
- console.log('index.html written successfully with proper encoding');