GameSentenceMiner 2.17.6__py3-none-any.whl → 2.18.0__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.
Files changed (51) hide show
  1. GameSentenceMiner/ai/ai_prompting.py +51 -51
  2. GameSentenceMiner/anki.py +236 -152
  3. GameSentenceMiner/gametext.py +7 -4
  4. GameSentenceMiner/gsm.py +49 -10
  5. GameSentenceMiner/locales/en_us.json +7 -3
  6. GameSentenceMiner/locales/ja_jp.json +8 -4
  7. GameSentenceMiner/locales/zh_cn.json +8 -4
  8. GameSentenceMiner/obs.py +238 -59
  9. GameSentenceMiner/ocr/owocr_helper.py +1 -1
  10. GameSentenceMiner/tools/ss_selector.py +7 -8
  11. GameSentenceMiner/ui/__init__.py +0 -0
  12. GameSentenceMiner/ui/anki_confirmation.py +187 -0
  13. GameSentenceMiner/{config_gui.py → ui/config_gui.py} +102 -37
  14. GameSentenceMiner/ui/screenshot_selector.py +215 -0
  15. GameSentenceMiner/util/configuration.py +124 -22
  16. GameSentenceMiner/util/db.py +22 -13
  17. GameSentenceMiner/util/downloader/download_tools.py +2 -2
  18. GameSentenceMiner/util/ffmpeg.py +24 -30
  19. GameSentenceMiner/util/get_overlay_coords.py +34 -34
  20. GameSentenceMiner/util/gsm_utils.py +31 -1
  21. GameSentenceMiner/util/text_log.py +11 -9
  22. GameSentenceMiner/vad.py +31 -12
  23. GameSentenceMiner/web/database_api.py +742 -123
  24. GameSentenceMiner/web/static/css/dashboard-shared.css +241 -0
  25. GameSentenceMiner/web/static/css/kanji-grid.css +94 -2
  26. GameSentenceMiner/web/static/css/overview.css +850 -0
  27. GameSentenceMiner/web/static/css/popups-shared.css +126 -0
  28. GameSentenceMiner/web/static/css/shared.css +97 -0
  29. GameSentenceMiner/web/static/css/stats.css +192 -597
  30. GameSentenceMiner/web/static/js/anki_stats.js +6 -4
  31. GameSentenceMiner/web/static/js/database.js +209 -5
  32. GameSentenceMiner/web/static/js/goals.js +610 -0
  33. GameSentenceMiner/web/static/js/kanji-grid.js +267 -4
  34. GameSentenceMiner/web/static/js/overview.js +1176 -0
  35. GameSentenceMiner/web/static/js/shared.js +25 -0
  36. GameSentenceMiner/web/static/js/stats.js +154 -1459
  37. GameSentenceMiner/web/stats.py +2 -2
  38. GameSentenceMiner/web/templates/anki_stats.html +5 -0
  39. GameSentenceMiner/web/templates/components/navigation.html +3 -1
  40. GameSentenceMiner/web/templates/database.html +73 -1
  41. GameSentenceMiner/web/templates/goals.html +376 -0
  42. GameSentenceMiner/web/templates/index.html +13 -11
  43. GameSentenceMiner/web/templates/overview.html +416 -0
  44. GameSentenceMiner/web/templates/stats.html +46 -251
  45. GameSentenceMiner/web/texthooking_page.py +18 -0
  46. {gamesentenceminer-2.17.6.dist-info → gamesentenceminer-2.18.0.dist-info}/METADATA +5 -1
  47. {gamesentenceminer-2.17.6.dist-info → gamesentenceminer-2.18.0.dist-info}/RECORD +51 -41
  48. {gamesentenceminer-2.17.6.dist-info → gamesentenceminer-2.18.0.dist-info}/WHEEL +0 -0
  49. {gamesentenceminer-2.17.6.dist-info → gamesentenceminer-2.18.0.dist-info}/entry_points.txt +0 -0
  50. {gamesentenceminer-2.17.6.dist-info → gamesentenceminer-2.18.0.dist-info}/licenses/LICENSE +0 -0
  51. {gamesentenceminer-2.17.6.dist-info → gamesentenceminer-2.18.0.dist-info}/top_level.txt +0 -0
@@ -48,8 +48,8 @@ def get_gradient_color(frequency, max_frequency):
48
48
  if max_frequency == 0:
49
49
  return "#ebedf0" # Default color for no encounters
50
50
 
51
- # kanji with 500+ encounters should always get cyan color cause i think u should know them
52
- if frequency > 500:
51
+ # kanji with 300+ encounters should always get cyan color cause i think u should know them
52
+ if frequency > 300:
53
53
  return "#2ee6e0"
54
54
 
55
55
  # Normalize frequency to 0-1 range with square root transformation
@@ -17,6 +17,10 @@
17
17
  <!-- Include shared CSS -->
18
18
  <link rel="stylesheet" href="/static/css/shared.css">
19
19
 
20
+ <!-- Include shared dashboard and popup CSS -->
21
+ <link rel="stylesheet" href="/static/css/dashboard-shared.css">
22
+ <link rel="stylesheet" href="/static/css/popups-shared.css">
23
+
20
24
  <!-- Include stats-specific CSS -->
21
25
  <link rel="stylesheet" href="/static/css/stats.css">
22
26
 
@@ -81,6 +85,7 @@
81
85
  Missing High-Frequency Kanji
82
86
  </h3>
83
87
  <p class="dashboard-card-subtitle">Kanji seen often in GSM but not present in your Anki collection</p>
88
+ <p class="dashboard-card-subtitle">Note: Please Sort By frequency, as grey means the Kanji might be in your Anki</p>
84
89
  </div>
85
90
  <div class="dashboard-streak-indicator" id="missingKanjiStreak" style="display: none;">
86
91
  <span id="missingStreakValue">0</span> to learn
@@ -2,10 +2,12 @@
2
2
  <div class="navigation" style="display: flex; justify-content: center; align-items: center; margin-bottom: 30px; padding: 15px; background: var(--bg-secondary); border-radius: 8px; box-shadow: 0 2px 8px var(--shadow-color); border: 1px solid var(--border-color);">
3
3
  <div style="display: flex; gap: 15px;">
4
4
  <!-- <a href="/" class="nav-link">Home</a> -->
5
+ <a href="/overview" class="nav-link">Overview</a>
5
6
  <a href="/stats" class="nav-link">Statistics</a>
7
+ <a href="/goals" class="nav-link">Goals</a>
8
+ <a href="/anki_stats" class="nav-link">Anki Stats</a>
6
9
  <a href="/search" class="nav-link">Search</a>
7
10
  <a href="/database" class="nav-link">Database Management</a>
8
- <a href="/anki_stats" class="nav-link">Anki Stats</a>
9
11
  </div>
10
12
  <button class="theme-toggle" id="settingsToggle" title="Settings">
11
13
  <span id="settingsIcon">⚙️</span>
@@ -95,11 +95,19 @@
95
95
  <div id="gamesDeletionModal" class="modal">
96
96
  <div class="modal-content" style="max-width: 800px;">
97
97
  <div class="modal-header">
98
- <h3>Delete Games</h3>
98
+ <h3>Manage Games</h3>
99
99
  <span class="close-btn" data-action="closeModal" data-modal="gamesDeletionModal">&times;</span>
100
100
  </div>
101
101
  <div class="modal-body">
102
102
  <p class="warning-text">Select games to delete. This will permanently remove all associated sentences and data.</p>
103
+
104
+ <p class="warning-text">When Merging, the first selected game will be the target for all merged data.</p>
105
+
106
+ <div style="margin-bottom: 15px; padding: 10px; background: var(--bg-tertiary); border-radius: 5px; border-left: 4px solid var(--accent-color);">
107
+ <small style="color: var(--text-secondary); font-size: 13px;">
108
+ <strong>Note:</strong> Game names come from the OBS Scene, to change the name for future stats, change the scene name in OBS.
109
+ </small>
110
+ </div>
103
111
 
104
112
  <div id="gamesLoadingIndicator" class="loading-indicator">
105
113
  <div class="spinner"></div>
@@ -118,6 +126,7 @@
118
126
  </div>
119
127
  <div class="modal-footer">
120
128
  <button class="action-btn" data-action="closeModal" data-modal="gamesDeletionModal">Cancel</button>
129
+ <button class="action-btn warning" id="mergeSelectedGamesBtn" data-action="mergeSelectedGames" disabled>Merge Selected Games</button>
121
130
  <button class="action-btn danger" id="deleteSelectedGamesBtn" data-action="deleteSelectedGames" disabled>Delete Selected</button>
122
131
  </div>
123
132
  </div>
@@ -272,6 +281,69 @@
272
281
  </div>
273
282
  </div>
274
283
 
284
+ <!-- Game Merge Confirmation Modal -->
285
+ <div id="gameMergeModal" class="modal">
286
+ <div class="modal-content" style="max-width: 700px;">
287
+ <div class="modal-header">
288
+ <h3>Confirm Game Merge</h3>
289
+ <span class="close-btn" data-action="closeModal" data-modal="gameMergeModal">&times;</span>
290
+ </div>
291
+ <div class="modal-body">
292
+ <p class="warning-text">
293
+ ⚠️ This action is irreversible! You are about to merge multiple games into a single game entry.
294
+ </p>
295
+
296
+ <div class="merge-summary">
297
+ <h4 style="margin-bottom: 15px; color: var(--text-primary);">Merge Details:</h4>
298
+
299
+ <div class="primary-game-info" style="background: var(--bg-tertiary); padding: 15px; border-radius: 8px; margin-bottom: 20px; border-left: 4px solid var(--success-color);">
300
+ <h5 style="margin: 0 0 10px 0; color: var(--success-color);">
301
+ 📁 Primary Game (will keep this name):
302
+ </h5>
303
+ <div id="primaryGameName" style="font-weight: 600; font-size: 16px; color: var(--text-primary);"></div>
304
+ <div id="primaryGameStats" style="color: var(--text-secondary); font-size: 14px; margin-top: 5px;"></div>
305
+ </div>
306
+
307
+ <div class="secondary-games-info" style="background: var(--bg-tertiary); padding: 15px; border-radius: 8px; margin-bottom: 20px; border-left: 4px solid var(--warning-color);">
308
+ <h5 style="margin: 0 0 10px 0; color: var(--warning-color);">
309
+ 🔄 Games to be merged (will be removed):
310
+ </h5>
311
+ <div id="secondaryGamesList" style="max-height: 150px; overflow-y: auto;"></div>
312
+ </div>
313
+
314
+ <div class="merge-stats" style="background: var(--bg-secondary); padding: 15px; border-radius: 8px; border: 1px solid var(--border-color);">
315
+ <div class="stats-row">
316
+ <span class="stats-label">Total sentences after merge:</span>
317
+ <span class="stats-value" id="totalSentencesAfterMerge">0</span>
318
+ </div>
319
+ <div class="stats-row">
320
+ <span class="stats-label">Total characters after merge:</span>
321
+ <span class="stats-value" id="totalCharactersAfterMerge">0</span>
322
+ </div>
323
+ <div class="stats-row">
324
+ <span class="stats-label">Games being consolidated:</span>
325
+ <span class="stats-value" id="gamesBeingMerged">0</span>
326
+ </div>
327
+ </div>
328
+ </div>
329
+
330
+ <div id="mergeError" class="error-text" style="display: none;"></div>
331
+ <div id="mergeSuccess" class="success-text" style="display: none;"></div>
332
+
333
+ <div id="mergeLoadingIndicator" class="loading-indicator" style="display: none;">
334
+ <div class="spinner"></div>
335
+ <span>Merging games...</span>
336
+ </div>
337
+ </div>
338
+ <div class="modal-footer">
339
+ <button class="action-btn" data-action="closeModal" data-modal="gameMergeModal">Cancel</button>
340
+ <button class="action-btn warning" id="confirmMergeBtn" data-action="confirmGameMerge">
341
+ 🔀 Confirm Merge
342
+ </button>
343
+ </div>
344
+ </div>
345
+ </div>
346
+
275
347
  <!-- Include shared JavaScript first (required dependency for database.js) -->
276
348
  <script src="/static/js/shared.js"></script>
277
349
  <script src="/static/js/database.js"></script>
@@ -0,0 +1,376 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>GSM - Goals</title>
8
+ <!-- Include Chart.js from a CDN -->
9
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
10
+
11
+ <!-- Include html2canvas for screenshot functionality -->
12
+ <script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
13
+
14
+ <!-- Include shared theme styles -->
15
+ {% include 'components/theme-styles.html' %}
16
+
17
+ <!-- Include shared CSS -->
18
+ <link rel="stylesheet" href="/static/css/shared.css">
19
+
20
+ <!-- Include shared dashboard and popup CSS -->
21
+ <link rel="stylesheet" href="/static/css/dashboard-shared.css">
22
+ <link rel="stylesheet" href="/static/css/popups-shared.css">
23
+
24
+ <!-- Include stats-specific CSS for goals -->
25
+ <link rel="stylesheet" href="/static/css/stats.css">
26
+ </head>
27
+
28
+ <body>
29
+
30
+ <div class="container">
31
+ <h1>GSM - Goals</h1>
32
+
33
+ <!-- Include shared navigation -->
34
+ {% include 'components/navigation.html' %}
35
+
36
+ <!-- Goal Progress Chart -->
37
+ <div class="dashboard-card goal-progress-chart" id="goalProgressChart" style="margin-bottom: 30px;">
38
+ <div class="dashboard-card-header">
39
+ <h3 class="dashboard-card-title">
40
+ <span class="dashboard-card-icon">🎯</span>
41
+ Goal Progress Chart
42
+ </h3>
43
+ <p class="dashboard-card-subtitle">Track your reading goals and projected completion dates</p>
44
+ </div>
45
+
46
+ <div class="goal-progress-grid">
47
+ <!-- Reading Hours Goal -->
48
+ <div class="goal-progress-item">
49
+ <div class="goal-progress-header">
50
+ <div class="goal-progress-label">
51
+ <span class="goal-icon">⏱️</span>
52
+ Reading Hours
53
+ </div>
54
+ <div class="goal-progress-values">
55
+ <span class="goal-current" id="goalHoursCurrent">-</span>
56
+ <span class="goal-separator">/</span>
57
+ <span class="goal-target" id="goalHoursTarget">-</span>
58
+ </div>
59
+ </div>
60
+ <div class="goal-progress-bar">
61
+ <div class="goal-progress-fill" id="goalHoursProgress" data-percentage="0"></div>
62
+ </div>
63
+ <div class="goal-progress-info">
64
+ <span class="goal-percentage" id="goalHoursPercentage">0%</span>
65
+ <span class="goal-projection" id="goalHoursProjection">-</span>
66
+ </div>
67
+ </div>
68
+
69
+ <!-- Character Count Goal -->
70
+ <div class="goal-progress-item">
71
+ <div class="goal-progress-header">
72
+ <div class="goal-progress-label">
73
+ <span class="goal-icon">📖</span>
74
+ Characters Read
75
+ </div>
76
+ <div class="goal-progress-values">
77
+ <span class="goal-current" id="goalCharsCurrent">-</span>
78
+ <span class="goal-separator">/</span>
79
+ <span class="goal-target" id="goalCharsTarget">-</span>
80
+ </div>
81
+ </div>
82
+ <div class="goal-progress-bar">
83
+ <div class="goal-progress-fill" id="goalCharsProgress" data-percentage="0"></div>
84
+ </div>
85
+ <div class="goal-progress-info">
86
+ <span class="goal-percentage" id="goalCharsPercentage">0%</span>
87
+ <span class="goal-projection" id="goalCharsProjection">-</span>
88
+ </div>
89
+ </div>
90
+
91
+ <!-- Games Goal -->
92
+ <div class="goal-progress-item">
93
+ <div class="goal-progress-header">
94
+ <div class="goal-progress-label">
95
+ <span class="goal-icon">🎮</span>
96
+ Games
97
+ </div>
98
+ <div class="goal-progress-values">
99
+ <span class="goal-current" id="goalGamesCurrent">-</span>
100
+ <span class="goal-separator">/</span>
101
+ <span class="goal-target" id="goalGamesTarget">-</span>
102
+ </div>
103
+ </div>
104
+ <div class="goal-progress-bar">
105
+ <div class="goal-progress-fill" id="goalGamesProgress" data-percentage="0"></div>
106
+ </div>
107
+ <div class="goal-progress-info">
108
+ <span class="goal-percentage" id="goalGamesPercentage">0%</span>
109
+ <span class="goal-projection" id="goalGamesProjection">-</span>
110
+ </div>
111
+ </div>
112
+ </div>
113
+
114
+ <!-- Loading/Error states -->
115
+ <div class="goal-progress-loading" id="goalProgressLoading" style="display: none;">
116
+ <div class="spinner"></div>
117
+ <span>Loading goal progress...</span>
118
+ </div>
119
+
120
+ <div class="goal-progress-error" id="goalProgressError" style="display: none;">
121
+ <div class="goal-progress-error-icon">⚠️</div>
122
+ <div class="goal-progress-error-message">Failed to load goal progress</div>
123
+ <button class="retry-btn" onclick="loadGoalProgress()">Retry</button>
124
+ </div>
125
+ </div>
126
+
127
+ <!-- Goals Today Card -->
128
+ <div class="dashboard-card today-goals" id="todayGoalsCard" style="margin-bottom: 30px;">
129
+ <div class="dashboard-card-header">
130
+ <h3 class="dashboard-card-title">
131
+ <span class="dashboard-card-icon">📅</span>
132
+ Goals Today
133
+ </h3>
134
+ <p class="dashboard-card-subtitle" id="todayGoalsDate">Loading...</p>
135
+ </div>
136
+
137
+ <div id="noTargetsMessage" class="no-targets-message" style="display: none; text-align: center; padding: 40px 20px; color: var(--text-tertiary); font-style: italic;">
138
+ Set target dates in settings to see your daily goals
139
+ </div>
140
+
141
+ <div class="dashboard-stats-grid" id="todayGoalsStats">
142
+ <div class="dashboard-stat-item goal-stat-item tooltip" data-tooltip="Your progress toward today's hours goal" id="hoursGoalItem" style="display: none;">
143
+ <span class="dashboard-stat-value">
144
+ <span id="todayHoursProgress">-</span>
145
+ <span class="goal-separator">/</span>
146
+ <span id="todayHoursRequired">-</span>
147
+ </span>
148
+ <span class="dashboard-stat-label">Hours Required</span>
149
+ </div>
150
+ <div class="dashboard-stat-item goal-stat-item tooltip" data-tooltip="Your progress toward today's character goal" id="charsGoalItem" style="display: none;">
151
+ <span class="dashboard-stat-value">
152
+ <span id="todayCharsProgress">-</span>
153
+ <span class="goal-separator">/</span>
154
+ <span id="todayCharsRequired">-</span>
155
+ </span>
156
+ <span class="dashboard-stat-label">Characters Required</span>
157
+ </div>
158
+ </div>
159
+
160
+ <div class="dashboard-progress-section" id="todayGoalsProgress" style="display: none;">
161
+ <div class="dashboard-progress-title">Daily Progress</div>
162
+ <div class="dashboard-progress-items">
163
+ <div class="dashboard-progress-item" id="hoursDaysRemaining" style="display: none;">
164
+ <div class="dashboard-progress-value neutral" id="hoursRemainingValue">-</div>
165
+ <div class="dashboard-progress-label">Days Until Hours Goal</div>
166
+ </div>
167
+ <div class="dashboard-progress-item" id="charsDaysRemaining" style="display: none;">
168
+ <div class="dashboard-progress-value neutral" id="charsRemainingValue">-</div>
169
+ <div class="dashboard-progress-label">Days Until Chars Goal</div>
170
+ </div>
171
+ <div class="dashboard-progress-item" id="gamesDaysRemaining" style="display: none;">
172
+ <div class="dashboard-progress-value neutral" id="gamesRemainingValue">-</div>
173
+ <div class="dashboard-progress-label">Days Until Games Goal</div>
174
+ </div>
175
+ </div>
176
+ </div>
177
+ </div>
178
+
179
+ <!-- Your Stats by Goal Date Card -->
180
+ <div class="dashboard-card projection-card" id="projectionCard" style="margin-bottom: 30px;">
181
+ <div class="dashboard-card-header">
182
+ <h3 class="dashboard-card-title">
183
+ <span class="dashboard-card-icon">📊</span>
184
+ Your Stats by Goal Date
185
+ </h3>
186
+ <p class="dashboard-card-subtitle">Based on your 30-day average reading pace</p>
187
+ </div>
188
+
189
+ <div id="noProjectionsMessage" class="no-targets-message" style="display: none; text-align: center; padding: 40px 20px; color: var(--text-tertiary); font-style: italic;">
190
+ Set target dates in settings to see projections
191
+ </div>
192
+
193
+ <div class="dashboard-stats-grid" id="projectionStats">
194
+ <div class="dashboard-stat-item tooltip" data-tooltip="Total hours you'll have by your target date" id="hoursProjectionItem" style="display: none;">
195
+ <span class="dashboard-stat-value" id="projectionHoursValue">-</span>
196
+ <span class="dashboard-stat-label">Total Hours by Target</span>
197
+ </div>
198
+ <div class="dashboard-stat-item tooltip" data-tooltip="Total characters you'll have by your target date" id="charsProjectionItem" style="display: none;">
199
+ <span class="dashboard-stat-value" id="projectionCharsValue">-</span>
200
+ <span class="dashboard-stat-label">Total Characters by Target</span>
201
+ </div>
202
+ <div class="dashboard-stat-item tooltip" data-tooltip="Total games you'll have by your target date" id="gamesProjectionItem" style="display: none;">
203
+ <span class="dashboard-stat-value" id="projectionGamesValue">-</span>
204
+ <span class="dashboard-stat-label">Total Games by Target</span>
205
+ </div>
206
+ </div>
207
+
208
+ <div class="dashboard-progress-section" id="projectionProgress" style="display: none;">
209
+ <div class="dashboard-progress-title">Projection Summary</div>
210
+ <div class="dashboard-progress-items">
211
+ <div class="dashboard-progress-item" id="hoursProjectionSummary" style="display: none;">
212
+ <div class="dashboard-progress-value positive" id="hoursProjectionStatus">-</div>
213
+ <div class="dashboard-progress-label">Hours Status</div>
214
+ </div>
215
+ <div class="dashboard-progress-item" id="charsProjectionSummary" style="display: none;">
216
+ <div class="dashboard-progress-value positive" id="charsProjectionStatus">-</div>
217
+ <div class="dashboard-progress-label">Characters Status</div>
218
+ </div>
219
+ <div class="dashboard-progress-item" id="gamesProjectionSummary" style="display: none;">
220
+ <div class="dashboard-progress-value positive" id="gamesProjectionStatus">-</div>
221
+ <div class="dashboard-progress-label">Games Status</div>
222
+ </div>
223
+ </div>
224
+ </div>
225
+ </div>
226
+
227
+ <!-- Settings Modal -->
228
+ <div id="settingsModal" class="modal">
229
+ <div class="modal-content">
230
+ <div class="modal-header">
231
+ <h3>Goals Settings</h3>
232
+ <span class="close-btn" id="closeSettingsModal">&times;</span>
233
+ </div>
234
+ <div class="modal-body">
235
+ <p style="color: var(--text-secondary); margin-bottom: 20px;">
236
+ Configure your reading goals and target dates.
237
+ </p>
238
+ <form id="settingsForm">
239
+ <!-- Reading Goals Section -->
240
+ <div style="margin-bottom: 20px;">
241
+ <label
242
+ style="display: block; font-weight: 600; margin-bottom: 8px; color: var(--text-primary);">
243
+ Reading Goals Configuration
244
+ </label>
245
+ <p style="color: var(--text-secondary); margin-bottom: 20px; font-size: 14px;">
246
+ Set your long-term reading targets and deadlines for tracking progress.
247
+ </p>
248
+
249
+ <div style="margin-bottom: 15px;">
250
+ <label for="readingHoursTarget"
251
+ style="display: block; font-weight: 600; margin-bottom: 8px; color: var(--text-primary);">
252
+ Reading Hours Target
253
+ </label>
254
+ <input type="number" id="readingHoursTarget" name="reading_hours_target" min="1"
255
+ max="10000"
256
+ style="width: 90%; padding: 10px; border: 1px solid var(--border-color); border-radius: 5px; background: var(--bg-tertiary); color: var(--text-primary); font-size: 14px;"
257
+ placeholder="1500">
258
+ <small
259
+ style="color: var(--text-tertiary); font-size: 12px; margin-top: 4px; display: block;">
260
+ Total reading hours goal (1-10,000 hours) - Default: 1500 hours
261
+ </small>
262
+ </div>
263
+
264
+ <div style="margin-bottom: 15px;">
265
+ <label for="readingHoursTargetDate"
266
+ style="display: block; font-weight: 600; margin-bottom: 8px; color: var(--text-primary);">
267
+ Reading Hours Target Date
268
+ </label>
269
+ <input type="date" id="readingHoursTargetDate" name="reading_hours_target_date"
270
+ style="width: 90%; padding: 10px; border: 1px solid var(--border-color); border-radius: 5px; background: var(--bg-tertiary); color: var(--text-primary); font-size: 14px;">
271
+ <small
272
+ style="color: var(--text-tertiary); font-size: 12px; margin-top: 4px; display: block;">
273
+ Target date to achieve your reading hours goal
274
+ </small>
275
+ </div>
276
+
277
+ <div style="margin-bottom: 15px;">
278
+ <label for="characterCountTarget"
279
+ style="display: block; font-weight: 600; margin-bottom: 8px; color: var(--text-primary);">
280
+ Character Count Target
281
+ </label>
282
+ <input type="number" id="characterCountTarget" name="character_count_target" min="1000"
283
+ max="1000000000"
284
+ style="width: 90%; padding: 10px; border: 1px solid var(--border-color); border-radius: 5px; background: var(--bg-tertiary); color: var(--text-primary); font-size: 14px;"
285
+ placeholder="25000000">
286
+ <small
287
+ style="color: var(--text-tertiary); font-size: 12px; margin-top: 4px; display: block;">
288
+ Total characters read goal (1,000-1,000,000,000) - Default: 25 million
289
+ </small>
290
+ </div>
291
+
292
+ <div style="margin-bottom: 15px;">
293
+ <label for="characterCountTargetDate"
294
+ style="display: block; font-weight: 600; margin-bottom: 8px; color: var(--text-primary);">
295
+ Character Count Target Date
296
+ </label>
297
+ <input type="date" id="characterCountTargetDate" name="character_count_target_date"
298
+ style="width: 90%; padding: 10px; border: 1px solid var(--border-color); border-radius: 5px; background: var(--bg-tertiary); color: var(--text-primary); font-size: 14px;">
299
+ <small
300
+ style="color: var(--text-tertiary); font-size: 12px; margin-top: 4px; display: block;">
301
+ Target date to achieve your character count goal
302
+ </small>
303
+ </div>
304
+
305
+ <div style="margin-bottom: 15px;">
306
+ <label for="gamesTarget"
307
+ style="display: block; font-weight: 600; margin-bottom: 8px; color: var(--text-primary);">
308
+ Games/Visual Novels Target
309
+ </label>
310
+ <input type="number" id="gamesTarget" name="games_target" min="1" max="1000"
311
+ style="width: 90%; padding: 10px; border: 1px solid var(--border-color); border-radius: 5px; background: var(--bg-tertiary); color: var(--text-primary); font-size: 14px;"
312
+ placeholder="100">
313
+ <small
314
+ style="color: var(--text-tertiary); font-size: 12px; margin-top: 4px; display: block;">
315
+ Number of games/visual novels to complete (1-1,000) - Default: 100
316
+ </small>
317
+ </div>
318
+
319
+ <div style="margin-bottom: 15px;">
320
+ <label for="gamesTargetDate"
321
+ style="display: block; font-weight: 600; margin-bottom: 8px; color: var(--text-primary);">
322
+ Games/Visual Novels Target Date
323
+ </label>
324
+ <input type="date" id="gamesTargetDate" name="games_target_date"
325
+ style="width: 90%; padding: 10px; border: 1px solid var(--border-color); border-radius: 5px; background: var(--bg-tertiary); color: var(--text-primary); font-size: 14px;"
326
+ data-date-format="yyyy-mm-dd">
327
+ <small
328
+ style="color: var(--text-tertiary); font-size: 12px; margin-top: 4px; display: block;">
329
+ Target date to achieve your games goal (Format: YYYY-MM-DD)
330
+ </small>
331
+ </div>
332
+ </div>
333
+ </form>
334
+
335
+ <div id="settingsError"
336
+ style="display: none; background: var(--danger-color); color: white; padding: 10px; border-radius: 5px; margin-bottom: 15px; font-size: 14px;">
337
+ </div>
338
+ <div id="settingsSuccess"
339
+ style="display: none; background: var(--success-color); color: white; padding: 10px; border-radius: 5px; margin-bottom: 15px; font-size: 14px;">
340
+ </div>
341
+ </div>
342
+ <div class="modal-footer">
343
+ <button id="cancelSettingsBtn" class="cancel-btn">Cancel</button>
344
+ <button id="saveSettingsBtn" class="confirm-delete-btn">Save Settings</button>
345
+ </div>
346
+ </div>
347
+ </div>
348
+ </div>
349
+
350
+ {% set hours_target = stats_config.reading_hours_target | default(1500) %}
351
+ {% set chars_target = stats_config.character_count_target | default(25000000) %}
352
+ {% set games_target = stats_config.games_target | default(100) %}
353
+ {% set hours_target_date = stats_config.reading_hours_target_date | default('') %}
354
+ {% set chars_target_date = stats_config.character_count_target_date | default('') %}
355
+ {% set games_target_date = stats_config.games_target_date | default('') %}
356
+
357
+ {% set stats_config = {
358
+ 'readingHoursTarget': hours_target,
359
+ 'characterCountTarget': chars_target,
360
+ 'gamesTarget': games_target,
361
+ 'readingHoursTargetDate': hours_target_date,
362
+ 'characterCountTargetDate': chars_target_date,
363
+ 'gamesTargetDate': games_target_date
364
+ } %}
365
+
366
+ <!-- Inject stats config values for frontend -->
367
+ <script>
368
+ window.statsConfig = {{ stats_config | tojson }};
369
+ </script>
370
+ <!-- Include shared JavaScript first (required dependency for goals.js) -->
371
+ <script src="/static/js/shared.js"></script>
372
+ <script src="/static/js/goals.js"></script>
373
+
374
+ </body>
375
+
376
+ </html>