nova-trame 0.26.2__py3-none-any.whl → 1.0.0__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.
@@ -129,7 +129,7 @@ class DataSelector(datagrid.VGrid):
129
129
  return get_server(None, client_type="vue3").state
130
130
 
131
131
  def create_ui(self, *args: Any, **kwargs: Any) -> None:
132
- with VBoxLayout(classes="nova-data-selector", height="100%") as self._layout:
132
+ with VBoxLayout(classes="nova-data-selector", stretch=True) as self._layout:
133
133
  with HBoxLayout(valign="center"):
134
134
  self._layout.filter = html.Div(classes="flex-1-1")
135
135
  with vuetify.VBtn(
@@ -138,9 +138,9 @@ class DataSelector(datagrid.VGrid):
138
138
  vuetify.VIcon("mdi-refresh")
139
139
  vuetify.VTooltip("Refresh Contents", activator="parent")
140
140
 
141
- with GridLayout(columns=2, classes="flex-1-0 h-0", valign="start"):
141
+ with GridLayout(columns=2, stretch=True):
142
142
  if isinstance(self._subdirectory, tuple) or not self._subdirectory:
143
- with html.Div(classes="d-flex flex-column h-100 overflow-hidden"):
143
+ with VBoxLayout(stretch=True):
144
144
  vuetify.VListSubheader("Available Directories", classes="flex-0-1 justify-center px-0")
145
145
  vuetify.VTreeview(
146
146
  v_if=(f"{self._directories_name}.length > 0",),
@@ -91,7 +91,7 @@ class NeutronDataSelector(DataSelector):
91
91
  if data_source == "oncat" and subdirectory:
92
92
  warn("subdirectory will be ignored since data will be pulled from ONCat.", stacklevel=1)
93
93
 
94
- if isinstance(facility, str) and allow_custom_directories:
94
+ if isinstance(facility, str) and facility and allow_custom_directories:
95
95
  warn("allow_custom_directories will be ignored since the facility parameter is fixed.", stacklevel=1)
96
96
 
97
97
  self._facility = facility
@@ -292,6 +292,8 @@ class NeutronDataSelector(DataSelector):
292
292
  projection=set_state_param(self.state, self._projection, projection)
293
293
  )
294
294
 
295
+ super()._setup_bindings()
296
+
295
297
  # These update methods notify the rest of the application when the component changes bound parameters.
296
298
  def update_facility(self, facility: str) -> None:
297
299
  self._vm.set_binding_parameters(
@@ -9,10 +9,11 @@ from mimetypes import types_map
9
9
  from pathlib import Path
10
10
  from threading import Thread
11
11
  from typing import Any, Optional
12
+ from warnings import warn
12
13
 
13
14
  import tornado
14
15
  from aiohttp import ClientSession, WSMsgType, web
15
- from matplotlib import get_data_path
16
+ from matplotlib import get_data_path, rcParams
16
17
  from matplotlib.backends.backend_webagg import FigureManagerWebAgg, new_figure_manager_given_figure # type: ignore
17
18
  from matplotlib.figure import Figure
18
19
  from trame.app import get_server
@@ -55,7 +56,10 @@ class _MPLApplication(tornado.web.Application):
55
56
  self.supports_binary = message["value"]
56
57
  else:
57
58
  manager = self.application.manager # type: ignore
58
- manager.handle_json(message)
59
+ try:
60
+ manager.handle_json(message)
61
+ except Exception:
62
+ manager.refresh_all()
59
63
 
60
64
  def send_json(self, content: Any) -> None:
61
65
  set_event_loop(self.application.loop) # type: ignore
@@ -69,6 +73,13 @@ class _MPLApplication(tornado.web.Application):
69
73
  data_uri = "data:image/png;base64," + blob.encode("base64").replace("\n", "")
70
74
  self.write_message(data_uri)
71
75
 
76
+ def write_message(self, *args: Any, **kwargs: Any) -> Any:
77
+ # We need the websocket to remain alive if a message fails to write.
78
+ try:
79
+ super().write_message(*args, **kwargs)
80
+ except Exception:
81
+ pass
82
+
72
83
  def __init__(self, figure: Figure) -> None:
73
84
  self.figure = figure
74
85
  self.manager = new_figure_manager_given_figure(id(figure), figure)
@@ -214,17 +225,22 @@ class MatplotlibFigure(matplotlib.Figure):
214
225
  -------
215
226
  None
216
227
  """ # noqa: E501
228
+ self._server = get_server(None, client_type="vue3")
217
229
  self._webagg = webagg
230
+ if "classes" in kwargs:
231
+ kwargs["classes"] += " flex-1-1"
232
+ else:
233
+ kwargs["classes"] = "flex-1-1"
218
234
  if webagg:
219
- self._port = MatplotlibFigure._get_free_port()
220
- if "classes" in kwargs:
221
- kwargs["classes"] += " nova-mpl"
222
- else:
223
- kwargs["classes"] = "nova-mpl"
235
+ if "id" in kwargs:
236
+ kwargs.pop("id")
237
+ warn("id parameter to MatplotlibFigure is ignored when webagg=True.", stacklevel=1)
224
238
 
225
- html.Div(id=f"nova_mpl_{self._port}", **kwargs)
239
+ self._port = MatplotlibFigure._get_free_port()
240
+ self._id = f"nova_mpl_{self._port}"
241
+ kwargs["classes"] += " nova-mpl"
226
242
 
227
- self._server = get_server(None, client_type="vue3")
243
+ html.Div(id=self._id, **kwargs)
228
244
 
229
245
  self._figure = figure
230
246
  self._initialized = False
@@ -236,8 +252,63 @@ class MatplotlibFigure(matplotlib.Figure):
236
252
  self.update()
237
253
  else:
238
254
  super().__init__(figure, **kwargs)
239
-
240
- def update(self, figure: Optional[Figure] = None) -> None:
255
+ self._id = self._key
256
+
257
+ self._query_selector = f"window.document.querySelector('#{self._id}')"
258
+ self._trigger = (
259
+ f"if ({self._query_selector} === null) {{ return; }}"
260
+ # webagg figures receive a fixed width and height. This blocks the flexbox scaling, so I temporarily hide
261
+ # the figure to allow the container to grow/shrink naturally in flexbox.
262
+ f"window.document.querySelectorAll('.nova-mpl').forEach((item) => {{ item.style.display = 'none'; }});"
263
+ f"const height = {self._query_selector}.parentNode.offsetHeight;"
264
+ f"const width = {self._query_selector}.parentNode.offsetWidth;"
265
+ # Revert the display value to allow the figure to render again.
266
+ f"window.document.querySelectorAll('.nova-mpl').forEach((item) => {{ item.style.display = ''; }});"
267
+ "window.trame.trigger("
268
+ f" '{self._id}_resize',"
269
+ f" [height, width]"
270
+ ");"
271
+ )
272
+ self._resize_figure = client.JSEval(exec=self._trigger).exec
273
+ self._resize_listener = client.JSEval(
274
+ exec=(
275
+ # ResizeObserver is necessary to detect changes in size unrelated to the viewport size such as when
276
+ # content is conditionally rendered that changes the size of the figure's container.
277
+ "const resizeObserver = new window.ResizeObserver(() => {"
278
+ f" window.delay_manager.debounce('{self._id}', function() {{ {self._trigger} }}, 500);"
279
+ "});"
280
+ f"resizeObserver.observe({self._query_selector}.parentNode);"
281
+ )
282
+ ).exec
283
+
284
+ @self._server.controller.trigger(f"{self._id}_resize")
285
+ def resize_figure(height: int, width: int) -> None:
286
+ if self._figure:
287
+ if self._webagg:
288
+ # Reserve space for the controls injected by webagg.
289
+ height -= 48
290
+ width -= 4
291
+
292
+ if height <= 0 or width <= 0:
293
+ return
294
+
295
+ if self._webagg:
296
+ # Webagg does not respect the Figure object's DPI.
297
+ dpi = rcParams["figure.dpi"]
298
+ else:
299
+ dpi = self._figure.get_dpi()
300
+ new_width = width / dpi
301
+ new_height = height / dpi
302
+ current_size = self._figure.get_size_inches()
303
+ if current_size[0] != new_width or current_size[1] != new_height:
304
+ self._figure.set_size_inches(new_width, new_height)
305
+
306
+ self.update(skip_resize=True)
307
+
308
+ client.ClientTriggers(mounted=self._resize_listener)
309
+ client.ClientTriggers(mounted=self._resize_figure)
310
+
311
+ def update(self, figure: Optional[Figure] = None, skip_resize: bool = False) -> None:
241
312
  if self._webagg:
242
313
  if figure:
243
314
  self._figure = figure
@@ -255,7 +326,10 @@ class MatplotlibFigure(matplotlib.Figure):
255
326
  else:
256
327
  super().update(figure)
257
328
 
258
- self._server.state.flush()
329
+ if not skip_resize and hasattr(self, "_resize_figure"):
330
+ self._resize_figure()
331
+ else:
332
+ self._server.state.flush()
259
333
 
260
334
  def _setup_figure_websocket(self) -> None:
261
335
  thread = Thread(target=self._mpl_run_ws_server, daemon=True)
@@ -1,6 +1,7 @@
1
1
  """Trame implementation of the GridLayout class."""
2
2
 
3
3
  from typing import Any, Optional, Union
4
+ from warnings import warn
4
5
 
5
6
  from trame.widgets import html
6
7
  from trame_client.widgets.core import AbstractElement
@@ -19,6 +20,7 @@ class GridLayout(html.Div):
19
20
  halign: Optional[str] = None,
20
21
  valign: Optional[str] = None,
21
22
  gap: Optional[Union[int, str]] = "0em",
23
+ stretch: bool = False,
22
24
  **kwargs: Any,
23
25
  ) -> None:
24
26
  """Constructor for GridLayout.
@@ -38,10 +40,14 @@ class GridLayout(html.Div):
38
40
  <https://developer.mozilla.org/en-US/docs/Web/CSS/justify-items>`__ for available options.
39
41
  valign : optional[str]
40
42
  The vertical alignment of items in the grid. See `MDN
41
- <https://developer.mozilla.org/en-US/docs/Web/CSS/align-items>`__ for available options.
43
+ <https://developer.mozilla.org/en-US/docs/Web/CSS/align-items>`__ for available options. Note that this
44
+ parameter is ignored when stretch=True.
42
45
  gap : optional[str]
43
46
  The gap to place between items (works both horizontally and vertically). Can be any CSS gap value (e.g.
44
47
  "4px" or "0.25em"). Defaults to no gap between items.
48
+ stretch : optional[bool]
49
+ If True, then this layout component will stretch to attempt to fill the space of it's parent container.
50
+ Defaults to False.
45
51
  kwargs : Any
46
52
  Additional keyword arguments to pass to html.Div.
47
53
 
@@ -69,6 +75,13 @@ class GridLayout(html.Div):
69
75
  if isinstance(classes, list):
70
76
  classes = " ".join(classes)
71
77
  classes += " d-grid"
78
+ if stretch:
79
+ if valign:
80
+ warn("Ignoring valign parameter to GridLayout since stretch=True.", stacklevel=1)
81
+ valign = "stretch"
82
+ classes += " flex-1-1 overflow-y-auto"
83
+ else:
84
+ classes += " flex-0-1"
72
85
 
73
86
  widget_style = self.get_root_styles(columns, height, width, halign, valign, gap)
74
87
  user_style = kwargs.pop("style", {})
@@ -18,6 +18,7 @@ class HBoxLayout(html.Div):
18
18
  valign: Optional[str] = None,
19
19
  gap: Optional[Union[int, str]] = "0em",
20
20
  vspace: Optional[Union[int, str]] = "0em",
21
+ stretch: bool = False,
21
22
  **kwargs: Any,
22
23
  ) -> None:
23
24
  """Constructor for HBoxLayout.
@@ -42,6 +43,9 @@ class HBoxLayout(html.Div):
42
43
  vspace : optional[str]
43
44
  The vertical gap to place between items. Can be any CSS gap value (e.g. "4px" or "0.25em"). Defaults to no
44
45
  gap between items.
46
+ stretch : optional[bool]
47
+ If True, then this layout component will stretch to attempt to fill the space of it's parent container.
48
+ Defaults to False.
45
49
  kwargs : Any
46
50
  Additional keyword arguments to pass to html.Div.
47
51
 
@@ -60,6 +64,10 @@ class HBoxLayout(html.Div):
60
64
  if isinstance(classes, list):
61
65
  classes = " ".join(classes)
62
66
  classes += " d-flex flex-row"
67
+ if stretch:
68
+ classes += " flex-1-1 overflow-y-auto"
69
+ else:
70
+ classes += " flex-0-1"
63
71
 
64
72
  widget_style = self.get_root_styles(height, width, halign, valign, gap, vspace)
65
73
  user_style = kwargs.pop("style", {})
@@ -18,6 +18,7 @@ class VBoxLayout(html.Div):
18
18
  valign: Optional[str] = None,
19
19
  gap: Optional[Union[int, str]] = "0em",
20
20
  vspace: Optional[Union[int, str]] = "0em",
21
+ stretch: bool = False,
21
22
  **kwargs: Any,
22
23
  ) -> None:
23
24
  """Constructor for VBoxLayout.
@@ -42,6 +43,9 @@ class VBoxLayout(html.Div):
42
43
  vspace : optional[str]
43
44
  The vertical gap to place between items. Can be any CSS gap value (e.g. "4px" or "0.25em"). Defaults to no
44
45
  gap between items.
46
+ stretch : optional[bool]
47
+ If True, then this layout component will stretch to attempt to fill the space of it's parent container.
48
+ Defaults to False.
45
49
  kwargs : Any
46
50
  Additional keyword arguments to pass to html.Div.
47
51
 
@@ -60,6 +64,10 @@ class VBoxLayout(html.Div):
60
64
  if isinstance(classes, list):
61
65
  classes = " ".join(classes)
62
66
  classes += " d-flex flex-column"
67
+ if stretch:
68
+ classes += " flex-1-1 overflow-y-auto"
69
+ else:
70
+ classes += " flex-0-1"
63
71
 
64
72
  widget_style = self.get_root_styles(height, width, halign, valign, gap, vspace)
65
73
  user_style = kwargs.pop("style", {})
@@ -70,12 +70,24 @@ html {
70
70
  }
71
71
 
72
72
  .v-window {
73
+ display: flex;
74
+ flex: 1 1;
73
75
  overflow-x: visible !important;
74
76
  overflow-y: visible !important;
77
+
78
+ .v-window__container {
79
+ flex: 1 1;
80
+
81
+ .v-window-item {
82
+ flex: 1 1;
83
+ }
84
+ }
75
85
  }
76
86
 
77
87
  @media only screen and (max-width: 959px) {
78
88
  .d-grid {
89
+ /* This forces all grid rows to have the same height. If one doesn't want this to happen, then they should use the box layouts. :) */
90
+ grid-auto-rows: 1fr;
79
91
  grid-template-columns: repeat(1, 1fr) !important;
80
92
  }
81
93
 
@@ -4,11 +4,15 @@ class RevoGrid {
4
4
  this.modelKey = modelKey
5
5
  this.dataKey = dataKey
6
6
  this.stateKey = stateKey
7
+ this.lastSelection = null
8
+ this.shiftPressed = false
7
9
 
8
10
  this.grid = document.querySelector(`#${this.id}`)
9
11
  this.grid.addEventListener('viewportscroll', () => {
10
12
  this.updateCheckboxes()
11
13
  })
14
+
15
+ this.initShiftKeyListeners()
12
16
  }
13
17
 
14
18
  updateCheckboxes() {
@@ -66,11 +70,32 @@ class RevoGrid {
66
70
  const path = props.data[props.rowIndex].path
67
71
  const index = modelValue.indexOf(path)
68
72
 
69
- // We need to assign instead of modifying in place in order for the Trame watcher to pick up changes.
73
+ // I use _.set instead of modifying the modelValue in place in order for the Trame watcher to properly detect the change.
70
74
  if (e.target.checked && index < 0) {
71
- _.set(trameState, this.modelKey, _.concat(modelValue, path))
75
+ const newIndex = props.data.findIndex((entry) => entry.path === path)
76
+
77
+ if (this.shiftPressed && this.lastSelection !== null) {
78
+ let newPaths = []
79
+ // JavaScript doesn't allow a backwards step during slice, so we need to order the start/stop correctly.
80
+ if (this.lastSelection < newIndex) {
81
+ newPaths = props.data.slice(this.lastSelection, newIndex + 1)
82
+ } else {
83
+ newPaths = props.data.slice(newIndex, this.lastSelection)
84
+ }
85
+ // Exclude paths that are already selected to avoid duplicates.
86
+ newPaths = newPaths.map((entry) => entry.path).filter((path) => !modelValue.includes(path))
87
+
88
+ _.set(trameState, this.modelKey, _.concat(modelValue, newPaths))
89
+ } else {
90
+ _.set(trameState, this.modelKey, _.concat(modelValue, path))
91
+ }
92
+
93
+ this.lastSelection = newIndex
72
94
  } else if (index >= 0) {
73
95
  _.set(trameState, this.modelKey, modelValue.toSpliced(index, 1))
96
+
97
+ // Only allow range selection if the last action was to select a file.
98
+ this.lastSelection = null
74
99
  }
75
100
 
76
101
  // Update the UI
@@ -103,6 +128,18 @@ class RevoGrid {
103
128
 
104
129
  return [inputVNode, 'Available Datafiles']
105
130
  }
131
+
132
+ initShiftKeyListeners() {
133
+ window.document.addEventListener('keydown', (e) => {
134
+ this.shiftPressed = e.shiftKey
135
+ })
136
+
137
+ window.document.addEventListener('keyup', (e) => {
138
+ if (e.key === 'Shift') {
139
+ this.shiftPressed = false
140
+ }
141
+ })
142
+ }
106
143
  }
107
144
 
108
145
  class RevoGridManager {
@@ -3,6 +3,7 @@
3
3
  import asyncio
4
4
  import json
5
5
  import logging
6
+ import os
6
7
  import sys
7
8
  from asyncio import create_task
8
9
  from functools import partial
@@ -210,6 +211,10 @@ class ThemedApp:
210
211
  -------
211
212
  `trame_client.ui.core.AbstractLayout <https://trame.readthedocs.io/en/latest/core.ui.html#trame_client.ui.core.AbstractLayout>`_
212
213
  """
214
+ # This detects if Poetry is running Python so that we can show links to NOVA resources during development.
215
+ # Poetry should not be used in production.
216
+ show_nova_resources = os.environ.get("VIRTUAL_ENV") is not None
217
+
213
218
  with VAppLayout(self.server, vuetify_config=self.vuetify_config) as layout:
214
219
  self.local_storage = LocalStorageManager(self.server.controller)
215
220
 
@@ -225,8 +230,32 @@ class ThemedApp:
225
230
  with vuetify.VAppBar() as toolbar:
226
231
  layout.toolbar = toolbar
227
232
 
228
- with vuetify.VAppBarTitle() as toolbar_title:
233
+ with vuetify.VAppBarTitle(classes="flex-0-1") as toolbar_title:
229
234
  layout.toolbar_title = toolbar_title
235
+
236
+ if show_nova_resources:
237
+ vuetify.VBtn(
238
+ "NOVA Examples",
239
+ classes="ml-4",
240
+ href="https://github.com/nova-model/nova-examples/",
241
+ __properties=["target"],
242
+ target="_blank",
243
+ )
244
+ html.Div("·", classes="mx-1")
245
+ vuetify.VBtn(
246
+ "NOVA Tutorial",
247
+ href="https://nova.ornl.gov/tutorial/",
248
+ __properties=["target"],
249
+ target="_blank",
250
+ )
251
+ html.Div("·", classes="mx-1")
252
+ vuetify.VBtn(
253
+ "NOVA Documentation",
254
+ href="https://nova-application-development.readthedocs.io/en/latest/",
255
+ __properties=["target"],
256
+ target="_blank",
257
+ )
258
+
230
259
  vuetify.VSpacer()
231
260
  with html.Div(classes="mr-2") as actions:
232
261
  layout.actions = actions
@@ -259,11 +288,13 @@ class ThemedApp:
259
288
 
260
289
  with vuetify.VMain(classes="align-stretch d-flex flex-column h-screen"):
261
290
  # [slot override example]
262
- layout.pre_content = vuetify.VSheet(classes="bg-background ")
291
+ layout.pre_content = vuetify.VSheet(classes="bg-background flex-0-1 mt-1 ")
263
292
  # [slot override example complete]
264
- with vuetify.VContainer(classes="flex-0-1 overflow-hidden pt-0 pb-0", fluid=True):
265
- layout.content = html.Div(classes="h-100 overflow-y-auto pb-1 ")
266
- layout.post_content = vuetify.VSheet(classes="bg-background ")
293
+ with vuetify.VContainer(classes="flex-1-1 overflow-hidden pt-0 pb-2", fluid=True):
294
+ layout.content = vuetify.VCard(
295
+ classes="d-flex flex-column flex-1-1 h-100 overflow-y-auto pa-1 "
296
+ )
297
+ layout.post_content = vuetify.VSheet(classes="bg-background flex-0-1 mb-1 ")
267
298
 
268
299
  with vuetify.VFooter(
269
300
  app=True,
@@ -1,18 +1,19 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: nova-trame
3
- Version: 0.26.2
3
+ Version: 1.0.0
4
4
  Summary: A Python Package for injecting curated themes and custom components into Trame applications
5
- License: MIT
5
+ License-Expression: MIT
6
+ License-File: LICENSE
6
7
  Keywords: NDIP,Python,Trame,Vuetify
7
8
  Author: John Duggan
8
9
  Author-email: dugganjw@ornl.gov
9
10
  Requires-Python: >=3.10,<4.0
10
- Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Programming Language :: Python :: 3.10
13
13
  Classifier: Programming Language :: Python :: 3.11
14
14
  Classifier: Programming Language :: Python :: 3.12
15
15
  Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
16
17
  Requires-Dist: altair
17
18
  Requires-Dist: blinker (>=1.9.0,<2.0.0)
18
19
  Requires-Dist: libsass
@@ -37,7 +38,7 @@ Description-Content-Type: text/markdown
37
38
  nova-trame
38
39
  ==========
39
40
 
40
- `nova-trame` is a Python package for streamlining development of Trame applications used in the NOVA project.
41
+ `nova-trame` is a Python package for streamlining development of Trame applications used in the NOVA framework.
41
42
 
42
43
  You can install this package directly with
43
44
 
@@ -45,6 +46,13 @@ You can install this package directly with
45
46
  pip install nova-trame
46
47
  ```
47
48
 
49
+ Once installed, you can check that it's working by running the widget gallery we use for visually testing components available through NOVA and Trame. We use [Poetry](https://python-poetry.org/) internally and recommend it for running the gallery.
50
+
51
+ ```commandline
52
+ poetry install
53
+ poetry run app
54
+ ```
55
+
48
56
  A user guide, examples, and a full API for this package can be found at [https://nova-application-development.readthedocs.io/en/stable/](https://nova-application-development.readthedocs.io/projects/nova-trame/en/stable/).
49
57
 
50
58
  Developers: please read [this document](DEVELOPMENT.md)
@@ -7,32 +7,32 @@ nova/trame/model/ornl/neutron_data_selector.py,sha256=YMoNEpDKgjP_y18oYj-N9Ijkxt
7
7
  nova/trame/model/ornl/oncat_data_selector.py,sha256=3JEkWGMU-esWA9DUTglju9hEP9LyZ7EUXLj1yO5BIDs,4755
8
8
  nova/trame/model/remote_file_input.py,sha256=eAk7ZsFgNKcnpJ6KmOQDhiI6pPZpcrr1GMKkRLEWht8,4338
9
9
  nova/trame/view/components/__init__.py,sha256=60BeS69aOrFnkptjuD17rfPE1f4Z35iBH56TRmW5MW8,451
10
- nova/trame/view/components/data_selector.py,sha256=T5CFtWPio3YwiPvPEBrWn7vgRo-3l0GqdGKzonc_cwc,14970
10
+ nova/trame/view/components/data_selector.py,sha256=ByZ1RTyJRfmHNfN-nFi_Ihljo0yjbyfa_El-jHl9oyM,14907
11
11
  nova/trame/view/components/execution_buttons.py,sha256=Br6uAmE5bY67TTYc5ZTHECNJ_RJqKmv17HAKPpQtbeg,4576
12
12
  nova/trame/view/components/file_upload.py,sha256=WOaFXeNNwN0DYZJr-W6vWdBiTpr7m-lq3WKJaHmeMe8,4560
13
13
  nova/trame/view/components/input_field.py,sha256=xzCmNEoB4ljGx99-gGgTV0UwriwtS8ce22zPA4QneZw,17372
14
14
  nova/trame/view/components/ornl/__init__.py,sha256=HnxzzSsxw0vQSDCVFfWsAxx1n3HnU37LMuQkfiewmSU,90
15
- nova/trame/view/components/ornl/neutron_data_selector.py,sha256=wVSLmRXiGOv2l-nZ0DgDwjrgTMZDu9e9ECm3k824k3o,16749
15
+ nova/trame/view/components/ornl/neutron_data_selector.py,sha256=3IxEYM3wK7RskxbjDG94JeWBCEWXBeF3xBDWt3Bh1OU,16797
16
16
  nova/trame/view/components/progress_bar.py,sha256=zhbJwPy_HPQ8YL-ISN8sCRUQ7qY6qqo9wiV59BmvL8I,3038
17
17
  nova/trame/view/components/remote_file_input.py,sha256=mcz_bmI2rD8gdmIOKLhlzfj-XoWBwC99T9ZgQORaKqE,14674
18
18
  nova/trame/view/components/tool_outputs.py,sha256=IbYV4VjrkWAE354Bh5KH76SPsxGLIkOXChijS4-ce_Y,2408
19
19
  nova/trame/view/components/visualization/__init__.py,sha256=reqkkbhD5uSksHHlhVMy1qNUCwSekS5HlXk6wCREYxU,152
20
20
  nova/trame/view/components/visualization/interactive_2d_plot.py,sha256=z2s1janxAclpMEdDJk3z-CQ6r3KPNoR_SXPx9ppWnuQ,3481
21
- nova/trame/view/components/visualization/matplotlib_figure.py,sha256=q0HLaaLFjM3_V1oUk-VBHWvokFY6AQZzmnMcynTroik,12488
21
+ nova/trame/view/components/visualization/matplotlib_figure.py,sha256=8JgbF6elZC8EFZn-c2mLqGSyg0Lb4NLVAKJVSBb9-5g,16010
22
22
  nova/trame/view/layouts/__init__.py,sha256=cMrlB5YMUoK8EGB83b34UU0kPTVrH8AxsYvKRtpUNEc,141
23
- nova/trame/view/layouts/grid.py,sha256=vqEX-jghs6j9_sVtijdRH7uhlD9loWNi90k2qgg4Dhg,5534
24
- nova/trame/view/layouts/hbox.py,sha256=cdwnGk93ec6dXAeEamRQx1WTj5T7Ygsmsy0xz130tWM,3519
23
+ nova/trame/view/layouts/grid.py,sha256=DU5u5JTE0ulzCaJsEWyTenBH9lOQD7mtoC6RZXxDTPE,6110
24
+ nova/trame/view/layouts/hbox.py,sha256=w6ow7Qzmq4slODz_9f7kEigCVPE2PhUmPODedYH34f4,3850
25
25
  nova/trame/view/layouts/utils.py,sha256=Hg34VQWTG3yHBsgNvmfatR4J-uL3cko7UxSJpT-h3JI,376
26
- nova/trame/view/layouts/vbox.py,sha256=XRV14e32MY1HWc9FTVTv1vOatWWbhLMd0lYwZP-isTg,3520
26
+ nova/trame/view/layouts/vbox.py,sha256=Kkci79zDKF6qNH4HeDPYquJcixXx3BS63NVmS3FlOiw,3851
27
27
  nova/trame/view/theme/__init__.py,sha256=70_marDlTigIcPEOGiJb2JTs-8b2sGM5SlY7XBPtBDM,54
28
- nova/trame/view/theme/assets/core_style.scss,sha256=3-3qMc5gpaDhfuVWAF_psBH5alxwiuK-hPGhVgi2cW0,4335
28
+ nova/trame/view/theme/assets/core_style.scss,sha256=Bf7ajLULXPoJWH2KZk47VsAsMMC7DOYBjG_54l0qsjs,4652
29
29
  nova/trame/view/theme/assets/favicon.png,sha256=Xbp1nUmhcBDeObjsebEbEAraPDZ_M163M_ZLtm5AbQc,1927
30
30
  nova/trame/view/theme/assets/js/delay_manager.js,sha256=BN4OL88QsyZG4XQ1sTorHpN1rwD4GnWoVKHvl5F5ydo,776
31
31
  nova/trame/view/theme/assets/js/lodash.min.js,sha256=KCyAYJ-fsqtp_HMwbjhy6IKjlA5lrVrtWt1JdMsC57k,73016
32
- nova/trame/view/theme/assets/js/revo_grid.js,sha256=fbuEWO8etw-xgo9tjJGjJXdd5wL8qpgabPmrnU6Jp8k,4081
32
+ nova/trame/view/theme/assets/js/revo_grid.js,sha256=73v-GP4IUIg8OgMnRCoHB6ERFU18nx0VYaEecV_7u-o,5695
33
33
  nova/trame/view/theme/assets/vuetify_config.json,sha256=a0FSgpLYWGFlRGSMhMq61MyDFBEBwvz55G4qjkM08cs,5627
34
34
  nova/trame/view/theme/exit_button.py,sha256=Kqv1GVJZGrSsj6_JFjGU3vm3iNuMolLC2T1x2IsdmV0,3094
35
- nova/trame/view/theme/theme.py,sha256=8JqSrEbhxK1SccXE1_jUdel9Wtc2QNObVEwtbVWG_QY,13146
35
+ nova/trame/view/theme/theme.py,sha256=DMA53DYUS6qpLkqAlfEMOuNlyNgHD13uBMfSeLytGyw,14698
36
36
  nova/trame/view/utilities/local_storage.py,sha256=vD8f2VZIpxhIKjZwEaD7siiPCTZO4cw9AfhwdawwYLY,3218
37
37
  nova/trame/view_model/data_selector.py,sha256=jAtq5hpohQ6YiLBbgLJfNUzWZBpN2bjCG_c_FCJu2ns,3186
38
38
  nova/trame/view_model/execution_buttons.py,sha256=MfKSp95D92EqpD48C15cBo6dLO0Yld4FeRZMJNxJf7Y,3551
@@ -40,8 +40,8 @@ nova/trame/view_model/ornl/neutron_data_selector.py,sha256=PIKQyzcHpwu81DNk3d8Af
40
40
  nova/trame/view_model/progress_bar.py,sha256=6AUKHF3hfzbdsHqNEnmHRgDcBKY5TT8ywDx9S6ovnsc,2854
41
41
  nova/trame/view_model/remote_file_input.py,sha256=zWOflmCDJYYR_pacHphwzricV667GSRokh-mlxpBAOo,3646
42
42
  nova/trame/view_model/tool_outputs.py,sha256=ev6LY7fJ0H2xAJn9f5ww28c8Kpom2SYc2FbvFcoN4zg,829
43
- nova_trame-0.26.2.dist-info/LICENSE,sha256=Iu5QiDbwNbREg75iYaxIJ_V-zppuv4QFuBhAW-qiAlM,1061
44
- nova_trame-0.26.2.dist-info/METADATA,sha256=mDqDXOmyd5tFAnh7-VtPdiYu1ij7ajZcanzkgC6Ojps,1763
45
- nova_trame-0.26.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
46
- nova_trame-0.26.2.dist-info/entry_points.txt,sha256=J2AmeSwiTYZ4ZqHHp9HO6v4MaYQTTBPbNh6WtoqOT58,42
47
- nova_trame-0.26.2.dist-info/RECORD,,
43
+ nova_trame-1.0.0.dist-info/METADATA,sha256=nqvFzLDJCyIuPqZmNK9rIXR6x0nfjIRt0cmU17zDFXI,2096
44
+ nova_trame-1.0.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
45
+ nova_trame-1.0.0.dist-info/entry_points.txt,sha256=J2AmeSwiTYZ4ZqHHp9HO6v4MaYQTTBPbNh6WtoqOT58,42
46
+ nova_trame-1.0.0.dist-info/licenses/LICENSE,sha256=Iu5QiDbwNbREg75iYaxIJ_V-zppuv4QFuBhAW-qiAlM,1061
47
+ nova_trame-1.0.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.3
2
+ Generator: poetry-core 2.2.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any