figpack 0.2.15__py3-none-any.whl → 0.2.17__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 (53) hide show
  1. figpack/__init__.py +4 -3
  2. figpack/core/__init__.py +2 -2
  3. figpack/core/_bundle_utils.py +80 -33
  4. figpack/core/_view_figure.py +1 -1
  5. figpack/core/extension_view.py +9 -27
  6. figpack/core/figpack_extension.py +0 -71
  7. figpack/core/figpack_view.py +2 -2
  8. figpack/core/zarr.py +61 -0
  9. figpack/figpack-figure-dist/assets/index-DBwmtEpB.js +91 -0
  10. figpack/figpack-figure-dist/assets/{index-D9a3K6eW.css → index-DHWczh-Q.css} +1 -1
  11. figpack/figpack-figure-dist/index.html +2 -2
  12. figpack/views/Box.py +2 -3
  13. figpack/views/DataFrame.py +6 -12
  14. figpack/views/Gallery.py +2 -3
  15. figpack/views/Image.py +3 -9
  16. figpack/views/Markdown.py +3 -4
  17. figpack/views/MatplotlibFigure.py +2 -11
  18. figpack/views/MultiChannelTimeseries.py +2 -6
  19. figpack/views/PlotlyExtension/PlotlyExtension.py +8 -60
  20. figpack/views/PlotlyExtension/_plotly_extension.py +46 -0
  21. figpack/views/PlotlyExtension/plotly_view.js +84 -80
  22. figpack/views/Spectrogram.py +2 -7
  23. figpack/views/Splitter.py +2 -3
  24. figpack/views/TabLayout.py +2 -3
  25. figpack/views/TimeseriesGraph.py +6 -10
  26. figpack/views/__init__.py +1 -0
  27. {figpack-0.2.15.dist-info → figpack-0.2.17.dist-info}/METADATA +21 -2
  28. figpack-0.2.17.dist-info/RECORD +43 -0
  29. figpack/figpack-figure-dist/assets/index-DtOnN02w.js +0 -846
  30. figpack/franklab/__init__.py +0 -5
  31. figpack/franklab/views/TrackAnimation.py +0 -153
  32. figpack/franklab/views/__init__.py +0 -9
  33. figpack/spike_sorting/__init__.py +0 -5
  34. figpack/spike_sorting/views/AutocorrelogramItem.py +0 -32
  35. figpack/spike_sorting/views/Autocorrelograms.py +0 -118
  36. figpack/spike_sorting/views/AverageWaveforms.py +0 -147
  37. figpack/spike_sorting/views/CrossCorrelogramItem.py +0 -35
  38. figpack/spike_sorting/views/CrossCorrelograms.py +0 -132
  39. figpack/spike_sorting/views/RasterPlot.py +0 -288
  40. figpack/spike_sorting/views/RasterPlotItem.py +0 -28
  41. figpack/spike_sorting/views/SpikeAmplitudes.py +0 -374
  42. figpack/spike_sorting/views/SpikeAmplitudesItem.py +0 -38
  43. figpack/spike_sorting/views/UnitMetricsGraph.py +0 -129
  44. figpack/spike_sorting/views/UnitSimilarityScore.py +0 -40
  45. figpack/spike_sorting/views/UnitsTable.py +0 -89
  46. figpack/spike_sorting/views/UnitsTableColumn.py +0 -40
  47. figpack/spike_sorting/views/UnitsTableRow.py +0 -36
  48. figpack/spike_sorting/views/__init__.py +0 -41
  49. figpack-0.2.15.dist-info/RECORD +0 -60
  50. {figpack-0.2.15.dist-info → figpack-0.2.17.dist-info}/WHEEL +0 -0
  51. {figpack-0.2.15.dist-info → figpack-0.2.17.dist-info}/entry_points.txt +0 -0
  52. {figpack-0.2.15.dist-info → figpack-0.2.17.dist-info}/licenses/LICENSE +0 -0
  53. {figpack-0.2.15.dist-info → figpack-0.2.17.dist-info}/top_level.txt +0 -0
@@ -1 +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;bottom:0;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}@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}}.VerticalToolbar{overflow:hidden;position:absolute}.HorizontalToolbar{display:flex;justify-content:center;align-items:center;overflow:hidden;margin:auto}.SortableTableWidget .MuiTableCell-root{padding:1px 0;font-size:12px;border:solid 1px #f0f0f0}.SortableTableWidget .MuiTableRow-root.selectedRow{background-color:#b5d1ff}.SortableTableWidget .MuiTableRow-root.currentRow{background-color:#95b1df}.SortCaretSpan{font-size:16px;color:gray;padding-left:3px;padding-right:5px;padding-top:2px}.SortableTableWidget .unitLabel{width:10px;height:10px;position:relative;display:inline-block}.SortableTableWidget .SortableTableCheckbox{padding:1px}.NiceTable .MuiTableCell-root{padding:5px}.NiceTable .MuiIconButton-root{padding:1px}
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;bottom:0;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}@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}}
@@ -5,8 +5,8 @@
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-DtOnN02w.js"></script>
9
- <link rel="stylesheet" crossorigin href="./assets/index-D9a3K6eW.css">
8
+ <script type="module" crossorigin src="./assets/index-DBwmtEpB.js"></script>
9
+ <link rel="stylesheet" crossorigin href="./assets/index-DHWczh-Q.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
figpack/views/Box.py CHANGED
@@ -4,9 +4,8 @@ Box view for figpack - a layout container that handles other views
4
4
 
5
5
  from typing import Any, Dict, List, Literal, Optional
6
6
 
7
- import zarr
8
-
9
7
  from ..core.figpack_view import FigpackView
8
+ from ..core.zarr import Group
10
9
  from .LayoutItem import LayoutItem
11
10
 
12
11
 
@@ -43,7 +42,7 @@ class Box(FigpackView):
43
42
  self.items = items
44
43
  self.title = title
45
44
 
46
- def _write_to_zarr_group(self, group: zarr.Group) -> None:
45
+ def _write_to_zarr_group(self, group: Group) -> None:
47
46
  """
48
47
  Write the Box layout data to a Zarr group
49
48
 
@@ -3,13 +3,12 @@ DataFrame view for figpack - displays pandas DataFrames as interactive tables
3
3
  """
4
4
 
5
5
  import json
6
- from typing import Any, Dict, Union
7
6
 
8
7
  import numpy as np
9
- import pandas as pd
10
8
  import zarr
11
9
 
12
10
  from ..core.figpack_view import FigpackView
11
+ from ..core.zarr import Group
13
12
 
14
13
 
15
14
  class DataFrame(FigpackView):
@@ -17,7 +16,7 @@ class DataFrame(FigpackView):
17
16
  A DataFrame visualization component for displaying pandas DataFrames as interactive tables
18
17
  """
19
18
 
20
- def __init__(self, df: pd.DataFrame):
19
+ def __init__(self, df):
21
20
  """
22
21
  Initialize a DataFrame view
23
22
 
@@ -27,12 +26,14 @@ class DataFrame(FigpackView):
27
26
  Raises:
28
27
  ValueError: If df is not a pandas DataFrame
29
28
  """
29
+ import pandas as pd
30
+
30
31
  if not isinstance(df, pd.DataFrame):
31
32
  raise ValueError("df must be a pandas DataFrame")
32
33
 
33
34
  self.df = df
34
35
 
35
- def _write_to_zarr_group(self, group: zarr.Group) -> None:
36
+ def _write_to_zarr_group(self, group: Group) -> None:
36
37
  """
37
38
  Write the DataFrame data to a Zarr group
38
39
 
@@ -54,11 +55,6 @@ class DataFrame(FigpackView):
54
55
  group.create_dataset(
55
56
  "csv_data",
56
57
  data=csv_array,
57
- dtype=np.uint8,
58
- chunks=True,
59
- compressor=zarr.Blosc(
60
- cname="zstd", clevel=3, shuffle=zarr.Blosc.SHUFFLE
61
- ),
62
58
  )
63
59
 
64
60
  # Store metadata about the DataFrame
@@ -104,6 +100,4 @@ class DataFrame(FigpackView):
104
100
  group.attrs["data_size"] = 0
105
101
  group.attrs["column_info"] = "[]"
106
102
  # Create empty array as placeholder
107
- group.create_dataset(
108
- "csv_data", data=np.array([], dtype=np.uint8), dtype=np.uint8
109
- )
103
+ group.create_dataset("csv_data", data=np.array([], dtype=np.uint8))
figpack/views/Gallery.py CHANGED
@@ -4,9 +4,8 @@ Gallery view for figpack - a gallery layout container that handles other views w
4
4
 
5
5
  from typing import Any, Dict, List, Optional
6
6
 
7
- import zarr
8
-
9
7
  from ..core.figpack_view import FigpackView
8
+ from ..core.zarr import Group
10
9
  from .GalleryItem import GalleryItem
11
10
 
12
11
 
@@ -45,7 +44,7 @@ class Gallery(FigpackView):
45
44
  max(0, min(initial_item_index, len(items) - 1)) if items else 0
46
45
  )
47
46
 
48
- def _write_to_zarr_group(self, group: zarr.Group) -> None:
47
+ def _write_to_zarr_group(self, group: Group) -> None:
49
48
  """
50
49
  Write the Gallery data to a Zarr group
51
50
 
figpack/views/Image.py CHANGED
@@ -8,6 +8,7 @@ import numpy as np
8
8
  import zarr
9
9
 
10
10
  from ..core.figpack_view import FigpackView
11
+ from ..core.zarr import Group
11
12
 
12
13
 
13
14
  class Image(FigpackView):
@@ -69,7 +70,7 @@ class Image(FigpackView):
69
70
  except Exception as e:
70
71
  raise ValueError(f"Failed to download image from URL: {str(e)}")
71
72
 
72
- def _write_to_zarr_group(self, group: zarr.Group) -> None:
73
+ def _write_to_zarr_group(self, group: Group) -> None:
73
74
  """
74
75
  Write the image data to a Zarr group
75
76
 
@@ -98,11 +99,6 @@ class Image(FigpackView):
98
99
  group.create_dataset(
99
100
  "image_data",
100
101
  data=image_array,
101
- dtype=np.uint8,
102
- chunks=True,
103
- compressor=zarr.Blosc(
104
- cname="zstd", clevel=3, shuffle=zarr.Blosc.SHUFFLE
105
- ),
106
102
  )
107
103
 
108
104
  # Try to determine format from file signature
@@ -124,6 +120,4 @@ class Image(FigpackView):
124
120
  group.attrs["image_format"] = "Unknown"
125
121
  group.attrs["data_size"] = 0
126
122
  # Create empty array as placeholder
127
- group.create_dataset(
128
- "image_data", data=np.array([], dtype=np.uint8), dtype=np.uint8
129
- )
123
+ group.create_dataset("image_data", data=np.array([], dtype=np.uint8))
figpack/views/Markdown.py CHANGED
@@ -6,6 +6,7 @@ import numpy as np
6
6
  import zarr
7
7
 
8
8
  from ..core.figpack_view import FigpackView
9
+ from ..core.zarr import Group
9
10
 
10
11
 
11
12
  class Markdown(FigpackView):
@@ -22,7 +23,7 @@ class Markdown(FigpackView):
22
23
  """
23
24
  self.content = content
24
25
 
25
- def _write_to_zarr_group(self, group: zarr.Group) -> None:
26
+ def _write_to_zarr_group(self, group: Group) -> None:
26
27
  """
27
28
  Write the markdown data to a Zarr group
28
29
 
@@ -37,9 +38,7 @@ class Markdown(FigpackView):
37
38
  content_array = np.frombuffer(content_bytes, dtype=np.uint8)
38
39
 
39
40
  # Store the markdown content as a zarr array
40
- group.create_dataset(
41
- "content_data", data=content_array, dtype=np.uint8, chunks=True
42
- )
41
+ group.create_dataset("content_data", data=content_array, chunks=True)
43
42
 
44
43
  # Store content size in attrs
45
44
  group.attrs["data_size"] = len(content_bytes)
@@ -9,6 +9,7 @@ from typing import Any, Union
9
9
  import zarr
10
10
 
11
11
  from ..core.figpack_view import FigpackView
12
+ from ..core.zarr import Group
12
13
 
13
14
 
14
15
  class MatplotlibFigure(FigpackView):
@@ -25,7 +26,7 @@ class MatplotlibFigure(FigpackView):
25
26
  """
26
27
  self.fig = fig
27
28
 
28
- def _write_to_zarr_group(self, group: zarr.Group) -> None:
29
+ def _write_to_zarr_group(self, group: Group) -> None:
29
30
  """
30
31
  Write the matplotlib figure data to a Zarr group
31
32
 
@@ -54,11 +55,6 @@ class MatplotlibFigure(FigpackView):
54
55
  group.create_dataset(
55
56
  "svg_data",
56
57
  data=svg_array,
57
- dtype=np.uint8,
58
- chunks=True,
59
- compressor=zarr.Blosc(
60
- cname="zstd", clevel=3, shuffle=zarr.Blosc.SHUFFLE
61
- ),
62
58
  )
63
59
 
64
60
  # Store figure dimensions for reference
@@ -77,11 +73,6 @@ class MatplotlibFigure(FigpackView):
77
73
  group.create_dataset(
78
74
  "svg_data",
79
75
  data=np.array([], dtype=np.uint8),
80
- dtype=np.uint8,
81
- chunks=True,
82
- compressor=zarr.Blosc(
83
- cname="zstd", clevel=3, shuffle=zarr.Blosc.SHUFFLE
84
- ),
85
76
  )
86
77
  group.attrs["error"] = f"Failed to export matplotlib figure: {str(e)}"
87
78
  group.attrs["figure_width_inches"] = 6.0
@@ -6,9 +6,9 @@ import math
6
6
  from typing import List, Optional, Union
7
7
 
8
8
  import numpy as np
9
- import zarr
10
9
 
11
10
  from ..core.figpack_view import FigpackView
11
+ from ..core.zarr import Group
12
12
 
13
13
 
14
14
  class MultiChannelTimeseries(FigpackView):
@@ -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: zarr.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
 
@@ -198,8 +198,6 @@ class MultiChannelTimeseries(FigpackView):
198
198
  "data",
199
199
  data=self.data,
200
200
  chunks=original_chunks,
201
- compression="blosc",
202
- compression_opts={"cname": "lz4", "clevel": 5, "shuffle": 1},
203
201
  )
204
202
 
205
203
  # Store downsampled data arrays
@@ -216,8 +214,6 @@ class MultiChannelTimeseries(FigpackView):
216
214
  dataset_name,
217
215
  data=downsampled_array,
218
216
  chunks=ds_chunks,
219
- compression="blosc",
220
- compression_opts={"cname": "lz4", "clevel": 5, "shuffle": 1},
221
217
  )
222
218
 
223
219
  print(
@@ -1,58 +1,11 @@
1
1
  import json
2
2
  import numpy as np
3
- import zarr
4
- import urllib.request
5
- import urllib.error
6
3
  from datetime import date, datetime
7
4
 
8
- from figpack import FigpackExtension, ExtensionRegistry, ExtensionView
5
+ import figpack
9
6
 
10
7
 
11
- def _download_plotly_library():
12
- url = "https://cdn.plot.ly/plotly-2.35.2.min.js"
13
- try:
14
- with urllib.request.urlopen(url) as response:
15
- return response.read().decode("utf-8")
16
- except urllib.error.URLError as e:
17
- raise RuntimeError(f"Failed to download plotly library from {url}: {e}")
18
-
19
-
20
- def _load_javascript_code():
21
- """Load the JavaScript code from the plotly.js file"""
22
- import os
23
-
24
- js_path = os.path.join(os.path.dirname(__file__), "plotly_view.js")
25
- try:
26
- with open(js_path, "r", encoding="utf-8") as f:
27
- return f.read()
28
- except FileNotFoundError:
29
- raise FileNotFoundError(
30
- f"Could not find plotly.js at {js_path}. "
31
- "Make sure the JavaScript file is present in the package."
32
- )
33
-
34
-
35
- # Download the plotly library and create the extension with additional files
36
- try:
37
- plotly_lib_js = _download_plotly_library()
38
- additional_files = {"plotly.min.js": plotly_lib_js}
39
- except Exception as e:
40
- print(f"Warning: Could not download plotly library: {e}")
41
- print("Extension will fall back to CDN loading")
42
- additional_files = {}
43
-
44
- # Create and register the plotly extension
45
- _plotly_extension = FigpackExtension(
46
- name="figpack_plotly",
47
- javascript_code=_load_javascript_code(),
48
- additional_files=additional_files,
49
- version="1.0.0",
50
- )
51
-
52
- ExtensionRegistry.register(_plotly_extension)
53
-
54
-
55
- class PlotlyFigure(ExtensionView):
8
+ class PlotlyFigure(figpack.ExtensionView):
56
9
  """
57
10
  A Plotly graph visualization view using the plotly library.
58
11
 
@@ -66,13 +19,14 @@ class PlotlyFigure(ExtensionView):
66
19
  Args:
67
20
  fig: The plotly figure object
68
21
  """
69
- # for some reason, we need to reregister here to avoid issues with pytest
70
- ExtensionRegistry.register(_plotly_extension)
71
- super().__init__(extension_name="figpack_plotly")
22
+ # It's important that we only import conditionally, so we are not always downloading plotly
23
+ from ._plotly_extension import _plotly_extension
24
+
25
+ super().__init__(extension=_plotly_extension, view_type="plotly.PlotlyFigure")
72
26
 
73
27
  self.fig = fig
74
28
 
75
- def _write_to_zarr_group(self, group: zarr.Group) -> None:
29
+ def _write_to_zarr_group(self, group: figpack.Group) -> None:
76
30
  """
77
31
  Write the plotly figure data to a Zarr group
78
32
 
@@ -92,13 +46,7 @@ class PlotlyFigure(ExtensionView):
92
46
  json_array = np.frombuffer(json_bytes, dtype=np.uint8)
93
47
 
94
48
  # Store the figure data as compressed array
95
- group.create_dataset(
96
- "figure_data",
97
- data=json_array,
98
- dtype=np.uint8,
99
- chunks=True,
100
- compressor=zarr.Blosc(cname="zstd", clevel=3, shuffle=zarr.Blosc.SHUFFLE),
101
- )
49
+ group.create_dataset("figure_data", data=json_array)
102
50
 
103
51
  # Store data size for reference
104
52
  group.attrs["data_size"] = len(json_bytes)
@@ -0,0 +1,46 @@
1
+ import urllib.request
2
+ import urllib.error
3
+
4
+ from ...core.figpack_extension import FigpackExtension
5
+
6
+
7
+ def _load_javascript_code():
8
+ """Load the JavaScript code from the plotly.js file"""
9
+ import os
10
+
11
+ js_path = os.path.join(os.path.dirname(__file__), "plotly_view.js")
12
+ try:
13
+ with open(js_path, "r", encoding="utf-8") as f:
14
+ return f.read()
15
+ except FileNotFoundError:
16
+ raise FileNotFoundError(
17
+ f"Could not find plotly.js at {js_path}. "
18
+ "Make sure the JavaScript file is present in the package."
19
+ )
20
+
21
+
22
+ def _download_plotly_library():
23
+ url = "https://cdn.plot.ly/plotly-2.35.2.min.js"
24
+ try:
25
+ with urllib.request.urlopen(url) as response:
26
+ return response.read().decode("utf-8")
27
+ except urllib.error.URLError as e:
28
+ raise RuntimeError(f"Failed to download plotly library from {url}: {e}")
29
+
30
+
31
+ # Download the plotly library and create the extension with additional files
32
+ try:
33
+ plotly_lib_js = _download_plotly_library()
34
+ additional_files = {"plotly.min.js": plotly_lib_js}
35
+ except Exception as e:
36
+ print(f"Warning: Could not download plotly library: {e}")
37
+ print("Extension will fall back to CDN loading")
38
+ additional_files = {}
39
+
40
+ # Create and register the plotly extension
41
+ _plotly_extension = FigpackExtension(
42
+ name="figpack-plotly",
43
+ javascript_code=_load_javascript_code(),
44
+ additional_files=additional_files,
45
+ version="1.0.0",
46
+ )
@@ -5,8 +5,8 @@
5
5
 
6
6
  const loadFigureData = async (zarrGroup) => {
7
7
  // Get the figure data from the zarr array
8
- const data = await zarrGroup.file.getDatasetData(
9
- joinPath(zarrGroup.path, "figure_data"),
8
+ const data = await zarrGroup.getDatasetData(
9
+ "figure_data",
10
10
  {},
11
11
  );
12
12
  if (!data || data.length === 0) {
@@ -24,83 +24,87 @@ const loadFigureData = async (zarrGroup) => {
24
24
  return parsedData;
25
25
  };
26
26
 
27
- (function() {
28
- window.figpackExtensions = window.figpackExtensions || {};
29
-
30
- window.figpackExtensions['figpack_plotly'] = {
31
- render: async function(container, zarrGroup, width, height, onResize) {
32
- container.innerHTML = '';
33
-
34
- try {
35
- const figureData = await loadFigureData(zarrGroup);
36
-
37
- const makePlot = () => {
38
- window.Plotly.newPlot(
39
- container,
40
- figureData.data || [],
41
- {
42
- ...figureData.layout,
43
- width: width,
44
- height: height,
45
- margin: { l: 50, r: 50, t: 50, b: 50 },
46
- },
47
- {
48
- responsive: true,
49
- displayModeBar: true,
50
- displaylogo: false,
51
- },
52
- )
53
- };
54
-
55
- makePlot();
56
-
57
- // Handle resize events
58
- onResize((newWidth, newHeight) => {
59
- window.Plotly.relayout(container, {width: newWidth, height: newHeight});
60
- });
61
-
62
- return {
63
- destroy: () => {
64
- window.Plotly.purge(container);
65
- }
66
- };
67
-
68
- } catch (error) {
69
- console.error('Error rendering plotly figure:', error);
70
- this.renderError(container, width, height, error.message);
71
- return { destroy: () => {} };
27
+ const renderPlotlyFigure = async (params) => {
28
+ const { container, zarrGroup, width, height, onResize } = params;
29
+ container.innerHTML = "";
30
+
31
+ try {
32
+ const figureData = await loadFigureData(zarrGroup);
33
+
34
+ const makePlot = () => {
35
+ window.Plotly.newPlot(
36
+ container,
37
+ figureData.data || [],
38
+ {
39
+ ...figureData.layout,
40
+ width: width,
41
+ height: height,
42
+ margin: { l: 50, r: 50, t: 50, b: 50 },
43
+ },
44
+ {
45
+ responsive: true,
46
+ displayModeBar: true,
47
+ displaylogo: false,
48
+ },
49
+ );
50
+ };
51
+
52
+ makePlot();
53
+
54
+ // Handle resize events
55
+ onResize((newWidth, newHeight) => {
56
+ window.Plotly.relayout(container, { width: newWidth, height: newHeight });
57
+ });
58
+
59
+ return {
60
+ destroy: () => {
61
+ window.Plotly.purge(container);
72
62
  }
73
- },
74
-
75
- renderError: function(container, width, height, message) {
76
- container.innerHTML = `
77
- <div style="
78
- width: ${width}px;
79
- height: ${height}px;
80
- display: flex;
81
- align-items: center;
82
- justify-content: center;
83
- background-color: #f8f9fa;
84
- border: 1px solid #dee2e6;
85
- color: #6c757d;
86
- font-family: system-ui, -apple-system, sans-serif;
87
- font-size: 14px;
88
- text-align: center;
89
- padding: 20px;
90
- box-sizing: border-box;
91
- ">
92
- <div>
93
- <div style="margin-bottom: 10px; font-weight: 500;">Force Graph Error</div>
94
- <div style="font-size: 12px;">${message}</div>
95
- </div>
96
- </div>
97
- `;
98
- }
99
- };
100
- })();
101
-
102
- const joinPath = function(p1, p2) {
103
- if (p1.endsWith('/')) p1 = p1.slice(0, -1);
104
- if (p2.startsWith('/')) p2 = p2.slice(1);
105
- return p1 + '/' + p2;
63
+ };
64
+
65
+ } catch (error) {
66
+ console.error('Error rendering plotly figure:', error);
67
+ renderError(container, width, height, error.message);
68
+ return { destroy: () => {} };
69
+ }
106
70
  };
71
+
72
+ const renderError = (container, width, height, message) => {
73
+ container.innerHTML = `
74
+ <div style="
75
+ width: ${width}px;
76
+ height: ${height}px;
77
+ display: flex;
78
+ align-items: center;
79
+ justify-content: center;
80
+ background-color: #f8f9fa;
81
+ border: 1px solid #dee2e6;
82
+ color: #6c757d;
83
+ font-family: system-ui, -apple-system, sans-serif;
84
+ font-size: 14px;
85
+ text-align: center;
86
+ padding: 20px;
87
+ box-sizing: border-box;
88
+ ">
89
+ <div>
90
+ <div style="margin-bottom: 10px; font-weight: 500;">Plotly Figure Error</div>
91
+ <div style="font-size: 12px;">${message}</div>
92
+ </div>
93
+ </div>
94
+ `;
95
+ };
96
+
97
+ const registerExtension = () => {
98
+ const registerFPViewComponent = window.figpack_p1.registerFPViewComponent;
99
+ registerFPViewComponent({
100
+ name: "plotly.PlotlyFigure",
101
+ render: renderPlotlyFigure,
102
+ });
103
+
104
+ // const registerFPViewContextCreator = window.figpack_p1.registerFPViewContextCreator;
105
+
106
+ const registerFPExtension = window.figpack_p1.registerFPExtension;
107
+ registerFPExtension({ name: "figpack-plotly" });
108
+ };
109
+
110
+ registerExtension();
@@ -9,6 +9,7 @@ import numpy as np
9
9
  import zarr
10
10
 
11
11
  from ..core.figpack_view import FigpackView
12
+ from ..core.zarr import Group
12
13
 
13
14
 
14
15
  class Spectrogram(FigpackView):
@@ -185,7 +186,7 @@ class Spectrogram(FigpackView):
185
186
 
186
187
  return (chunk_timepoints, n_frequencies)
187
188
 
188
- def _write_to_zarr_group(self, group: zarr.Group) -> None:
189
+ def _write_to_zarr_group(self, group: Group) -> None:
189
190
  """
190
191
  Write the spectrogram data to a Zarr group
191
192
 
@@ -212,8 +213,6 @@ class Spectrogram(FigpackView):
212
213
  group.create_dataset(
213
214
  "frequencies",
214
215
  data=self.frequencies,
215
- compression="blosc",
216
- compression_opts={"cname": "lz4", "clevel": 5, "shuffle": 1},
217
216
  )
218
217
 
219
218
  # Store original data with optimal chunking
@@ -222,8 +221,6 @@ class Spectrogram(FigpackView):
222
221
  "data",
223
222
  data=self.data,
224
223
  chunks=original_chunks,
225
- compression="blosc",
226
- compression_opts={"cname": "lz4", "clevel": 5, "shuffle": 1},
227
224
  )
228
225
 
229
226
  # Store downsampled data arrays
@@ -240,8 +237,6 @@ class Spectrogram(FigpackView):
240
237
  dataset_name,
241
238
  data=downsampled_array,
242
239
  chunks=ds_chunks,
243
- compression="blosc",
244
- compression_opts={"cname": "lz4", "clevel": 5, "shuffle": 1},
245
240
  )
246
241
 
247
242
  print(f"Stored Spectrogram with {len(downsample_factors)} downsampled levels:")
figpack/views/Splitter.py CHANGED
@@ -4,9 +4,8 @@ Splitter view for figpack - a resizable split layout container
4
4
 
5
5
  from typing import Literal
6
6
 
7
- import zarr
8
-
9
7
  from ..core.figpack_view import FigpackView
8
+ from ..core.zarr import Group
10
9
  from .LayoutItem import LayoutItem
11
10
 
12
11
 
@@ -37,7 +36,7 @@ class Splitter(FigpackView):
37
36
  self.item2 = item2
38
37
  self.split_pos = max(0.1, min(0.9, split_pos)) # Clamp between 0.1 and 0.9
39
38
 
40
- def _write_to_zarr_group(self, group: zarr.Group) -> None:
39
+ def _write_to_zarr_group(self, group: Group) -> None:
41
40
  """
42
41
  Write the Splitter layout data to a Zarr group
43
42