GameSentenceMiner 2.15.10__py3-none-any.whl → 2.15.12__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 (24) hide show
  1. GameSentenceMiner/anki.py +31 -0
  2. GameSentenceMiner/ocr/owocr_helper.py +5 -5
  3. GameSentenceMiner/web/static/css/kanji-grid.css +107 -0
  4. GameSentenceMiner/web/static/css/search.css +14 -0
  5. GameSentenceMiner/web/static/css/shared.css +932 -0
  6. GameSentenceMiner/web/static/css/stats.css +499 -0
  7. GameSentenceMiner/web/static/js/anki_stats.js +84 -0
  8. GameSentenceMiner/web/static/js/database.js +541 -0
  9. GameSentenceMiner/web/static/js/kanji-grid.js +203 -0
  10. GameSentenceMiner/web/static/js/search.js +273 -0
  11. GameSentenceMiner/web/static/js/shared.js +506 -0
  12. GameSentenceMiner/web/static/js/stats.js +1427 -0
  13. GameSentenceMiner/web/templates/anki_stats.html +205 -0
  14. GameSentenceMiner/web/templates/components/navigation.html +16 -0
  15. GameSentenceMiner/web/templates/components/theme-styles.html +128 -0
  16. GameSentenceMiner/web/templates/stats.html +4 -0
  17. GameSentenceMiner/web/texthooking_page.py +50 -0
  18. {gamesentenceminer-2.15.10.dist-info → gamesentenceminer-2.15.12.dist-info}/METADATA +1 -1
  19. {gamesentenceminer-2.15.10.dist-info → gamesentenceminer-2.15.12.dist-info}/RECORD +23 -11
  20. GameSentenceMiner/web/templates/text_replacements.html +0 -449
  21. {gamesentenceminer-2.15.10.dist-info → gamesentenceminer-2.15.12.dist-info}/WHEEL +0 -0
  22. {gamesentenceminer-2.15.10.dist-info → gamesentenceminer-2.15.12.dist-info}/entry_points.txt +0 -0
  23. {gamesentenceminer-2.15.10.dist-info → gamesentenceminer-2.15.12.dist-info}/licenses/LICENSE +0 -0
  24. {gamesentenceminer-2.15.10.dist-info → gamesentenceminer-2.15.12.dist-info}/top_level.txt +0 -0
@@ -1,449 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Text Error Fixes (Electron)</title>
7
- <link rel="stylesheet" href="/static/style.css">
8
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet">
9
- <style>
10
- :root {
11
- /* Light theme colors */
12
- --bg-primary: #f8f9fa;
13
- --bg-secondary: #ffffff;
14
- --bg-tertiary: #e9ecef;
15
- --text-primary: #212529;
16
- --text-secondary: #495057;
17
- --text-tertiary: #6c757d;
18
- --border-color: #dee2e6;
19
- --shadow-color: rgba(0, 0, 0, 0.08);
20
- --accent-color: #007bff;
21
- --success-color: #28a745;
22
- --warning-color: #ffc107;
23
- --danger-color: #dc3545;
24
- --info-color: #17a2b8;
25
- }
26
-
27
- [data-theme="dark"] {
28
- /* Dark theme colors */
29
- --bg-primary: #1a1a1a;
30
- --bg-secondary: #2d2d2d;
31
- --bg-tertiary: #3a3a3a;
32
- --text-primary: #e1e1e1;
33
- --text-secondary: #b8b8b8;
34
- --text-tertiary: #8a8a8a;
35
- --border-color: #404040;
36
- --shadow-color: rgba(0, 0, 0, 0.3);
37
- --accent-color: #4dabf7;
38
- --success-color: #51cf66;
39
- --warning-color: #ffd43b;
40
- --danger-color: #ff6b6b;
41
- --info-color: #22b8cf;
42
- }
43
-
44
- @media (prefers-color-scheme: dark) {
45
- :root:not([data-theme="light"]) {
46
- /* Auto dark mode colors */
47
- --bg-primary: #1a1a1a;
48
- --bg-secondary: #2d2d2d;
49
- --bg-tertiary: #3a3a3a;
50
- --text-primary: #e1e1e1;
51
- --text-secondary: #b8b8b8;
52
- --text-tertiary: #8a8a8a;
53
- --border-color: #404040;
54
- --shadow-color: rgba(0, 0, 0, 0.3);
55
- --accent-color: #4dabf7;
56
- --success-color: #51cf66;
57
- --warning-color: #ffd43b;
58
- --danger-color: #ff6b6b;
59
- --info-color: #22b8cf;
60
- }
61
- }
62
-
63
- .nav-link {
64
- display: inline-block;
65
- padding: 8px 16px;
66
- background-color: var(--bg-tertiary);
67
- color: var(--text-primary);
68
- text-decoration: none;
69
- border-radius: 5px;
70
- transition: all 0.3s ease;
71
- border: 1px solid var(--border-color);
72
- }
73
-
74
- .nav-link:hover {
75
- background-color: var(--accent-color);
76
- color: var(--bg-secondary);
77
- transform: translateY(-1px);
78
- }
79
-
80
- .theme-toggle {
81
- background: var(--bg-tertiary);
82
- border: 2px solid var(--border-color);
83
- border-radius: 50%;
84
- width: 40px;
85
- height: 40px;
86
- display: flex;
87
- align-items: center;
88
- justify-content: center;
89
- cursor: pointer;
90
- transition: all 0.3s ease;
91
- color: var(--text-primary);
92
- font-size: 18px;
93
- margin-left: 15px;
94
- }
95
-
96
- .theme-toggle:hover {
97
- background: var(--bg-primary);
98
- transform: scale(1.1);
99
- }
100
-
101
- .theme-toggle:active {
102
- transform: scale(0.95);
103
- }
104
-
105
- @media (max-width: 768px) {
106
- .navigation {
107
- padding: 10px;
108
- flex-direction: column;
109
- gap: 10px;
110
- }
111
-
112
- .navigation > div {
113
- flex-direction: column;
114
- gap: 10px;
115
- }
116
-
117
- .navigation .nav-link {
118
- display: block !important;
119
- text-align: center;
120
- width: 100%;
121
- }
122
-
123
- .theme-toggle {
124
- margin-left: 0;
125
- align-self: center;
126
- }
127
- }
128
- </style>
129
- </head>
130
- <body> <div class="container">
131
- <h1>Text Error Fixes</h1>
132
-
133
- <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);">
134
- <div style="display: flex; gap: 15px;">
135
- <a href="/" class="nav-link">Home</a>
136
- <a href="/stats" class="nav-link">Statistics</a>
137
- <a href="/search" class="nav-link">Search</a>
138
- <a href="/text_replacements_page" class="nav-link">Text Replacements</a>
139
- </div>
140
- <button class="theme-toggle" id="themeToggle" title="Toggle dark mode">
141
- <span id="themeIcon">🌙</span>
142
- </button>
143
- </div>
144
-
145
- <!-- Explanation Section -->
146
- <div style="background: var(--bg-secondary); padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px var(--shadow-color); margin-bottom: 30px; border: 1px solid var(--border-color);">
147
- <h2 style="color: var(--text-secondary); margin-bottom: 15px;">What are Text Replacements?</h2>
148
- <p style="color: var(--text-primary); line-height: 1.6; margin-bottom: 10px;">
149
- Text replacements help you automatically fix common errors and clean up text captured from games. This is especially useful for:
150
- </p>
151
- <ul style="color: var(--text-primary); line-height: 1.6; margin-left: 20px; margin-bottom: 15px;">
152
- <li><strong>OCR Error Correction:</strong> Fix text recognition mistakes from screenshots</li>
153
- <li><strong>Text Standardization:</strong> Replace inconsistent character names or terms</li>
154
- <li><strong>Regex Pattern Support:</strong> Use "re:" prefix for advanced pattern matching (e.g., "re:.{3,}" for sequences)</li>
155
- <li><strong>Game-Specific Fixes:</strong> Handle unique formatting issues from different games</li>
156
- </ul>
157
- <p style="color: var(--text-tertiary); font-size: 14px; margin: 0;">
158
- <strong>Pro Tip:</strong> Replacements are applied in the order they appear. Use regex patterns for complex transformations.
159
- </p>
160
- </div>
161
-
162
- <div class="control"> <button id="add-new-btn" class="button-blue">Add New Entry</button> <div class="search-container"> <input type="text" id="search-input" placeholder="Search key or value..." class="inputField"> <button id="search-button" class="button-green">Search</button> </div>
163
- <button id="go-back-btn" class="button-gray">Go Back</button>
164
- </div>
165
-
166
- <div id="data-table-container" class="table-container"> <table id="data-table" class="data-table"> <thead>
167
- <tr>
168
- <th>
169
- Text To Replace (Key)
170
- </th>
171
- <th>
172
- Replacement Text (Value)
173
- </th>
174
- <th>
175
- Actions
176
- </th>
177
- </tr>
178
- </thead>
179
- <tbody id="data-table-body">
180
- </tbody>
181
- </table>
182
- <p id="no-entries-message" class="no-entries-message hidden">No entries found.</p> </div>
183
- </div>
184
-
185
- <div id="entry-modal" class="modal"> <div class="modal-content"> <span class="close-button">&times;</span> <h2 id="modal-title">Add New Entry</h2> <form id="entry-form">
186
- <p>"re:" at the beginning = regex pattern (ex. re:.{3,}) </p>
187
- <div class="form-group"> <label for="key-input" class="form-label">Text To Replace (Key):</label> <input type="text" id="key-input" name="key" required class="form-input"> <input type="hidden" id="original-key-input">
188
- </div>
189
- <div class="form-group"> <label for="value-input" class="form-label">Replacement Text (Value):</label> <input type="text" id="value-input" name="value" class="form-input">
190
- </div>
191
- <div class="flex-end"> <button type="submit" class="button-blue">Save Entry</button> </div>
192
- </form>
193
- </div>
194
- </div>
195
-
196
- <script>
197
-
198
- let textData = {};
199
- let data = {};
200
-
201
- async function loadData() {
202
- try {
203
- const response = await fetch('/load-data');
204
- if (response.ok) {
205
- data = await response.json();
206
- textData = data.args?.replacements || {};
207
- } else {
208
- console.error('Failed to load data from server');
209
- }
210
- } catch (error) {
211
- console.error('Error loading data:', error);
212
- }
213
- renderTable();
214
- }
215
-
216
- async function saveData() {
217
- try {
218
- data.args.replacements = textData;
219
- const response = await fetch('/save-data', {
220
- method: 'POST',
221
- headers: { 'Content-Type': 'application/json' },
222
- body: JSON.stringify(data),
223
- });
224
- if (!response.ok) {
225
- console.error('Failed to save data to server');
226
- }
227
- } catch (error) {
228
- console.error('Error saving data:', error);
229
- }
230
- }
231
-
232
- function renderTable(dataToRender = textData) {
233
- const tableBody = document.getElementById('data-table-body');
234
- let tableHtml = '';
235
- const noEntriesMessage = document.getElementById('no-entries-message');
236
- const dataTable = document.getElementById('data-table');
237
- const keys = Object.keys(dataToRender);
238
-
239
- if (keys.length === 0) {
240
- noEntriesMessage.classList.remove('hidden');
241
- dataTable.classList.add('hidden');
242
- } else {
243
- noEntriesMessage.classList.add('hidden');
244
- dataTable.classList.remove('hidden');
245
- keys.forEach(key => {
246
- const value = dataToRender[key];
247
- tableHtml += `
248
- <tr>
249
- <td>${escapeHTML(key)}</td> <td>${escapeHTML(value)}</td> <td>
250
- <button class="action-button edit-btn" data-key="${escapeHTML(key)}">Edit</button>
251
- <button class="action-button delete-button delete-btn" data-key="${escapeHTML(key)}">Delete</button>
252
- </td>
253
- </tr>
254
- `;
255
- });
256
- tableBody.innerHTML = tableHtml;
257
- document.querySelectorAll('.edit-btn').forEach(button => {
258
- button.addEventListener('click', handleEditClick);
259
- });
260
- document.querySelectorAll('.delete-btn').forEach(button => {
261
- button.addEventListener('click', handleDeleteClick);
262
- });
263
- }
264
- }
265
-
266
- function escapeHTML(str) {
267
- const div = document.createElement('div');
268
- div.appendChild(document.createTextNode(str));
269
- return div.innerHTML;
270
- }
271
-
272
- const modal = document.getElementById('entry-modal');
273
- const modalTitle = document.getElementById('modal-title');
274
- const entryForm = document.getElementById('entry-form');
275
- const keyInput = document.getElementById('key-input');
276
- const valueInput = document.getElementById('value-input');
277
- const originalKeyInput = document.getElementById('original-key-input');
278
- const closeButton = document.querySelector('.close-button');
279
- const addNewBtn = document.getElementById('add-new-btn');
280
-
281
- addNewBtn.addEventListener('click', () => {
282
- modalTitle.textContent = 'Add New Entry';
283
- keyInput.value = '';
284
- valueInput.value = '';
285
- originalKeyInput.value = '';
286
- keyInput.disabled = false;
287
- modal.style.display = 'flex';
288
- });
289
-
290
- function handleEditClick(event) {
291
- const keyToEdit = event.target.dataset.key;
292
- const valueToEdit = textData[keyToEdit];
293
- modalTitle.textContent = 'Edit Entry';
294
- keyInput.value = keyToEdit;
295
- valueInput.value = valueToEdit;
296
- originalKeyInput.value = keyToEdit;
297
- modal.style.display = 'flex';
298
- }
299
-
300
- closeButton.addEventListener('click', () => {
301
- modal.style.display = 'none';
302
- });
303
-
304
- window.addEventListener('mousedown', (event) => {
305
- if (event.target === modal) {
306
- modal.style.display = 'none';
307
- }
308
- });
309
-
310
- entryForm.addEventListener('submit', async (event) => {
311
- event.preventDefault();
312
- const key = keyInput.value.trim();
313
- const value = valueInput.value.trim() || "";
314
- const originalKey = originalKeyInput.value;
315
-
316
- if (!key) {
317
- // Basic validation
318
- alert('Key and Value cannot be empty.');
319
- return;
320
- }
321
-
322
- let keyEdited = false;
323
- if (originalKey && originalKey !== key) {
324
- delete textData[originalKey];
325
- keyEdited = true;
326
- }
327
-
328
- if (originalKey) {
329
- if (keyEdited) {
330
- textData = { [key]: value, ...textData };
331
- } else {
332
- textData[key] = value;
333
- }
334
- } else {
335
- if (textData.hasOwnProperty(key)) {
336
- alert(`Key "${key}" already exists. Please use the Edit function to modify it.`);
337
- return;
338
- }
339
- textData = { [key]: value, ...textData };
340
- }
341
-
342
- await saveData();
343
- renderTable();
344
- modal.style.display = 'none';
345
- });
346
-
347
- function handleDeleteClick(event) {
348
- const keyToDelete = event.target.dataset.key;
349
- if (confirm(`Are you sure you want to delete the entry with key "${keyToDelete}"?`)) {
350
- if (textData.hasOwnProperty(keyToDelete)) {
351
- delete textData[keyToDelete];
352
- saveData();
353
- renderTable();
354
- }
355
- }
356
- }
357
-
358
- const searchInput = document.getElementById('search-input');
359
- const searchButton = document.getElementById('search-button');
360
-
361
- function performSearch() {
362
- const query = searchInput.value.toLowerCase();
363
- const filteredData = {};
364
- for (const key in textData) {
365
- if (textData.hasOwnProperty(key)) {
366
- const value = textData[key];
367
- if (key.toLowerCase().includes(query) || value.toLowerCase().includes(query)) {
368
- filteredData[key] = value;
369
- }
370
- }
371
- }
372
- renderTable(filteredData);
373
- }
374
-
375
- searchButton.addEventListener('click', performSearch);
376
- searchInput.addEventListener('input', performSearch);
377
-
378
- const goBackBtn = document.getElementById('go-back-btn');
379
- goBackBtn.addEventListener('click', () => {
380
- window.history.back();
381
- });
382
-
383
- loadData();
384
-
385
- // Dark mode toggle functionality
386
- function initializeThemeToggle() {
387
- const themeToggle = document.getElementById('themeToggle');
388
- const themeIcon = document.getElementById('themeIcon');
389
- const documentElement = document.documentElement;
390
-
391
- // Check for saved theme preference or default to browser preference
392
- function getPreferredTheme() {
393
- const savedTheme = localStorage.getItem('theme');
394
- if (savedTheme) {
395
- return savedTheme;
396
- }
397
-
398
- // Check browser preference
399
- if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
400
- return 'dark';
401
- }
402
-
403
- return 'light';
404
- }
405
-
406
- // Apply theme
407
- function applyTheme(theme) {
408
- if (theme === 'dark') {
409
- documentElement.setAttribute('data-theme', 'dark');
410
- themeIcon.textContent = '☀️';
411
- themeToggle.title = 'Switch to light mode';
412
- } else {
413
- documentElement.setAttribute('data-theme', 'light');
414
- themeIcon.textContent = '🌙';
415
- themeToggle.title = 'Switch to dark mode';
416
- }
417
- }
418
-
419
- // Initialize theme
420
- const currentTheme = getPreferredTheme();
421
- applyTheme(currentTheme);
422
-
423
- // Toggle theme on button click
424
- themeToggle.addEventListener('click', () => {
425
- const currentTheme = documentElement.getAttribute('data-theme');
426
- const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
427
-
428
- applyTheme(newTheme);
429
- localStorage.setItem('theme', newTheme);
430
- });
431
-
432
- // Listen for browser theme changes
433
- if (window.matchMedia) {
434
- const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
435
- mediaQuery.addEventListener('change', (e) => {
436
- // Only auto-switch if user hasn't manually set a preference
437
- if (!localStorage.getItem('theme')) {
438
- applyTheme(e.matches ? 'dark' : 'light');
439
- }
440
- });
441
- }
442
- }
443
-
444
- // Initialize theme toggle
445
- initializeThemeToggle();
446
-
447
- </script>
448
- </body>
449
- </html>