pinokiod 3.48.0 → 3.50.0
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.
- package/kernel/environment.js +6 -0
- package/package.json +1 -1
- package/server/index.js +37 -13
- package/server/public/common.js +85 -8
- package/server/public/style.css +15 -27
- package/server/public/urldropdown.css +174 -0
- package/server/public/urldropdown.js +396 -0
- package/server/public/window_storage.js +30 -0
- package/server/views/app.ejs +19 -27
- package/server/views/columns.ejs +59 -30
- package/server/views/connect.ejs +21 -1
- package/server/views/container.ejs +21 -1
- package/server/views/download.ejs +2 -0
- package/server/views/index.ejs +20 -1
- package/server/views/init/index.ejs +4 -0
- package/server/views/net.ejs +21 -1
- package/server/views/network.ejs +21 -1
- package/server/views/review.ejs +8 -0
- package/server/views/rows.ejs +61 -16
- package/server/views/screenshots.ejs +21 -1
- package/server/views/settings.ejs +22 -2
- package/server/views/setup.ejs +2 -0
- package/server/views/tools.ejs +21 -1
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL Dropdown functionality for process selection
|
|
3
|
+
* Fetches running processes from /info/procs API and displays them in a dropdown
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
function initUrlDropdown(config = {}) {
|
|
7
|
+
const urlInput = document.querySelector('.urlbar input[type="url"]');
|
|
8
|
+
const dropdown = document.getElementById('url-dropdown');
|
|
9
|
+
const mobileButton = document.getElementById('mobile-link-button');
|
|
10
|
+
|
|
11
|
+
if (!urlInput || !dropdown) {
|
|
12
|
+
console.warn('URL dropdown elements not found');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Configuration options
|
|
17
|
+
const options = {
|
|
18
|
+
clearBehavior: config.clearBehavior || 'empty', // 'empty' or 'restore'
|
|
19
|
+
defaultValue: config.defaultValue || '',
|
|
20
|
+
apiEndpoint: config.apiEndpoint || '/info/procs',
|
|
21
|
+
...config
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
let isDropdownVisible = false;
|
|
25
|
+
let allProcesses = []; // Store all processes for filtering
|
|
26
|
+
let filteredProcesses = []; // Store currently filtered processes
|
|
27
|
+
|
|
28
|
+
// Initialize input field state based on clear behavior
|
|
29
|
+
initializeInputValue();
|
|
30
|
+
|
|
31
|
+
// Handle page navigation events
|
|
32
|
+
window.addEventListener('pageshow', function(event) {
|
|
33
|
+
if (event.persisted || window.performance?.navigation?.type === 2) {
|
|
34
|
+
initializeInputValue();
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Event listeners
|
|
39
|
+
urlInput.addEventListener('focus', function() {
|
|
40
|
+
// Auto-select text for restore behavior to make filtering easier
|
|
41
|
+
if (options.clearBehavior === 'restore' && urlInput.value) {
|
|
42
|
+
// Use setTimeout to ensure the focus event completes first
|
|
43
|
+
setTimeout(() => {
|
|
44
|
+
urlInput.select();
|
|
45
|
+
}, 0);
|
|
46
|
+
}
|
|
47
|
+
showDropdown();
|
|
48
|
+
});
|
|
49
|
+
urlInput.addEventListener('input', handleInputChange);
|
|
50
|
+
|
|
51
|
+
// Hide dropdown when clicking outside
|
|
52
|
+
document.addEventListener('click', function(e) {
|
|
53
|
+
if (!e.target.closest('.url-input-container')) {
|
|
54
|
+
hideDropdown();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
function initializeInputValue() {
|
|
59
|
+
if (options.clearBehavior === 'empty') {
|
|
60
|
+
urlInput.value = '';
|
|
61
|
+
} else if (options.clearBehavior === 'restore') {
|
|
62
|
+
const originalValue = urlInput.getAttribute('value') || options.defaultValue;
|
|
63
|
+
if (urlInput.value !== originalValue) {
|
|
64
|
+
urlInput.value = originalValue;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function showDropdown() {
|
|
70
|
+
if (isDropdownVisible && allProcesses.length > 0) {
|
|
71
|
+
// If dropdown is already visible and we have data, show all initially
|
|
72
|
+
showAllProcesses();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
isDropdownVisible = true;
|
|
77
|
+
dropdown.style.display = 'block';
|
|
78
|
+
|
|
79
|
+
// If we already have processes data, show all initially
|
|
80
|
+
if (allProcesses.length > 0) {
|
|
81
|
+
showAllProcesses();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Otherwise, show loading and fetch data
|
|
86
|
+
dropdown.innerHTML = '<div class="url-dropdown-loading">Loading running processes...</div>';
|
|
87
|
+
|
|
88
|
+
// Fetch processes from API
|
|
89
|
+
fetch(options.apiEndpoint)
|
|
90
|
+
.then(response => {
|
|
91
|
+
if (!response.ok) {
|
|
92
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
93
|
+
}
|
|
94
|
+
return response.json();
|
|
95
|
+
})
|
|
96
|
+
.then(data => {
|
|
97
|
+
allProcesses = data.info || [];
|
|
98
|
+
showAllProcesses(); // Show all processes when dropdown first opens
|
|
99
|
+
})
|
|
100
|
+
.catch(error => {
|
|
101
|
+
console.error('Failed to fetch processes:', error);
|
|
102
|
+
dropdown.innerHTML = '<div class="url-dropdown-empty">Failed to load processes</div>';
|
|
103
|
+
allProcesses = [];
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function showAllProcesses() {
|
|
108
|
+
filteredProcesses = allProcesses;
|
|
109
|
+
populateDropdown(filteredProcesses);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function handleInputChange() {
|
|
113
|
+
if (!isDropdownVisible) return;
|
|
114
|
+
|
|
115
|
+
const query = urlInput.value.toLowerCase().trim();
|
|
116
|
+
|
|
117
|
+
// Special case: if text is selected (user just focused), don't filter yet
|
|
118
|
+
if (urlInput.selectionStart === 0 && urlInput.selectionEnd === urlInput.value.length) {
|
|
119
|
+
// Text is fully selected, show all processes until user starts typing
|
|
120
|
+
filteredProcesses = allProcesses;
|
|
121
|
+
} else if (!query) {
|
|
122
|
+
// No query, show all processes
|
|
123
|
+
filteredProcesses = allProcesses;
|
|
124
|
+
} else {
|
|
125
|
+
// Filter processes based on name and URL
|
|
126
|
+
filteredProcesses = allProcesses.filter(process => {
|
|
127
|
+
const url = `http://${process.ip}`;
|
|
128
|
+
const name = process.name.toLowerCase();
|
|
129
|
+
const urlLower = url.toLowerCase();
|
|
130
|
+
|
|
131
|
+
return name.includes(query) || urlLower.includes(query);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
populateDropdown(filteredProcesses);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function hideDropdown() {
|
|
139
|
+
isDropdownVisible = false;
|
|
140
|
+
dropdown.style.display = 'none';
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function populateDropdown(processes) {
|
|
144
|
+
if (processes.length === 0) {
|
|
145
|
+
const query = urlInput.value.toLowerCase().trim();
|
|
146
|
+
const message = query
|
|
147
|
+
? `No processes match "${query}"`
|
|
148
|
+
: 'No running processes found';
|
|
149
|
+
dropdown.innerHTML = `<div class="url-dropdown-empty">${message}</div>`;
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const items = processes.map(process => {
|
|
154
|
+
const url = `http://${process.ip}`;
|
|
155
|
+
return `
|
|
156
|
+
<div class="url-dropdown-item" data-url="${url}">
|
|
157
|
+
<div class="url-dropdown-name">${escapeHtml(process.name)}</div>
|
|
158
|
+
<div class="url-dropdown-url">${escapeHtml(url)}</div>
|
|
159
|
+
</div>
|
|
160
|
+
`;
|
|
161
|
+
}).join('');
|
|
162
|
+
|
|
163
|
+
dropdown.innerHTML = items;
|
|
164
|
+
|
|
165
|
+
// Add click handlers to dropdown items
|
|
166
|
+
dropdown.querySelectorAll('.url-dropdown-item').forEach(item => {
|
|
167
|
+
item.addEventListener('click', function() {
|
|
168
|
+
const url = this.getAttribute('data-url');
|
|
169
|
+
urlInput.value = url;
|
|
170
|
+
hideDropdown();
|
|
171
|
+
// Submit the form
|
|
172
|
+
urlInput.closest('form').dispatchEvent(new Event('submit'));
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Utility function to escape HTML
|
|
178
|
+
function escapeHtml(text) {
|
|
179
|
+
const div = document.createElement('div');
|
|
180
|
+
div.textContent = text;
|
|
181
|
+
return div.innerHTML;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Mobile modal functionality
|
|
185
|
+
function createMobileModal() {
|
|
186
|
+
const overlay = document.createElement('div');
|
|
187
|
+
overlay.className = 'url-modal-overlay';
|
|
188
|
+
overlay.id = 'url-modal-overlay';
|
|
189
|
+
|
|
190
|
+
const content = document.createElement('div');
|
|
191
|
+
content.className = 'url-modal-content';
|
|
192
|
+
|
|
193
|
+
const closeButton = document.createElement('span');
|
|
194
|
+
closeButton.className = 'url-modal-close';
|
|
195
|
+
closeButton.innerHTML = '×';
|
|
196
|
+
closeButton.onclick = closeMobileModal;
|
|
197
|
+
|
|
198
|
+
const modalInput = document.createElement('input');
|
|
199
|
+
modalInput.type = 'url';
|
|
200
|
+
modalInput.className = 'url-modal-input';
|
|
201
|
+
modalInput.placeholder = 'enter a local url';
|
|
202
|
+
|
|
203
|
+
const modalDropdown = document.createElement('div');
|
|
204
|
+
modalDropdown.className = 'url-dropdown';
|
|
205
|
+
modalDropdown.id = 'url-modal-dropdown';
|
|
206
|
+
modalDropdown.style.position = 'relative';
|
|
207
|
+
modalDropdown.style.top = '0';
|
|
208
|
+
modalDropdown.style.left = '0';
|
|
209
|
+
modalDropdown.style.right = '0';
|
|
210
|
+
modalDropdown.style.marginTop = '10px';
|
|
211
|
+
|
|
212
|
+
content.appendChild(closeButton);
|
|
213
|
+
content.appendChild(modalInput);
|
|
214
|
+
content.appendChild(modalDropdown);
|
|
215
|
+
overlay.appendChild(content);
|
|
216
|
+
|
|
217
|
+
return { overlay, input: modalInput, dropdown: modalDropdown };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function showMobileModal() {
|
|
221
|
+
let modal = document.getElementById('url-modal-overlay');
|
|
222
|
+
if (!modal) {
|
|
223
|
+
const { overlay, input: modalInput, dropdown: modalDropdown } = createMobileModal();
|
|
224
|
+
modal = overlay;
|
|
225
|
+
document.body.appendChild(modal);
|
|
226
|
+
|
|
227
|
+
// Initialize dropdown functionality for modal
|
|
228
|
+
modalInput.addEventListener('focus', function() {
|
|
229
|
+
if (options.clearBehavior === 'restore' && modalInput.value) {
|
|
230
|
+
setTimeout(() => modalInput.select(), 0);
|
|
231
|
+
}
|
|
232
|
+
showModalDropdown(modalDropdown);
|
|
233
|
+
});
|
|
234
|
+
modalInput.addEventListener('input', function() {
|
|
235
|
+
handleModalInputChange(modalInput, modalDropdown);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Close modal when clicking outside content
|
|
239
|
+
modal.addEventListener('click', function(e) {
|
|
240
|
+
if (e.target === modal) {
|
|
241
|
+
closeMobileModal();
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// Handle form submission
|
|
246
|
+
modalInput.addEventListener('keypress', function(e) {
|
|
247
|
+
if (e.key === 'Enter') {
|
|
248
|
+
e.preventDefault();
|
|
249
|
+
if (modalInput.value) {
|
|
250
|
+
urlInput.value = modalInput.value;
|
|
251
|
+
urlInput.closest('form').dispatchEvent(new Event('submit'));
|
|
252
|
+
closeMobileModal();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
modal.style.display = 'flex';
|
|
259
|
+
const modalInput = modal.querySelector('.url-modal-input');
|
|
260
|
+
|
|
261
|
+
// Set initial value based on config
|
|
262
|
+
if (options.clearBehavior === 'restore') {
|
|
263
|
+
modalInput.value = urlInput.value || options.defaultValue || '';
|
|
264
|
+
} else {
|
|
265
|
+
modalInput.value = '';
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
setTimeout(() => modalInput.focus(), 100);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function closeMobileModal() {
|
|
272
|
+
const modal = document.getElementById('url-modal-overlay');
|
|
273
|
+
if (modal) {
|
|
274
|
+
modal.style.display = 'none';
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function showModalDropdown(modalDropdown) {
|
|
279
|
+
modalDropdown.style.display = 'block';
|
|
280
|
+
|
|
281
|
+
if (allProcesses.length > 0) {
|
|
282
|
+
populateModalDropdown(allProcesses, modalDropdown);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
modalDropdown.innerHTML = '<div class="url-dropdown-loading">Loading running processes...</div>';
|
|
287
|
+
|
|
288
|
+
fetch(options.apiEndpoint)
|
|
289
|
+
.then(response => {
|
|
290
|
+
if (!response.ok) {
|
|
291
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
292
|
+
}
|
|
293
|
+
return response.json();
|
|
294
|
+
})
|
|
295
|
+
.then(data => {
|
|
296
|
+
allProcesses = data.info || [];
|
|
297
|
+
populateModalDropdown(allProcesses, modalDropdown);
|
|
298
|
+
})
|
|
299
|
+
.catch(error => {
|
|
300
|
+
console.error('Failed to fetch processes:', error);
|
|
301
|
+
modalDropdown.innerHTML = '<div class="url-dropdown-empty">Failed to load processes</div>';
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function handleModalInputChange(modalInput, modalDropdown) {
|
|
306
|
+
const query = modalInput.value.toLowerCase().trim();
|
|
307
|
+
let filtered = allProcesses;
|
|
308
|
+
|
|
309
|
+
if (modalInput.selectionStart === 0 && modalInput.selectionEnd === modalInput.value.length) {
|
|
310
|
+
filtered = allProcesses;
|
|
311
|
+
} else if (query) {
|
|
312
|
+
filtered = allProcesses.filter(process => {
|
|
313
|
+
const url = `http://${process.ip}`;
|
|
314
|
+
const name = process.name.toLowerCase();
|
|
315
|
+
const urlLower = url.toLowerCase();
|
|
316
|
+
return name.includes(query) || urlLower.includes(query);
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
populateModalDropdown(filtered, modalDropdown);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function populateModalDropdown(processes, modalDropdown) {
|
|
324
|
+
const modalInput = modalDropdown.parentElement.querySelector('.url-modal-input');
|
|
325
|
+
|
|
326
|
+
if (processes.length === 0) {
|
|
327
|
+
const query = modalInput.value.toLowerCase().trim();
|
|
328
|
+
const message = query ? `No processes match "${query}"` : 'No running processes found';
|
|
329
|
+
modalDropdown.innerHTML = `<div class="url-dropdown-empty">${message}</div>`;
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const items = processes.map(process => {
|
|
334
|
+
const url = `http://${process.ip}`;
|
|
335
|
+
return `
|
|
336
|
+
<div class="url-dropdown-item" data-url="${url}">
|
|
337
|
+
<div class="url-dropdown-name">${escapeHtml(process.name)}</div>
|
|
338
|
+
<div class="url-dropdown-url">${escapeHtml(url)}</div>
|
|
339
|
+
</div>
|
|
340
|
+
`;
|
|
341
|
+
}).join('');
|
|
342
|
+
|
|
343
|
+
modalDropdown.innerHTML = items;
|
|
344
|
+
|
|
345
|
+
modalDropdown.querySelectorAll('.url-dropdown-item').forEach(item => {
|
|
346
|
+
item.addEventListener('click', function() {
|
|
347
|
+
const url = this.getAttribute('data-url');
|
|
348
|
+
modalInput.value = url;
|
|
349
|
+
urlInput.value = url;
|
|
350
|
+
urlInput.closest('form').dispatchEvent(new Event('submit'));
|
|
351
|
+
closeMobileModal();
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Set up mobile button click handler
|
|
357
|
+
if (mobileButton) {
|
|
358
|
+
mobileButton.addEventListener('click', showMobileModal);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Public API
|
|
362
|
+
return {
|
|
363
|
+
show: showDropdown,
|
|
364
|
+
hide: hideDropdown,
|
|
365
|
+
showAll: showAllProcesses,
|
|
366
|
+
showMobileModal: showMobileModal,
|
|
367
|
+
closeMobileModal: closeMobileModal,
|
|
368
|
+
refresh: function() {
|
|
369
|
+
allProcesses = []; // Clear cache to force refetch
|
|
370
|
+
if (isDropdownVisible) {
|
|
371
|
+
showDropdown();
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
filter: handleInputChange,
|
|
375
|
+
destroy: function() {
|
|
376
|
+
// Remove the focus event listener (need to store reference)
|
|
377
|
+
urlInput.removeEventListener('input', handleInputChange);
|
|
378
|
+
if (mobileButton) {
|
|
379
|
+
mobileButton.removeEventListener('click', showMobileModal);
|
|
380
|
+
}
|
|
381
|
+
hideDropdown();
|
|
382
|
+
closeMobileModal();
|
|
383
|
+
allProcesses = [];
|
|
384
|
+
filteredProcesses = [];
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Auto-initialize if DOM is already loaded, otherwise wait for it
|
|
390
|
+
if (document.readyState === 'loading') {
|
|
391
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
392
|
+
// Will be initialized by individual templates with their specific config
|
|
393
|
+
});
|
|
394
|
+
} else {
|
|
395
|
+
// DOM is already loaded, templates can initialize immediately
|
|
396
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const WINDOW_ID = (() => {
|
|
2
|
+
// Try to get existing window ID or create a new one
|
|
3
|
+
let id = sessionStorage.getItem('__window_id');
|
|
4
|
+
if (!id) {
|
|
5
|
+
id = Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
|
6
|
+
try { sessionStorage.setItem('__window_id', id); } catch (_) {}
|
|
7
|
+
}
|
|
8
|
+
return id;
|
|
9
|
+
})();
|
|
10
|
+
|
|
11
|
+
// Window-specific storage wrapper
|
|
12
|
+
window.windowStorage = {
|
|
13
|
+
setItem: (key, value) => {
|
|
14
|
+
try {
|
|
15
|
+
sessionStorage.setItem(`${WINDOW_ID}:${key}`, value);
|
|
16
|
+
} catch (_) {}
|
|
17
|
+
},
|
|
18
|
+
removeItem: (key, value) => {
|
|
19
|
+
try {
|
|
20
|
+
sessionStorage.removeItem(`${WINDOW_ID}:${key}`)
|
|
21
|
+
} catch (_) {}
|
|
22
|
+
},
|
|
23
|
+
getItem: (key) => {
|
|
24
|
+
try {
|
|
25
|
+
return sessionStorage.getItem(`${WINDOW_ID}:${key}`);
|
|
26
|
+
} catch (_) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
package/server/views/app.ejs
CHANGED
|
@@ -1663,13 +1663,30 @@ body.dark .pinokio-git-commit-hash:hover {
|
|
|
1663
1663
|
body.minimized {
|
|
1664
1664
|
flex-direction: row !important;
|
|
1665
1665
|
}
|
|
1666
|
+
body.minimized aside {
|
|
1667
|
+
display: none;
|
|
1668
|
+
}
|
|
1669
|
+
@media only screen and (max-width: 800px) {
|
|
1670
|
+
.mode-selector .btn2 {
|
|
1671
|
+
width: unset;
|
|
1672
|
+
}
|
|
1673
|
+
.mode-selector .btn2 .caption {
|
|
1674
|
+
display: none;
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
/*
|
|
1666
1678
|
@media only screen and (max-width: 800px) {
|
|
1667
1679
|
body {
|
|
1668
1680
|
flex-direction: row !important;
|
|
1669
1681
|
}
|
|
1682
|
+
aside {
|
|
1683
|
+
display: none;
|
|
1684
|
+
}
|
|
1670
1685
|
}
|
|
1686
|
+
*/
|
|
1671
1687
|
</style>
|
|
1672
1688
|
<link href="/app.css" rel="stylesheet"/>
|
|
1689
|
+
<script src="/window_storage.js"></script>
|
|
1673
1690
|
<script src="/timeago.min.js"></script>
|
|
1674
1691
|
<script src="/hotkeys.min.js"></script>
|
|
1675
1692
|
<script src="/sweetalert2.js"></script>
|
|
@@ -2003,24 +2020,6 @@ body.minimized {
|
|
|
2003
2020
|
document.querySelector("main").appendChild(frame)
|
|
2004
2021
|
loaded[name] = true
|
|
2005
2022
|
}
|
|
2006
|
-
/*
|
|
2007
|
-
if (document.body.classList.contains("minimized")) {
|
|
2008
|
-
document.querySelector("#collapse i").className = "fa-solid fa-compress"
|
|
2009
|
-
} else {
|
|
2010
|
-
document.querySelector("#collapse i").className = "fa-solid fa-expand"
|
|
2011
|
-
}
|
|
2012
|
-
*/
|
|
2013
|
-
document.querySelector("#collapse").addEventListener("click", (e) => {
|
|
2014
|
-
document.body.classList.toggle("minimized")
|
|
2015
|
-
let frame_key = window.frameElement?.name || "";
|
|
2016
|
-
if (document.body.classList.contains("minimized")) {
|
|
2017
|
-
// document.querySelector("#collapse i").className = "fa-solid fa-compress"
|
|
2018
|
-
sessionStorage.setItem(frame_key + ":window_mode", "minimized")
|
|
2019
|
-
} else {
|
|
2020
|
-
// document.querySelector("#collapse i").className = "fa-solid fa-expand"
|
|
2021
|
-
sessionStorage.setItem(frame_key + ":window_mode", "full")
|
|
2022
|
-
}
|
|
2023
|
-
})
|
|
2024
2023
|
document.addEventListener("click", (e) => {
|
|
2025
2024
|
interacted = true
|
|
2026
2025
|
})
|
|
@@ -2256,7 +2255,7 @@ body.minimized {
|
|
|
2256
2255
|
<% if (type !== "run") { %>
|
|
2257
2256
|
let _url = new URL(target.href)
|
|
2258
2257
|
let frame_key = window.frameElement?.name || "";
|
|
2259
|
-
|
|
2258
|
+
windowStorage.setItem(frame_key + ":url", _url.pathname + _url.search + _url.hash)
|
|
2260
2259
|
<% } %>
|
|
2261
2260
|
|
|
2262
2261
|
// hide all frames
|
|
@@ -3223,7 +3222,7 @@ body.minimized {
|
|
|
3223
3222
|
refresh_du("logs")
|
|
3224
3223
|
let frame_key = window.frameElement?.name || "";
|
|
3225
3224
|
|
|
3226
|
-
let selection_url =
|
|
3225
|
+
let selection_url = windowStorage.getItem(frame_key + ":url")
|
|
3227
3226
|
console.log({ frame_key, selection_url })
|
|
3228
3227
|
let selection
|
|
3229
3228
|
if (selection_url) {
|
|
@@ -3237,13 +3236,6 @@ body.minimized {
|
|
|
3237
3236
|
selection.click()
|
|
3238
3237
|
// }, 100)
|
|
3239
3238
|
}
|
|
3240
|
-
let window_mode = sessionStorage.getItem(frame_key + ":window_mode")
|
|
3241
|
-
console.log({ window_mode })
|
|
3242
|
-
if (window_mode) {
|
|
3243
|
-
if (window_mode === "minimized") {
|
|
3244
|
-
document.body.classList.add("minimized")
|
|
3245
|
-
}
|
|
3246
|
-
}
|
|
3247
3239
|
<% if (type !== 'run') { %>
|
|
3248
3240
|
fetch("<%=repos%>").then((res) => {
|
|
3249
3241
|
return res.text()
|