figpack 0.1.1__py3-none-any.whl → 0.1.3__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.

Potentially problematic release.


This version of figpack might be problematic. Click here for more details.

Files changed (37) hide show
  1. figpack/__init__.py +3 -1
  2. figpack/cli.py +312 -0
  3. figpack/core/_bundle_utils.py +11 -1
  4. figpack/core/_show_view.py +34 -8
  5. figpack/core/{_upload_view.py → _upload_bundle.py} +140 -49
  6. figpack/core/figpack_view.py +32 -24
  7. figpack/figpack-gui-dist/assets/index-BDa2iJW9.css +1 -0
  8. figpack/figpack-gui-dist/assets/index-ByLxmrzp.js +846 -0
  9. figpack/figpack-gui-dist/assets/neurosift-logo-CLsuwLMO.png +0 -0
  10. figpack/figpack-gui-dist/index.html +4 -4
  11. figpack/spike_sorting/__init__.py +5 -0
  12. figpack/spike_sorting/views/AutocorrelogramItem.py +41 -0
  13. figpack/spike_sorting/views/Autocorrelograms.py +76 -0
  14. figpack/spike_sorting/views/CrossCorrelogramItem.py +45 -0
  15. figpack/spike_sorting/views/CrossCorrelograms.py +82 -0
  16. figpack/spike_sorting/views/UnitSimilarityScore.py +40 -0
  17. figpack/spike_sorting/views/UnitsTable.py +68 -0
  18. figpack/spike_sorting/views/UnitsTableColumn.py +40 -0
  19. figpack/spike_sorting/views/UnitsTableRow.py +36 -0
  20. figpack/spike_sorting/views/__init__.py +23 -0
  21. figpack/views/Image.py +82 -0
  22. figpack/views/Markdown.py +34 -0
  23. figpack/views/MatplotlibFigure.py +65 -0
  24. figpack/views/PlotlyFigure.py +58 -0
  25. figpack/views/__init__.py +4 -0
  26. figpack-0.1.3.dist-info/METADATA +126 -0
  27. figpack-0.1.3.dist-info/RECORD +38 -0
  28. figpack-0.1.3.dist-info/entry_points.txt +2 -0
  29. figpack-0.1.3.dist-info/top_level.txt +1 -0
  30. figpack/figpack-gui-dist/assets/index-BW-ONVCL.js +0 -65
  31. figpack/figpack-gui-dist/assets/index-CeWL3OeJ.css +0 -1
  32. figpack-0.1.1.dist-info/METADATA +0 -33
  33. figpack-0.1.1.dist-info/RECORD +0 -22
  34. figpack-0.1.1.dist-info/top_level.txt +0 -2
  35. figpack-gui/node_modules/flatted/python/flatted.py +0 -149
  36. {figpack-0.1.1.dist-info → figpack-0.1.3.dist-info}/WHEEL +0 -0
  37. {figpack-0.1.1.dist-info → figpack-0.1.3.dist-info}/licenses/LICENSE +0 -0
@@ -2,11 +2,11 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
- <link rel="icon" type="image/svg+xml" href="/vite.svg" />
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
- <title>Vite + React + TS</title>
8
- <script type="module" crossorigin src="./assets/index-BW-ONVCL.js"></script>
9
- <link rel="stylesheet" crossorigin href="./assets/index-CeWL3OeJ.css">
7
+ <title>figpack figure</title>
8
+ <script type="module" crossorigin src="./assets/index-ByLxmrzp.js"></script>
9
+ <link rel="stylesheet" crossorigin href="./assets/index-BDa2iJW9.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
@@ -0,0 +1,5 @@
1
+ """
2
+ Spike sorting views for figpack
3
+ """
4
+
5
+ from . import views
@@ -0,0 +1,41 @@
1
+ """
2
+ AutocorrelogramItem for spike sorting views
3
+ """
4
+
5
+ from typing import Union
6
+ import numpy as np
7
+
8
+
9
+ class AutocorrelogramItem:
10
+ """
11
+ Represents a single autocorrelogram for a unit
12
+ """
13
+
14
+ def __init__(
15
+ self,
16
+ *,
17
+ unit_id: Union[str, int],
18
+ bin_edges_sec: np.ndarray,
19
+ bin_counts: np.ndarray,
20
+ ):
21
+ """
22
+ Initialize an AutocorrelogramItem
23
+
24
+ Args:
25
+ unit_id: Identifier for the unit
26
+ bin_edges_sec: Array of bin edges in seconds
27
+ bin_counts: Array of bin counts
28
+ """
29
+ self.unit_id = unit_id
30
+ self.bin_edges_sec = np.array(bin_edges_sec, dtype=np.float32)
31
+ self.bin_counts = np.array(bin_counts, dtype=np.int32)
32
+
33
+ def to_dict(self):
34
+ """
35
+ Convert the autocorrelogram item to a dictionary representation
36
+ """
37
+ return {
38
+ "unit_id": str(self.unit_id),
39
+ "bin_edges_sec": self.bin_edges_sec.tolist(),
40
+ "bin_counts": self.bin_counts.tolist(),
41
+ }
@@ -0,0 +1,76 @@
1
+ """
2
+ Autocorrelograms view for figpack - displays multiple autocorrelograms
3
+ """
4
+
5
+ import zarr
6
+ import numpy as np
7
+ from typing import List, Optional
8
+ from ...core.figpack_view import FigpackView
9
+ from .AutocorrelogramItem import AutocorrelogramItem
10
+
11
+
12
+ class Autocorrelograms(FigpackView):
13
+ """
14
+ A view that displays multiple autocorrelograms for spike sorting analysis
15
+ """
16
+
17
+ def __init__(
18
+ self,
19
+ *,
20
+ autocorrelograms: List[AutocorrelogramItem],
21
+ height: Optional[int] = 400,
22
+ ):
23
+ """
24
+ Initialize an Autocorrelograms view
25
+
26
+ Args:
27
+ autocorrelograms: List of AutocorrelogramItem objects
28
+ height: Height of the view in pixels
29
+ """
30
+ self.autocorrelograms = autocorrelograms
31
+ self.height = height
32
+
33
+ def _write_to_zarr_group(self, group: zarr.Group) -> None:
34
+ """
35
+ Write the Autocorrelograms data to a Zarr group
36
+
37
+ Args:
38
+ group: Zarr group to write data into
39
+ """
40
+ # Set the view type
41
+ group.attrs["view_type"] = "Autocorrelograms"
42
+
43
+ # Set view properties
44
+ if self.height is not None:
45
+ group.attrs["height"] = self.height
46
+
47
+ # Store the number of autocorrelograms
48
+ group.attrs["num_autocorrelograms"] = len(self.autocorrelograms)
49
+
50
+ # Store metadata for each autocorrelogram
51
+ autocorrelogram_metadata = []
52
+ for i, autocorr in enumerate(self.autocorrelograms):
53
+ autocorr_name = f"autocorrelogram_{i}"
54
+
55
+ # Store metadata
56
+ metadata = {
57
+ "name": autocorr_name,
58
+ "unit_id": str(autocorr.unit_id),
59
+ "num_bins": len(autocorr.bin_counts),
60
+ }
61
+ autocorrelogram_metadata.append(metadata)
62
+
63
+ # Create arrays for this autocorrelogram
64
+ group.create_dataset(
65
+ f"{autocorr_name}/bin_edges_sec",
66
+ data=autocorr.bin_edges_sec,
67
+ dtype=np.float32,
68
+ )
69
+ group.create_dataset(
70
+ f"{autocorr_name}/bin_counts",
71
+ data=autocorr.bin_counts,
72
+ dtype=np.int32,
73
+ )
74
+
75
+ # Store the autocorrelogram metadata
76
+ group.attrs["autocorrelograms"] = autocorrelogram_metadata
@@ -0,0 +1,45 @@
1
+ """
2
+ CrossCorrelogramItem for spike sorting views
3
+ """
4
+
5
+ from typing import Union
6
+ import numpy as np
7
+
8
+
9
+ class CrossCorrelogramItem:
10
+ """
11
+ Represents a single cross-correlogram between two units
12
+ """
13
+
14
+ def __init__(
15
+ self,
16
+ *,
17
+ unit_id1: Union[str, int],
18
+ unit_id2: Union[str, int],
19
+ bin_edges_sec: np.ndarray,
20
+ bin_counts: np.ndarray,
21
+ ):
22
+ """
23
+ Initialize a CrossCorrelogramItem
24
+
25
+ Args:
26
+ unit_id1: Identifier for the first unit
27
+ unit_id2: Identifier for the second unit
28
+ bin_edges_sec: Array of bin edges in seconds
29
+ bin_counts: Array of bin counts
30
+ """
31
+ self.unit_id1 = unit_id1
32
+ self.unit_id2 = unit_id2
33
+ self.bin_edges_sec = np.array(bin_edges_sec, dtype=np.float32)
34
+ self.bin_counts = np.array(bin_counts, dtype=np.int32)
35
+
36
+ def to_dict(self):
37
+ """
38
+ Convert the cross-correlogram item to a dictionary representation
39
+ """
40
+ return {
41
+ "unit_id1": str(self.unit_id1),
42
+ "unit_id2": str(self.unit_id2),
43
+ "bin_edges_sec": self.bin_edges_sec.tolist(),
44
+ "bin_counts": self.bin_counts.tolist(),
45
+ }
@@ -0,0 +1,82 @@
1
+ """
2
+ CrossCorrelograms view for figpack - displays multiple cross-correlograms
3
+ """
4
+
5
+ import zarr
6
+ import numpy as np
7
+ from typing import List, Optional
8
+ from ...core.figpack_view import FigpackView
9
+ from .CrossCorrelogramItem import CrossCorrelogramItem
10
+
11
+
12
+ class CrossCorrelograms(FigpackView):
13
+ """
14
+ A view that displays multiple cross-correlograms for spike sorting analysis
15
+ """
16
+
17
+ def __init__(
18
+ self,
19
+ *,
20
+ cross_correlograms: List[CrossCorrelogramItem],
21
+ hide_unit_selector: Optional[bool] = False,
22
+ height: Optional[int] = 500,
23
+ ):
24
+ """
25
+ Initialize a CrossCorrelograms view
26
+
27
+ Args:
28
+ cross_correlograms: List of CrossCorrelogramItem objects
29
+ hide_unit_selector: Whether to hide the unit selector widget
30
+ height: Height of the view in pixels
31
+ """
32
+ self.cross_correlograms = cross_correlograms
33
+ self.hide_unit_selector = hide_unit_selector
34
+ self.height = height
35
+
36
+ def _write_to_zarr_group(self, group: zarr.Group) -> None:
37
+ """
38
+ Write the CrossCorrelograms data to a Zarr group
39
+
40
+ Args:
41
+ group: Zarr group to write data into
42
+ """
43
+ # Set the view type
44
+ group.attrs["view_type"] = "CrossCorrelograms"
45
+
46
+ # Set view properties
47
+ if self.height is not None:
48
+ group.attrs["height"] = self.height
49
+ if self.hide_unit_selector is not None:
50
+ group.attrs["hide_unit_selector"] = self.hide_unit_selector
51
+
52
+ # Store the number of cross-correlograms
53
+ group.attrs["num_cross_correlograms"] = len(self.cross_correlograms)
54
+
55
+ # Store metadata for each cross-correlogram
56
+ cross_correlogram_metadata = []
57
+ for i, cross_corr in enumerate(self.cross_correlograms):
58
+ cross_corr_name = f"cross_correlogram_{i}"
59
+
60
+ # Store metadata
61
+ metadata = {
62
+ "name": cross_corr_name,
63
+ "unit_id1": str(cross_corr.unit_id1),
64
+ "unit_id2": str(cross_corr.unit_id2),
65
+ "num_bins": len(cross_corr.bin_counts),
66
+ }
67
+ cross_correlogram_metadata.append(metadata)
68
+
69
+ # Create arrays for this cross-correlogram
70
+ group.create_dataset(
71
+ f"{cross_corr_name}/bin_edges_sec",
72
+ data=cross_corr.bin_edges_sec,
73
+ dtype=np.float32,
74
+ )
75
+ group.create_dataset(
76
+ f"{cross_corr_name}/bin_counts",
77
+ data=cross_corr.bin_counts,
78
+ dtype=np.int32,
79
+ )
80
+
81
+ # Store the cross-correlogram metadata
82
+ group.attrs["cross_correlograms"] = cross_correlogram_metadata
@@ -0,0 +1,40 @@
1
+ """
2
+ UnitSimilarityScore for spike sorting views
3
+ """
4
+
5
+ from typing import Union
6
+
7
+
8
+ class UnitSimilarityScore:
9
+ """
10
+ Represents a similarity score between two units
11
+ """
12
+
13
+ def __init__(
14
+ self,
15
+ *,
16
+ unit_id1: Union[str, int],
17
+ unit_id2: Union[str, int],
18
+ similarity: float,
19
+ ):
20
+ """
21
+ Initialize a UnitSimilarityScore
22
+
23
+ Args:
24
+ unit_id1: Identifier for the first unit
25
+ unit_id2: Identifier for the second unit
26
+ similarity: Similarity score between the units (typically 0-1)
27
+ """
28
+ self.unit_id1 = unit_id1
29
+ self.unit_id2 = unit_id2
30
+ self.similarity = similarity
31
+
32
+ def to_dict(self):
33
+ """
34
+ Convert the similarity score to a dictionary representation
35
+ """
36
+ return {
37
+ "unitId1": self.unit_id1,
38
+ "unitId2": self.unit_id2,
39
+ "similarity": self.similarity,
40
+ }
@@ -0,0 +1,68 @@
1
+ """
2
+ UnitsTable view for figpack - displays a table of units with their properties
3
+ """
4
+
5
+ import zarr
6
+ import numpy as np
7
+ from typing import List, Optional
8
+ from ...core.figpack_view import FigpackView
9
+ from .UnitsTableColumn import UnitsTableColumn
10
+ from .UnitsTableRow import UnitsTableRow
11
+ from .UnitSimilarityScore import UnitSimilarityScore
12
+
13
+
14
+ class UnitsTable(FigpackView):
15
+ """
16
+ A view that displays a table of units with their properties and optional similarity scores
17
+ """
18
+
19
+ def __init__(
20
+ self,
21
+ *,
22
+ columns: List[UnitsTableColumn],
23
+ rows: List[UnitsTableRow],
24
+ similarity_scores: Optional[List[UnitSimilarityScore]] = None,
25
+ height: Optional[int] = 600,
26
+ ):
27
+ """
28
+ Initialize a UnitsTable view
29
+
30
+ Args:
31
+ columns: List of UnitsTableColumn objects defining the table structure
32
+ rows: List of UnitsTableRow objects containing the data
33
+ similarity_scores: Optional list of UnitSimilarityScore objects
34
+ height: Height of the view in pixels
35
+ """
36
+ self.columns = columns
37
+ self.rows = rows
38
+ self.similarity_scores = similarity_scores or []
39
+ self.height = height
40
+
41
+ def _write_to_zarr_group(self, group: zarr.Group) -> None:
42
+ """
43
+ Write the UnitsTable data to a Zarr group
44
+
45
+ Args:
46
+ group: Zarr group to write data into
47
+ """
48
+ # Set the view type
49
+ group.attrs["view_type"] = "UnitsTable"
50
+
51
+ # Set view properties
52
+ if self.height is not None:
53
+ group.attrs["height"] = self.height
54
+
55
+ # Store columns metadata
56
+ columns_metadata = [col.to_dict() for col in self.columns]
57
+ group.attrs["columns"] = columns_metadata
58
+
59
+ # Store rows metadata
60
+ rows_metadata = [row.to_dict() for row in self.rows]
61
+ group.attrs["rows"] = rows_metadata
62
+
63
+ # Store similarity scores if provided
64
+ if self.similarity_scores:
65
+ similarity_scores_metadata = [
66
+ score.to_dict() for score in self.similarity_scores
67
+ ]
68
+ group.attrs["similarityScores"] = similarity_scores_metadata
@@ -0,0 +1,40 @@
1
+ """
2
+ UnitsTableColumn for spike sorting views
3
+ """
4
+
5
+ from typing import Literal
6
+
7
+
8
+ class UnitsTableColumn:
9
+ """
10
+ Represents a column in a units table
11
+ """
12
+
13
+ def __init__(
14
+ self,
15
+ *,
16
+ key: str,
17
+ label: str,
18
+ dtype: Literal["int", "float", "str", "bool"],
19
+ ):
20
+ """
21
+ Initialize a UnitsTableColumn
22
+
23
+ Args:
24
+ key: The key used to access values in the row data
25
+ label: Display label for the column
26
+ dtype: Data type of the column values
27
+ """
28
+ self.key = key
29
+ self.label = label
30
+ self.dtype = dtype
31
+
32
+ def to_dict(self):
33
+ """
34
+ Convert the column to a dictionary representation
35
+ """
36
+ return {
37
+ "key": self.key,
38
+ "label": self.label,
39
+ "dtype": self.dtype,
40
+ }
@@ -0,0 +1,36 @@
1
+ """
2
+ UnitsTableRow for spike sorting views
3
+ """
4
+
5
+ from typing import Union, Dict, Any
6
+
7
+
8
+ class UnitsTableRow:
9
+ """
10
+ Represents a row in a units table
11
+ """
12
+
13
+ def __init__(
14
+ self,
15
+ *,
16
+ unit_id: Union[str, int],
17
+ values: Dict[str, Any],
18
+ ):
19
+ """
20
+ Initialize a UnitsTableRow
21
+
22
+ Args:
23
+ unit_id: Identifier for the unit
24
+ values: Dictionary of column key to value mappings
25
+ """
26
+ self.unit_id = unit_id
27
+ self.values = values
28
+
29
+ def to_dict(self):
30
+ """
31
+ Convert the row to a dictionary representation
32
+ """
33
+ return {
34
+ "unitId": self.unit_id,
35
+ "values": self.values,
36
+ }
@@ -0,0 +1,23 @@
1
+ """
2
+ Spike sorting views for figpack
3
+ """
4
+
5
+ from .AutocorrelogramItem import AutocorrelogramItem
6
+ from .Autocorrelograms import Autocorrelograms
7
+ from .CrossCorrelogramItem import CrossCorrelogramItem
8
+ from .CrossCorrelograms import CrossCorrelograms
9
+ from .UnitsTableColumn import UnitsTableColumn
10
+ from .UnitsTableRow import UnitsTableRow
11
+ from .UnitSimilarityScore import UnitSimilarityScore
12
+ from .UnitsTable import UnitsTable
13
+
14
+ __all__ = [
15
+ "AutocorrelogramItem",
16
+ "Autocorrelograms",
17
+ "CrossCorrelogramItem",
18
+ "CrossCorrelograms",
19
+ "UnitsTableColumn",
20
+ "UnitsTableRow",
21
+ "UnitSimilarityScore",
22
+ "UnitsTable",
23
+ ]
figpack/views/Image.py ADDED
@@ -0,0 +1,82 @@
1
+ """
2
+ Image view for figpack - displays PNG and JPG images
3
+ """
4
+
5
+ import zarr
6
+ import numpy as np
7
+ from typing import Union
8
+ from ..core.figpack_view import FigpackView
9
+
10
+
11
+ class Image(FigpackView):
12
+ """
13
+ An image visualization component for PNG and JPG files
14
+ """
15
+
16
+ def __init__(self, image_path_or_data: Union[str, bytes]):
17
+ """
18
+ Initialize an Image view
19
+
20
+ Args:
21
+ image_path_or_data: Path to image file or raw image bytes
22
+ """
23
+ self.image_path_or_data = image_path_or_data
24
+
25
+ def _write_to_zarr_group(self, group: zarr.Group) -> None:
26
+ """
27
+ Write the image 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"] = "Image"
34
+
35
+ try:
36
+ # Get the raw image data
37
+ if isinstance(self.image_path_or_data, str):
38
+ # Load from file path
39
+ with open(self.image_path_or_data, "rb") as f:
40
+ image_data = f.read()
41
+ elif isinstance(self.image_path_or_data, bytes):
42
+ # Use bytes directly
43
+ image_data = self.image_path_or_data
44
+ else:
45
+ raise ValueError("image_path_or_data must be a file path or bytes")
46
+
47
+ # Convert bytes to numpy array of uint8
48
+ image_array = np.frombuffer(image_data, dtype=np.uint8)
49
+
50
+ # Store the raw image data as a zarr array
51
+ group.create_dataset(
52
+ "image_data",
53
+ data=image_array,
54
+ dtype=np.uint8,
55
+ chunks=True,
56
+ compressor=zarr.Blosc(
57
+ cname="zstd", clevel=3, shuffle=zarr.Blosc.SHUFFLE
58
+ ),
59
+ )
60
+
61
+ # Try to determine format from file signature
62
+ format_type = "Unknown"
63
+ if len(image_data) >= 8:
64
+ # Check PNG signature
65
+ if image_data[:8] == b"\x89PNG\r\n\x1a\n":
66
+ format_type = "PNG"
67
+ # Check JPEG signature
68
+ elif image_data[:2] == b"\xff\xd8":
69
+ format_type = "JPEG"
70
+
71
+ group.attrs["image_format"] = format_type
72
+ group.attrs["data_size"] = len(image_data)
73
+
74
+ except Exception as e:
75
+ # If image loading fails, store error information
76
+ group.attrs["error"] = f"Failed to load image: {str(e)}"
77
+ group.attrs["image_format"] = "Unknown"
78
+ group.attrs["data_size"] = 0
79
+ # Create empty array as placeholder
80
+ group.create_dataset(
81
+ "image_data", data=np.array([], dtype=np.uint8), dtype=np.uint8
82
+ )
@@ -0,0 +1,34 @@
1
+ """
2
+ Markdown view for figpack - displays markdown content
3
+ """
4
+
5
+ import zarr
6
+ from ..core.figpack_view import FigpackView
7
+
8
+
9
+ class Markdown(FigpackView):
10
+ """
11
+ A markdown content visualization component
12
+ """
13
+
14
+ def __init__(self, content: str):
15
+ """
16
+ Initialize a Markdown view
17
+
18
+ Args:
19
+ content: The markdown content to display
20
+ """
21
+ self.content = content
22
+
23
+ def _write_to_zarr_group(self, group: zarr.Group) -> None:
24
+ """
25
+ Write the markdown data to a Zarr group
26
+
27
+ Args:
28
+ group: Zarr group to write data into
29
+ """
30
+ # Set the view type
31
+ group.attrs["view_type"] = "Markdown"
32
+
33
+ # Store the markdown content
34
+ group.attrs["content"] = self.content
@@ -0,0 +1,65 @@
1
+ """
2
+ MatplotlibFigure view for figpack - displays matplotlib figures
3
+ """
4
+
5
+ import zarr
6
+ import io
7
+ from typing import Union, Any
8
+ from ..core.figpack_view import FigpackView
9
+
10
+
11
+ class MatplotlibFigure(FigpackView):
12
+ """
13
+ A matplotlib figure visualization component
14
+ """
15
+
16
+ def __init__(self, fig):
17
+ """
18
+ Initialize a MatplotlibFigure view
19
+
20
+ Args:
21
+ fig: The matplotlib figure object
22
+ """
23
+ self.fig = fig
24
+
25
+ def _write_to_zarr_group(self, group: zarr.Group) -> None:
26
+ """
27
+ Write the matplotlib figure 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"] = "MatplotlibFigure"
34
+
35
+ try:
36
+ # Convert matplotlib figure to SVG string
37
+ svg_buffer = io.StringIO()
38
+ self.fig.savefig(
39
+ svg_buffer,
40
+ format="svg",
41
+ bbox_inches="tight",
42
+ facecolor="white",
43
+ edgecolor="none",
44
+ )
45
+ svg_string = svg_buffer.getvalue()
46
+ svg_buffer.close()
47
+
48
+ # Store the SVG data
49
+ group.attrs["svg_data"] = svg_string
50
+
51
+ # Store figure dimensions for reference
52
+ fig_width, fig_height = self.fig.get_size_inches()
53
+ group.attrs["figure_width_inches"] = float(fig_width)
54
+ group.attrs["figure_height_inches"] = float(fig_height)
55
+
56
+ # Store DPI for reference
57
+ group.attrs["figure_dpi"] = float(self.fig.dpi)
58
+
59
+ except Exception as e:
60
+ # If SVG export fails, store error information
61
+ group.attrs["svg_data"] = ""
62
+ group.attrs["error"] = f"Failed to export matplotlib figure: {str(e)}"
63
+ group.attrs["figure_width_inches"] = 6.0
64
+ group.attrs["figure_height_inches"] = 4.0
65
+ group.attrs["figure_dpi"] = 100.0