figpack 0.2.17__py3-none-any.whl → 0.2.40__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.
Files changed (44) hide show
  1. figpack/__init__.py +1 -1
  2. figpack/cli.py +288 -2
  3. figpack/core/_bundle_utils.py +40 -7
  4. figpack/core/_file_handler.py +195 -0
  5. figpack/core/_save_figure.py +12 -8
  6. figpack/core/_server_manager.py +146 -7
  7. figpack/core/_show_view.py +2 -2
  8. figpack/core/_upload_bundle.py +63 -53
  9. figpack/core/_view_figure.py +48 -12
  10. figpack/core/_zarr_consolidate.py +185 -0
  11. figpack/core/extension_view.py +9 -5
  12. figpack/core/figpack_extension.py +1 -1
  13. figpack/core/figpack_view.py +52 -21
  14. figpack/core/zarr.py +2 -2
  15. figpack/extensions.py +356 -0
  16. figpack/figpack-figure-dist/assets/index-ST_DU17U.js +95 -0
  17. figpack/figpack-figure-dist/assets/index-V5m_wCvw.css +1 -0
  18. figpack/figpack-figure-dist/index.html +6 -2
  19. figpack/views/Box.py +4 -4
  20. figpack/views/CaptionedView.py +64 -0
  21. figpack/views/DataFrame.py +1 -1
  22. figpack/views/Gallery.py +2 -2
  23. figpack/views/Iframe.py +43 -0
  24. figpack/views/Image.py +2 -3
  25. figpack/views/Markdown.py +8 -4
  26. figpack/views/MatplotlibFigure.py +1 -1
  27. figpack/views/MountainLayout.py +72 -0
  28. figpack/views/MountainLayoutItem.py +50 -0
  29. figpack/views/MultiChannelTimeseries.py +1 -1
  30. figpack/views/PlotlyExtension/PlotlyExtension.py +14 -14
  31. figpack/views/Spectrogram.py +3 -1
  32. figpack/views/Splitter.py +3 -3
  33. figpack/views/TabLayout.py +2 -2
  34. figpack/views/TimeseriesGraph.py +113 -20
  35. figpack/views/__init__.py +4 -0
  36. {figpack-0.2.17.dist-info → figpack-0.2.40.dist-info}/METADATA +25 -1
  37. figpack-0.2.40.dist-info/RECORD +50 -0
  38. figpack/figpack-figure-dist/assets/index-DBwmtEpB.js +0 -91
  39. figpack/figpack-figure-dist/assets/index-DHWczh-Q.css +0 -1
  40. figpack-0.2.17.dist-info/RECORD +0 -43
  41. {figpack-0.2.17.dist-info → figpack-0.2.40.dist-info}/WHEEL +0 -0
  42. {figpack-0.2.17.dist-info → figpack-0.2.40.dist-info}/entry_points.txt +0 -0
  43. {figpack-0.2.17.dist-info → figpack-0.2.40.dist-info}/licenses/LICENSE +0 -0
  44. {figpack-0.2.17.dist-info → figpack-0.2.40.dist-info}/top_level.txt +0 -0
@@ -0,0 +1 @@
1
+ .unitEntryBase{padding-right:13px;padding-top:10px}.unselectedUnitEntry{border:1px transparent solid;font-weight:400;color:#000;background-color:#fff}.selectedUnitEntry{border:1px solid blue;font-weight:700;color:#000;background-color:#b5d1ff}.unitIdsColumnLabelStyle{min-width:200px;font-weight:700;padding:7px 5px}.unitLabelsStyle{padding-right:3px;color:#333}.plotUnitLabel{text-align:left;margin-left:20px;margin-top:5px;font-weight:700}.plotWrapperButtonParent{display:inline-block}.plotWrapperStyleButton{padding-top:50px}.plotUnselectedStyle{border:3px solid rgb(202,224,231)}.plotSelectedStyle{border:3px solid #9999ff;background-color:#fff}.plotUnselectableStyle{border:3px solid transparent}.plotCurrentStyle{border:3px solid blue;background-color:#fff}.plotLabelStyle{font-weight:700;text-align:center;overflow:hidden;white-space:nowrap}:root{font-family:system-ui,Avenir,Helvetica,Arial,sans-serif;line-height:1.5;font-weight:400;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}a{font-weight:500;color:#646cff;text-decoration:inherit}a:hover{color:#535bf2}body{margin:0;overflow:hidden}h1{font-size:3.2em;line-height:1.1}button{border-radius:8px;border:1px solid transparent;padding:.6em 1.2em;font-size:1em;font-weight:500;font-family:inherit;background-color:#1a1a1a;cursor:pointer;transition:border-color .25s}button:hover{border-color:#646cff}button:focus,button:focus-visible{outline:4px auto -webkit-focus-ring-color}.status-bar{position:fixed;left:0;right:0;height:30px;background-color:#2a2a2a;color:#fff;display:flex;align-items:center;padding:0 12px;font-size:12px;border-top:1px solid #444;z-index:1000}.status-bar.warning{background-color:#ff9800;color:#000}.status-bar.error{background-color:#f44336;color:#fff}.status-bar.expired{background-color:#d32f2f;color:#fff}.manage-button{background-color:#444;color:#fff;border:none;padding:4px 8px;border-radius:4px;cursor:pointer;font-size:11px;height:22px;margin-left:12px;transition:background-color .2s}.manage-button:hover{background-color:#666}.about-button{background-color:#444;color:#fff;border:none;padding:4px 8px;border-radius:4px;cursor:pointer;font-size:11px;height:22px;margin-left:12px;transition:background-color .2s}.about-button:hover{background-color:#666}.about-dialog-overlay{position:fixed;inset:0;background-color:#00000080;display:flex;justify-content:center;align-items:center;z-index:2000}.about-dialog{background-color:#2a2a2a;color:#fff;border-radius:8px;max-width:600px;max-height:80vh;width:90%;box-shadow:0 4px 20px #0000004d;overflow:hidden}.about-dialog-header{display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid #444}.about-dialog-header h2{margin:0;font-size:18px;font-weight:600}.about-dialog-close{background:none;border:none;color:#fff;font-size:24px;cursor:pointer;padding:0;width:30px;height:30px;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:background-color .2s}.about-dialog-close:hover{background-color:#444}.about-dialog-content{padding:20px;overflow-y:auto;max-height:calc(80vh - 80px)}.about-dialog-description{line-height:1.6}.about-dialog-description h1,.about-dialog-description h2,.about-dialog-description h3{margin-top:0;margin-bottom:12px}.about-dialog-description p{margin-bottom:12px}.about-dialog-description code{background-color:#444;padding:2px 4px;border-radius:3px;font-family:Courier New,monospace;font-size:.9em}.about-dialog-description pre{background-color:#444;padding:12px;border-radius:4px;overflow-x:auto;margin-bottom:12px}.about-dialog-description ul,.about-dialog-description ol{margin-bottom:12px;padding-left:20px}.about-dialog-description blockquote{border-left:4px solid #666;padding-left:16px;margin:12px 0;font-style:italic}.manage-edits-view{display:flex;align-items:center;margin-left:12px}.edits-notification{color:#ff9800;font-weight:500;margin-right:8px}.save-changes-button{background-color:#ff9800;color:#000;border:none;padding:4px 8px;border-radius:4px;cursor:pointer;font-size:11px;height:22px;transition:background-color .2s}.save-changes-button:hover{background-color:#f57c00}.dialog-overlay{position:fixed;inset:0;background-color:#00000080;display:flex;justify-content:center;align-items:center;z-index:2000}.dialog-content{background-color:#2a2a2a;color:#fff;border-radius:8px;max-width:500px;width:90%;box-shadow:0 4px 20px #0000004d;overflow:hidden}.dialog-header{display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid #444}.dialog-header h3{margin:0;font-size:18px;font-weight:600}.dialog-close{background:none;border:none;color:#fff;font-size:24px;cursor:pointer;padding:0;width:30px;height:30px;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:background-color .2s}.dialog-close:hover{background-color:#444}.dialog-body{padding:20px}.dialog-body p{margin:0 0 12px;line-height:1.5}.dialog-footer{padding:16px 20px;border-top:1px solid #444;display:flex;justify-content:flex-end}.dialog-button{background-color:#444;color:#fff;border:none;padding:8px 16px;border-radius:4px;cursor:pointer;font-size:14px;transition:background-color .2s}.dialog-button:hover{background-color:#666}.dialog-fullscreen{position:fixed;inset:0;background-color:#2a2a2a;color:#fff;z-index:2000;display:flex;flex-direction:column}.dialog-content-fullscreen{display:flex;flex-direction:column;height:100%;overflow:hidden}.dialog-content-fullscreen .dialog-header{flex-shrink:0}.dialog-content-fullscreen .dialog-body{flex:1;overflow-y:auto;padding:20px}.dialog-content-fullscreen .dialog-footer{flex-shrink:0}@media (prefers-color-scheme: light){:root{color:#213547;background-color:#fff}a:hover{color:#747bff}button{background-color:#f9f9f9}.status-bar{background-color:#f5f5f5;color:#333;border-top:1px solid #ddd}.status-bar.warning{background-color:#fff3cd;color:#856404;border-top:1px solid #ffeaa7}.status-bar.error,.status-bar.expired{background-color:#f8d7da;color:#721c24;border-top:1px solid #f5c6cb}.manage-button{background-color:#e0e0e0;color:#333;border:1px solid #ccc}.manage-button:hover{background-color:#d0d0d0}.about-button{background-color:#e0e0e0;color:#333;border:1px solid #ccc}.about-button:hover{background-color:#d0d0d0}.about-dialog{background-color:#fff;color:#333;box-shadow:0 4px 20px #00000026}.about-dialog-header{border-bottom:1px solid #ddd}.about-dialog-close{color:#333}.about-dialog-close:hover{background-color:#f0f0f0}.about-dialog-description code,.about-dialog-description pre{background-color:#f5f5f5;color:#333}.about-dialog-description blockquote{border-left:4px solid #ccc}.edits-notification{color:#e65100}.save-changes-button{background-color:#ff9800;color:#fff;border:1px solid #f57c00}.save-changes-button:hover{background-color:#f57c00}.dialog-content{background-color:#fff;color:#333;box-shadow:0 4px 20px #00000026}.dialog-header{border-bottom:1px solid #ddd}.dialog-close{color:#333}.dialog-close:hover{background-color:#f0f0f0}.dialog-footer{border-top:1px solid #ddd}.dialog-button{background-color:#e0e0e0;color:#333;border:1px solid #ccc}.dialog-button:hover{background-color:#d0d0d0}.dialog-fullscreen{background-color:#fff;color:#333}}
@@ -5,8 +5,12 @@
5
5
  <link rel="icon" type="image/png" href="./assets/neurosift-logo-CLsuwLMO.png" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>figpack figure</title>
8
- <script type="module" crossorigin src="./assets/index-DBwmtEpB.js"></script>
9
- <link rel="stylesheet" crossorigin href="./assets/index-DHWczh-Q.css">
8
+ <script>
9
+ // Allow script injection from trusted domains (see src/main.tsx)
10
+ window.script_injection_allowed_domains = ['https://manage.figpack.org', 'https://figpack.org', "https://documents.figpack.org"];
11
+ </script>
12
+ <script type="module" crossorigin src="./assets/index-ST_DU17U.js"></script>
13
+ <link rel="stylesheet" crossorigin href="./assets/index-V5m_wCvw.css">
10
14
  </head>
11
15
  <body>
12
16
  <div id="root"></div>
figpack/views/Box.py CHANGED
@@ -2,7 +2,7 @@
2
2
  Box view for figpack - a layout container that handles other views
3
3
  """
4
4
 
5
- from typing import Any, Dict, List, Literal, Optional
5
+ from typing import List, Literal, Optional
6
6
 
7
7
  from ..core.figpack_view import FigpackView
8
8
  from ..core.zarr import Group
@@ -21,7 +21,7 @@ class Box(FigpackView):
21
21
  show_titles: bool = True,
22
22
  items: List[LayoutItem],
23
23
  title: Optional[str] = None,
24
- ):
24
+ ) -> None:
25
25
  """
26
26
  Initialize a Box layout view
27
27
 
@@ -42,7 +42,7 @@ class Box(FigpackView):
42
42
  self.items = items
43
43
  self.title = title
44
44
 
45
- def _write_to_zarr_group(self, group: Group) -> None:
45
+ def write_to_zarr_group(self, group: Group) -> None:
46
46
  """
47
47
  Write the Box layout data to a Zarr group
48
48
 
@@ -73,7 +73,7 @@ class Box(FigpackView):
73
73
  item_group = group.create_group(item_name)
74
74
 
75
75
  # Recursively write the child view to the subgroup
76
- item.view._write_to_zarr_group(item_group)
76
+ item.view.write_to_zarr_group(item_group)
77
77
 
78
78
  # Store the items metadata
79
79
  group.attrs["items"] = items_metadata
@@ -0,0 +1,64 @@
1
+ """
2
+ CaptionedView for figpack - displays a view with a caption below it
3
+ """
4
+
5
+ from typing import Optional
6
+ import numpy as np
7
+
8
+ from ..core.figpack_view import FigpackView
9
+ from ..core.zarr import Group
10
+
11
+
12
+ class CaptionedView(FigpackView):
13
+ """
14
+ A view that displays a child view with a text caption below it.
15
+ The caption height dynamically adjusts based on text length and container width.
16
+ """
17
+
18
+ def __init__(
19
+ self,
20
+ *,
21
+ view: FigpackView,
22
+ caption: str,
23
+ font_size: Optional[int] = None,
24
+ ):
25
+ """
26
+ Initialize a CaptionedView
27
+
28
+ Args:
29
+ view: The child view to display above the caption
30
+ caption: The text caption to display below the view
31
+ font_size: Optional font size for the caption text (default: 14)
32
+ """
33
+ self.view = view
34
+ self.caption = caption
35
+ self.font_size = font_size if font_size is not None else 14
36
+
37
+ def write_to_zarr_group(self, group: Group) -> None:
38
+ """
39
+ Write the CaptionedView data to a Zarr group
40
+
41
+ Args:
42
+ group: Zarr group to write data into
43
+ """
44
+ # Set the view type
45
+ group.attrs["view_type"] = "CaptionedView"
46
+
47
+ # Set caption properties
48
+ group.attrs["font_size"] = self.font_size
49
+
50
+ # Convert caption string to numpy array of bytes
51
+ caption_bytes = self.caption.encode("utf-8")
52
+ caption_array = np.frombuffer(caption_bytes, dtype=np.uint8)
53
+
54
+ # Store the caption as a zarr array
55
+ group.create_dataset("caption_data", data=caption_array)
56
+
57
+ # Store caption size in attrs
58
+ group.attrs["caption_size"] = len(caption_bytes)
59
+
60
+ # Create a subgroup for the child view
61
+ child_group = group.create_group("child_view")
62
+
63
+ # Recursively write the child view to the subgroup
64
+ self.view.write_to_zarr_group(child_group)
@@ -33,7 +33,7 @@ class DataFrame(FigpackView):
33
33
 
34
34
  self.df = df
35
35
 
36
- def _write_to_zarr_group(self, group: Group) -> None:
36
+ def write_to_zarr_group(self, group: Group) -> None:
37
37
  """
38
38
  Write the DataFrame data to a Zarr group
39
39
 
figpack/views/Gallery.py CHANGED
@@ -44,7 +44,7 @@ class Gallery(FigpackView):
44
44
  max(0, min(initial_item_index, len(items) - 1)) if items else 0
45
45
  )
46
46
 
47
- def _write_to_zarr_group(self, group: Group) -> None:
47
+ def write_to_zarr_group(self, group: Group) -> None:
48
48
  """
49
49
  Write the Gallery data to a Zarr group
50
50
 
@@ -80,7 +80,7 @@ class Gallery(FigpackView):
80
80
 
81
81
  # Recursively write the child view to the subgroup
82
82
  # This allows any figpack view to be contained within a gallery item
83
- item.view._write_to_zarr_group(item_group)
83
+ item.view.write_to_zarr_group(item_group)
84
84
 
85
85
  # Store the complete items metadata in the group attributes
86
86
  # This will be used by the frontend to render the gallery structure
@@ -0,0 +1,43 @@
1
+ """
2
+ Iframe view for figpack - displays content in an iframe
3
+ """
4
+
5
+ import numpy as np
6
+
7
+ from ..core.figpack_view import FigpackView
8
+ from ..core.zarr import Group
9
+
10
+
11
+ class Iframe(FigpackView):
12
+ """
13
+ An iframe visualization component for displaying web content
14
+ """
15
+
16
+ def __init__(self, url: str):
17
+ """
18
+ Initialize an Iframe view
19
+
20
+ Args:
21
+ url: The URL to display in the iframe
22
+ """
23
+ self.url = url
24
+
25
+ def write_to_zarr_group(self, group: Group) -> None:
26
+ """
27
+ Write the iframe data to a Zarr group
28
+
29
+ Args:
30
+ group: Zarr group to write data into
31
+ """
32
+ # Set the view type
33
+ group.attrs["view_type"] = "Iframe"
34
+
35
+ # Convert URL to numpy array of bytes
36
+ url_bytes = self.url.encode("utf-8")
37
+ url_array = np.frombuffer(url_bytes, dtype=np.uint8)
38
+
39
+ # Store the URL as a zarr array
40
+ group.create_dataset("url_data", data=url_array)
41
+
42
+ # Store URL size in attrs
43
+ group.attrs["data_size"] = len(url_bytes)
figpack/views/Image.py CHANGED
@@ -5,7 +5,6 @@ Image view for figpack - displays PNG and JPG images
5
5
  from typing import Union
6
6
 
7
7
  import numpy as np
8
- import zarr
9
8
 
10
9
  from ..core.figpack_view import FigpackView
11
10
  from ..core.zarr import Group
@@ -16,7 +15,7 @@ class Image(FigpackView):
16
15
  An image visualization component for PNG and JPG files
17
16
  """
18
17
 
19
- def __init__(self, image_path_or_data: Union[str, bytes]):
18
+ def __init__(self, image_path_or_data: Union[str, bytes]) -> None:
20
19
  """
21
20
  Initialize an Image view
22
21
 
@@ -70,7 +69,7 @@ class Image(FigpackView):
70
69
  except Exception as e:
71
70
  raise ValueError(f"Failed to download image from URL: {str(e)}")
72
71
 
73
- def _write_to_zarr_group(self, group: Group) -> None:
72
+ def write_to_zarr_group(self, group: Group) -> None:
74
73
  """
75
74
  Write the image data to a Zarr group
76
75
 
figpack/views/Markdown.py CHANGED
@@ -2,8 +2,8 @@
2
2
  Markdown view for figpack - displays markdown content
3
3
  """
4
4
 
5
+ from typing import Optional
5
6
  import numpy as np
6
- import zarr
7
7
 
8
8
  from ..core.figpack_view import FigpackView
9
9
  from ..core.zarr import Group
@@ -14,7 +14,7 @@ class Markdown(FigpackView):
14
14
  A markdown content visualization component
15
15
  """
16
16
 
17
- def __init__(self, content: str):
17
+ def __init__(self, content: str, *, font_size: Optional[int] = None):
18
18
  """
19
19
  Initialize a Markdown view
20
20
 
@@ -22,8 +22,9 @@ class Markdown(FigpackView):
22
22
  content: The markdown content to display
23
23
  """
24
24
  self.content = content
25
+ self.font_size = font_size
25
26
 
26
- def _write_to_zarr_group(self, group: Group) -> None:
27
+ def write_to_zarr_group(self, group: Group) -> None:
27
28
  """
28
29
  Write the markdown data to a Zarr group
29
30
 
@@ -33,12 +34,15 @@ class Markdown(FigpackView):
33
34
  # Set the view type
34
35
  group.attrs["view_type"] = "Markdown"
35
36
 
37
+ if self.font_size is not None:
38
+ group.attrs["font_size"] = self.font_size
39
+
36
40
  # Convert string content to numpy array of bytes
37
41
  content_bytes = self.content.encode("utf-8")
38
42
  content_array = np.frombuffer(content_bytes, dtype=np.uint8)
39
43
 
40
44
  # Store the markdown content as a zarr array
41
- group.create_dataset("content_data", data=content_array, chunks=True)
45
+ group.create_dataset("content_data", data=content_array)
42
46
 
43
47
  # Store content size in attrs
44
48
  group.attrs["data_size"] = len(content_bytes)
@@ -26,7 +26,7 @@ class MatplotlibFigure(FigpackView):
26
26
  """
27
27
  self.fig = fig
28
28
 
29
- def _write_to_zarr_group(self, group: Group) -> None:
29
+ def write_to_zarr_group(self, group: Group) -> None:
30
30
  """
31
31
  Write the matplotlib figure data to a Zarr group
32
32
 
@@ -0,0 +1,72 @@
1
+ """
2
+ MountainLayout view for figpack - a workspace-style layout container with left panel and split right panel
3
+ """
4
+
5
+ from typing import List
6
+
7
+ from ..core.figpack_view import FigpackView
8
+ from ..core.zarr import Group
9
+ from .MountainLayoutItem import MountainLayoutItem
10
+
11
+
12
+ class MountainLayout(FigpackView):
13
+ """
14
+ A workspace-style layout container view that provides:
15
+ - Left panel with view buttons (top) and control views (bottom)
16
+ - Right panel split into north and south tab workspaces
17
+ - Views can be opened/closed and reopened in either workspace area
18
+ """
19
+
20
+ def __init__(
21
+ self,
22
+ *,
23
+ items: List[MountainLayoutItem],
24
+ ):
25
+ """
26
+ Initialize a MountainLayout view
27
+
28
+ Args:
29
+ items: List of MountainLayoutItem objects containing the child views.
30
+ Control items (is_control=True) will appear in the bottom-left panel.
31
+ Regular items will have buttons in the top-left panel and can be opened
32
+ in the north/south workspaces on the right.
33
+ """
34
+ self.items = items
35
+
36
+ def write_to_zarr_group(self, group: Group) -> None:
37
+ """
38
+ Write the MountainLayout data to a Zarr group
39
+
40
+ This method serializes the mountain layout structure and all its child views
41
+ into a Zarr group format that can be read by the frontend components.
42
+ Each item's view is written to its own subgroup within the main layout group.
43
+
44
+ Args:
45
+ group: Zarr group to write data into
46
+ """
47
+ # Set the view type identifier for the frontend
48
+ group.attrs["view_type"] = "MountainLayout"
49
+
50
+ # Create a list to store metadata for all layout items
51
+ items_metadata = []
52
+
53
+ # Process each mountain layout item
54
+ for i, item in enumerate(self.items):
55
+ # Generate a unique name for this item's subgroup
56
+ item_name = f"mountain_item_{i}"
57
+
58
+ # Store item metadata (label, is_control, etc.) for the frontend
59
+ item_metadata = item.to_dict()
60
+ item_metadata["name"] = item_name
61
+ items_metadata.append(item_metadata)
62
+
63
+ # Create a subgroup for this item's view
64
+ item_group = group.create_group(item_name)
65
+
66
+ # Recursively write the child view to the subgroup
67
+ # This allows any figpack view to be contained within a mountain layout item
68
+ item.view.write_to_zarr_group(item_group)
69
+
70
+ # Store the complete items metadata in the group attributes
71
+ # This will be used by the frontend to render the mountain layout structure
72
+ group.attrs["items"] = items_metadata
@@ -0,0 +1,50 @@
1
+ """
2
+ MountainLayoutItem class for figpack MountainLayout view - represents an item in a mountain layout container
3
+ """
4
+
5
+ from typing import Optional, Union
6
+
7
+ from ..core.figpack_view import FigpackView
8
+
9
+
10
+ class MountainLayoutItem:
11
+ """
12
+ Represents an item in a MountainLayout with label, view, and control properties
13
+ """
14
+
15
+ def __init__(
16
+ self,
17
+ *,
18
+ label: str,
19
+ view: FigpackView,
20
+ is_control: bool = False,
21
+ control_height: Optional[int] = None,
22
+ ):
23
+ """
24
+ Initialize a MountainLayoutItem
25
+
26
+ Args:
27
+ label: The label text to display for this item
28
+ view: The figpack view to be contained in this layout item
29
+ is_control: Whether this item is a control view (shows in bottom-left panel)
30
+ control_height: Height in pixels for control views (optional)
31
+ """
32
+ self.label = label
33
+ self.view = view
34
+ self.is_control = is_control
35
+ self.control_height = control_height
36
+
37
+ def to_dict(self) -> dict:
38
+ """
39
+ Convert the MountainLayoutItem to a dictionary for serialization
40
+
41
+ Returns:
42
+ Dictionary representation of the MountainLayoutItem
43
+ """
44
+ result = {
45
+ "label": self.label,
46
+ "is_control": self.is_control,
47
+ }
48
+ if self.control_height is not None:
49
+ result["control_height"] = self.control_height
50
+ return result
@@ -174,7 +174,7 @@ class MultiChannelTimeseries(FigpackView):
174
174
  else: # len(shape) == 3
175
175
  return (chunk_timepoints, 2, n_channels)
176
176
 
177
- def _write_to_zarr_group(self, group: Group) -> None:
177
+ def write_to_zarr_group(self, group: Group) -> None:
178
178
  """
179
179
  Write the multi-channel timeseries data to a Zarr group
180
180
 
@@ -26,14 +26,14 @@ class PlotlyFigure(figpack.ExtensionView):
26
26
 
27
27
  self.fig = fig
28
28
 
29
- def _write_to_zarr_group(self, group: figpack.Group) -> None:
29
+ def write_to_zarr_group(self, group: figpack.Group) -> None:
30
30
  """
31
31
  Write the plotly figure data to a Zarr group
32
32
 
33
33
  Args:
34
34
  group: Zarr group to write data into
35
35
  """
36
- super()._write_to_zarr_group(group)
36
+ super().write_to_zarr_group(group)
37
37
 
38
38
  # Convert the plotly figure to a dictionary
39
39
  fig_dict = self.fig.to_dict()
@@ -55,15 +55,15 @@ class PlotlyFigure(figpack.ExtensionView):
55
55
  class CustomJSONEncoder(json.JSONEncoder):
56
56
  """Custom JSON encoder that handles numpy arrays and datetime objects"""
57
57
 
58
- def default(self, obj):
59
- if isinstance(obj, np.ndarray):
60
- return obj.tolist()
61
- elif isinstance(obj, (np.integer, np.floating)):
62
- return obj.item()
63
- elif isinstance(obj, (datetime, date)):
64
- return obj.isoformat()
65
- elif isinstance(obj, np.datetime64):
66
- return str(obj)
67
- elif hasattr(obj, "isoformat"): # Handle other datetime-like objects
68
- return obj.isoformat()
69
- return super().default(obj)
58
+ def default(self, o):
59
+ if isinstance(o, np.ndarray):
60
+ return o.tolist()
61
+ elif isinstance(o, (np.integer, np.floating)):
62
+ return o.item()
63
+ elif isinstance(o, (datetime, date)):
64
+ return o.isoformat()
65
+ elif isinstance(o, np.datetime64):
66
+ return str(o)
67
+ elif hasattr(o, "isoformat"): # Handle other datetime-like objects
68
+ return o.isoformat()
69
+ return super().default(o)
@@ -62,12 +62,14 @@ class Spectrogram(FigpackView):
62
62
 
63
63
  # Store frequency information
64
64
  if uniform_specified:
65
+ assert frequency_delta_hz is not None, "Frequency delta must be provided"
65
66
  assert frequency_delta_hz > 0, "Frequency delta must be positive"
66
67
  self.uniform_frequencies = True
67
68
  self.frequency_min_hz = frequency_min_hz
68
69
  self.frequency_delta_hz = frequency_delta_hz
69
70
  self.frequencies = None
70
71
  else:
72
+ assert frequencies is not None, "Frequencies array must be provided"
71
73
  assert (
72
74
  len(frequencies) == data.shape[1]
73
75
  ), f"Number of frequencies ({len(frequencies)}) must match data frequency dimension ({data.shape[1]})"
@@ -186,7 +188,7 @@ class Spectrogram(FigpackView):
186
188
 
187
189
  return (chunk_timepoints, n_frequencies)
188
190
 
189
- def _write_to_zarr_group(self, group: Group) -> None:
191
+ def write_to_zarr_group(self, group: Group) -> None:
190
192
  """
191
193
  Write the spectrogram data to a Zarr group
192
194
 
figpack/views/Splitter.py CHANGED
@@ -36,7 +36,7 @@ class Splitter(FigpackView):
36
36
  self.item2 = item2
37
37
  self.split_pos = max(0.1, min(0.9, split_pos)) # Clamp between 0.1 and 0.9
38
38
 
39
- def _write_to_zarr_group(self, group: Group) -> None:
39
+ def write_to_zarr_group(self, group: Group) -> None:
40
40
  """
41
41
  Write the Splitter layout data to a Zarr group
42
42
 
@@ -61,7 +61,7 @@ class Splitter(FigpackView):
61
61
 
62
62
  # Create subgroups for each item's view
63
63
  item1_group = group.create_group("item1")
64
- self.item1.view._write_to_zarr_group(item1_group)
64
+ self.item1.view.write_to_zarr_group(item1_group)
65
65
 
66
66
  item2_group = group.create_group("item2")
67
- self.item2.view._write_to_zarr_group(item2_group)
67
+ self.item2.view.write_to_zarr_group(item2_group)
@@ -32,7 +32,7 @@ class TabLayout(FigpackView):
32
32
  max(0, min(initial_tab_index, len(items) - 1)) if items else 0
33
33
  )
34
34
 
35
- def _write_to_zarr_group(self, group: Group) -> None:
35
+ def write_to_zarr_group(self, group: Group) -> None:
36
36
  """
37
37
  Write the TabLayout data to a Zarr group
38
38
 
@@ -61,7 +61,7 @@ class TabLayout(FigpackView):
61
61
  item_group = group.create_group(item_name)
62
62
 
63
63
  # Recursively write the child view to the subgroup
64
- item.view._write_to_zarr_group(item_group)
64
+ item.view.write_to_zarr_group(item_group)
65
65
 
66
66
  # Store the items metadata
67
67
  group.attrs["items"] = items_metadata