pywebexec 1.1.2__py3-none-any.whl → 1.4.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.
- pywebexec/pywebexec.py +255 -94
- pywebexec/static/css/Consolas NF.ttf +0 -0
- pywebexec/static/css/style.css +187 -18
- pywebexec/static/css/xterm.css +209 -0
- pywebexec/static/images/down-arrow.svg +1 -0
- pywebexec/static/images/favicon.svg +8 -1
- pywebexec/static/images/popup.svg +1 -0
- pywebexec/static/images/running.gif +0 -0
- pywebexec/static/js/commands.js +223 -0
- pywebexec/static/js/popup.js +141 -0
- pywebexec/static/js/script.js +248 -110
- pywebexec/static/js/xterm/LICENSE +21 -0
- pywebexec/static/js/xterm/ansi_up.min.js +7 -0
- pywebexec/static/js/xterm/xterm-addon-fit.js +1 -0
- pywebexec/static/js/xterm/xterm.js +1 -0
- pywebexec/templates/index.html +23 -7
- pywebexec/templates/popup.html +24 -0
- pywebexec/version.py +2 -2
- {pywebexec-1.1.2.dist-info → pywebexec-1.4.12.dist-info}/METADATA +49 -19
- pywebexec-1.4.12.dist-info/RECORD +31 -0
- pywebexec/static/images/running.svg +0 -1
- pywebexec-1.1.2.dist-info/RECORD +0 -20
- {pywebexec-1.1.2.dist-info → pywebexec-1.4.12.dist-info}/LICENSE +0 -0
- {pywebexec-1.1.2.dist-info → pywebexec-1.4.12.dist-info}/WHEEL +0 -0
- {pywebexec-1.1.2.dist-info → pywebexec-1.4.12.dist-info}/entry_points.txt +0 -0
- {pywebexec-1.1.2.dist-info → pywebexec-1.4.12.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,223 @@
|
|
1
|
+
// commands.js
|
2
|
+
let commandInput = document.getElementById('commandName');
|
3
|
+
let paramsInput = document.getElementById('params');
|
4
|
+
let commandListSelect = document.getElementById('commandList');
|
5
|
+
let showCommandListButton = document.getElementById('showCommandListButton');
|
6
|
+
let isHandlingKeydown = false;
|
7
|
+
|
8
|
+
function unfilterCommands() {
|
9
|
+
const options = commandListSelect.options;
|
10
|
+
for (let i = 0; i < options.length; i++) {
|
11
|
+
options[i].style.display = 'block';
|
12
|
+
}
|
13
|
+
commandListSelect.size = Math.min(20, commandListSelect.options.length);
|
14
|
+
}
|
15
|
+
|
16
|
+
function filterCommands() {
|
17
|
+
const value = commandInput.value.slice(0, commandInput.selectionStart);
|
18
|
+
const options = commandListSelect.options;
|
19
|
+
let nbVisibleItems = 0;
|
20
|
+
for (let i = 0; i < options.length; i++) {
|
21
|
+
if (options[i].text.startsWith(value)) {
|
22
|
+
options[i].style.display = 'block';
|
23
|
+
nbVisibleItems += 1;
|
24
|
+
} else {
|
25
|
+
options[i].style.display = 'none';
|
26
|
+
}
|
27
|
+
}
|
28
|
+
if (nbVisibleItems > 1) {
|
29
|
+
commandListSelect.size = Math.min(20, nbVisibleItems);
|
30
|
+
commandListSelect.style.display = 'block';
|
31
|
+
} else {
|
32
|
+
commandListSelect.style.display = 'none';
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
function setCommandListPosition() {
|
37
|
+
const rect = commandInput.getBoundingClientRect();
|
38
|
+
commandListSelect.style.left = `${rect.left}px`;
|
39
|
+
commandListSelect.style.top = `${rect.bottom}px`;
|
40
|
+
}
|
41
|
+
|
42
|
+
function adjustInputWidth(input) {
|
43
|
+
input.style.width = 'auto';
|
44
|
+
input.style.width = `${input.scrollWidth}px`;
|
45
|
+
}
|
46
|
+
|
47
|
+
|
48
|
+
paramsInput.addEventListener('input', () => adjustInputWidth(paramsInput));
|
49
|
+
commandInput.addEventListener('input', () => {
|
50
|
+
adjustInputWidth(commandInput);
|
51
|
+
filterCommands(); // Filter commands on input
|
52
|
+
});
|
53
|
+
|
54
|
+
paramsInput.addEventListener('mouseover', () => {
|
55
|
+
paramsInput.focus();
|
56
|
+
paramsInput.setSelectionRange(0, paramsInput.value.length);
|
57
|
+
});
|
58
|
+
|
59
|
+
commandInput.addEventListener('mouseover', () => {
|
60
|
+
commandInput.focus();
|
61
|
+
commandInput.setSelectionRange(0, commandInput.value.length);
|
62
|
+
});
|
63
|
+
|
64
|
+
commandInput.addEventListener('input', (event) => {
|
65
|
+
if (event.inputType === 'deleteContentBackward') {
|
66
|
+
const newValue = commandInput.value.slice(0, -1);
|
67
|
+
commandInput.value = newValue;
|
68
|
+
commandInput.setSelectionRange(newValue.length, newValue.length);
|
69
|
+
}
|
70
|
+
const value = commandInput.value;
|
71
|
+
const options = commandListSelect.options;
|
72
|
+
if (value) {
|
73
|
+
const match = Array.from(options).find(option => option.text.startsWith(value));
|
74
|
+
if (match) {
|
75
|
+
commandInput.value = match.text;
|
76
|
+
commandInput.setSelectionRange(value.length, match.text.length);
|
77
|
+
} else {
|
78
|
+
commandInput.value = value.slice(0, -1);
|
79
|
+
}
|
80
|
+
}
|
81
|
+
filterCommands();
|
82
|
+
adjustInputWidth(commandInput); // Adjust width on input
|
83
|
+
});
|
84
|
+
|
85
|
+
commandInput.addEventListener('keydown', (event) => {
|
86
|
+
if (event.key === ' ' || event.key === 'ArrowRight') {
|
87
|
+
event.preventDefault();
|
88
|
+
paramsInput.focus();
|
89
|
+
paramsInput.setSelectionRange(0, paramsInput.value.length);
|
90
|
+
} else if (event.key === 'ArrowDown') {
|
91
|
+
/*setCommandListPosition();*/
|
92
|
+
unfilterCommands();
|
93
|
+
if (commandListSelect.options.length > 1) {
|
94
|
+
commandListSelect.style.display = 'block';
|
95
|
+
commandListSelect.focus();
|
96
|
+
commandListSelect.selectedIndex = 0;
|
97
|
+
}
|
98
|
+
event.preventDefault();
|
99
|
+
}
|
100
|
+
});
|
101
|
+
|
102
|
+
paramsInput.addEventListener('keydown', (event) => {
|
103
|
+
if (paramsInput.selectionStart > 0) return;
|
104
|
+
if (event.key === 'ArrowLeft') {
|
105
|
+
commandInput.focus();
|
106
|
+
commandInput.setSelectionRange(0, commandInput.value.length);
|
107
|
+
event.preventDefault();
|
108
|
+
return;
|
109
|
+
}
|
110
|
+
if (event.key === 'Backspace') {
|
111
|
+
val = paramsInput.value
|
112
|
+
paramsInput.value = val.slice(0, paramsInput.selectionStart) + val.slice(paramsInput.selectionEnd)
|
113
|
+
commandInput.focus();
|
114
|
+
commandInput.setSelectionRange(0, commandInput.value.length);
|
115
|
+
event.preventDefault();
|
116
|
+
}
|
117
|
+
});
|
118
|
+
|
119
|
+
commandListSelect.addEventListener('keydown', (event) => {
|
120
|
+
if (event.key === 'Enter') {
|
121
|
+
event.preventDefault(); // Prevent form submission
|
122
|
+
const selectedOption = commandListSelect.options[commandListSelect.selectedIndex];
|
123
|
+
commandInput.value = selectedOption.text;
|
124
|
+
commandListSelect.style.display = 'none';
|
125
|
+
adjustInputWidth(commandInput);
|
126
|
+
paramsInput.focus();
|
127
|
+
return;
|
128
|
+
}
|
129
|
+
if (event.key === 'ArrowUp' && commandListSelect.selectedIndex == 0) {
|
130
|
+
commandInput.focus();
|
131
|
+
commandListSelect.style.display = 'none'
|
132
|
+
}
|
133
|
+
});
|
134
|
+
|
135
|
+
commandListSelect.addEventListener('click', (event) => {
|
136
|
+
event.preventDefault(); // Prevent form submission
|
137
|
+
const selectedOption = commandListSelect.options[commandListSelect.selectedIndex];
|
138
|
+
commandInput.value = selectedOption.text;
|
139
|
+
commandListSelect.style.display = 'none';
|
140
|
+
adjustInputWidth(commandInput);
|
141
|
+
paramsInput.focus();
|
142
|
+
});
|
143
|
+
|
144
|
+
|
145
|
+
commandInput.addEventListener('click', () => {
|
146
|
+
setCommandListPosition();
|
147
|
+
if (commandListSelect.style.display == 'none')
|
148
|
+
commandListSelect.style.display = 'block';
|
149
|
+
else
|
150
|
+
commandListSelect.style.display = 'none';
|
151
|
+
filterCommands();
|
152
|
+
});
|
153
|
+
|
154
|
+
commandInput.addEventListener('blur', (event) => {
|
155
|
+
if (event.relatedTarget === showCommandListButton || event.relatedTarget === commandListSelect) {
|
156
|
+
event.preventDefault();
|
157
|
+
return;
|
158
|
+
}
|
159
|
+
commandListSelect.style.display = 'none';
|
160
|
+
adjustInputWidth(commandInput);
|
161
|
+
});
|
162
|
+
|
163
|
+
showCommandListButton.addEventListener('click', (event) => {
|
164
|
+
event.preventDefault();
|
165
|
+
setCommandListPosition();
|
166
|
+
unfilterCommands();
|
167
|
+
if (commandListSelect.style.display == 'none')
|
168
|
+
commandListSelect.style.display = 'block';
|
169
|
+
else
|
170
|
+
commandListSelect.style.display = 'none';
|
171
|
+
});
|
172
|
+
|
173
|
+
window.addEventListener('click', (event) => {
|
174
|
+
if (!commandInput.contains(event.target) && !commandListSelect.contains(event.target) && !showCommandListButton.contains(event.target)) {
|
175
|
+
commandListSelect.style.display = 'none';
|
176
|
+
}
|
177
|
+
});
|
178
|
+
|
179
|
+
window.addEventListener('keydown', (event) => {
|
180
|
+
if ([commandInput, paramsInput, commandListSelect].includes(document.activeElement)) return;
|
181
|
+
if (event.code === `Key${event.key.toUpperCase()}`) {
|
182
|
+
commandInput.focus();
|
183
|
+
commandInput.dispatchEvent(new KeyboardEvent('keydown', event));
|
184
|
+
}
|
185
|
+
});
|
186
|
+
|
187
|
+
window.addEventListener('resize', () => {
|
188
|
+
setCommandListPosition();
|
189
|
+
});
|
190
|
+
|
191
|
+
window.addEventListener('load', () => {
|
192
|
+
fetchExecutables();
|
193
|
+
adjustInputWidth(paramsInput); // Adjust width on load
|
194
|
+
adjustInputWidth(commandInput); // Adjust width on load
|
195
|
+
setCommandListPosition();
|
196
|
+
});
|
197
|
+
|
198
|
+
async function fetchExecutables() {
|
199
|
+
try {
|
200
|
+
const response = await fetch(`/executables${urlToken}`);
|
201
|
+
if (!response.ok) {
|
202
|
+
throw new Error('Failed to fetch command status');
|
203
|
+
}
|
204
|
+
const executables = await response.json();
|
205
|
+
commandListSelect.innerHTML = '';
|
206
|
+
executables.forEach(executable => {
|
207
|
+
const option = document.createElement('option');
|
208
|
+
option.className = 'command-item';
|
209
|
+
option.text = executable;
|
210
|
+
commandListSelect.appendChild(option);
|
211
|
+
});
|
212
|
+
} catch (error) {
|
213
|
+
alert("Failed to fetch executables");
|
214
|
+
}
|
215
|
+
commandListSelect.size = Math.min(20, commandListSelect.options.length);
|
216
|
+
if (commandListSelect.options.length == 1) {
|
217
|
+
commandInput.value = commandListSelect.options[0].text;
|
218
|
+
showCommandListButton.style.display = 'none';
|
219
|
+
}
|
220
|
+
if (commandListSelect.options.length == 0)
|
221
|
+
document.getElementById('launchForm').style.display = 'none';
|
222
|
+
|
223
|
+
}
|
@@ -0,0 +1,141 @@
|
|
1
|
+
const maxScrollback = 99999;
|
2
|
+
const maxSize = 10485760; // 10MB
|
3
|
+
let terminal = new Terminal({
|
4
|
+
cursorBlink: false,
|
5
|
+
cursorInactiveStyle: 'none',
|
6
|
+
disableStdin: true,
|
7
|
+
convertEol: true,
|
8
|
+
fontFamily: 'Consolas NF, monospace, courier-new, courier',
|
9
|
+
scrollback: maxScrollback,
|
10
|
+
theme: {
|
11
|
+
background: '#111412',
|
12
|
+
black: '#111412',
|
13
|
+
green: '#088a5b',
|
14
|
+
blue: "#2760aa",
|
15
|
+
red: '#ba1611',
|
16
|
+
yellow: "#cf8700",
|
17
|
+
magenta: "#4c3d80",
|
18
|
+
cyan: "#00a7aa",
|
19
|
+
brightBlack: "#243C4F",
|
20
|
+
brightBlue: "#5584b1",
|
21
|
+
brightGreen: "#18Ed93",
|
22
|
+
},
|
23
|
+
customGlyphs: false,
|
24
|
+
rescaleOverlappingGlyphs: true,
|
25
|
+
});
|
26
|
+
|
27
|
+
const fitAddon = new FitAddon.FitAddon();
|
28
|
+
terminal.loadAddon(fitAddon);
|
29
|
+
terminal.open(document.getElementById('output'));
|
30
|
+
fitAddon.fit();
|
31
|
+
|
32
|
+
let currentCommandId = null;
|
33
|
+
let outputInterval = null;
|
34
|
+
let nextOutputLink = null;
|
35
|
+
let fullOutput = '';
|
36
|
+
let outputLength = 0;
|
37
|
+
let title = null;
|
38
|
+
let slider = null;
|
39
|
+
|
40
|
+
function getTokenParam() {
|
41
|
+
const urlParams = new URLSearchParams(window.location.search);
|
42
|
+
return urlParams.get('token') ? `?token=${urlParams.get('token')}` : '';
|
43
|
+
}
|
44
|
+
const urlToken = getTokenParam();
|
45
|
+
|
46
|
+
async function fetchOutput(url) {
|
47
|
+
try {
|
48
|
+
const response = await fetch(url);
|
49
|
+
if (!response.ok) {
|
50
|
+
document.getElementById('dimmer').style.display = 'none';
|
51
|
+
return;
|
52
|
+
}
|
53
|
+
const data = await response.json();
|
54
|
+
if (data.error) {
|
55
|
+
terminal.write(data.error);
|
56
|
+
clearInterval(outputInterval);
|
57
|
+
} else {
|
58
|
+
percentage = slider.value;
|
59
|
+
fullOutput += data.output;
|
60
|
+
if (fullOutput.length > maxSize)
|
61
|
+
fullOutput = fullOutput.slice(-maxSize);
|
62
|
+
if (percentage == 100)
|
63
|
+
terminal.write(data.output);
|
64
|
+
else {
|
65
|
+
percentage = Math.round((outputLength * 100)/fullOutput.length);
|
66
|
+
slider.value = percentage;
|
67
|
+
document.getElementById('outputPercentage').innerText = `${percentage}%`;
|
68
|
+
}
|
69
|
+
nextOutputLink = data.links.next;
|
70
|
+
if (data.status != 'running') {
|
71
|
+
title.innerText = `${data.status} ${title.innerText.split(' ').slice(1).join(' ')}`;
|
72
|
+
clearInterval(outputInterval);
|
73
|
+
}
|
74
|
+
}
|
75
|
+
} catch (error) {
|
76
|
+
document.getElementById('dimmer').style.display = 'block';
|
77
|
+
console.log('Error fetching output:', error);
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
async function viewOutput(command_id) {
|
82
|
+
slider.value = 100;
|
83
|
+
adjustOutputHeight();
|
84
|
+
currentCommandId = command_id;
|
85
|
+
nextOutputLink = `/command_output/${command_id}${urlToken}`;
|
86
|
+
clearInterval(outputInterval);
|
87
|
+
terminal.clear();
|
88
|
+
terminal.reset();
|
89
|
+
fullOutput = '';
|
90
|
+
try {
|
91
|
+
const response = await fetch(`/command_status/${command_id}${urlToken}`);
|
92
|
+
if (!response.ok) {
|
93
|
+
return;
|
94
|
+
}
|
95
|
+
const data = await response.json();
|
96
|
+
title.innerText = `${data.status} ${data.command} ${data.params.join(' ')}`;
|
97
|
+
if (data.command == 'term')
|
98
|
+
terminal.options.cursorInactiveStyle = 'outline';
|
99
|
+
else
|
100
|
+
terminal.options.cursorInactiveStyle = 'none';
|
101
|
+
if (data.status === 'running') {
|
102
|
+
fetchOutput(nextOutputLink);
|
103
|
+
outputInterval = setInterval(() => fetchOutput(nextOutputLink), 1000);
|
104
|
+
} else {
|
105
|
+
fetchOutput(nextOutputLink);
|
106
|
+
}
|
107
|
+
} catch (error) {
|
108
|
+
console.log('Error viewing output:', error);
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
function adjustOutputHeight() {
|
113
|
+
const outputDiv = document.getElementById('output');
|
114
|
+
const windowHeight = window.innerHeight;
|
115
|
+
const outputTop = outputDiv.getBoundingClientRect().top;
|
116
|
+
const maxHeight = windowHeight - outputTop - 60; // Adjusted for slider height
|
117
|
+
outputDiv.style.height = `${maxHeight}px`;
|
118
|
+
fitAddon.fit();
|
119
|
+
sliderUpdateOutput();
|
120
|
+
}
|
121
|
+
|
122
|
+
function sliderUpdateOutput() {
|
123
|
+
const percentage = slider.value;
|
124
|
+
outputLength = Math.floor((fullOutput.length * percentage) / 100);
|
125
|
+
const limitedOutput = fullOutput.slice(0, outputLength);
|
126
|
+
terminal.clear();
|
127
|
+
terminal.reset();
|
128
|
+
terminal.write(limitedOutput);
|
129
|
+
document.getElementById('outputPercentage').innerText = `${percentage}%`;
|
130
|
+
}
|
131
|
+
|
132
|
+
|
133
|
+
window.addEventListener('resize', adjustOutputHeight);
|
134
|
+
window.addEventListener('load', () => {
|
135
|
+
title = document.getElementById('outputTitle');
|
136
|
+
slider = document.getElementById('outputSlider');
|
137
|
+
slider.addEventListener('input', sliderUpdateOutput);
|
138
|
+
const commandId = window.location.pathname.split('/').slice(-1)[0];
|
139
|
+
viewOutput(commandId);
|
140
|
+
});
|
141
|
+
|