pywebexec 1.4.6__py3-none-any.whl → 1.4.8__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 +4 -0
- pywebexec/static/css/style.css +12 -0
- pywebexec/static/images/popup.svg +1 -0
- pywebexec/static/js/popup.js +133 -0
- pywebexec/static/js/script.js +11 -1
- pywebexec/templates/popup.html +21 -0
- pywebexec/version.py +2 -2
- {pywebexec-1.4.6.dist-info → pywebexec-1.4.8.dist-info}/METADATA +1 -1
- {pywebexec-1.4.6.dist-info → pywebexec-1.4.8.dist-info}/RECORD +13 -10
- {pywebexec-1.4.6.dist-info → pywebexec-1.4.8.dist-info}/LICENSE +0 -0
- {pywebexec-1.4.6.dist-info → pywebexec-1.4.8.dist-info}/WHEEL +0 -0
- {pywebexec-1.4.6.dist-info → pywebexec-1.4.8.dist-info}/entry_points.txt +0 -0
- {pywebexec-1.4.6.dist-info → pywebexec-1.4.8.dist-info}/top_level.txt +0 -0
pywebexec/pywebexec.py
CHANGED
@@ -682,6 +682,10 @@ def list_executables():
|
|
682
682
|
executables.sort() # Sort the list of executables alphabetically
|
683
683
|
return jsonify(executables)
|
684
684
|
|
685
|
+
@app.route('/popup/<command_id>')
|
686
|
+
def popup(command_id):
|
687
|
+
return render_template('popup.html', command_id=command_id)
|
688
|
+
|
685
689
|
def main():
|
686
690
|
basef = f"{CONFDIR}/pywebexec_{args.listen}:{args.port}"
|
687
691
|
if args.action == "start":
|
pywebexec/static/css/style.css
CHANGED
@@ -266,4 +266,16 @@ span {
|
|
266
266
|
#outputSlider {
|
267
267
|
width: 100%;
|
268
268
|
}
|
269
|
+
.popup-button {
|
270
|
+
background: none;
|
271
|
+
border: none;
|
272
|
+
cursor: pointer;
|
273
|
+
padding: 0;
|
274
|
+
margin-left: 5px;
|
275
|
+
vertical-align: middle;
|
276
|
+
}
|
277
|
+
.popup-button img {
|
278
|
+
width: 16px;
|
279
|
+
height: 16px;
|
280
|
+
}
|
269
281
|
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg fill="#000000" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <title>popout</title> <path d="M15.694 13.541l2.666 2.665 5.016-5.017 2.59 2.59 0.004-7.734-7.785-0.046 2.526 2.525-5.017 5.017zM25.926 16.945l-1.92-1.947 0.035 9.007-16.015 0.009 0.016-15.973 8.958-0.040-2-2h-7c-1.104 0-2 0.896-2 2v16c0 1.104 0.896 2 2 2h16c1.104 0 2-0.896 2-2l-0.074-7.056z"></path> </g></svg>
|
@@ -0,0 +1,133 @@
|
|
1
|
+
let terminal = new Terminal({
|
2
|
+
cursorBlink: false,
|
3
|
+
cursorInactiveStyle: 'none',
|
4
|
+
disableStdin: true,
|
5
|
+
convertEol: true,
|
6
|
+
fontFamily: 'Consolas NF, monospace, courier-new, courier',
|
7
|
+
scrollback: 999999,
|
8
|
+
theme: {
|
9
|
+
background: '#111412',
|
10
|
+
black: '#111412',
|
11
|
+
green: '#088a5b',
|
12
|
+
blue: "#2760aa",
|
13
|
+
red: '#ba1611',
|
14
|
+
yellow: "#cf8700",
|
15
|
+
magenta: "#4c3d80",
|
16
|
+
cyan: "#00a7aa",
|
17
|
+
brightBlack: "#243C4F",
|
18
|
+
brightBlue: "#5584b1",
|
19
|
+
},
|
20
|
+
customGlyphs: false,
|
21
|
+
rescaleOverlappingGlyphs: true,
|
22
|
+
});
|
23
|
+
|
24
|
+
const fitAddon = new FitAddon.FitAddon();
|
25
|
+
terminal.loadAddon(fitAddon);
|
26
|
+
terminal.open(document.getElementById('output'));
|
27
|
+
fitAddon.fit();
|
28
|
+
|
29
|
+
let currentCommandId = null;
|
30
|
+
let outputInterval = null;
|
31
|
+
let nextOutputLink = null;
|
32
|
+
let fullOutput = '';
|
33
|
+
let outputLength = 0;
|
34
|
+
let title = null;
|
35
|
+
|
36
|
+
function getTokenParam() {
|
37
|
+
const urlParams = new URLSearchParams(window.location.search);
|
38
|
+
return urlParams.get('token') ? `?token=${urlParams.get('token')}` : '';
|
39
|
+
}
|
40
|
+
const urlToken = getTokenParam();
|
41
|
+
|
42
|
+
async function fetchOutput(url) {
|
43
|
+
try {
|
44
|
+
const response = await fetch(url);
|
45
|
+
if (!response.ok) {
|
46
|
+
return;
|
47
|
+
}
|
48
|
+
const data = await response.json();
|
49
|
+
if (data.error) {
|
50
|
+
terminal.write(data.error);
|
51
|
+
clearInterval(outputInterval);
|
52
|
+
} else {
|
53
|
+
slider = document.getElementById('outputSlider')
|
54
|
+
percentage = slider.value;
|
55
|
+
fullOutput += data.output;
|
56
|
+
if (percentage == 100)
|
57
|
+
terminal.write(data.output);
|
58
|
+
else {
|
59
|
+
percentage = Math.round((outputLength * 100)/fullOutput.length);
|
60
|
+
slider.value = percentage;
|
61
|
+
document.getElementById('outputPercentage').innerText = `${percentage}%`;
|
62
|
+
}
|
63
|
+
nextOutputLink = data.links.next;
|
64
|
+
if (data.status != 'running') {
|
65
|
+
title.innerText = `${data.status} ${title.innerText.split(' ').slice(1).join(' ')}`;
|
66
|
+
clearInterval(outputInterval);
|
67
|
+
}
|
68
|
+
}
|
69
|
+
} catch (error) {
|
70
|
+
console.log('Error fetching output:', error);
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
async function viewOutput(command_id) {
|
75
|
+
document.getElementById('outputSlider').value = 100;
|
76
|
+
adjustOutputHeight();
|
77
|
+
currentCommandId = command_id;
|
78
|
+
nextOutputLink = `/command_output/${command_id}${urlToken}`;
|
79
|
+
clearInterval(outputInterval);
|
80
|
+
terminal.clear();
|
81
|
+
terminal.reset();
|
82
|
+
fullOutput = '';
|
83
|
+
try {
|
84
|
+
const response = await fetch(`/command_status/${command_id}${urlToken}`);
|
85
|
+
if (!response.ok) {
|
86
|
+
return;
|
87
|
+
}
|
88
|
+
const data = await response.json();
|
89
|
+
title.innerText = `${data.status} ${data.command} ${data.params.join(' ')}`;
|
90
|
+
if (data.command == 'term')
|
91
|
+
terminal.options.cursorInactiveStyle = 'outline';
|
92
|
+
else
|
93
|
+
terminal.options.cursorInactiveStyle = 'none';
|
94
|
+
if (data.status === 'running') {
|
95
|
+
fetchOutput(nextOutputLink);
|
96
|
+
outputInterval = setInterval(() => fetchOutput(nextOutputLink), 1000);
|
97
|
+
} else {
|
98
|
+
fetchOutput(nextOutputLink);
|
99
|
+
}
|
100
|
+
} catch (error) {
|
101
|
+
console.log('Error viewing output:', error);
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
function adjustOutputHeight() {
|
106
|
+
const outputDiv = document.getElementById('output');
|
107
|
+
const windowHeight = window.innerHeight;
|
108
|
+
const outputTop = outputDiv.getBoundingClientRect().top;
|
109
|
+
const maxHeight = windowHeight - outputTop - 60; // Adjusted for slider height
|
110
|
+
outputDiv.style.height = `${maxHeight}px`;
|
111
|
+
fitAddon.fit();
|
112
|
+
}
|
113
|
+
|
114
|
+
function sliderUpdateOutput() {
|
115
|
+
const slider = document.getElementById('outputSlider');
|
116
|
+
const percentage = slider.value;
|
117
|
+
outputLength = Math.floor((fullOutput.length * percentage) / 100);
|
118
|
+
const limitedOutput = fullOutput.slice(0, outputLength);
|
119
|
+
terminal.clear();
|
120
|
+
terminal.reset();
|
121
|
+
terminal.write(limitedOutput);
|
122
|
+
document.getElementById('outputPercentage').innerText = `${percentage}%`;
|
123
|
+
}
|
124
|
+
|
125
|
+
document.getElementById('outputSlider').addEventListener('input', sliderUpdateOutput);
|
126
|
+
|
127
|
+
window.addEventListener('resize', adjustOutputHeight);
|
128
|
+
window.addEventListener('load', () => {
|
129
|
+
title = document.getElementById('outputTitle');
|
130
|
+
const commandId = window.location.pathname.split('/').slice(-1)[0];
|
131
|
+
viewOutput(commandId);
|
132
|
+
});
|
133
|
+
|
pywebexec/static/js/script.js
CHANGED
@@ -107,7 +107,12 @@ async function fetchCommands() {
|
|
107
107
|
<td>
|
108
108
|
${command.command.startsWith('term') ? '' : command.status === 'running' ? `<button onclick="stopCommand('${command.command_id}', event)">Stop</button>` : `<button onclick="relaunchCommand('${command.command_id}', event)">Run</button>`}
|
109
109
|
</td>
|
110
|
-
<td class="monospace outcol"
|
110
|
+
<td class="monospace outcol">
|
111
|
+
<button class="popup-button" onclick="openPopup('${command.command_id}', event)">
|
112
|
+
<img src="/static/images/popup.svg" alt="Popup">
|
113
|
+
</button>
|
114
|
+
${command.last_output_line || ''}
|
115
|
+
</td>
|
111
116
|
`;
|
112
117
|
commandsTbody.appendChild(commandRow);
|
113
118
|
});
|
@@ -180,6 +185,11 @@ async function viewOutput(command_id) {
|
|
180
185
|
}
|
181
186
|
}
|
182
187
|
|
188
|
+
async function openPopup(command_id) {
|
189
|
+
const popupUrl = `/popup/${command_id}${urlToken}`;
|
190
|
+
window.open(popupUrl, '_blank', 'width=1000,height=600');
|
191
|
+
}
|
192
|
+
|
183
193
|
async function relaunchCommand(command_id, event) {
|
184
194
|
event.stopPropagation();
|
185
195
|
event.stopImmediatePropagation();
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<link rel="icon" href="/static/images/favicon.svg" type="image/svg+xml">
|
6
|
+
<title id="outputTitle">Command Output</title>
|
7
|
+
<link rel="stylesheet" href="/static/css/style.css">
|
8
|
+
<link rel="stylesheet" href="/static/css/xterm.css">
|
9
|
+
</head>
|
10
|
+
<body>
|
11
|
+
<div id="output" class="output"></div>
|
12
|
+
<div class="slider-container">
|
13
|
+
<input type="range" id="outputSlider" min="0" max="100" value="100">
|
14
|
+
<label for="outputSlider"><span id="outputPercentage">100%</span></label>
|
15
|
+
</div>
|
16
|
+
<script src="/static/js/xterm/ansi_up.min.js"></script>
|
17
|
+
<script src="/static/js/xterm/xterm.js"></script>
|
18
|
+
<script src="/static/js/xterm/xterm-addon-fit.js"></script>
|
19
|
+
<script type="text/javascript" src="/static/js/popup.js"></script>
|
20
|
+
</body>
|
21
|
+
</html>
|
pywebexec/version.py
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
pywebexec/__init__.py,sha256=4spIsVaF8RJt8S58AG_wWoORRNkws9Iwqprj27C3ljM,99
|
2
|
-
pywebexec/pywebexec.py,sha256=
|
3
|
-
pywebexec/version.py,sha256=
|
2
|
+
pywebexec/pywebexec.py,sha256=wediupUiLmpCaER3b_-07QitIiTimgmEBBxFLGT6-DQ,26125
|
3
|
+
pywebexec/version.py,sha256=5V0g1zXAYqZG41mH-4FajHcVE05C9vUQm3ybra2NhKU,411
|
4
4
|
pywebexec/static/css/Consolas NF.ttf,sha256=DJEOzF0eqZ-kxu3Gs_VE8X0NJqiobBzmxWDGpdgGRxI,1313900
|
5
|
-
pywebexec/static/css/style.css,sha256=
|
5
|
+
pywebexec/static/css/style.css,sha256=sDBhZ-csW5THIKfTDsHqxCSg6rIJ91Oh15sjw_HjJo4,5702
|
6
6
|
pywebexec/static/css/xterm.css,sha256=gy8_LGA7Q61DUf8ElwFQzHqHMBQnbbEmpgZcbdgeSHI,5383
|
7
7
|
pywebexec/static/images/aborted.svg,sha256=_mP43hU5QdRLFZIknBgjx-dIXrHgQG23-QV27ApXK2A,381
|
8
8
|
pywebexec/static/images/copy.svg,sha256=d9OwtGh5GzzZHzYcDrLfNxZYLth1Q64x7bRyYxu4Px0,622
|
@@ -10,19 +10,22 @@ pywebexec/static/images/copy_ok.svg,sha256=mEqUVUhSq8xaJK2msQkxRawnz_KwlCZ-tok8Q
|
|
10
10
|
pywebexec/static/images/down-arrow.svg,sha256=4TclEmntMvKk_F_ADXgTpGtviYo826EDmmZiGE7HQBI,121
|
11
11
|
pywebexec/static/images/failed.svg,sha256=ADZ7IKrUyOXtqpivnz3VcH0-Wru-I5MOi3OJAkI3hxk,1439
|
12
12
|
pywebexec/static/images/favicon.svg,sha256=9gSN5Oak1zTWhTCyutlupPBKUxcbdoVt7dvhk8xvEug,1224
|
13
|
+
pywebexec/static/images/popup.svg,sha256=0Bl9A_v5cBsMPn6FnOlVWlAQKgd2zqiWQbhjcL9BDzI,559
|
13
14
|
pywebexec/static/images/running.gif,sha256=iYuzQGkMxrakSIwt6gPieKCImGZoSAHmU5MUNZa7cpw,25696
|
14
15
|
pywebexec/static/images/success.svg,sha256=PJDcCSTevJh7rkfSFLtc7P0pbeh8PVQBS8DaOLQemmc,489
|
15
16
|
pywebexec/static/js/commands.js,sha256=8JDb3Q55EJOYf2Q9Uy6qEuqAnn1oGjM0RndgQ4aOjqo,7725
|
16
|
-
pywebexec/static/js/
|
17
|
+
pywebexec/static/js/popup.js,sha256=n5JaFwuoiBkYQJGOIva__qVY7eQBNQ9vHEr0lIKHTY4,4399
|
18
|
+
pywebexec/static/js/script.js,sha256=B7_rpQiiwgKpqtAprZx0RcSYjwPvd4SRbvgyF9dNzP0,11973
|
17
19
|
pywebexec/static/js/xterm/LICENSE,sha256=EU1P4eXTull-_T9I80VuwnJXubB-zLzUl3xpEYj2T1M,1083
|
18
20
|
pywebexec/static/js/xterm/ansi_up.min.js,sha256=KNGV0vEr30hNqKQimTAvGVy-icD5A1JqMQTtvYtKR2Y,13203
|
19
21
|
pywebexec/static/js/xterm/xterm-addon-fit.js,sha256=Pprm9pZe4SadVXS5Bc8b9VnC9Ex4QlWwA0pxOH53Gck,1460
|
20
22
|
pywebexec/static/js/xterm/xterm.js,sha256=Bzka76jZwEhVt_LlS0e0qMw7ryGa1p5qfxFyeohphBo,283371
|
21
23
|
pywebexec/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
22
24
|
pywebexec/templates/index.html,sha256=DYtT555wSNhnFtzzHhPMWJireynCJNnAuTytpoORQeE,2321
|
23
|
-
pywebexec
|
24
|
-
pywebexec-1.4.
|
25
|
-
pywebexec-1.4.
|
26
|
-
pywebexec-1.4.
|
27
|
-
pywebexec-1.4.
|
28
|
-
pywebexec-1.4.
|
25
|
+
pywebexec/templates/popup.html,sha256=6kmZKb0tGJ7jsr9DqDEriSMlbkW-PJTtCiUE9U5_FOE,837
|
26
|
+
pywebexec-1.4.8.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
|
27
|
+
pywebexec-1.4.8.dist-info/METADATA,sha256=1yv4C31GoevftBY7JMF3wp14lHRZmkCg4PVZoXjjgx8,7399
|
28
|
+
pywebexec-1.4.8.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
29
|
+
pywebexec-1.4.8.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
|
30
|
+
pywebexec-1.4.8.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
|
31
|
+
pywebexec-1.4.8.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|