diff --git a/src/robofish/io/file.py b/src/robofish/io/file.py
index 69b3be29c70d8a5d210a80342d3d8781547178f6..c523840881fed70947183654948bcdf178b3d54a 100644
--- a/src/robofish/io/file.py
+++ b/src/robofish/io/file.py
@@ -627,7 +627,7 @@ class File(h5py.File):
         """
         return robofish.io.validate(self, strict_validate)
 
-    def to_string(self, output_format: str = "shape") -> str:
+    def to_string(self, output_format: str = "shape", max_width: int = 120) -> str:
         """The file is formatted to a human readable format.
         Args:
             output_format: ['shape', 'full'] show the shape, or the full content of datasets
@@ -636,46 +636,111 @@ class File(h5py.File):
         """
 
         def recursive_stringify(
-            obj: h5py.Group, output_format: str, level: int = 0
+            obj: h5py.Group,
+            output_format: str,
+            parent_indices: List[int] = [],
+            parent_siblings: List[int] = [],
         ) -> str:
             """This function crawls recursively into hdf5 groups.
             Datasets and attributes are directly attached, for groups, the function is recursively called again.
             Args:
                 obj: a h5py group
                 output_format: ['shape', 'full'] show the shape, or the full content of datasets
-                level: the current indentation level
             Returns:
                 A string representation of the group
             """
+
+            def lines(dataset_attribute: bool = False) -> str:
+                """Get box-drawing characters for the graph lines."""
+                line = ""
+                for pi, ps in zip(parent_indices, parent_siblings):
+                    if pi < ps - 1:
+                        line += "│ "
+                    else:
+                        line += "  "
+                if dataset_attribute:
+                    line += "  "
+                line += "─ "
+                junction_index = 2 * len(parent_indices) + dataset_attribute * 2 - 1
+                last = "└"
+                other = "├"
+                if dataset_attribute:
+                    j = (
+                        last
+                        if list(value.attrs.keys()).index(d_key) == len(value.attrs) - 1
+                        else other
+                    )
+                else:
+                    j = last if index == num_children - 1 else other
+                line = line[: junction_index + 1] + j + line[junction_index + 1 :]
+                if isinstance(value, h5py.Group) or (
+                    isinstance(value, h5py.Dataset)
+                    and not dataset_attribute
+                    and value.attrs
+                ):
+                    line = line[:-1] + "┬─"
+                else:
+                    line = line[:-1] + "──"
+
+                return line + " "
+
             s = ""
-            level_str = "|---" * level
-            for key, value in obj.attrs.items():
-                s += "%s %s:\t%s\n" % (level_str, key, value)
+            max_key_len = 0
+            num_children = 0
+            if obj.attrs:
+                max_key_len = max(len(key) for key in obj.attrs)
+                num_children += len(obj.attrs)
+            if hasattr(obj, "items"):
+                max_key_len = max([len(key) for key in obj] + [max_key_len])
+                num_children += len(obj)
+            index = 0
+            if obj.attrs:
+                for key, value in obj.attrs.items():
+                    value = str(value).replace("\n", " ").strip()
+                    if len(value) > max_width - max_key_len - len(lines()):
+                        value = (
+                            value[: max_width - max_key_len - len(lines()) - 3] + "..."
+                        )
+                    s += f"{lines()}{key: <{max_key_len}}  {value}\n"
+                    index += 1
             if hasattr(obj, "items"):
                 for key, value in obj.items():
                     if isinstance(value, h5py.Dataset):
                         if output_format == "shape":
-                            s += "%s %s:\t Shape %s\n" % (level_str, key, value.shape)
+                            s += (
+                                f"{lines()}"
+                                f"{key: <{max_key_len}}  Shape {value.shape}\n"
+                            )
                         else:
-                            s += "%s %s:\n%s\n" % (
-                                level_str,
-                                key,
-                                np.array2string(
-                                    value,
-                                    precision=2,
-                                    separator=" ",
-                                    suppress_small=True,
-                                ),
+                            s += f"{lines()}{key}:\n"
+                            s += np.array2string(
+                                value,
+                                precision=2,
+                                separator=" ",
+                                suppress_small=True,
                             )
+                            s += "\n"
 
+                        if value.attrs:
+                            d_max_key_len = max(len(dk) for dk in value.attrs)
                         for d_key, d_value in value.attrs.items():
-                            s += "%s %s:\t%s\n" % (level_str + "|---", d_key, d_value)
+                            d_value = str(d_value).replace("\n", " ").strip()
+                            if len(d_value) > max_width - d_max_key_len - len(
+                                lines(True)
+                            ):
+                                d_value = d_value[
+                                    : max_width - d_max_key_len - len(lines(True))
+                                ]
+                                d_value = d_value[:-3] + "..."
+                            s += f"{lines(True)}{d_key: <{d_max_key_len}}  {d_value}\n"
                     if isinstance(value, h5py.Group):
-                        s += "%s| %s\n%s" % (
-                            level_str,
-                            key,
-                            recursive_stringify(value, output_format, level + 1),
+                        s += f"{lines()}{key}\n" + recursive_stringify(
+                            obj=value,
+                            output_format=output_format,
+                            parent_indices=parent_indices + [index],
+                            parent_siblings=parent_siblings + [num_children],
                         )
+                    index += 1
             return s
 
         return recursive_stringify(self, output_format)