GameSentenceMiner 2.16.5__py3-none-any.whl → 2.16.7__py3-none-any.whl
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.
- GameSentenceMiner/anki.py +7 -2
- GameSentenceMiner/config_gui.py +8 -0
- GameSentenceMiner/locales/en_us.json +5 -1
- GameSentenceMiner/locales/ja_jp.json +4 -0
- GameSentenceMiner/locales/zh_cn.json +4 -0
- GameSentenceMiner/util/configuration.py +35 -0
- GameSentenceMiner/web/database_api.py +207 -93
- GameSentenceMiner/web/static/css/stats.css +243 -0
- GameSentenceMiner/web/static/js/search.js +46 -22
- GameSentenceMiner/web/static/js/shared.js +60 -12
- GameSentenceMiner/web/static/js/stats.js +363 -20
- GameSentenceMiner/web/stats.py +3 -3
- GameSentenceMiner/web/templates/search.html +6 -1
- GameSentenceMiner/web/templates/stats.html +163 -12
- GameSentenceMiner/web/texthooking_page.py +1 -1
- {gamesentenceminer-2.16.5.dist-info → gamesentenceminer-2.16.7.dist-info}/METADATA +1 -1
- {gamesentenceminer-2.16.5.dist-info → gamesentenceminer-2.16.7.dist-info}/RECORD +22 -22
- /GameSentenceMiner/web/{websockets.py → gsm_websocket.py} +0 -0
- {gamesentenceminer-2.16.5.dist-info → gamesentenceminer-2.16.7.dist-info}/WHEEL +0 -0
- {gamesentenceminer-2.16.5.dist-info → gamesentenceminer-2.16.7.dist-info}/entry_points.txt +0 -0
- {gamesentenceminer-2.16.5.dist-info → gamesentenceminer-2.16.7.dist-info}/licenses/LICENSE +0 -0
- {gamesentenceminer-2.16.5.dist-info → gamesentenceminer-2.16.7.dist-info}/top_level.txt +0 -0
@@ -468,6 +468,249 @@
|
|
468
468
|
}
|
469
469
|
}
|
470
470
|
|
471
|
+
/* Goal Progress Chart Styles */
|
472
|
+
.goal-progress-chart {
|
473
|
+
background: var(--bg-secondary);
|
474
|
+
border-radius: 12px;
|
475
|
+
box-shadow: 0 4px 16px var(--shadow-color);
|
476
|
+
border: 1px solid var(--border-color);
|
477
|
+
padding: 24px;
|
478
|
+
transition: all 0.3s ease;
|
479
|
+
position: relative;
|
480
|
+
overflow: hidden;
|
481
|
+
}
|
482
|
+
|
483
|
+
.goal-progress-chart::before {
|
484
|
+
content: '';
|
485
|
+
position: absolute;
|
486
|
+
top: 0;
|
487
|
+
left: 0;
|
488
|
+
right: 0;
|
489
|
+
height: 4px;
|
490
|
+
background: linear-gradient(90deg, #2ee6e0, #3be62f, #e6dc2e);
|
491
|
+
border-radius: 12px 12px 0 0;
|
492
|
+
}
|
493
|
+
|
494
|
+
.goal-progress-chart:hover {
|
495
|
+
transform: translateY(-2px);
|
496
|
+
box-shadow: 0 8px 24px var(--shadow-color);
|
497
|
+
}
|
498
|
+
|
499
|
+
.goal-progress-grid {
|
500
|
+
display: grid;
|
501
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
502
|
+
gap: 24px;
|
503
|
+
margin-top: 20px;
|
504
|
+
}
|
505
|
+
|
506
|
+
.goal-progress-item {
|
507
|
+
background: var(--bg-tertiary);
|
508
|
+
border-radius: 8px;
|
509
|
+
padding: 20px;
|
510
|
+
border: 1px solid var(--border-color);
|
511
|
+
transition: all 0.3s ease;
|
512
|
+
}
|
513
|
+
|
514
|
+
.goal-progress-item:hover {
|
515
|
+
background: var(--border-color);
|
516
|
+
transform: scale(1.02);
|
517
|
+
}
|
518
|
+
|
519
|
+
.goal-progress-header {
|
520
|
+
display: flex;
|
521
|
+
justify-content: space-between;
|
522
|
+
align-items: center;
|
523
|
+
margin-bottom: 12px;
|
524
|
+
}
|
525
|
+
|
526
|
+
.goal-progress-label {
|
527
|
+
display: flex;
|
528
|
+
align-items: center;
|
529
|
+
gap: 8px;
|
530
|
+
font-weight: 600;
|
531
|
+
color: var(--text-primary);
|
532
|
+
font-size: 16px;
|
533
|
+
}
|
534
|
+
|
535
|
+
.goal-icon {
|
536
|
+
font-size: 20px;
|
537
|
+
opacity: 0.8;
|
538
|
+
}
|
539
|
+
|
540
|
+
.goal-progress-values {
|
541
|
+
display: flex;
|
542
|
+
align-items: center;
|
543
|
+
gap: 4px;
|
544
|
+
font-size: 14px;
|
545
|
+
font-weight: 600;
|
546
|
+
}
|
547
|
+
|
548
|
+
.goal-current {
|
549
|
+
color: var(--text-primary);
|
550
|
+
}
|
551
|
+
|
552
|
+
.goal-separator {
|
553
|
+
color: var(--text-tertiary);
|
554
|
+
margin: 0 4px;
|
555
|
+
}
|
556
|
+
|
557
|
+
.goal-target {
|
558
|
+
color: var(--text-secondary);
|
559
|
+
}
|
560
|
+
|
561
|
+
.goal-progress-bar {
|
562
|
+
height: 12px;
|
563
|
+
background: var(--bg-primary);
|
564
|
+
border-radius: 6px;
|
565
|
+
overflow: hidden;
|
566
|
+
margin-bottom: 12px;
|
567
|
+
border: 1px solid var(--border-color);
|
568
|
+
}
|
569
|
+
|
570
|
+
.goal-progress-fill {
|
571
|
+
height: 100%;
|
572
|
+
border-radius: 6px;
|
573
|
+
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
574
|
+
background: var(--text-tertiary); /* Default color for 0% */
|
575
|
+
position: relative;
|
576
|
+
overflow: hidden;
|
577
|
+
}
|
578
|
+
|
579
|
+
.goal-progress-fill[data-percentage="100"] {
|
580
|
+
background: #2ee6e0; /* 100% completion - cyan */
|
581
|
+
}
|
582
|
+
|
583
|
+
.goal-progress-fill[data-percentage="75"] {
|
584
|
+
background: #3be62f; /* 75% completion - green */
|
585
|
+
}
|
586
|
+
|
587
|
+
.goal-progress-fill[data-percentage="50"] {
|
588
|
+
background: #3be62f; /* 50% completion - green */
|
589
|
+
}
|
590
|
+
|
591
|
+
.goal-progress-fill[data-percentage="25"] {
|
592
|
+
background: #e6dc2e; /* 25% completion - yellow */
|
593
|
+
}
|
594
|
+
|
595
|
+
/* Dynamic progress fill colors based on completion percentage */
|
596
|
+
.goal-progress-fill.completion-100 { background: #2ee6e0; }
|
597
|
+
.goal-progress-fill.completion-75 { background: #3be62f; }
|
598
|
+
.goal-progress-fill.completion-50 { background: #3be62f; }
|
599
|
+
.goal-progress-fill.completion-25 { background: #e6dc2e; }
|
600
|
+
.goal-progress-fill.completion-0 {
|
601
|
+
background: var(--text-tertiary);
|
602
|
+
}
|
603
|
+
|
604
|
+
[data-theme="dark"] .goal-progress-fill.completion-0 {
|
605
|
+
background: #333;
|
606
|
+
}
|
607
|
+
|
608
|
+
[data-theme="light"] .goal-progress-fill.completion-0 {
|
609
|
+
background: #fff;
|
610
|
+
border: 1px solid var(--border-color);
|
611
|
+
}
|
612
|
+
|
613
|
+
.goal-progress-info {
|
614
|
+
display: flex;
|
615
|
+
justify-content: space-between;
|
616
|
+
align-items: center;
|
617
|
+
font-size: 12px;
|
618
|
+
}
|
619
|
+
|
620
|
+
.goal-percentage {
|
621
|
+
font-weight: 600;
|
622
|
+
color: var(--text-primary);
|
623
|
+
}
|
624
|
+
|
625
|
+
.goal-projection {
|
626
|
+
color: var(--text-secondary);
|
627
|
+
font-style: italic;
|
628
|
+
}
|
629
|
+
|
630
|
+
/* Loading and Error States */
|
631
|
+
.goal-progress-loading {
|
632
|
+
display: flex;
|
633
|
+
align-items: center;
|
634
|
+
justify-content: center;
|
635
|
+
padding: 40px;
|
636
|
+
color: var(--text-tertiary);
|
637
|
+
}
|
638
|
+
|
639
|
+
.goal-progress-loading .spinner {
|
640
|
+
margin-right: 10px;
|
641
|
+
}
|
642
|
+
|
643
|
+
.goal-progress-error {
|
644
|
+
text-align: center;
|
645
|
+
padding: 40px 20px;
|
646
|
+
color: var(--danger-color);
|
647
|
+
}
|
648
|
+
|
649
|
+
.goal-progress-error-icon {
|
650
|
+
font-size: 48px;
|
651
|
+
margin-bottom: 16px;
|
652
|
+
opacity: 0.7;
|
653
|
+
}
|
654
|
+
|
655
|
+
.goal-progress-error-message {
|
656
|
+
font-size: 16px;
|
657
|
+
margin-bottom: 16px;
|
658
|
+
}
|
659
|
+
|
660
|
+
/* Responsive Design for Goal Progress Chart */
|
661
|
+
@media (max-width: 768px) {
|
662
|
+
.goal-progress-grid {
|
663
|
+
grid-template-columns: 1fr;
|
664
|
+
gap: 16px;
|
665
|
+
}
|
666
|
+
|
667
|
+
.goal-progress-item {
|
668
|
+
padding: 16px;
|
669
|
+
}
|
670
|
+
|
671
|
+
.goal-progress-header {
|
672
|
+
flex-direction: column;
|
673
|
+
align-items: flex-start;
|
674
|
+
gap: 8px;
|
675
|
+
}
|
676
|
+
|
677
|
+
.goal-progress-values {
|
678
|
+
font-size: 12px;
|
679
|
+
}
|
680
|
+
|
681
|
+
.goal-progress-label {
|
682
|
+
font-size: 14px;
|
683
|
+
}
|
684
|
+
|
685
|
+
.goal-progress-info {
|
686
|
+
flex-direction: column;
|
687
|
+
align-items: flex-start;
|
688
|
+
gap: 4px;
|
689
|
+
}
|
690
|
+
}
|
691
|
+
|
692
|
+
@media (max-width: 480px) {
|
693
|
+
.goal-progress-chart {
|
694
|
+
padding: 16px;
|
695
|
+
}
|
696
|
+
|
697
|
+
.goal-progress-item {
|
698
|
+
padding: 12px;
|
699
|
+
}
|
700
|
+
|
701
|
+
.goal-icon {
|
702
|
+
font-size: 16px;
|
703
|
+
}
|
704
|
+
|
705
|
+
.goal-progress-label {
|
706
|
+
font-size: 13px;
|
707
|
+
}
|
708
|
+
|
709
|
+
.goal-progress-bar {
|
710
|
+
height: 10px;
|
711
|
+
}
|
712
|
+
}
|
713
|
+
|
471
714
|
/* Navigation styles */
|
472
715
|
.navigation .nav-link:hover {
|
473
716
|
background-color: var(--accent-color) !important;
|
@@ -13,12 +13,14 @@ class SentenceSearchApp {
|
|
13
13
|
this.errorMessage = document.getElementById('errorMessage');
|
14
14
|
this.searchStats = document.getElementById('searchStats');
|
15
15
|
this.searchTime = document.getElementById('searchTime');
|
16
|
+
this.regexCheckbox = document.getElementById('regexCheckbox');
|
16
17
|
|
17
18
|
this.currentPage = 1;
|
18
19
|
this.pageSize = 20;
|
19
20
|
this.searchTimeout = null;
|
20
21
|
this.currentQuery = '';
|
21
22
|
this.totalResults = 0;
|
23
|
+
this.currentUseRegex = false;
|
22
24
|
|
23
25
|
// Move initialization logic to async method
|
24
26
|
this.initialize();
|
@@ -66,6 +68,13 @@ class SentenceSearchApp {
|
|
66
68
|
this.currentPage++;
|
67
69
|
this.performSearch();
|
68
70
|
});
|
71
|
+
|
72
|
+
// Regex checkbox toggle triggers search
|
73
|
+
if (this.regexCheckbox) {
|
74
|
+
this.regexCheckbox.addEventListener('change', () => {
|
75
|
+
this.performSearch();
|
76
|
+
});
|
77
|
+
}
|
69
78
|
}
|
70
79
|
|
71
80
|
async loadGamesList() {
|
@@ -94,22 +103,24 @@ class SentenceSearchApp {
|
|
94
103
|
const query = this.searchInput.value.trim();
|
95
104
|
const gameFilter = this.gameFilter.value;
|
96
105
|
const sortBy = this.sortFilter.value;
|
97
|
-
|
98
|
-
|
99
|
-
|
106
|
+
const useRegex = this.regexCheckbox && this.regexCheckbox.checked;
|
107
|
+
|
108
|
+
// Reset to first page for new searches or regex toggle
|
109
|
+
if (query !== this.currentQuery || useRegex !== this.currentUseRegex) {
|
100
110
|
this.currentPage = 1;
|
101
111
|
}
|
102
112
|
this.currentQuery = query;
|
103
|
-
|
113
|
+
this.currentUseRegex = useRegex;
|
114
|
+
|
104
115
|
// Show appropriate state
|
105
116
|
if (!query) {
|
106
117
|
this.showEmptyState();
|
107
118
|
return;
|
108
119
|
}
|
109
|
-
|
120
|
+
|
110
121
|
this.showLoadingState();
|
111
122
|
const startTime = Date.now();
|
112
|
-
|
123
|
+
|
113
124
|
try {
|
114
125
|
const params = new URLSearchParams({
|
115
126
|
q: query,
|
@@ -117,22 +128,25 @@ class SentenceSearchApp {
|
|
117
128
|
page_size: this.pageSize,
|
118
129
|
sort: sortBy
|
119
130
|
});
|
120
|
-
|
131
|
+
|
121
132
|
if (gameFilter) {
|
122
133
|
params.append('game', gameFilter);
|
123
134
|
}
|
124
|
-
|
135
|
+
if (useRegex) {
|
136
|
+
params.append('use_regex', 'true');
|
137
|
+
}
|
138
|
+
|
125
139
|
const response = await fetch(`/api/search-sentences?${params}`);
|
126
140
|
const data = await response.json();
|
127
|
-
|
141
|
+
|
128
142
|
const searchTime = Date.now() - startTime;
|
129
|
-
|
143
|
+
|
130
144
|
if (!response.ok) {
|
131
145
|
throw new Error(data.error || 'Search failed');
|
132
146
|
}
|
133
|
-
|
147
|
+
|
134
148
|
this.displayResults(data, searchTime);
|
135
|
-
|
149
|
+
|
136
150
|
} catch (error) {
|
137
151
|
this.showErrorState(error.message);
|
138
152
|
}
|
@@ -198,17 +212,27 @@ class SentenceSearchApp {
|
|
198
212
|
|
199
213
|
highlightSearchTerms(text, query) {
|
200
214
|
if (!query) return escapeHtml(text);
|
201
|
-
|
215
|
+
|
216
|
+
const useRegex = this.regexCheckbox && this.regexCheckbox.checked;
|
202
217
|
const escapedText = escapeHtml(text);
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
218
|
+
|
219
|
+
if (useRegex) {
|
220
|
+
try {
|
221
|
+
const pattern = new RegExp(query, 'gi');
|
222
|
+
return escapedText.replace(pattern, '<span class="search-highlight">$&</span>');
|
223
|
+
} catch (e) {
|
224
|
+
// If invalid regex, just return escaped text
|
225
|
+
return escapedText;
|
226
|
+
}
|
227
|
+
} else {
|
228
|
+
const searchTerms = query.split(' ').filter(term => term.length > 0);
|
229
|
+
let result = escapedText;
|
230
|
+
searchTerms.forEach(term => {
|
231
|
+
const regex = new RegExp(`(${escapeRegex(term)})`, 'gi');
|
232
|
+
result = result.replace(regex, '<span class="search-highlight">$1</span>');
|
233
|
+
});
|
234
|
+
return result;
|
235
|
+
}
|
212
236
|
}
|
213
237
|
|
214
238
|
updatePagination(data) {
|
@@ -19,12 +19,17 @@ function closeModal(modalId) {
|
|
19
19
|
|
20
20
|
// Initialize modal close functionality (backdrop clicks and ESC key)
|
21
21
|
function initializeModalHandlers() {
|
22
|
-
// Close modals
|
22
|
+
// Close modals only if both mousedown and mouseup are on the backdrop
|
23
23
|
document.querySelectorAll('.modal').forEach(modal => {
|
24
|
-
|
25
|
-
|
24
|
+
let backdropMouseDown = false;
|
25
|
+
modal.addEventListener('mousedown', (e) => {
|
26
|
+
backdropMouseDown = (e.target === modal);
|
27
|
+
});
|
28
|
+
modal.addEventListener('mouseup', (e) => {
|
29
|
+
if (backdropMouseDown && e.target === modal) {
|
26
30
|
closeModal(modal.id);
|
27
31
|
}
|
32
|
+
backdropMouseDown = false;
|
28
33
|
});
|
29
34
|
});
|
30
35
|
|
@@ -224,6 +229,9 @@ class SettingsManager {
|
|
224
229
|
this.sessionGapInput = document.getElementById('sessionGap');
|
225
230
|
this.heatmapYearSelect = document.getElementById('heatmapYear');
|
226
231
|
this.streakRequirementInput = document.getElementById('streakRequirement');
|
232
|
+
this.readingHoursTargetInput = document.getElementById('readingHoursTarget');
|
233
|
+
this.characterCountTargetInput = document.getElementById('characterCountTarget');
|
234
|
+
this.gamesTargetInput = document.getElementById('gamesTarget');
|
227
235
|
}
|
228
236
|
|
229
237
|
attachEventListeners() {
|
@@ -245,17 +253,18 @@ class SettingsManager {
|
|
245
253
|
this.saveSettingsBtn.addEventListener('click', () => this.saveSettings());
|
246
254
|
}
|
247
255
|
|
248
|
-
// Close modal when clicking outside
|
249
|
-
if (this.settingsModal) {
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
}
|
256
|
+
// // Close modal when clicking outside
|
257
|
+
// if (this.settingsModal) {
|
258
|
+
// this.settingsModal.addEventListener('click', (e) => {
|
259
|
+
// if (e.target === this.settingsModal) {
|
260
|
+
// this.closeModal();
|
261
|
+
// }
|
262
|
+
// });
|
263
|
+
// }
|
256
264
|
|
257
265
|
// Clear messages when user starts typing
|
258
|
-
[this.afkTimerInput, this.sessionGapInput, this.heatmapYearSelect, this.streakRequirementInput
|
266
|
+
[this.afkTimerInput, this.sessionGapInput, this.heatmapYearSelect, this.streakRequirementInput,
|
267
|
+
this.readingHoursTargetInput, this.characterCountTargetInput, this.gamesTargetInput]
|
259
268
|
.filter(Boolean)
|
260
269
|
.forEach(input => {
|
261
270
|
input.addEventListener('input', () => this.clearMessages());
|
@@ -318,6 +327,15 @@ class SettingsManager {
|
|
318
327
|
if (this.streakRequirementInput) {
|
319
328
|
this.streakRequirementInput.value = settings.streak_requirement_hours || 1;
|
320
329
|
}
|
330
|
+
if (this.readingHoursTargetInput) {
|
331
|
+
this.readingHoursTargetInput.value = settings.reading_hours_target || 1500;
|
332
|
+
}
|
333
|
+
if (this.characterCountTargetInput) {
|
334
|
+
this.characterCountTargetInput.value = settings.character_count_target || 25000000;
|
335
|
+
}
|
336
|
+
if (this.gamesTargetInput) {
|
337
|
+
this.gamesTargetInput.value = settings.games_target || 100;
|
338
|
+
}
|
321
339
|
|
322
340
|
// Load saved year preference
|
323
341
|
const savedYear = localStorage.getItem('selectedHeatmapYear') || 'all';
|
@@ -399,6 +417,33 @@ class SettingsManager {
|
|
399
417
|
settings.streak_requirement_hours = streakRequirement;
|
400
418
|
}
|
401
419
|
|
420
|
+
if (this.readingHoursTargetInput) {
|
421
|
+
const readingHoursTarget = parseInt(this.readingHoursTargetInput.value);
|
422
|
+
if (isNaN(readingHoursTarget) || readingHoursTarget < 1 || readingHoursTarget > 10000) {
|
423
|
+
this.showError('Reading hours target must be between 1 and 10,000 hours');
|
424
|
+
return;
|
425
|
+
}
|
426
|
+
settings.reading_hours_target = readingHoursTarget;
|
427
|
+
}
|
428
|
+
|
429
|
+
if (this.characterCountTargetInput) {
|
430
|
+
const characterCountTarget = parseInt(this.characterCountTargetInput.value);
|
431
|
+
if (isNaN(characterCountTarget) || characterCountTarget < 1000 || characterCountTarget > 1000000000) {
|
432
|
+
this.showError('Character count target must be between 1,000 and 1,000,000,000 characters');
|
433
|
+
return;
|
434
|
+
}
|
435
|
+
settings.character_count_target = characterCountTarget;
|
436
|
+
}
|
437
|
+
|
438
|
+
if (this.gamesTargetInput) {
|
439
|
+
const gamesTarget = parseInt(this.gamesTargetInput.value);
|
440
|
+
if (isNaN(gamesTarget) || gamesTarget < 1 || gamesTarget > 1000) {
|
441
|
+
this.showError('Games target must be between 1 and 1,000');
|
442
|
+
return;
|
443
|
+
}
|
444
|
+
settings.games_target = gamesTarget;
|
445
|
+
}
|
446
|
+
|
402
447
|
// Show loading state
|
403
448
|
if (this.saveSettingsBtn) {
|
404
449
|
this.saveSettingsBtn.disabled = true;
|
@@ -421,6 +466,9 @@ class SettingsManager {
|
|
421
466
|
|
422
467
|
this.showSuccess('Settings saved successfully! Changes will apply to new calculations.');
|
423
468
|
|
469
|
+
// Dispatch event to notify other components that settings were updated
|
470
|
+
window.dispatchEvent(new CustomEvent('settingsUpdated'));
|
471
|
+
|
424
472
|
// Auto-close modal after 2 seconds
|
425
473
|
setTimeout(() => {
|
426
474
|
this.closeModal();
|