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.
- figpack/__init__.py +1 -1
- figpack/cli.py +288 -2
- figpack/core/_bundle_utils.py +40 -7
- figpack/core/_file_handler.py +195 -0
- figpack/core/_save_figure.py +12 -8
- figpack/core/_server_manager.py +146 -7
- figpack/core/_show_view.py +2 -2
- figpack/core/_upload_bundle.py +63 -53
- figpack/core/_view_figure.py +48 -12
- figpack/core/_zarr_consolidate.py +185 -0
- figpack/core/extension_view.py +9 -5
- figpack/core/figpack_extension.py +1 -1
- figpack/core/figpack_view.py +52 -21
- figpack/core/zarr.py +2 -2
- figpack/extensions.py +356 -0
- figpack/figpack-figure-dist/assets/index-ST_DU17U.js +95 -0
- figpack/figpack-figure-dist/assets/index-V5m_wCvw.css +1 -0
- figpack/figpack-figure-dist/index.html +6 -2
- figpack/views/Box.py +4 -4
- figpack/views/CaptionedView.py +64 -0
- figpack/views/DataFrame.py +1 -1
- figpack/views/Gallery.py +2 -2
- figpack/views/Iframe.py +43 -0
- figpack/views/Image.py +2 -3
- figpack/views/Markdown.py +8 -4
- figpack/views/MatplotlibFigure.py +1 -1
- figpack/views/MountainLayout.py +72 -0
- figpack/views/MountainLayoutItem.py +50 -0
- figpack/views/MultiChannelTimeseries.py +1 -1
- figpack/views/PlotlyExtension/PlotlyExtension.py +14 -14
- figpack/views/Spectrogram.py +3 -1
- figpack/views/Splitter.py +3 -3
- figpack/views/TabLayout.py +2 -2
- figpack/views/TimeseriesGraph.py +113 -20
- figpack/views/__init__.py +4 -0
- {figpack-0.2.17.dist-info → figpack-0.2.40.dist-info}/METADATA +25 -1
- figpack-0.2.40.dist-info/RECORD +50 -0
- figpack/figpack-figure-dist/assets/index-DBwmtEpB.js +0 -91
- figpack/figpack-figure-dist/assets/index-DHWczh-Q.css +0 -1
- figpack-0.2.17.dist-info/RECORD +0 -43
- {figpack-0.2.17.dist-info → figpack-0.2.40.dist-info}/WHEEL +0 -0
- {figpack-0.2.17.dist-info → figpack-0.2.40.dist-info}/entry_points.txt +0 -0
- {figpack-0.2.17.dist-info → figpack-0.2.40.dist-info}/licenses/LICENSE +0 -0
- {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
|
|
9
|
-
|
|
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
|
|
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
|
|
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.
|
|
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)
|
figpack/views/DataFrame.py
CHANGED
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
|
|
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.
|
|
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
|
figpack/views/Iframe.py
ADDED
|
@@ -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
|
|
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
|
|
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
|
|
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)
|
|
@@ -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
|
|
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
|
|
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().
|
|
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,
|
|
59
|
-
if isinstance(
|
|
60
|
-
return
|
|
61
|
-
elif isinstance(
|
|
62
|
-
return
|
|
63
|
-
elif isinstance(
|
|
64
|
-
return
|
|
65
|
-
elif isinstance(
|
|
66
|
-
return str(
|
|
67
|
-
elif hasattr(
|
|
68
|
-
return
|
|
69
|
-
return super().default(
|
|
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)
|
figpack/views/Spectrogram.py
CHANGED
|
@@ -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
|
|
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
|
|
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.
|
|
64
|
+
self.item1.view.write_to_zarr_group(item1_group)
|
|
65
65
|
|
|
66
66
|
item2_group = group.create_group("item2")
|
|
67
|
-
self.item2.view.
|
|
67
|
+
self.item2.view.write_to_zarr_group(item2_group)
|
figpack/views/TabLayout.py
CHANGED
|
@@ -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
|
|
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.
|
|
64
|
+
item.view.write_to_zarr_group(item_group)
|
|
65
65
|
|
|
66
66
|
# Store the items metadata
|
|
67
67
|
group.attrs["items"] = items_metadata
|