GameSentenceMiner 2.8.10__py3-none-any.whl → 2.8.11__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/web/templates/__init__.py +0 -0
- GameSentenceMiner/web/templates/text_replacements.html +238 -0
- GameSentenceMiner/web/templates/utility.html +426 -0
- {gamesentenceminer-2.8.10.dist-info → gamesentenceminer-2.8.11.dist-info}/METADATA +1 -1
- {gamesentenceminer-2.8.10.dist-info → gamesentenceminer-2.8.11.dist-info}/RECORD +9 -6
- {gamesentenceminer-2.8.10.dist-info → gamesentenceminer-2.8.11.dist-info}/WHEEL +0 -0
- {gamesentenceminer-2.8.10.dist-info → gamesentenceminer-2.8.11.dist-info}/entry_points.txt +0 -0
- {gamesentenceminer-2.8.10.dist-info → gamesentenceminer-2.8.11.dist-info}/licenses/LICENSE +0 -0
- {gamesentenceminer-2.8.10.dist-info → gamesentenceminer-2.8.11.dist-info}/top_level.txt +0 -0
File without changes
|
@@ -0,0 +1,238 @@
|
|
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
|
+
</head>
|
10
|
+
<body> <div class="container">
|
11
|
+
<h1>Text Error Fixes</h1>
|
12
|
+
|
13
|
+
<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>
|
14
|
+
<button id="go-back-btn" class="button-gray">Go Back</button>
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<div id="data-table-container" class="table-container"> <table id="data-table" class="data-table"> <thead>
|
18
|
+
<tr>
|
19
|
+
<th>
|
20
|
+
Text To Replace (Key)
|
21
|
+
</th>
|
22
|
+
<th>
|
23
|
+
Replacement Text (Value)
|
24
|
+
</th>
|
25
|
+
<th>
|
26
|
+
Actions
|
27
|
+
</th>
|
28
|
+
</tr>
|
29
|
+
</thead>
|
30
|
+
<tbody id="data-table-body">
|
31
|
+
</tbody>
|
32
|
+
</table>
|
33
|
+
<p id="no-entries-message" class="no-entries-message hidden">No entries found.</p> </div>
|
34
|
+
</div>
|
35
|
+
|
36
|
+
<div id="entry-modal" class="modal"> <div class="modal-content"> <span class="close-button">×</span> <h2 id="modal-title">Add New Entry</h2> <form id="entry-form">
|
37
|
+
<p>"re:" at the beginning = regex pattern (ex. re:.{3,}) </p>
|
38
|
+
<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">
|
39
|
+
</div>
|
40
|
+
<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">
|
41
|
+
</div>
|
42
|
+
<div class="flex-end"> <button type="submit" class="button-blue">Save Entry</button> </div>
|
43
|
+
</form>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
|
47
|
+
<script>
|
48
|
+
|
49
|
+
let textData = {};
|
50
|
+
let data = {};
|
51
|
+
|
52
|
+
async function loadData() {
|
53
|
+
try {
|
54
|
+
const response = await fetch('/load-data');
|
55
|
+
if (response.ok) {
|
56
|
+
data = await response.json();
|
57
|
+
textData = data.args?.replacements || {};
|
58
|
+
} else {
|
59
|
+
console.error('Failed to load data from server');
|
60
|
+
}
|
61
|
+
} catch (error) {
|
62
|
+
console.error('Error loading data:', error);
|
63
|
+
}
|
64
|
+
renderTable();
|
65
|
+
}
|
66
|
+
|
67
|
+
async function saveData() {
|
68
|
+
try {
|
69
|
+
data.args.replacements = textData;
|
70
|
+
const response = await fetch('/save-data', {
|
71
|
+
method: 'POST',
|
72
|
+
headers: { 'Content-Type': 'application/json' },
|
73
|
+
body: JSON.stringify(data),
|
74
|
+
});
|
75
|
+
if (!response.ok) {
|
76
|
+
console.error('Failed to save data to server');
|
77
|
+
}
|
78
|
+
} catch (error) {
|
79
|
+
console.error('Error saving data:', error);
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
function renderTable(dataToRender = textData) {
|
84
|
+
const tableBody = document.getElementById('data-table-body');
|
85
|
+
let tableHtml = '';
|
86
|
+
const noEntriesMessage = document.getElementById('no-entries-message');
|
87
|
+
const dataTable = document.getElementById('data-table');
|
88
|
+
const keys = Object.keys(dataToRender);
|
89
|
+
|
90
|
+
if (keys.length === 0) {
|
91
|
+
noEntriesMessage.classList.remove('hidden');
|
92
|
+
dataTable.classList.add('hidden');
|
93
|
+
} else {
|
94
|
+
noEntriesMessage.classList.add('hidden');
|
95
|
+
dataTable.classList.remove('hidden');
|
96
|
+
keys.forEach(key => {
|
97
|
+
const value = dataToRender[key];
|
98
|
+
tableHtml += `
|
99
|
+
<tr>
|
100
|
+
<td>${escapeHTML(key)}</td> <td>${escapeHTML(value)}</td> <td>
|
101
|
+
<button class="action-button edit-btn" data-key="${escapeHTML(key)}">Edit</button>
|
102
|
+
<button class="action-button delete-button delete-btn" data-key="${escapeHTML(key)}">Delete</button>
|
103
|
+
</td>
|
104
|
+
</tr>
|
105
|
+
`;
|
106
|
+
});
|
107
|
+
tableBody.innerHTML = tableHtml;
|
108
|
+
document.querySelectorAll('.edit-btn').forEach(button => {
|
109
|
+
button.addEventListener('click', handleEditClick);
|
110
|
+
});
|
111
|
+
document.querySelectorAll('.delete-btn').forEach(button => {
|
112
|
+
button.addEventListener('click', handleDeleteClick);
|
113
|
+
});
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
function escapeHTML(str) {
|
118
|
+
const div = document.createElement('div');
|
119
|
+
div.appendChild(document.createTextNode(str));
|
120
|
+
return div.innerHTML;
|
121
|
+
}
|
122
|
+
|
123
|
+
const modal = document.getElementById('entry-modal');
|
124
|
+
const modalTitle = document.getElementById('modal-title');
|
125
|
+
const entryForm = document.getElementById('entry-form');
|
126
|
+
const keyInput = document.getElementById('key-input');
|
127
|
+
const valueInput = document.getElementById('value-input');
|
128
|
+
const originalKeyInput = document.getElementById('original-key-input');
|
129
|
+
const closeButton = document.querySelector('.close-button');
|
130
|
+
const addNewBtn = document.getElementById('add-new-btn');
|
131
|
+
|
132
|
+
addNewBtn.addEventListener('click', () => {
|
133
|
+
modalTitle.textContent = 'Add New Entry';
|
134
|
+
keyInput.value = '';
|
135
|
+
valueInput.value = '';
|
136
|
+
originalKeyInput.value = '';
|
137
|
+
keyInput.disabled = false;
|
138
|
+
modal.style.display = 'flex';
|
139
|
+
});
|
140
|
+
|
141
|
+
function handleEditClick(event) {
|
142
|
+
const keyToEdit = event.target.dataset.key;
|
143
|
+
const valueToEdit = textData[keyToEdit];
|
144
|
+
modalTitle.textContent = 'Edit Entry';
|
145
|
+
keyInput.value = keyToEdit;
|
146
|
+
valueInput.value = valueToEdit;
|
147
|
+
originalKeyInput.value = keyToEdit;
|
148
|
+
modal.style.display = 'flex';
|
149
|
+
}
|
150
|
+
|
151
|
+
closeButton.addEventListener('click', () => {
|
152
|
+
modal.style.display = 'none';
|
153
|
+
});
|
154
|
+
|
155
|
+
window.addEventListener('mousedown', (event) => {
|
156
|
+
if (event.target === modal) {
|
157
|
+
modal.style.display = 'none';
|
158
|
+
}
|
159
|
+
});
|
160
|
+
|
161
|
+
entryForm.addEventListener('submit', async (event) => {
|
162
|
+
event.preventDefault();
|
163
|
+
const key = keyInput.value.trim();
|
164
|
+
const value = valueInput.value.trim() || "";
|
165
|
+
const originalKey = originalKeyInput.value;
|
166
|
+
|
167
|
+
if (!key) {
|
168
|
+
// Basic validation
|
169
|
+
alert('Key and Value cannot be empty.');
|
170
|
+
return;
|
171
|
+
}
|
172
|
+
|
173
|
+
let keyEdited = false;
|
174
|
+
if (originalKey && originalKey !== key) {
|
175
|
+
delete textData[originalKey];
|
176
|
+
keyEdited = true;
|
177
|
+
}
|
178
|
+
|
179
|
+
if (originalKey) {
|
180
|
+
if (keyEdited) {
|
181
|
+
textData = { [key]: value, ...textData };
|
182
|
+
} else {
|
183
|
+
textData[key] = value;
|
184
|
+
}
|
185
|
+
} else {
|
186
|
+
if (textData.hasOwnProperty(key)) {
|
187
|
+
alert(`Key "${key}" already exists. Please use the Edit function to modify it.`);
|
188
|
+
return;
|
189
|
+
}
|
190
|
+
textData = { [key]: value, ...textData };
|
191
|
+
}
|
192
|
+
|
193
|
+
await saveData();
|
194
|
+
renderTable();
|
195
|
+
modal.style.display = 'none';
|
196
|
+
});
|
197
|
+
|
198
|
+
function handleDeleteClick(event) {
|
199
|
+
const keyToDelete = event.target.dataset.key;
|
200
|
+
if (confirm(`Are you sure you want to delete the entry with key "${keyToDelete}"?`)) {
|
201
|
+
if (textData.hasOwnProperty(keyToDelete)) {
|
202
|
+
delete textData[keyToDelete];
|
203
|
+
saveData();
|
204
|
+
renderTable();
|
205
|
+
}
|
206
|
+
}
|
207
|
+
}
|
208
|
+
|
209
|
+
const searchInput = document.getElementById('search-input');
|
210
|
+
const searchButton = document.getElementById('search-button');
|
211
|
+
|
212
|
+
function performSearch() {
|
213
|
+
const query = searchInput.value.toLowerCase();
|
214
|
+
const filteredData = {};
|
215
|
+
for (const key in textData) {
|
216
|
+
if (textData.hasOwnProperty(key)) {
|
217
|
+
const value = textData[key];
|
218
|
+
if (key.toLowerCase().includes(query) || value.toLowerCase().includes(query)) {
|
219
|
+
filteredData[key] = value;
|
220
|
+
}
|
221
|
+
}
|
222
|
+
}
|
223
|
+
renderTable(filteredData);
|
224
|
+
}
|
225
|
+
|
226
|
+
searchButton.addEventListener('click', performSearch);
|
227
|
+
searchInput.addEventListener('input', performSearch);
|
228
|
+
|
229
|
+
const goBackBtn = document.getElementById('go-back-btn');
|
230
|
+
goBackBtn.addEventListener('click', () => {
|
231
|
+
window.history.back();
|
232
|
+
});
|
233
|
+
|
234
|
+
loadData();
|
235
|
+
|
236
|
+
</script>
|
237
|
+
</body>
|
238
|
+
</html>
|
@@ -0,0 +1,426 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<title>GSM TextHooker</title>
|
6
|
+
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
7
|
+
<style>
|
8
|
+
body {
|
9
|
+
background-color: #121212;
|
10
|
+
color: #e0e0e0;
|
11
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
12
|
+
margin: 20px;
|
13
|
+
}
|
14
|
+
|
15
|
+
h2 {
|
16
|
+
color: #ffffff;
|
17
|
+
text-align: center;
|
18
|
+
font-weight: 300;
|
19
|
+
margin-bottom: 20px;
|
20
|
+
}
|
21
|
+
|
22
|
+
.textline {
|
23
|
+
margin: 15px 0;
|
24
|
+
padding: 15px;
|
25
|
+
display: flex;
|
26
|
+
align-items: center;
|
27
|
+
gap: 15px;
|
28
|
+
}
|
29
|
+
.textline:last-child {
|
30
|
+
border-bottom: none;
|
31
|
+
}
|
32
|
+
|
33
|
+
.textline > p {
|
34
|
+
font-size: 24px;
|
35
|
+
flex: 1;
|
36
|
+
min-width: 200px;
|
37
|
+
}
|
38
|
+
|
39
|
+
.textline > em {
|
40
|
+
color: #aaa;
|
41
|
+
font-size: 0.9em;
|
42
|
+
margin-right: 10px;
|
43
|
+
}
|
44
|
+
|
45
|
+
.textline > button {
|
46
|
+
background-color: #1a73e8;
|
47
|
+
color: #ffffff;
|
48
|
+
border: none;
|
49
|
+
padding: 8px 15px;
|
50
|
+
font-size: 14px;
|
51
|
+
cursor: pointer;
|
52
|
+
transition: background-color 0.3s;
|
53
|
+
border-radius: 5px;
|
54
|
+
}
|
55
|
+
|
56
|
+
.textline > button:hover {
|
57
|
+
background-color: #1669c1;
|
58
|
+
cursor: pointer;
|
59
|
+
}
|
60
|
+
|
61
|
+
.textline-buttons {
|
62
|
+
margin-left: auto; /* Align buttons to the right */
|
63
|
+
display: flex;
|
64
|
+
gap: 10px;
|
65
|
+
}
|
66
|
+
|
67
|
+
@media (max-width: 600px) {
|
68
|
+
.textline {
|
69
|
+
flex-direction: column;
|
70
|
+
align-items: flex-start;
|
71
|
+
}
|
72
|
+
.textline-buttons{
|
73
|
+
margin-top: 10px;
|
74
|
+
}
|
75
|
+
.textline > strong{
|
76
|
+
min-width: auto;
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
.initial-event {
|
81
|
+
margin: 15px 0;
|
82
|
+
padding: 15px;
|
83
|
+
}
|
84
|
+
|
85
|
+
hr.initial-events-separator {
|
86
|
+
border: 0;
|
87
|
+
border-top: 2px solid #aaa;
|
88
|
+
margin: 20px 0;
|
89
|
+
}
|
90
|
+
|
91
|
+
.multi-line-checkbox {
|
92
|
+
transform: scale(1.5);
|
93
|
+
margin-right: 10px;
|
94
|
+
background-color: #00FFFF !important; /* Cyan/Electric Blue */
|
95
|
+
border: 4px solid #00FFFF; /* Keep the border the same color */
|
96
|
+
}
|
97
|
+
|
98
|
+
.multi-line-checkbox:checked {
|
99
|
+
/* You'll likely need to target the checkmark specifically */
|
100
|
+
/* Example assuming it's a pseudo-element with a font-based check: */
|
101
|
+
/* &::before { */
|
102
|
+
/* color: #FFFF00; /* Bright Yellow */
|
103
|
+
/* } */
|
104
|
+
/* If it's a background image, you might need to adjust the background or use a filter. */
|
105
|
+
}
|
106
|
+
|
107
|
+
</style>
|
108
|
+
</head>
|
109
|
+
<body>
|
110
|
+
<div id="initial-events">
|
111
|
+
|
112
|
+
</div>
|
113
|
+
<hr class="initial-events-separator" id="initial-events-separator" style="display: none;">
|
114
|
+
<div id="session-events">
|
115
|
+
|
116
|
+
</div>
|
117
|
+
<div>
|
118
|
+
<button onclick="window.location.href='/textreplacements'" style="margin-top: 20px; background-color: #1a73e8; color: #ffffff; border: none; padding: 10px 20px; font-size: 16px; cursor: pointer; transition: background-color 0.3s; border-radius: 5px;">
|
119
|
+
Text Replacements
|
120
|
+
</button>
|
121
|
+
</div>
|
122
|
+
<script>
|
123
|
+
let mainStyle = document.querySelector('head style');
|
124
|
+
console.log(mainStyle);
|
125
|
+
let displayedEventIds = new Set();
|
126
|
+
let isTabActive = true;
|
127
|
+
let isFetching = false; // Flag to track if a fetch is in progress
|
128
|
+
let intervalId = 0;
|
129
|
+
const fetchInterval = 5000; // Define the interval as a constant
|
130
|
+
const websocketPort = {{ websocket_port }} || 55001;
|
131
|
+
|
132
|
+
// Drag selection variables
|
133
|
+
let isDragging = false;
|
134
|
+
let dragStartCheckbox = null;
|
135
|
+
let newCheckboxState = false;
|
136
|
+
let hoveredCheckboxes = new Set();
|
137
|
+
let checkboxes = []; // Will hold all checkbox elements
|
138
|
+
let checkboxes_being_updated = new Set();
|
139
|
+
|
140
|
+
// Shift click selection variable
|
141
|
+
let lastChecked = null;
|
142
|
+
|
143
|
+
async function fetchEvents() {
|
144
|
+
if (document.hidden || isFetching) {
|
145
|
+
return;
|
146
|
+
}
|
147
|
+
isFetching = true
|
148
|
+
try {
|
149
|
+
const res = await fetch('/data');
|
150
|
+
if (!res.ok) {
|
151
|
+
throw new Error(`HTTP error! Status: ${res.status}`);
|
152
|
+
}
|
153
|
+
const events = await res.json();
|
154
|
+
|
155
|
+
events.forEach(ev => {
|
156
|
+
if (!displayedEventIds.has(ev.id)) {
|
157
|
+
if (ev.history) {
|
158
|
+
addNewEventToHistory(ev)
|
159
|
+
document.getElementById('initial-events-separator').style.display = 'block';
|
160
|
+
} else {
|
161
|
+
addNewEvent(ev)
|
162
|
+
}
|
163
|
+
}
|
164
|
+
const checkbox = document.querySelector(`[data-event-id="${ev.id}"]`);
|
165
|
+
if (checkbox && !checkboxes_being_updated.has(ev.id)) {
|
166
|
+
checkbox.checked = ev.checked;
|
167
|
+
}
|
168
|
+
});
|
169
|
+
checkboxes = Array.from(document.querySelectorAll('#session-events input[type="checkbox"]')); // Update checkboxes array after new events
|
170
|
+
} catch (error) {
|
171
|
+
console.error("Error fetching events:", error);
|
172
|
+
} finally {
|
173
|
+
isFetching = false;
|
174
|
+
}
|
175
|
+
}
|
176
|
+
|
177
|
+
function addNewEventToHistory(event) {
|
178
|
+
displayedEventIds.add(event.id);
|
179
|
+
const container = document.getElementById('initial-events');
|
180
|
+
const div = document.createElement('div');
|
181
|
+
// div.className = 'textline';
|
182
|
+
|
183
|
+
const shadowRoot = div.attachShadow({ mode: 'open' });
|
184
|
+
|
185
|
+
const wrapper = document.createElement('div');
|
186
|
+
wrapper.className = 'textline';
|
187
|
+
wrapper.innerHTML = `<p>${event.text}</p>
|
188
|
+
<em class="clock-icon">${event.time.replace(' GMT', '')}</em>
|
189
|
+
`;
|
190
|
+
|
191
|
+
const style = document.createElement('style');
|
192
|
+
style.textContent = mainStyle.innerHTML;
|
193
|
+
shadowRoot.appendChild(style);
|
194
|
+
shadowRoot.appendChild(wrapper);
|
195
|
+
|
196
|
+
container.appendChild(div);
|
197
|
+
window.scrollTo({
|
198
|
+
top: document.documentElement.scrollHeight,
|
199
|
+
behavior: 'smooth'
|
200
|
+
});
|
201
|
+
}
|
202
|
+
|
203
|
+
function addNewEvent(event) {
|
204
|
+
displayedEventIds.add(event.id);
|
205
|
+
const container = document.getElementById('session-events');
|
206
|
+
const div = document.createElement('div');
|
207
|
+
// div.className = 'textline';
|
208
|
+
|
209
|
+
const shadowRoot = div.attachShadow({ mode: 'open' }); // 'open' allows access from the main DOM
|
210
|
+
|
211
|
+
const wrapper = document.createElement('div');
|
212
|
+
wrapper.className = 'textline';
|
213
|
+
wrapper.innerHTML = `
|
214
|
+
<input type="checkbox"
|
215
|
+
class="multi-line-checkbox"
|
216
|
+
id="multi-line-checkbox-${event.id}"
|
217
|
+
${event.checked ? 'checked' : ''}
|
218
|
+
aria-label="Mark item"
|
219
|
+
data-event-id="${event.id}"
|
220
|
+
onchange="toggleCheckbox('${event.id}', this.checked)">
|
221
|
+
<p>${event.text}</p>
|
222
|
+
<div class="textline-buttons">
|
223
|
+
<button onclick="buttonClick('${event.id}', 'Screenshot')" title="Screenshot" style="background-color: #333; color: #fff; border: 1px solid #555; padding: 6px 10px; font-size: 10px; border-radius: 4px; cursor: pointer; transition: background-color 0.3s;">
|
224
|
+
📷
|
225
|
+
</button>
|
226
|
+
<button onclick="buttonClick('${event.id}', 'Audio')" title="Audio" style="background-color: #333; color: #fff; border: 1px solid #555; padding: 6px 10px; font-size: 10px; border-radius: 4px; cursor: pointer; transition: background-color 0.3s;">
|
227
|
+
🔊
|
228
|
+
</button>
|
229
|
+
</div>
|
230
|
+
`;
|
231
|
+
|
232
|
+
// Apply your component's styles within the shadow DOM
|
233
|
+
const style = document.createElement('style');
|
234
|
+
style.textContent = mainStyle.innerHTML;
|
235
|
+
shadowRoot.appendChild(style);
|
236
|
+
shadowRoot.appendChild(wrapper);
|
237
|
+
|
238
|
+
container.appendChild(div);
|
239
|
+
window.scrollTo({
|
240
|
+
top: document.documentElement.scrollHeight,
|
241
|
+
behavior: 'smooth'
|
242
|
+
});
|
243
|
+
}
|
244
|
+
|
245
|
+
function buttonClick(id, action) {
|
246
|
+
console.log(id);
|
247
|
+
const endpoint = action === 'Screenshot' ? '/get-screenshot' : '/play-audio';
|
248
|
+
fetch(endpoint, {
|
249
|
+
method: 'POST',
|
250
|
+
headers: { 'Content-Type': 'application/json' },
|
251
|
+
body: JSON.stringify({ id })
|
252
|
+
})
|
253
|
+
.then(response => {
|
254
|
+
if (!response.ok) {
|
255
|
+
throw new Error(`HTTP error! Status: ${response.status}`);
|
256
|
+
}
|
257
|
+
return response.json();
|
258
|
+
})
|
259
|
+
.then(data => {
|
260
|
+
console.log(`${action} action completed for event ID: ${id}`, data);
|
261
|
+
})
|
262
|
+
.catch(error => {
|
263
|
+
console.error(`Error performing ${action} action for event ID: ${id}`, error);
|
264
|
+
});
|
265
|
+
}
|
266
|
+
|
267
|
+
async function toggleCheckbox(id, checked) {
|
268
|
+
try {
|
269
|
+
checkboxes_being_updated.add(id);
|
270
|
+
const res = await fetch('/update', {
|
271
|
+
method: 'POST',
|
272
|
+
headers: { 'Content-Type': 'application/json' },
|
273
|
+
body: JSON.stringify({ id, checked })
|
274
|
+
});
|
275
|
+
checkboxes_being_updated.delete(id);
|
276
|
+
if (!res.ok) {
|
277
|
+
throw new Error(`HTTP error! Status: ${res.status}`);
|
278
|
+
}
|
279
|
+
} catch (error) {
|
280
|
+
console.error("Error updating checkbox:", error);
|
281
|
+
}
|
282
|
+
}
|
283
|
+
|
284
|
+
function handleMouseDown(e) {
|
285
|
+
if (e.target.type === 'checkbox') {
|
286
|
+
newCheckboxState = !e.target.checked;
|
287
|
+
isDragging = true;
|
288
|
+
dragStartCheckbox = e.target;
|
289
|
+
hoveredCheckboxes.add(e.target)
|
290
|
+
}
|
291
|
+
}
|
292
|
+
|
293
|
+
function handleMouseUp(e) {
|
294
|
+
if (e.target === dragStartCheckbox) {
|
295
|
+
isDragging = false;
|
296
|
+
dragStartCheckbox = null;
|
297
|
+
return;
|
298
|
+
}
|
299
|
+
if (isDragging) {
|
300
|
+
isDragging = false;
|
301
|
+
|
302
|
+
hoveredCheckboxes.forEach(checkbox => {
|
303
|
+
checkbox.checked = newCheckboxState; // Set all hovered checkboxes to the new state
|
304
|
+
const eventId = checkbox.dataset.eventId;
|
305
|
+
toggleCheckbox(eventId, newCheckboxState);
|
306
|
+
});
|
307
|
+
isDragging = false;
|
308
|
+
dragStartCheckbox = null;
|
309
|
+
}
|
310
|
+
|
311
|
+
}
|
312
|
+
|
313
|
+
function handleMouseOver(e) {
|
314
|
+
if (!isDragging || e.target.type !== 'checkbox' || e.target === dragStartCheckbox) {
|
315
|
+
return;
|
316
|
+
}
|
317
|
+
e.preventDefault(); // Prevent text selection during drag
|
318
|
+
if (dragStartCheckbox) {
|
319
|
+
hoveredCheckboxes.add(e.target);
|
320
|
+
}
|
321
|
+
}
|
322
|
+
|
323
|
+
function handleCheckboxClick(e) {
|
324
|
+
if (!e.shiftKey) {
|
325
|
+
lastChecked = e.target;
|
326
|
+
return;
|
327
|
+
}
|
328
|
+
|
329
|
+
if (!lastChecked) return;
|
330
|
+
|
331
|
+
let inBetween = false;
|
332
|
+
checkboxes.forEach(checkbox => {
|
333
|
+
if (checkbox === e.target || checkbox === lastChecked) {
|
334
|
+
inBetween = !inBetween;
|
335
|
+
}
|
336
|
+
|
337
|
+
if (inBetween) {
|
338
|
+
checkbox.checked = lastChecked.checked;
|
339
|
+
const eventId = checkbox.dataset.eventId;
|
340
|
+
toggleCheckbox(eventId, lastChecked.checked);
|
341
|
+
}
|
342
|
+
});
|
343
|
+
|
344
|
+
lastChecked = e.target;
|
345
|
+
}
|
346
|
+
|
347
|
+
document.addEventListener('mousedown', handleMouseDown);
|
348
|
+
document.addEventListener('mouseup', handleMouseUp);
|
349
|
+
document.addEventListener('mouseover', handleMouseOver);
|
350
|
+
document.addEventListener('click', handleCheckboxClick);
|
351
|
+
|
352
|
+
const websocketURL = 'ws://localhost:' + websocketPort;
|
353
|
+
let websocket = {};
|
354
|
+
let reconnectInterval = 1000; // Time in milliseconds to wait before attempting to reconnect
|
355
|
+
|
356
|
+
const connectWebSocket = () => {
|
357
|
+
if (websocket && websocket.readyState === WebSocket.OPEN) {
|
358
|
+
console.log('WebSocket already open, no need to reconnect.');
|
359
|
+
return;
|
360
|
+
}
|
361
|
+
if (websocket && websocket.readyState === WebSocket.CONNECTING) {
|
362
|
+
console.log('WebSocket is currently connecting, waiting...');
|
363
|
+
return;
|
364
|
+
}
|
365
|
+
|
366
|
+
websocket = new WebSocket(websocketURL);
|
367
|
+
|
368
|
+
websocket.onopen = (event) => {
|
369
|
+
console.log('WebSocket connection opened');
|
370
|
+
websocket.send(JSON.stringify({ type: 'initial_data_request' }));
|
371
|
+
};
|
372
|
+
|
373
|
+
websocket.onmessage = (event) => {
|
374
|
+
const data = JSON.parse(event.data);
|
375
|
+
console.log('Received message:', data);
|
376
|
+
if (data.event === 'text_received') {
|
377
|
+
console.log("Adding new event:", data.data);
|
378
|
+
addNewEvent(data.data);
|
379
|
+
} else {
|
380
|
+
console.log('Other message:', data);
|
381
|
+
}
|
382
|
+
};
|
383
|
+
|
384
|
+
websocket.onclose = (event) => {
|
385
|
+
console.log(`WebSocket connection closed. Attempting to reconnect in ${reconnectInterval / 1000} seconds...`);
|
386
|
+
// Only attempt to reconnect if the current websocket object is the one that closed
|
387
|
+
if (websocket === event.target) {
|
388
|
+
// Clear the current websocket reference to allow for a new connection
|
389
|
+
websocket = null;
|
390
|
+
setTimeout(connectWebSocket, reconnectInterval);
|
391
|
+
}
|
392
|
+
};
|
393
|
+
|
394
|
+
websocket.onerror = (error) => {
|
395
|
+
console.error('WebSocket error:', error);
|
396
|
+
// Optionally attempt to reconnect on error as well, ensuring we don't have an active connection
|
397
|
+
if (websocket === error.target || websocket === null) {
|
398
|
+
console.log(`Attempting to reconnect in ${reconnectInterval / 1000} seconds...`);
|
399
|
+
// Clear the current websocket reference
|
400
|
+
websocket = null;
|
401
|
+
setTimeout(connectWebSocket, reconnectInterval);
|
402
|
+
}
|
403
|
+
};
|
404
|
+
return websocket;
|
405
|
+
};
|
406
|
+
|
407
|
+
connectWebSocket();
|
408
|
+
|
409
|
+
|
410
|
+
fetchEvents();
|
411
|
+
|
412
|
+
console.log("Initial load, fetching events and starting interval...");
|
413
|
+
fetchEvents();
|
414
|
+
intervalId = setInterval(async () => {
|
415
|
+
if (isTabActive) {
|
416
|
+
await fetchEvents();
|
417
|
+
}
|
418
|
+
}, fetchInterval);
|
419
|
+
|
420
|
+
window.scrollTo({
|
421
|
+
top: document.documentElement.scrollHeight,
|
422
|
+
behavior: 'smooth'
|
423
|
+
});
|
424
|
+
</script>
|
425
|
+
</body>
|
426
|
+
</html>
|
@@ -48,9 +48,12 @@ GameSentenceMiner/web/static/site.webmanifest,sha256=kaeNT-FjFt-T7JGzOhXH7YSqsrD
|
|
48
48
|
GameSentenceMiner/web/static/style.css,sha256=bPZK0NVMuyRl5NNDuT7ZTzVLKlvSsdmeVHmAW4y5FM0,7001
|
49
49
|
GameSentenceMiner/web/static/web-app-manifest-192x192.png,sha256=EfSNnBmsSaLfESbkGfYwbKzcjKOdzuWo18ABADfN974,51117
|
50
50
|
GameSentenceMiner/web/static/web-app-manifest-512x512.png,sha256=wyqgCWCrLEUxSRXmaA3iJEESd-vM-ZmlTtZFBY4V8Pk,230819
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
gamesentenceminer-2.8.
|
55
|
-
gamesentenceminer-2.8.
|
56
|
-
gamesentenceminer-2.8.
|
51
|
+
GameSentenceMiner/web/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
52
|
+
GameSentenceMiner/web/templates/text_replacements.html,sha256=tV5c8mCaWSt_vKuUpbdbLAzXZ3ATZeDvQ9PnnAfqY0M,8598
|
53
|
+
GameSentenceMiner/web/templates/utility.html,sha256=8yJvmHqkHoMN06qWDckhGoguWyP7LUVZ2oms0tBRdEw,14297
|
54
|
+
gamesentenceminer-2.8.11.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
55
|
+
gamesentenceminer-2.8.11.dist-info/METADATA,sha256=YmPRvGsen47roUCdli3iCuoQkR0FROFbc4zerMJ3PI4,5933
|
56
|
+
gamesentenceminer-2.8.11.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
|
57
|
+
gamesentenceminer-2.8.11.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
|
58
|
+
gamesentenceminer-2.8.11.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
|
59
|
+
gamesentenceminer-2.8.11.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|