pywebexec 1.4.5__py3-none-any.whl → 1.4.7__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 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":
@@ -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
 
@@ -1 +1,8 @@
1
- <svg viewBox="0 0 24 24" fill="none" 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"> <path opacity="0.1" d="M3 8C3 6.11438 3 5.17157 3.58579 4.58579C4.17157 4 5.11438 4 7 4H12H17C18.8856 4 19.8284 4 20.4142 4.58579C21 5.17157 21 6.11438 21 8V12V16C21 17.8856 21 18.8284 20.4142 19.4142C19.8284 20 18.8856 20 17 20H12H7C5.11438 20 4.17157 20 3.58579 19.4142C3 18.8284 3 17.8856 3 16V12V8Z" fill="#323232"></path> <path d="M13 15H16" stroke="#323232" stroke-width="2" stroke-linecap="round"></path> <path d="M8 15L10.5 12.5V12.5C10.7761 12.2239 10.7761 11.7761 10.5 11.5V11.5L8 9" stroke="#323232" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path> <path d="M3 8C3 6.11438 3 5.17157 3.58579 4.58579C4.17157 4 5.11438 4 7 4H12H17C18.8856 4 19.8284 4 20.4142 4.58579C21 5.17157 21 6.11438 21 8V12V16C21 17.8856 21 18.8284 20.4142 19.4142C19.8284 20 18.8856 20 17 20H12H7C5.11438 20 4.17157 20 3.58579 19.4142C3 18.8284 3 17.8856 3 16V12V8Z" stroke="#323232" stroke-width="2" stroke-linejoin="round"></path> </g></svg>
1
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g>
2
+ <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g>
3
+ <g id="SVGRepo_iconCarrier">
4
+ <path opacity="0.8" d="M3 8C3 6.11438 3 5.17157 3.58579 4.58579C4.17157 4 5.11438 4 7 4H12H17C18.8856 4 19.8284 4 20.4142 4.58579C21 5.17157 21 6.11438 21 8V12V16C21 17.8856 21 18.8284 20.4142 19.4142C19.8284 20 18.8856 20 17 20H12H7C5.11438 20 4.17157 20 3.58579 19.4142C3 18.8284 3 17.8856 3 16V12V8Z" style="fill: rgba(5, 167, 98, 0.9);"></path>
5
+ <path d="M13 15H16" stroke="#323232" stroke-width="2" stroke-linecap="round"></path>
6
+ <path d="M8 15L10.5 12.5V12.5C10.7761 12.2239 10.7761 11.7761 10.5 11.5V11.5L8 9" stroke="#323232" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
7
+ <path d="M3 8C3 6.11438 3 5.17157 3.58579 4.58579C4.17157 4 5.11438 4 7 4H12H17C18.8856 4 19.8284 4 20.4142 4.58579C21 5.17157 21 6.11438 21 8V12V16C21 17.8856 21 18.8284 20.4142 19.4142C19.8284 20 18.8856 20 17 20H12H7C5.11438 20 4.17157 20 3.58579 19.4142C3 18.8284 3 17.8856 3 16V12V8Z" stroke="#323232" stroke-width="2" stroke-linejoin="round"></path>
8
+ </g></svg>
@@ -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,130 @@
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
+
35
+ function getTokenParam() {
36
+ const urlParams = new URLSearchParams(window.location.search);
37
+ return urlParams.get('token') ? `?token=${urlParams.get('token')}` : '';
38
+ }
39
+ const urlToken = getTokenParam();
40
+
41
+ async function fetchOutput(url) {
42
+ try {
43
+ const response = await fetch(url);
44
+ if (!response.ok) {
45
+ return;
46
+ }
47
+ const data = await response.json();
48
+ if (data.error) {
49
+ terminal.write(data.error);
50
+ clearInterval(outputInterval);
51
+ } else {
52
+ slider = document.getElementById('outputSlider')
53
+ percentage = slider.value;
54
+ fullOutput += data.output;
55
+ if (percentage == 100)
56
+ terminal.write(data.output);
57
+ else {
58
+ percentage = Math.round((outputLength * 100)/fullOutput.length);
59
+ slider.value = percentage;
60
+ document.getElementById('outputPercentage').innerText = `${percentage}%`;
61
+ }
62
+ nextOutputLink = data.links.next;
63
+ if (data.status != 'running') {
64
+ clearInterval(outputInterval);
65
+ }
66
+ }
67
+ } catch (error) {
68
+ console.log('Error fetching output:', error);
69
+ }
70
+ }
71
+
72
+ async function viewOutput(command_id) {
73
+ document.getElementById('outputSlider').value = 100;
74
+ adjustOutputHeight();
75
+ currentCommandId = command_id;
76
+ nextOutputLink = `/command_output/${command_id}${urlToken}`;
77
+ clearInterval(outputInterval);
78
+ terminal.clear();
79
+ terminal.reset();
80
+ fullOutput = '';
81
+ try {
82
+ const response = await fetch(`/command_status/${command_id}${urlToken}`);
83
+ if (!response.ok) {
84
+ return;
85
+ }
86
+ const data = await response.json();
87
+ document.getElementsByTagName('title')[0].innerText = `${data.command} ${data.status}`;
88
+ if (data.command == 'term')
89
+ terminal.options.cursorInactiveStyle = 'outline';
90
+ else
91
+ terminal.options.cursorInactiveStyle = 'none';
92
+ if (data.status === 'running') {
93
+ fetchOutput(nextOutputLink);
94
+ outputInterval = setInterval(() => fetchOutput(nextOutputLink), 1000);
95
+ } else {
96
+ fetchOutput(nextOutputLink);
97
+ }
98
+ } catch (error) {
99
+ console.log('Error viewing output:', error);
100
+ }
101
+ }
102
+
103
+ function adjustOutputHeight() {
104
+ const outputDiv = document.getElementById('output');
105
+ const windowHeight = window.innerHeight;
106
+ const outputTop = outputDiv.getBoundingClientRect().top;
107
+ const maxHeight = windowHeight - outputTop - 60; // Adjusted for slider height
108
+ outputDiv.style.height = `${maxHeight}px`;
109
+ fitAddon.fit();
110
+ }
111
+
112
+ function sliderUpdateOutput() {
113
+ const slider = document.getElementById('outputSlider');
114
+ const percentage = slider.value;
115
+ outputLength = Math.floor((fullOutput.length * percentage) / 100);
116
+ const limitedOutput = fullOutput.slice(0, outputLength);
117
+ terminal.clear();
118
+ terminal.reset();
119
+ terminal.write(limitedOutput);
120
+ document.getElementById('outputPercentage').innerText = `${percentage}%`;
121
+ }
122
+
123
+ document.getElementById('outputSlider').addEventListener('input', sliderUpdateOutput);
124
+
125
+ window.addEventListener('resize', adjustOutputHeight);
126
+ window.addEventListener('load', () => {
127
+ const commandId = window.location.pathname.split('/').slice(-1)[0];
128
+ viewOutput(commandId);
129
+ });
130
+
@@ -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">${command.last_output_line || ''}</td>
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=800,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>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
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '1.4.5'
16
- __version_tuple__ = version_tuple = (1, 4, 5)
15
+ __version__ = version = '1.4.7'
16
+ __version_tuple__ = version_tuple = (1, 4, 7)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pywebexec
3
- Version: 1.4.5
3
+ Version: 1.4.7
4
4
  Summary: Simple Python HTTP Exec Server
5
5
  Home-page: https://github.com/joknarf/pywebexec
6
6
  Author: Franck Jouvanceau
@@ -1,28 +1,31 @@
1
1
  pywebexec/__init__.py,sha256=4spIsVaF8RJt8S58AG_wWoORRNkws9Iwqprj27C3ljM,99
2
- pywebexec/pywebexec.py,sha256=w5XUxh9rJqbVO4Um9fJb4DcBd-VoOGc6cNXBjPVPkm8,26003
3
- pywebexec/version.py,sha256=HhvBOPJ1g3k0gtuZ-SLO8_784kPPejMh80IxITX8Cvc,411
2
+ pywebexec/pywebexec.py,sha256=wediupUiLmpCaER3b_-07QitIiTimgmEBBxFLGT6-DQ,26125
3
+ pywebexec/version.py,sha256=6M0bnfqGZEQvwGyTAgOkscrVPq1huIWdr9OdJlVIiPk,411
4
4
  pywebexec/static/css/Consolas NF.ttf,sha256=DJEOzF0eqZ-kxu3Gs_VE8X0NJqiobBzmxWDGpdgGRxI,1313900
5
- pywebexec/static/css/style.css,sha256=b02WQoXwVm00JpedEslvCHjr35txo2Bfjz3Iboco9U8,5500
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
9
9
  pywebexec/static/images/copy_ok.svg,sha256=mEqUVUhSq8xaJK2msQkxRawnz_KwlCZ-tok8QS6hJ3g,451
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
- pywebexec/static/images/favicon.svg,sha256=-UWJrb9i7Y5xCWYcqXHKgxzECe9zsoUDoqDtWnVZvD0,1179
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/script.js,sha256=_Z1P65x-s_heHMwXbVpPHJHqfQKcIZavVhRej6i3DIs,11570
17
+ pywebexec/static/js/popup.js,sha256=1AczXkJrqqjajT8ji4Rxz_xRyPSO8NJmIsx-ckmbNEw,4239
18
+ pywebexec/static/js/script.js,sha256=QAXrNqHrqrAF2MpgFagnMe59AIT57bAe7crzYYzKGB4,11972
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-1.4.5.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
24
- pywebexec-1.4.5.dist-info/METADATA,sha256=TnzocxAHgZin9dU8SieDAULbqTharvhmnXQ7DaBxNzc,7399
25
- pywebexec-1.4.5.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
26
- pywebexec-1.4.5.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
27
- pywebexec-1.4.5.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
28
- pywebexec-1.4.5.dist-info/RECORD,,
25
+ pywebexec/templates/popup.html,sha256=PfhAi1LwS06MvjqJA45IN_eLxy49Tq2Q_qOPzX8JTrU,820
26
+ pywebexec-1.4.7.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
27
+ pywebexec-1.4.7.dist-info/METADATA,sha256=Jzlkrd79ANeEkb5ZI98Bc0GnBQUWlEYNRLanQSDeyKQ,7399
28
+ pywebexec-1.4.7.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
29
+ pywebexec-1.4.7.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
30
+ pywebexec-1.4.7.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
31
+ pywebexec-1.4.7.dist-info/RECORD,,