GameSentenceMiner 2.17.7__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.
- GameSentenceMiner/ai/ai_prompting.py +6 -6
- GameSentenceMiner/anki.py +236 -152
- GameSentenceMiner/gametext.py +7 -4
- GameSentenceMiner/gsm.py +49 -10
- GameSentenceMiner/locales/en_us.json +7 -3
- GameSentenceMiner/locales/ja_jp.json +8 -4
- GameSentenceMiner/locales/zh_cn.json +8 -4
- GameSentenceMiner/obs.py +238 -59
- GameSentenceMiner/ocr/owocr_helper.py +1 -1
- GameSentenceMiner/tools/ss_selector.py +7 -8
- GameSentenceMiner/ui/__init__.py +0 -0
- GameSentenceMiner/ui/anki_confirmation.py +187 -0
- GameSentenceMiner/{config_gui.py → ui/config_gui.py} +100 -35
- GameSentenceMiner/ui/screenshot_selector.py +215 -0
- GameSentenceMiner/util/configuration.py +124 -22
- GameSentenceMiner/util/db.py +22 -13
- GameSentenceMiner/util/downloader/download_tools.py +2 -2
- GameSentenceMiner/util/ffmpeg.py +24 -30
- GameSentenceMiner/util/get_overlay_coords.py +34 -34
- GameSentenceMiner/util/gsm_utils.py +31 -1
- GameSentenceMiner/util/text_log.py +11 -9
- GameSentenceMiner/vad.py +31 -12
- GameSentenceMiner/web/database_api.py +742 -123
- GameSentenceMiner/web/static/css/dashboard-shared.css +241 -0
- GameSentenceMiner/web/static/css/kanji-grid.css +94 -2
- GameSentenceMiner/web/static/css/overview.css +850 -0
- GameSentenceMiner/web/static/css/popups-shared.css +126 -0
- GameSentenceMiner/web/static/css/shared.css +97 -0
- GameSentenceMiner/web/static/css/stats.css +192 -597
- GameSentenceMiner/web/static/js/anki_stats.js +6 -4
- GameSentenceMiner/web/static/js/database.js +209 -5
- GameSentenceMiner/web/static/js/goals.js +610 -0
- GameSentenceMiner/web/static/js/kanji-grid.js +267 -4
- GameSentenceMiner/web/static/js/overview.js +1176 -0
- GameSentenceMiner/web/static/js/shared.js +25 -0
- GameSentenceMiner/web/static/js/stats.js +154 -1459
- GameSentenceMiner/web/stats.py +2 -2
- GameSentenceMiner/web/templates/anki_stats.html +5 -0
- GameSentenceMiner/web/templates/components/navigation.html +3 -1
- GameSentenceMiner/web/templates/database.html +73 -1
- GameSentenceMiner/web/templates/goals.html +376 -0
- GameSentenceMiner/web/templates/index.html +13 -11
- GameSentenceMiner/web/templates/overview.html +416 -0
- GameSentenceMiner/web/templates/stats.html +46 -251
- GameSentenceMiner/web/texthooking_page.py +18 -0
- {gamesentenceminer-2.17.7.dist-info → gamesentenceminer-2.18.0.dist-info}/METADATA +5 -1
- {gamesentenceminer-2.17.7.dist-info → gamesentenceminer-2.18.0.dist-info}/RECORD +51 -41
- {gamesentenceminer-2.17.7.dist-info → gamesentenceminer-2.18.0.dist-info}/WHEEL +0 -0
- {gamesentenceminer-2.17.7.dist-info → gamesentenceminer-2.18.0.dist-info}/entry_points.txt +0 -0
- {gamesentenceminer-2.17.7.dist-info → gamesentenceminer-2.18.0.dist-info}/licenses/LICENSE +0 -0
- {gamesentenceminer-2.17.7.dist-info → gamesentenceminer-2.18.0.dist-info}/top_level.txt +0 -0
|
@@ -14,6 +14,7 @@ class KanjiGridRenderer {
|
|
|
14
14
|
emptyMessage: 'No kanji data available',
|
|
15
15
|
showCounter: true,
|
|
16
16
|
showLegend: true,
|
|
17
|
+
enableSorting: true, // Enable sorting dropdown
|
|
17
18
|
...options
|
|
18
19
|
};
|
|
19
20
|
|
|
@@ -25,18 +26,145 @@ class KanjiGridRenderer {
|
|
|
25
26
|
console.error(`KanjiGridRenderer: Container not found with selector: ${this.config.containerSelector}`);
|
|
26
27
|
return;
|
|
27
28
|
}
|
|
29
|
+
|
|
30
|
+
// Sorting state
|
|
31
|
+
this.currentKanjiData = null;
|
|
32
|
+
this.sortingConfigs = [];
|
|
33
|
+
this.currentSortMode = 'frequency'; // 'frequency' or config filename
|
|
34
|
+
|
|
35
|
+
// Initialize sorting if enabled
|
|
36
|
+
if (this.config.enableSorting) {
|
|
37
|
+
this.initializeSorting();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Initialize sorting functionality
|
|
43
|
+
*/
|
|
44
|
+
async initializeSorting() {
|
|
45
|
+
try {
|
|
46
|
+
// Load saved preference
|
|
47
|
+
this.currentSortMode = this.loadSortPreference();
|
|
48
|
+
|
|
49
|
+
// Fetch available sorting configs
|
|
50
|
+
await this.loadSortingConfigs();
|
|
51
|
+
|
|
52
|
+
// Create dropdown UI
|
|
53
|
+
this.createSortDropdown();
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error('Failed to initialize sorting:', error);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Load available sorting configurations from API
|
|
61
|
+
*/
|
|
62
|
+
async loadSortingConfigs() {
|
|
63
|
+
try {
|
|
64
|
+
const response = await fetch('/api/kanji-sorting-configs');
|
|
65
|
+
const data = await response.json();
|
|
66
|
+
this.sortingConfigs = data.configs || [];
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error('Failed to load sorting configs:', error);
|
|
69
|
+
this.sortingConfigs = [];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Create and insert the sort dropdown UI
|
|
75
|
+
*/
|
|
76
|
+
createSortDropdown() {
|
|
77
|
+
// Create dropdown container
|
|
78
|
+
const dropdownContainer = document.createElement('div');
|
|
79
|
+
dropdownContainer.className = 'kanji-sort-dropdown-container';
|
|
80
|
+
|
|
81
|
+
const label = document.createElement('label');
|
|
82
|
+
label.textContent = 'Sort by: ';
|
|
83
|
+
label.className = 'kanji-sort-label';
|
|
84
|
+
|
|
85
|
+
const select = document.createElement('select');
|
|
86
|
+
select.className = 'kanji-sort-dropdown';
|
|
87
|
+
select.id = 'kanjiSortDropdown';
|
|
88
|
+
|
|
89
|
+
// Add "Frequency (default)" option
|
|
90
|
+
const freqOption = document.createElement('option');
|
|
91
|
+
freqOption.value = 'frequency';
|
|
92
|
+
freqOption.textContent = 'Frequency (default)';
|
|
93
|
+
select.appendChild(freqOption);
|
|
94
|
+
|
|
95
|
+
// Add options from loaded configs
|
|
96
|
+
this.sortingConfigs.forEach(config => {
|
|
97
|
+
const option = document.createElement('option');
|
|
98
|
+
option.value = config.filename;
|
|
99
|
+
option.textContent = config.name;
|
|
100
|
+
select.appendChild(option);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Set current value
|
|
104
|
+
select.value = this.currentSortMode;
|
|
105
|
+
|
|
106
|
+
// Add change handler
|
|
107
|
+
select.addEventListener('change', (e) => this.handleSortChange(e.target.value));
|
|
108
|
+
|
|
109
|
+
dropdownContainer.appendChild(label);
|
|
110
|
+
dropdownContainer.appendChild(select);
|
|
111
|
+
|
|
112
|
+
// Insert at the top of the container
|
|
113
|
+
this.container.parentElement.insertBefore(dropdownContainer, this.container);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Handle sort mode change
|
|
118
|
+
* @param {string} sortMode - Selected sort mode
|
|
119
|
+
*/
|
|
120
|
+
async handleSortChange(sortMode) {
|
|
121
|
+
this.currentSortMode = sortMode;
|
|
122
|
+
this.saveSortPreference(sortMode);
|
|
123
|
+
|
|
124
|
+
// Re-render with current data
|
|
125
|
+
if (this.currentKanjiData) {
|
|
126
|
+
await this.render(this.currentKanjiData);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Save sort preference to localStorage
|
|
132
|
+
* @param {string} sortMode - Sort mode to save
|
|
133
|
+
*/
|
|
134
|
+
saveSortPreference(sortMode) {
|
|
135
|
+
try {
|
|
136
|
+
localStorage.setItem('kanjiGridSortMode', sortMode);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error('Failed to save sort preference:', error);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Load sort preference from localStorage
|
|
144
|
+
* @returns {string} Saved sort mode or 'frequency' as default
|
|
145
|
+
*/
|
|
146
|
+
loadSortPreference() {
|
|
147
|
+
try {
|
|
148
|
+
return localStorage.getItem('kanjiGridSortMode') || 'frequency';
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error('Failed to load sort preference:', error);
|
|
151
|
+
return 'frequency';
|
|
152
|
+
}
|
|
28
153
|
}
|
|
29
154
|
|
|
30
155
|
/**
|
|
31
156
|
* Render the kanji grid with provided data
|
|
32
157
|
* @param {Object|Array} kanjiData - Kanji data to render
|
|
33
158
|
*/
|
|
34
|
-
render(kanjiData) {
|
|
159
|
+
async render(kanjiData) {
|
|
35
160
|
if (!kanjiData) {
|
|
36
161
|
this.renderEmpty();
|
|
37
162
|
return;
|
|
38
163
|
}
|
|
39
164
|
|
|
165
|
+
// Store current data for re-rendering
|
|
166
|
+
this.currentKanjiData = kanjiData;
|
|
167
|
+
|
|
40
168
|
// Handle different data formats
|
|
41
169
|
let kanjiList = [];
|
|
42
170
|
if (Array.isArray(kanjiData)) {
|
|
@@ -62,16 +190,151 @@ class KanjiGridRenderer {
|
|
|
62
190
|
this.counter.textContent = kanjiList.length;
|
|
63
191
|
}
|
|
64
192
|
|
|
65
|
-
// Clear existing grid
|
|
193
|
+
// Clear existing grid and reset classes
|
|
66
194
|
this.container.innerHTML = '';
|
|
195
|
+
this.container.classList.remove('flat-mode');
|
|
196
|
+
|
|
197
|
+
// Render based on current sort mode
|
|
198
|
+
if (this.currentSortMode === 'frequency') {
|
|
199
|
+
this.renderFlat(kanjiList);
|
|
200
|
+
} else {
|
|
201
|
+
await this.renderGrouped(kanjiList, this.currentSortMode);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Render kanji in flat/frequency mode
|
|
207
|
+
* @param {Array} kanjiList - List of kanji items
|
|
208
|
+
*/
|
|
209
|
+
renderFlat(kanjiList) {
|
|
210
|
+
// Add flat-mode class for CSS Grid layout
|
|
211
|
+
this.container.classList.add('flat-mode');
|
|
67
212
|
|
|
68
|
-
// Render kanji cells
|
|
69
213
|
kanjiList.forEach(item => {
|
|
70
214
|
const cell = this.createKanjiCell(item);
|
|
71
215
|
this.container.appendChild(cell);
|
|
72
216
|
});
|
|
73
217
|
}
|
|
74
218
|
|
|
219
|
+
/**
|
|
220
|
+
* Render kanji in grouped mode based on JSON configuration
|
|
221
|
+
* @param {Array} kanjiList - List of kanji items
|
|
222
|
+
* @param {string} configFilename - Configuration filename
|
|
223
|
+
*/
|
|
224
|
+
async renderGrouped(kanjiList, configFilename) {
|
|
225
|
+
// Remove flat-mode class for block layout
|
|
226
|
+
this.container.classList.remove('flat-mode');
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
// Fetch the sorting configuration
|
|
230
|
+
const response = await fetch(`/api/kanji-sorting-config/${configFilename}`);
|
|
231
|
+
if (!response.ok) {
|
|
232
|
+
console.error('Failed to load sorting config, falling back to frequency');
|
|
233
|
+
this.renderFlat(kanjiList);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const config = await response.json();
|
|
238
|
+
let groups = config.groups || [];
|
|
239
|
+
|
|
240
|
+
// Create a map for quick kanji lookup
|
|
241
|
+
const kanjiMap = new Map();
|
|
242
|
+
kanjiList.forEach(item => {
|
|
243
|
+
kanjiMap.set(item.kanji, item);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Handle leftover kanji (add as additional group if exists)
|
|
247
|
+
if (config.leftover_group) {
|
|
248
|
+
const leftoverKanji = Array.from(kanjiMap.values()).filter(item => {
|
|
249
|
+
return !groups.some(group =>
|
|
250
|
+
group.characters && group.characters.includes(item.kanji)
|
|
251
|
+
);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
if (leftoverKanji.length > 0) {
|
|
255
|
+
groups = [...groups, {
|
|
256
|
+
name: config.leftover_group,
|
|
257
|
+
characters: leftoverKanji.map(item => item.kanji).join('')
|
|
258
|
+
}];
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Render each group in stacked layout
|
|
263
|
+
groups.forEach(group => {
|
|
264
|
+
const groupSection = this.createGroupSection(group, kanjiMap);
|
|
265
|
+
this.container.appendChild(groupSection);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
} catch (error) {
|
|
269
|
+
console.error('Error rendering grouped kanji:', error);
|
|
270
|
+
this.renderFlat(kanjiList);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Create a group section with header and kanji grid
|
|
276
|
+
* @param {Object} group - Group configuration {name, characters}
|
|
277
|
+
* @param {Map} kanjiMap - Map of kanji character to kanji data
|
|
278
|
+
* @returns {HTMLElement} Group section element
|
|
279
|
+
*/
|
|
280
|
+
createGroupSection(group, kanjiMap) {
|
|
281
|
+
const section = document.createElement('div');
|
|
282
|
+
section.className = 'kanji-group-section';
|
|
283
|
+
|
|
284
|
+
const characters = group.characters || '';
|
|
285
|
+
|
|
286
|
+
// Calculate statistics
|
|
287
|
+
const totalInGroup = characters.length;
|
|
288
|
+
let knownInGroup = 0;
|
|
289
|
+
let foundInGroup = 0;
|
|
290
|
+
|
|
291
|
+
for (const char of characters) {
|
|
292
|
+
if (kanjiMap.has(char)) {
|
|
293
|
+
foundInGroup++;
|
|
294
|
+
knownInGroup++; // For now, found = known
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const foundPercentage = totalInGroup > 0 ? ((foundInGroup / totalInGroup) * 100).toFixed(2) : 0;
|
|
299
|
+
const knownPercentage = totalInGroup > 0 ? ((knownInGroup / totalInGroup) * 100).toFixed(2) : 0;
|
|
300
|
+
|
|
301
|
+
// Create header
|
|
302
|
+
const header = document.createElement('h3');
|
|
303
|
+
header.className = 'kanji-group-header';
|
|
304
|
+
header.textContent = group.name;
|
|
305
|
+
section.appendChild(header);
|
|
306
|
+
|
|
307
|
+
// Create stats line
|
|
308
|
+
const stats = document.createElement('div');
|
|
309
|
+
stats.className = 'kanji-group-stats';
|
|
310
|
+
stats.textContent = `${foundInGroup} of ${totalInGroup} Found - ${foundPercentage}%, ${knownInGroup} of ${totalInGroup} Known - ${knownPercentage}%`;
|
|
311
|
+
section.appendChild(stats);
|
|
312
|
+
|
|
313
|
+
// Create grid for kanji
|
|
314
|
+
const grid = document.createElement('div');
|
|
315
|
+
grid.className = 'kanji-group-grid';
|
|
316
|
+
|
|
317
|
+
// Render all characters in the group
|
|
318
|
+
for (const char of characters) {
|
|
319
|
+
const kanjiData = kanjiMap.get(char);
|
|
320
|
+
if (kanjiData) {
|
|
321
|
+
const cell = this.createKanjiCell(kanjiData);
|
|
322
|
+
grid.appendChild(cell);
|
|
323
|
+
} else {
|
|
324
|
+
const cell = this.createKanjiCell({
|
|
325
|
+
kanji: char,
|
|
326
|
+
frequency: 0,
|
|
327
|
+
color: '#ebedf0'
|
|
328
|
+
});
|
|
329
|
+
grid.appendChild(cell);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
section.appendChild(grid);
|
|
334
|
+
|
|
335
|
+
return section;
|
|
336
|
+
}
|
|
337
|
+
|
|
75
338
|
/**
|
|
76
339
|
* Create individual kanji cell element
|
|
77
340
|
* @param {Object} item - Kanji item with kanji character and frequency
|
|
@@ -126,7 +389,7 @@ class KanjiGridRenderer {
|
|
|
126
389
|
* @returns {string} - CSS color value
|
|
127
390
|
*/
|
|
128
391
|
getFrequencyColor(frequency) {
|
|
129
|
-
if (frequency >
|
|
392
|
+
if (frequency > 300) return '#2ee6e0'; // Cyan for very frequent
|
|
130
393
|
else if (frequency > 100) return '#3be62f'; // Green for frequent
|
|
131
394
|
else if (frequency > 30) return '#e6dc2e'; // Yellow for moderate
|
|
132
395
|
else if (frequency > 10) return '#e6342e'; // Red for occasional
|