sbatchman 2.0.2__tar.gz → 2.0.3__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 (71) hide show
  1. {sbatchman-2.0.2/src/sbatchman.egg-info → sbatchman-2.0.3}/PKG-INFO +1 -1
  2. {sbatchman-2.0.2 → sbatchman-2.0.3}/pyproject.toml +1 -1
  3. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/static/builder.js +44 -8
  4. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/static/config.js +2 -1
  5. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/static/log.js +2 -0
  6. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/static/plotting.js +9 -5
  7. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/static/tabs.js +1 -0
  8. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/webapp.html +5 -2
  9. {sbatchman-2.0.2 → sbatchman-2.0.3/src/sbatchman.egg-info}/PKG-INFO +1 -1
  10. {sbatchman-2.0.2 → sbatchman-2.0.3}/MANIFEST.in +0 -0
  11. {sbatchman-2.0.2 → sbatchman-2.0.3}/README.md +0 -0
  12. {sbatchman-2.0.2 → sbatchman-2.0.3}/setup.cfg +0 -0
  13. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/__init__.py +0 -0
  14. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/cli.py +0 -0
  15. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/config/global_config.py +0 -0
  16. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/config/project_config.py +0 -0
  17. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/core/__init__.py +0 -0
  18. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/core/campaign.py +0 -0
  19. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/core/config_manager.py +0 -0
  20. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/core/job.py +0 -0
  21. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/core/jobs_manager.py +0 -0
  22. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/core/launcher.py +0 -0
  23. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/core/status.py +0 -0
  24. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/core/variables.py +0 -0
  25. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/exceptions.py +0 -0
  26. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/parser.py +0 -0
  27. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/remote/fetch.py +0 -0
  28. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/remote/ssh.py +0 -0
  29. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/remote/sync.py +0 -0
  30. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/schedulers/__init.py +0 -0
  31. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/schedulers/base.py +0 -0
  32. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/schedulers/local.py +0 -0
  33. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/schedulers/pbs.py +0 -0
  34. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/schedulers/slurm.py +0 -0
  35. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/tui/jobs_screen.py +0 -0
  36. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/tui/log_screen.py +0 -0
  37. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/tui/style.tcss +0 -0
  38. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/tui/tui_campaign.py +0 -0
  39. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/tui/tui_remote.py +0 -0
  40. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/tui/tui_status.py +0 -0
  41. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/docs.html +0 -0
  42. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/handler.py +0 -0
  43. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/split_webapp.py +0 -0
  44. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/static/actions.js +0 -0
  45. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/static/axis_addon.js +0 -0
  46. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/static/export_image.js +0 -0
  47. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/static/font.js +0 -0
  48. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/static/grid.js +0 -0
  49. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/static/grid_addon.js +0 -0
  50. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/static/main.js +0 -0
  51. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/static/opentype.js +0 -0
  52. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/static/plot_fields.xml +0 -0
  53. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/static/schema.js +0 -0
  54. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/static/state.js +0 -0
  55. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/static/style_addon.js +0 -0
  56. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/utils/agg.py +0 -0
  57. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/utils/core.py +0 -0
  58. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/utils/db.py +0 -0
  59. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/utils/export.py +0 -0
  60. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/utils/layout.py +0 -0
  61. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/utils/matplotlib_backend.py +0 -0
  62. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/utils/plots.py +0 -0
  63. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/utils/render.py +0 -0
  64. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/utils/script_paths.py +0 -0
  65. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/utils/style_addon.py +0 -0
  66. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman/visualize/utils/styles.py +0 -0
  67. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman.egg-info/SOURCES.txt +0 -0
  68. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman.egg-info/dependency_links.txt +0 -0
  69. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman.egg-info/entry_points.txt +0 -0
  70. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman.egg-info/requires.txt +0 -0
  71. {sbatchman-2.0.2 → sbatchman-2.0.3}/src/sbatchman.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sbatchman
3
- Version: 2.0.2
3
+ Version: 2.0.3
4
4
  Summary: A utility to create, launch and monitor code experiments on SLURM, PBS, or local machines.
5
5
  Author: Lorenzo Pichetti
6
6
  Author-email: Thomas Pasquali <thomas.pasquali@unitn.it>, Salvatore Andaloro <sbatchman@sasso.dev>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "sbatchman"
7
- version = "2.0.2"
7
+ version = "2.0.3"
8
8
  authors = [
9
9
  { name="Thomas Pasquali", email="thomas.pasquali@unitn.it" },
10
10
  { name="Salvatore Andaloro", email="sbatchman@sasso.dev" },
@@ -24,18 +24,46 @@ export function renderDataTable(container, dfData, emptyMsg) {
24
24
  }
25
25
 
26
26
  export function renderMultiPreview(container, labeledTables) {
27
- container.innerHTML = '';
28
- labeledTables.forEach(({ label, df }) => {
29
- const lbl = document.createElement('div');
27
+ container.replaceChildren();
28
+ labeledTables.forEach(({ label, df }, index) => {
29
+ const lbl = document.createElement('button');
30
+ lbl.type = 'button';
30
31
  lbl.className = 'preview-section-label';
31
32
  lbl.textContent = label;
32
- container.appendChild(lbl);
33
33
  const box = document.createElement('div');
34
- renderDataTable(box, df);
35
- container.appendChild(box);
34
+ box.hidden = index !== 0;
35
+ let rendered = false;
36
+ const render = () => {
37
+ if (rendered) return;
38
+ renderDataTable(box, df);
39
+ rendered = true;
40
+ };
41
+ lbl.setAttribute('aria-expanded', String(index === 0));
42
+ lbl.addEventListener('click', () => {
43
+ const open = box.hidden;
44
+ box.hidden = !open;
45
+ lbl.setAttribute('aria-expanded', String(open));
46
+ if (open) render();
47
+ });
48
+ container.append(lbl, box);
49
+ if (index === 0) render();
36
50
  });
37
51
  }
38
52
 
53
+ export function deferDataPreview(container, render) {
54
+ if (!container) return;
55
+ container._pendingPreviewRender = render;
56
+ container.replaceChildren();
57
+ if (container.classList.contains('open')) renderPendingDataPreview(container);
58
+ }
59
+
60
+ export function renderPendingDataPreview(container) {
61
+ if (!container?._pendingPreviewRender) return;
62
+ const render = container._pendingPreviewRender;
63
+ container._pendingPreviewRender = null;
64
+ render();
65
+ }
66
+
39
67
  // --- Plot type chips / extra options ----------------------------------
40
68
  export function renderPlotChips(id) {
41
69
  const wrap = document.getElementById(`chips-${id}`);
@@ -384,6 +412,7 @@ export function enhanceCodeEditors(root = document) {
384
412
  if (!layer) {
385
413
  const wrapper = document.createElement('div');
386
414
  wrapper.className = 'code-editor-wrap';
415
+ if (editor.classList.contains('tick-formatter-editor')) wrapper.classList.add('tick-formatter-wrap');
387
416
  editor.parentNode.insertBefore(wrapper, editor);
388
417
  wrapper.appendChild(editor);
389
418
  layer = document.createElement('pre');
@@ -392,7 +421,13 @@ export function enhanceCodeEditors(root = document) {
392
421
  wrapper.insertBefore(layer, editor);
393
422
  editor._codeHighlightLayer = layer;
394
423
  editor.classList.add('highlight-ready');
395
- editor.addEventListener('input', () => enhanceCodeEditors(editor));
424
+ editor.addEventListener('input', () => {
425
+ if (editor._highlightFrame) return;
426
+ editor._highlightFrame = requestAnimationFrame(() => {
427
+ editor._highlightFrame = 0;
428
+ enhanceCodeEditors(editor);
429
+ });
430
+ });
396
431
  editor.addEventListener('scroll', () => {
397
432
  layer.scrollTop = editor.scrollTop;
398
433
  layer.scrollLeft = editor.scrollLeft;
@@ -561,6 +596,7 @@ document.addEventListener('keydown', event => {
561
596
  });
562
597
 
563
598
  Object.assign(window, {
564
- renderDataTable, renderMultiPreview, renderPlotChips, renderExtraOpts, renderPlotSpecificFields,
599
+ renderDataTable, renderMultiPreview, deferDataPreview, renderPendingDataPreview,
600
+ renderPlotChips, renderExtraOpts, renderPlotSpecificFields,
565
601
  updateAxisControls, builderColumnsHTML, wireBuilderColumns, runShow, enhanceCodeEditors,
566
602
  });
@@ -410,6 +410,7 @@ export function wireConfigControls() {
410
410
  });
411
411
 
412
412
  document.getElementById('btn-export-all').addEventListener('click', async event => {
413
+ const exportButton = event.currentTarget;
413
414
  // window.getTabConfig here too, and for the same reason: this is a
414
415
  // *workspace* export, exactly the shape style_addon.js's window.download
415
416
  // wrap looks for to inject the global style.
@@ -423,7 +424,7 @@ export function wireConfigControls() {
423
424
  return;
424
425
  }
425
426
  const json = JSON.stringify(ws, null, 2);
426
- openWorkspaceExportMenu(event.currentTarget, json, `workspace-${Date.now()}.json`);
427
+ openWorkspaceExportMenu(exportButton, json, `workspace-${Date.now()}.json`);
427
428
  });
428
429
  document.getElementById('btn-import-all').addEventListener('click', () => document.getElementById('import-ws-file-input').click());
429
430
  document.getElementById('import-ws-file-input').addEventListener('change', e => {
@@ -15,6 +15,8 @@ export function addLogEntry(e) {
15
15
  div.className = `log-entry ${e.level}`;
16
16
  div.innerHTML = `<span class="log-ts">${e.ts}</span><span class="log-msg">${escHtml(e.msg)}</span>`;
17
17
  body.appendChild(div);
18
+ // Keep long sessions responsive instead of retaining every log row forever.
19
+ while (body.childElementCount > 500) body.firstElementChild.remove();
18
20
  body.scrollTop = body.scrollHeight;
19
21
  G.logCount++;
20
22
  const badge = document.getElementById('log-badge');
@@ -3,7 +3,7 @@
3
3
  // needs help with that it won't do on its own.
4
4
  import { G, getState, tabEl, currentDb } from './state.js';
5
5
  import { api, addLogEntries, clientLog } from './log.js';
6
- import { renderDataTable, renderMultiPreview } from './builder.js';
6
+ import { renderDataTable, renderMultiPreview, deferDataPreview } from './builder.js';
7
7
  // updateAxisControls is deliberately NOT imported here: axis_addon.js wraps
8
8
  // window.updateAxisControls to refresh the y2/error-column selects whenever
9
9
  // fresh columns arrive, and a static import would bypass that wrap (see the
@@ -122,7 +122,8 @@ export async function runPlot() {
122
122
  const hint = res.truncated ? ' (truncated to 10k rows)' : '';
123
123
  setStatus(tabId, `OK — Matplotlib rendered${hint}`, 'ok');
124
124
  clientLog(`Matplotlib plot rendered: "${tab.label}"${hint}`);
125
- renderDataTable(document.getElementById(`preview-${tabId}`), res.preview);
125
+ const preview = document.getElementById(`preview-${tabId}`);
126
+ deferDataPreview(preview, () => renderDataTable(preview, res.preview));
126
127
  return;
127
128
  }
128
129
  showPlotlyFigure(tabId);
@@ -143,7 +144,8 @@ export async function runPlot() {
143
144
  const hint = res.truncated ? ' (truncated to 10k rows)' : '';
144
145
  setStatus(tabId, `OK — ${res.traces.length} trace(s), ${res.columns?.length || 0} column(s)${hint}`, 'ok');
145
146
  clientLog(`Plot rendered: "${tab.label}" — ${res.traces.length} trace(s)${hint}`);
146
- renderDataTable(document.getElementById(`preview-${tabId}`), res.preview);
147
+ const preview = document.getElementById(`preview-${tabId}`);
148
+ deferDataPreview(preview, () => renderDataTable(preview, res.preview));
147
149
  } catch (e) {
148
150
  document.getElementById('btn-run').disabled = false;
149
151
  setStatus(tabId, e.message, 'err');
@@ -185,7 +187,8 @@ export async function runGridPlot(tabId) {
185
187
  setStatus(tabId, `OK — ${res.panel_count || panels.length} panel(s), Matplotlib rendered`, 'ok');
186
188
  clientLog(`Matplotlib grid rendered: "${tab.label}" — ${res.panel_count || panels.length} panel(s)`);
187
189
  const labeled = (res.previews || []).map((df, i) => ({ label: `Panel ${i + 1}`, df }));
188
- renderMultiPreview(document.getElementById(`preview-${tabId}`), labeled);
190
+ const preview = document.getElementById(`preview-${tabId}`);
191
+ deferDataPreview(preview, () => renderMultiPreview(preview, labeled));
189
192
  return;
190
193
  }
191
194
  showPlotlyFigure(tabId);
@@ -214,7 +217,8 @@ export async function runGridPlot(tabId) {
214
217
  clientLog(`Grid figure rendered: "${tab.label}" — ${panelCount} panel(s)`);
215
218
 
216
219
  const labeled = (res.previews || []).map((df, i) => ({ label: `Panel ${i + 1}`, df }));
217
- renderMultiPreview(document.getElementById(`preview-${tabId}`), labeled);
220
+ const preview = document.getElementById(`preview-${tabId}`);
221
+ deferDataPreview(preview, () => renderMultiPreview(preview, labeled));
218
222
  } catch (e) {
219
223
  document.getElementById('btn-run').disabled = false;
220
224
  setStatus(tabId, e.message, 'err');
@@ -105,6 +105,7 @@ function wireWorkspace(id) {
105
105
  document.getElementById(`preview-toggle-${id}`).addEventListener('click', () => {
106
106
  const pv = document.getElementById(`preview-${id}`);
107
107
  const open = pv.classList.toggle('open');
108
+ if (open) window.renderPendingDataPreview?.(pv);
108
109
  document.querySelector(`#ws-${id} .plot-area`)?.classList.toggle('preview-open', open);
109
110
  document.getElementById(`preview-toggle-${id}`).textContent = open ? 'Data ▴' : 'Data ▾';
110
111
  resizePlot(id);
@@ -161,7 +161,9 @@ textarea{resize:vertical}
161
161
  flex:0 0 var(--preview-height,25%)}
162
162
  .plot-area>.data-preview.open{display:block}
163
163
  .preview-section-label{font-size:9px;color:var(--text3);padding:5px 10px;background:var(--bg3);
164
- text-transform:uppercase;letter-spacing:.06em;position:sticky;top:0}
164
+ text-transform:uppercase;letter-spacing:.06em;position:sticky;top:0;display:block;width:100%;border:0;
165
+ text-align:left;font-family:var(--sans);cursor:pointer}
166
+ .preview-section-label:hover{color:var(--accent2)}
165
167
  table.data-table{border-collapse:collapse;font-family:var(--mono);font-size:10px;width:max-content;min-width:100%}
166
168
  table.data-table th{position:sticky;top:0;background:var(--bg4);color:var(--accent2);text-align:left;
167
169
  padding:4px 8px;border-bottom:1px solid var(--border2);white-space:nowrap}
@@ -267,6 +269,7 @@ table.data-table tr:hover td{background:var(--bg3)}
267
269
  .script-paths-dialog textarea{min-height:220px;resize:vertical}
268
270
  .script-paths-dialog .dialog-actions{display:flex;justify-content:flex-end;gap:7px;margin-top:10px}
269
271
  .code-editor-wrap{position:relative;display:grid;width:100%;min-width:0;min-height:150px;background:#0b1016;border:1px solid #46515e;border-radius:var(--radius);overflow:hidden}
272
+ .code-editor-wrap.tick-formatter-wrap{min-height:88px}
270
273
  .code-highlight{position:absolute;inset:0;margin:0;padding:9px 10px;overflow:hidden;pointer-events:none;
271
274
  color:#e6edf3;font-family:var(--mono);font-size:12px;line-height:1.5;tab-size:4;white-space:pre}
272
275
  .code-editor{position:relative;z-index:1;grid-area:1/1;display:block;width:100%;height:190px;min-height:150px;padding:9px 10px;
@@ -280,7 +283,7 @@ table.data-table tr:hover td{background:var(--bg3)}
280
283
  .code-editor-wrap:focus-within{border-color:var(--accent);box-shadow:0 0 0 2px rgba(0,212,212,.12)}
281
284
  .sql-editor{height:145px;min-height:110px}
282
285
  .python-editor{height:175px;min-height:120px}
283
- .python-editor.tick-formatter-editor{height:88px;min-height:70px}
286
+ .python-editor.tick-formatter-editor{height:88px;min-height:88px;resize:vertical;overflow:auto}
284
287
  .grid-dimension{width:4rem!important;min-width:4rem!important;padding:3px 4px!important;color:#f0f6fc!important;background:#0b1016!important;
285
288
  border-color:#66717f!important;font-size:12px!important;font-weight:600;letter-spacing:.01em}
286
289
  .grid-gap{width:4rem!important;min-width:4rem!important;padding:3px 4px!important;color:#f0f6fc!important;background:#0b1016!important;
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sbatchman
3
- Version: 2.0.2
3
+ Version: 2.0.3
4
4
  Summary: A utility to create, launch and monitor code experiments on SLURM, PBS, or local machines.
5
5
  Author: Lorenzo Pichetti
6
6
  Author-email: Thomas Pasquali <thomas.pasquali@unitn.it>, Salvatore Andaloro <sbatchman@sasso.dev>
File without changes
File without changes
File without changes