pywebexec 1.6.6__py3-none-any.whl → 1.6.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 CHANGED
@@ -197,17 +197,17 @@ def decode_line(line: bytes) -> str:
197
197
  return ""
198
198
 
199
199
 
200
- def last_line(fd, maxline=1000):
200
+ def last_line(fd, maxsize=500):
201
201
  """last non empty line of file"""
202
202
  line = "\n"
203
203
  fd.seek(0, os.SEEK_END)
204
204
  size = 0
205
205
  last_pos = 0
206
- while line in ["", "\n", "\r"] and size < maxline:
206
+ while line in ["", "\n", "\r"] and size < maxsize:
207
207
  try: # catch if file empty / only empty lines
208
208
  if last_pos:
209
209
  fd.seek(last_pos-2, os.SEEK_SET)
210
- while fd.read(1) not in [b"\n", b"\r"]:
210
+ while fd.read(1) not in [b"\n", b"\r"] and size < maxsize:
211
211
  fd.seek(-2, os.SEEK_CUR)
212
212
  size += 1
213
213
  except OSError:
@@ -1 +1 @@
1
- <svg viewBox="-3 0 35 34" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" fill="#000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><title>cross-circle</title><desc>Created with Sketch Beta.</desc><defs></defs><g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"><g id="Icon-Set-Filled" sketch:type="MSLayerGroup" transform="translate(-570.000000, -1089.000000)" fill="#ca0000"><path d="M591.657,1109.24 C592.048,1109.63 592.048,1110.27 591.657,1110.66 C591.267,1111.05 590.633,1111.05 590.242,1110.66 L586.006,1106.42 L581.74,1110.69 C581.346,1111.08 580.708,1111.08 580.314,1110.69 C579.921,1110.29 579.921,1109.65 580.314,1109.26 L584.58,1104.99 L580.344,1100.76 C579.953,1100.37 579.953,1099.73 580.344,1099.34 C580.733,1098.95 581.367,1098.95 581.758,1099.34 L585.994,1103.58 L590.292,1099.28 C590.686,1098.89 591.323,1098.89 591.717,1099.28 C592.11,1099.68 592.11,1100.31 591.717,1100.71 L587.42,1105.01 L591.657,1109.24 L591.657,1109.24 Z M586,1089 C577.163,1089 570,1096.16 570,1105 C570,1113.84 577.163,1121 586,1121 C594.837,1121 602,1113.84 602,1105 C602,1096.16 594.837,1089 586,1089 L586,1089 Z" id="cross-circle" sketch:type="MSShapeGroup"></path></g></g></g></svg>
1
+ <svg viewBox="-3 0 35 35" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" fill="#000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><title>cross-circle</title><desc>Created with Sketch Beta.</desc><defs></defs><g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"><g id="Icon-Set-Filled" sketch:type="MSLayerGroup" transform="translate(-570.000000, -1089.000000)" fill="#ca0000"><path d="M591.657,1109.24 C592.048,1109.63 592.048,1110.27 591.657,1110.66 C591.267,1111.05 590.633,1111.05 590.242,1110.66 L586.006,1106.42 L581.74,1110.69 C581.346,1111.08 580.708,1111.08 580.314,1110.69 C579.921,1110.29 579.921,1109.65 580.314,1109.26 L584.58,1104.99 L580.344,1100.76 C579.953,1100.37 579.953,1099.73 580.344,1099.34 C580.733,1098.95 581.367,1098.95 581.758,1099.34 L585.994,1103.58 L590.292,1099.28 C590.686,1098.89 591.323,1098.89 591.717,1099.28 C592.11,1099.68 592.11,1100.31 591.717,1100.71 L587.42,1105.01 L591.657,1109.24 L591.657,1109.24 Z M586,1089 C577.163,1089 570,1096.16 570,1105 C570,1113.84 577.163,1121 586,1121 C594.837,1121 602,1113.84 602,1105 C602,1096.16 594.837,1089 586,1089 L586,1089 Z" id="cross-circle" sketch:type="MSShapeGroup"></path></g></g></g></svg>
@@ -55,6 +55,18 @@ const fitAddon = new FitAddon.FitAddon();
55
55
  terminal.loadAddon(fitAddon);
56
56
  terminal.open(document.getElementById('output'));
57
57
  fitAddon.fit();
58
+ terminal.onTitleChange((title) => {
59
+ document.getElementById('commandInfo').innerText = title;
60
+ })
61
+ terminal.onSelectionChange(() => {
62
+ const selectionText = terminal.getSelection();
63
+ if (selectionText) {
64
+ navigator.clipboard.writeText(selectionText).catch(err => {
65
+ console.error('Failed to copy text to clipboard:', err);
66
+ });
67
+ }
68
+ });
69
+
58
70
 
59
71
  let currentCommandId = null;
60
72
  let outputInterval = null;
@@ -72,14 +84,12 @@ function getTokenParam() {
72
84
  }
73
85
  const urlToken = getTokenParam();
74
86
 
75
- function extractTitle(output) {
76
- const titleindex = output.lastIndexOf('\x1b]0;');
77
- if (titleindex < 0) return null;
78
- const endindex = output.slice(titleindex+4).indexOf('\x07');
79
- if (endindex < 0) return null;
80
- return output.slice(titleindex+4, titleindex+4+endindex);
87
+
88
+ function setCommandStatus(status) {
89
+ document.getElementById("commandStatus").className = `status-icon status-${status}`;
81
90
  }
82
91
 
92
+
83
93
  async function fetchOutput(url) {
84
94
  if (isPaused) return;
85
95
  try {
@@ -112,15 +122,10 @@ async function fetchOutput(url) {
112
122
  clearInterval(outputInterval);
113
123
  document.title = document.title.replace('[running]',`[${data.status}]`);
114
124
  toggleButton.style.display = 'none';
115
- document.getElementById('commandStatus').classList.remove('status-running');
116
- document.getElementById('commandStatus').classList.add(`status-${data.status}`);
125
+ setCommandStatus(data.status)
117
126
  } else {
118
127
  toggleButton.style.display = 'block';
119
- const title = extractTitle(data.output);
120
- if (title) {
121
- document.title = `${title} - [running]`;
122
- document.getElementById('commandInfo').innerHTML = `<span id="commandStatus" class="status-icon status-running"></span>${title}`;
123
- }
128
+ setCommandStatus(data.status)
124
129
  }
125
130
  }
126
131
  } catch (error) {
@@ -146,7 +151,8 @@ async function viewOutput(command_id) {
146
151
  const data = await response.json();
147
152
  const commandInfo = document.getElementById('commandInfo');
148
153
  const command = `${data.command.replace(/^\.\//, '')} ${data.params.join(' ')}`;
149
- commandInfo.innerHTML = `<span id="commandStatus" class="status-icon status-${data.status}"></span>${command}`;
154
+ setCommandStatus(data.status);
155
+ commandInfo.innerText = command;
150
156
  commandInfo.setAttribute('title', command);
151
157
  document.title = `${data.command} ${data.params.join(' ')} - [${data.status}]`;
152
158
  if (data.command == 'term')
@@ -76,11 +76,9 @@ terminal.loadAddon(fitAddon);
76
76
  terminal.open(document.getElementById('output'));
77
77
  fitAddon.fit();
78
78
 
79
- function getTokenParam() {
80
- const urlParams = new URLSearchParams(window.location.search);
81
- return urlParams.get('token') ? `?token=${urlParams.get('token')}` : '';
82
- }
83
- const urlToken = getTokenParam();
79
+ terminal.onTitleChange((title) => {
80
+ document.getElementById('commandInfo').innerText = title;
81
+ })
84
82
 
85
83
  terminal.onSelectionChange(() => {
86
84
  const selectionText = terminal.getSelection();
@@ -91,6 +89,13 @@ terminal.onSelectionChange(() => {
91
89
  }
92
90
  });
93
91
 
92
+ function getTokenParam() {
93
+ const urlParams = new URLSearchParams(window.location.search);
94
+ return urlParams.get('token') ? `?token=${urlParams.get('token')}` : '';
95
+ }
96
+ const urlToken = getTokenParam();
97
+
98
+
94
99
  document.getElementById('launchForm').addEventListener('submit', async (event) => {
95
100
  event.preventDefault();
96
101
  const commandName = document.getElementById('commandName').value;
@@ -175,14 +180,6 @@ async function fetchCommands(hide=false) {
175
180
  }
176
181
  }
177
182
 
178
- function extractTitle(output) {
179
- const titleindex = output.lastIndexOf('\x1b]0;');
180
- if (titleindex < 0) return null;
181
- const endindex = output.slice(titleindex+4).indexOf('\x07');
182
- if (endindex < 0) return null;
183
- return output.slice(titleindex+4, titleindex+4+endindex);
184
- }
185
-
186
183
  async function fetchOutput(url) {
187
184
  if (isPaused) return;
188
185
  try {
@@ -212,15 +209,11 @@ async function fetchOutput(url) {
212
209
  if (data.status != 'running') {
213
210
  clearInterval(outputInterval);
214
211
  toggleButton.style.display = 'none';
215
- document.getElementById('commandStatus').classList.remove('status-running');
216
- document.getElementById('commandStatus').classList.add(`status-${data.status}`);
212
+ setCommandStatus(data.status);
217
213
  fetchCommands();
218
214
  } else {
219
215
  toggleButton.style.display = 'block';
220
- const title = extractTitle(data.output);
221
- if (title) {
222
- document.getElementById('commandInfo').innerHTML = `<span id="commandStatus" class="status-icon status-running"></span>${title}`;
223
- }
216
+ setCommandStatus(data.status);
224
217
  }
225
218
  }
226
219
  } catch (error) {
@@ -228,6 +221,10 @@ async function fetchOutput(url) {
228
221
  }
229
222
  }
230
223
 
224
+ function setCommandStatus(status) {
225
+ document.getElementById("commandStatus").className = `status-icon status-${status}`;
226
+ }
227
+
231
228
  async function viewOutput(command_id) {
232
229
  slider.value = 100;
233
230
  outputPercentage.innerText = '100%';
@@ -246,7 +243,8 @@ async function viewOutput(command_id) {
246
243
  const data = await response.json();
247
244
  const commandInfo = document.getElementById('commandInfo');
248
245
  const command = `${data.command.replace(/^\.\//, '')} ${data.params.join(' ')}`;
249
- commandInfo.innerHTML = `<span id="commandStatus" class="status-icon status-${data.status}"></span>${command}`;
246
+ setCommandStatus(data.status)
247
+ commandInfo.innerText = command;
250
248
  commandInfo.setAttribute('title', command);
251
249
  if (data.command == 'term')
252
250
  terminal.options.cursorInactiveStyle = 'outline';
@@ -46,7 +46,7 @@
46
46
  <div id="output" class="output"></div>
47
47
  <div id="pausedMessage" class="paused-message">Paused</div>
48
48
  <div class="slider-container">
49
- <span id="commandInfo" class="command-info"></span>
49
+ <span class="command-info"><span id="commandStatus" class="status-icon"></span><span id="commandInfo"></span></span>
50
50
  <input type="range" id="outputSlider" min="0" max="100" value="100">
51
51
  <label for="outputSlider"><span id="outputPercentage">100%</span></label>
52
52
  <button id="toggleFetch" class="pause-resume-button pause"></button>
@@ -14,7 +14,7 @@
14
14
  <div id="output" class="output"></div>
15
15
  <div id="pausedMessage" class="paused-message">Paused</div>
16
16
  <div class="slider-container">
17
- <span id="commandInfo" class="command-info"></span>
17
+ <span class="command-info"><span id="commandStatus"></span><span id="commandInfo"></span></span>
18
18
  <input type="range" id="outputSlider" min="0" max="100" value="100">
19
19
  <label for="outputSlider"><span id="outputPercentage">100%</span></label>
20
20
  <button id="toggleFetch" class="pause-resume-button pause"></button>
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.6.6'
16
- __version_tuple__ = version_tuple = (1, 6, 6)
15
+ __version__ = version = '1.6.8'
16
+ __version_tuple__ = version_tuple = (1, 6, 8)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pywebexec
3
- Version: 1.6.6
3
+ Version: 1.6.8
4
4
  Summary: Simple Python HTTP Exec Server
5
5
  Home-page: https://github.com/joknarf/pywebexec
6
6
  Author: Franck Jouvanceau
@@ -1,6 +1,6 @@
1
1
  pywebexec/__init__.py,sha256=4spIsVaF8RJt8S58AG_wWoORRNkws9Iwqprj27C3ljM,99
2
- pywebexec/pywebexec.py,sha256=6roMSuUnfNx5fj9-gg0E1WEUEDbX82XYNIHzxsjoreE,31701
3
- pywebexec/version.py,sha256=Nz1l8vGPOAOAlxWDepNu28nfH9fOKk0GnGSnVyZJsFE,411
2
+ pywebexec/pywebexec.py,sha256=L47T3dB78GvW2CD09kRFncjaNjLxJ_NqB4VvOftSodo,31719
3
+ pywebexec/version.py,sha256=cmsiv9CCkKr77S4CsQCya3nhc7DDRNXIH_h8pHWpeYE,411
4
4
  pywebexec/static/css/Consolas NF.ttf,sha256=DJEOzF0eqZ-kxu3Gs_VE8X0NJqiobBzmxWDGpdgGRxI,1313900
5
5
  pywebexec/static/css/style.css,sha256=MJHUBpjWL4sLxM7a7DxypmPKaFJQbmA_ESNXsbLviNI,8201
6
6
  pywebexec/static/css/xterm.css,sha256=uo5phWaUiJgcz0DAzv46uoByLLbJLeetYosL1xf68rY,5559
@@ -8,7 +8,7 @@ pywebexec/static/images/aborted.svg,sha256=2nuvSwGBIZGWtlM1DrBO3qiSq1reDbcZDAj9r
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
- pywebexec/static/images/failed.svg,sha256=cDmn8eBPvYO74kk86p_la4w3gRJk8J2uHNWI8Q20fE8,1438
11
+ pywebexec/static/images/failed.svg,sha256=rSTBWOtiz7slGDobeU_vaLaMM8CoPeul_tQgWVBcodo,1438
12
12
  pywebexec/static/images/favicon.svg,sha256=9gSN5Oak1zTWhTCyutlupPBKUxcbdoVt7dvhk8xvEug,1224
13
13
  pywebexec/static/images/font-decrease.svg,sha256=89TJXQpfZ-XDJ2qC0c7cbLH-tWC-4AJQr_VOgsp06Gg,1782
14
14
  pywebexec/static/images/font-increase.svg,sha256=u-EBxUzmOhKx4UH96fb4gLZ0l6Jkew1HLhcSqz7Z1ak,1869
@@ -19,8 +19,8 @@ pywebexec/static/images/resume.svg,sha256=99LP1Ya2JXakRCO9kW8JMuT_4a_CannF65Eiuw
19
19
  pywebexec/static/images/running.svg,sha256=fBCYwYb2O9K4N3waC2nURP25NRwZlqR4PbDZy6JQMww,610
20
20
  pywebexec/static/images/success.svg,sha256=NVwezvVMplt46ElW798vqGfrL21Mw_DWHUp_qiD_FU8,489
21
21
  pywebexec/static/js/commands.js,sha256=h2fkd9qpypLBxvhEEbay23nwuqUwcKJA0vHugcyL8pU,7961
22
- pywebexec/static/js/popup.js,sha256=TCzGoPVS_EvwzGc544SvRgLYG7XWXRF8tIQGfu3atrU,8533
23
- pywebexec/static/js/script.js,sha256=mGY_-A1rZNjgpTU8FsVH0j6Tj7v4pHpSaREPbTttvEs,17376
22
+ pywebexec/static/js/popup.js,sha256=I5TLYUm5s2p12a0VfFONOxxzhXLBmnpVzM-B5W-GPis,8294
23
+ pywebexec/static/js/script.js,sha256=a3AyWqjReW5CwCbAtXX7dXDsL2ioANLynRPvaGqzWOA,16928
24
24
  pywebexec/static/js/xterm/LICENSE,sha256=EU1P4eXTull-_T9I80VuwnJXubB-zLzUl3xpEYj2T1M,1083
25
25
  pywebexec/static/js/xterm/addon-canvas.js,sha256=ez6QTVvsmLVNJmdJlM-ZQ5bErwlxAQ_9DUmDIptl2TM,94607
26
26
  pywebexec/static/js/xterm/addon-canvas.js.map,sha256=ECBA4B-BqUpdFeRzlsEWLSQnudnhLP-yPQJ8_hKquMo,379537
@@ -31,11 +31,11 @@ pywebexec/static/js/xterm/addon-unicode11.js.map,sha256=paDj5KKtTIUGedQn2x7CaUTD
31
31
  pywebexec/static/js/xterm/xterm.js,sha256=H5kaw7Syg-v5bmCuI6AKUnZd06Lkb6b92p8aqwMvdJU,289441
32
32
  pywebexec/static/js/xterm/xterm.js.map,sha256=Y7O2Pb-fIS7Z8AC1D5s04_aiW_Jf1f4mCfN0U_OI6Zw,1118392
33
33
  pywebexec/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
- pywebexec/templates/index.html,sha256=3XAXlpluzmTxC75KLzHDI4aJvQyQrOShB_weJ27PE2U,2815
35
- pywebexec/templates/popup.html,sha256=6FfzMjxuAC_xwyMGPf3p5tm6iPVY0QCsqY80eotD0z8,1362
36
- pywebexec-1.6.6.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
37
- pywebexec-1.6.6.dist-info/METADATA,sha256=4fsEDm8XyeQJAnkmeqzIi52Z27jwsu73YkAlfc3ebWU,7801
38
- pywebexec-1.6.6.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
39
- pywebexec-1.6.6.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
40
- pywebexec-1.6.6.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
41
- pywebexec-1.6.6.dist-info/RECORD,,
34
+ pywebexec/templates/index.html,sha256=5h8kLyzAhbvUDU9sEwrGIvD6FxAMcDZLXlN-ldlO8KU,2880
35
+ pywebexec/templates/popup.html,sha256=3ZqQcE9mYs-RXv0Lfb24zntOlvR137ZYI9mtCZNVAo0,1407
36
+ pywebexec-1.6.8.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
37
+ pywebexec-1.6.8.dist-info/METADATA,sha256=pwgecKrRdxhw5Y8sVWACMlLdpDRQXEp8aT7TesggWVk,7801
38
+ pywebexec-1.6.8.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
39
+ pywebexec-1.6.8.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
40
+ pywebexec-1.6.8.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
41
+ pywebexec-1.6.8.dist-info/RECORD,,