pywebexec 1.4.6__tar.gz → 1.4.7__tar.gz

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.
Files changed (38) hide show
  1. {pywebexec-1.4.6/pywebexec.egg-info → pywebexec-1.4.7}/PKG-INFO +1 -1
  2. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/pywebexec.py +4 -0
  3. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/static/css/style.css +12 -0
  4. pywebexec-1.4.7/pywebexec/static/images/popup.svg +1 -0
  5. pywebexec-1.4.7/pywebexec/static/js/popup.js +130 -0
  6. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/static/js/script.js +11 -1
  7. pywebexec-1.4.7/pywebexec/templates/popup.html +21 -0
  8. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/version.py +2 -2
  9. {pywebexec-1.4.6 → pywebexec-1.4.7/pywebexec.egg-info}/PKG-INFO +1 -1
  10. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec.egg-info/SOURCES.txt +4 -1
  11. {pywebexec-1.4.6 → pywebexec-1.4.7}/.github/workflows/python-publish.yml +0 -0
  12. {pywebexec-1.4.6 → pywebexec-1.4.7}/.gitignore +0 -0
  13. {pywebexec-1.4.6 → pywebexec-1.4.7}/LICENSE +0 -0
  14. {pywebexec-1.4.6 → pywebexec-1.4.7}/README.md +0 -0
  15. {pywebexec-1.4.6 → pywebexec-1.4.7}/pyproject.toml +0 -0
  16. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/__init__.py +0 -0
  17. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/static/css/Consolas NF.ttf +0 -0
  18. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/static/css/xterm.css +0 -0
  19. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/static/images/aborted.svg +0 -0
  20. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/static/images/copy.svg +0 -0
  21. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/static/images/copy_ok.svg +0 -0
  22. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/static/images/down-arrow.svg +0 -0
  23. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/static/images/failed.svg +0 -0
  24. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/static/images/favicon.svg +0 -0
  25. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/static/images/running.gif +0 -0
  26. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/static/images/success.svg +0 -0
  27. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/static/js/commands.js +0 -0
  28. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/static/js/xterm/LICENSE +0 -0
  29. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/static/js/xterm/ansi_up.min.js +0 -0
  30. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/static/js/xterm/xterm-addon-fit.js +0 -0
  31. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/static/js/xterm/xterm.js +0 -0
  32. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/templates/__init__.py +0 -0
  33. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec/templates/index.html +0 -0
  34. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec.egg-info/dependency_links.txt +0 -0
  35. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec.egg-info/entry_points.txt +0 -0
  36. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec.egg-info/requires.txt +0 -0
  37. {pywebexec-1.4.6 → pywebexec-1.4.7}/pywebexec.egg-info/top_level.txt +0 -0
  38. {pywebexec-1.4.6 → pywebexec-1.4.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pywebexec
3
- Version: 1.4.6
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
@@ -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
 
@@ -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>
@@ -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.6'
16
- __version_tuple__ = version_tuple = (1, 4, 6)
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.6
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
@@ -22,13 +22,16 @@ pywebexec/static/images/copy_ok.svg
22
22
  pywebexec/static/images/down-arrow.svg
23
23
  pywebexec/static/images/failed.svg
24
24
  pywebexec/static/images/favicon.svg
25
+ pywebexec/static/images/popup.svg
25
26
  pywebexec/static/images/running.gif
26
27
  pywebexec/static/images/success.svg
27
28
  pywebexec/static/js/commands.js
29
+ pywebexec/static/js/popup.js
28
30
  pywebexec/static/js/script.js
29
31
  pywebexec/static/js/xterm/LICENSE
30
32
  pywebexec/static/js/xterm/ansi_up.min.js
31
33
  pywebexec/static/js/xterm/xterm-addon-fit.js
32
34
  pywebexec/static/js/xterm/xterm.js
33
35
  pywebexec/templates/__init__.py
34
- pywebexec/templates/index.html
36
+ pywebexec/templates/index.html
37
+ pywebexec/templates/popup.html
File without changes
File without changes
File without changes
File without changes
File without changes