diff --git a/src/myfasthtml/controls/InstancesDebugger.py b/src/myfasthtml/controls/InstancesDebugger.py
index 5528b64..d4c1810 100644
--- a/src/myfasthtml/controls/InstancesDebugger.py
+++ b/src/myfasthtml/controls/InstancesDebugger.py
@@ -1,6 +1,7 @@
from myfasthtml.controls.Panel import Panel
+from myfasthtml.controls.Properties import Properties
from myfasthtml.controls.VisNetwork import VisNetwork
-from myfasthtml.core.commands import LambdaCommand
+from myfasthtml.core.commands import Command
from myfasthtml.core.instances import SingleInstance, InstancesManager
from myfasthtml.core.network_utils import from_parent_child_list
@@ -9,13 +10,23 @@ class InstancesDebugger(SingleInstance):
def __init__(self, parent, _id=None):
super().__init__(parent, _id=_id)
self._panel = Panel(self, _id="-panel")
+ self._command = Command("ShowInstance",
+ "Display selected Instance",
+ self.on_network_event).htmx(target=f"#{self._panel.get_id()}_r")
def render(self):
nodes, edges = self._get_nodes_and_edges()
- c = LambdaCommand(lambda event_data: print("received", event_data))
- vis_network = VisNetwork(self, nodes=nodes, edges=edges, _id="-vis", events_handlers={"select_node": c})
+ vis_network = VisNetwork(self, nodes=nodes, edges=edges, _id="-vis", events_handlers={"select_node": self._command})
return self._panel.set_main(vis_network)
+ def on_network_event(self, event_data: dict):
+ session, instance_id = event_data["nodes"][0].split("#")
+ properties = {"Id": "_id", "Parent Id": "_parent._id"}
+ return self._panel.set_right(Properties(self,
+ InstancesManager.get(session, instance_id),
+ properties,
+ _id="-properties"))
+
def _get_nodes_and_edges(self):
instances = self._get_instances()
nodes, edges = from_parent_child_list(
diff --git a/src/myfasthtml/controls/Panel.py b/src/myfasthtml/controls/Panel.py
index 74f2961..eecf312 100644
--- a/src/myfasthtml/controls/Panel.py
+++ b/src/myfasthtml/controls/Panel.py
@@ -46,6 +46,7 @@ class Panel(MultipleInstance):
sides, and adjust the width of the sides dynamically. The class also handles rendering
the panel with appropriate HTML elements and JavaScript for interactivity.
"""
+
def __init__(self, parent, conf=None, _id=None):
super().__init__(parent, _id=_id)
self.conf = conf or PanelConf()
@@ -66,35 +67,35 @@ class Panel(MultipleInstance):
def set_right(self, right):
self._right = right
- return self
+ return Div(self._right, id=f"{self._id}_r")
def set_left(self, left):
self._left = left
- return self
+ return Div(self._left, id=f"{self._id}_l")
def _mk_right(self):
if not self.conf.right:
return None
-
+
resizer = Div(
cls="mf-resizer mf-resizer-right",
data_command_id=self.commands.update_side_width("right").id,
data_side="right"
)
-
- return Div(resizer, self._right, cls="mf-panel-right")
-
+
+ return Div(resizer, Div(self._right, id=f"{self._id}_r"), cls="mf-panel-right")
+
def _mk_left(self):
if not self.conf.left:
return None
-
+
resizer = Div(
cls="mf-resizer mf-resizer-left",
data_command_id=self.commands.update_side_width("left").id,
data_side="left"
)
-
- return Div(self._left, resizer, cls="mf-panel-left")
+
+ return Div(Div(self._left, id=f"{self._id}_l"), resizer, cls="mf-panel-left")
def render(self):
return Div(
diff --git a/src/myfasthtml/controls/Properties.py b/src/myfasthtml/controls/Properties.py
new file mode 100644
index 0000000..8055d29
--- /dev/null
+++ b/src/myfasthtml/controls/Properties.py
@@ -0,0 +1,44 @@
+from fasthtml.components import Div
+from myutils.Expando import Expando
+
+from myfasthtml.core.instances import MultipleInstance
+
+
+class Properties(MultipleInstance):
+ def __init__(self, parent, obj=None, properties: dict = None, _id=None):
+ super().__init__(parent, _id=_id)
+ self.obj = obj
+ self.properties = properties or self._get_default_properties(obj)
+ self.expando = self._create_expando()
+
+ def set_obj(self, obj, properties: list[str] = None):
+ self.obj = obj
+ self.properties = properties or self._get_default_properties(obj)
+
+ def render(self):
+ return Div(
+ *[Div(k, ":", v) for k, v in self.expando.as_dict().items()],
+ id=self._id,
+ )
+
+ def _create_expando(self):
+ res = {}
+ for attr_name, mapping in self.properties.items():
+ attrs_path = mapping.split(".")
+ current = self.obj
+ for attr in attrs_path:
+ if hasattr(current, attr):
+ current = getattr(current, attr)
+ else:
+ res[attr_name] = None
+ break
+ res[attr_name] = current
+
+ return Expando(res)
+
+ @staticmethod
+ def _get_default_properties(obj):
+ return {k: k for k, v in dir(obj) if not k.startswith("_")} if obj else {}
+
+ def __ft__(self):
+ return self.render()
diff --git a/src/myfasthtml/controls/VisNetwork.py b/src/myfasthtml/controls/VisNetwork.py
index 67b7882..73e4b01 100644
--- a/src/myfasthtml/controls/VisNetwork.py
+++ b/src/myfasthtml/controls/VisNetwork.py
@@ -101,13 +101,15 @@ class VisNetwork(MultipleInstance):
event_handlers_js += f"""
network.on('{vis_event_name}', function(params) {{
const event_data = {{
+ event_name: '{event_name}',
nodes: params.nodes,
edges: params.edges,
pointer: params.pointer
}};
htmx.ajax('POST', '{command_htmx_options['url']}', {{
values: {{event_data: JSON.stringify(event_data)}},
- swap: 'none'
+ target: '{command_htmx_options['target']}',
+ swap: '{command_htmx_options['swap']}'
}});
}});
"""
diff --git a/src/myfasthtml/core/commands.py b/src/myfasthtml/core/commands.py
index 0f56e3b..4871fea 100644
--- a/src/myfasthtml/core/commands.py
+++ b/src/myfasthtml/core/commands.py
@@ -1,4 +1,5 @@
import inspect
+import json
import uuid
from typing import Optional
@@ -98,14 +99,12 @@ class BaseCommand:
return f"{ROUTE_ROOT}{Routes.Commands}?c_id={self.id}"
def ajax_htmx_options(self):
- res = {"url": self.url}
- if "hx-target" in self._htmx_extra:
- res["target"] = self._htmx_extra["hx-target"]
- if "hx-swap" in self._htmx_extra:
- res["swap"] = self._htmx_extra["hx-swap"]
- res["values"] = {}
-
- return res
+ return {
+ "url": self.url,
+ "target": self._htmx_extra.get("hx-target", "this"),
+ "swap": self._htmx_extra.get("hx-swap", "outerHTML"),
+ "values": {}
+ }
def get_ft(self):
return self._ft
@@ -151,6 +150,8 @@ class Command(BaseCommand):
return float(value)
elif param.annotation == list:
return value.split(",")
+ elif param.annotation == dict:
+ return json.loads(value)
return value
def ajax_htmx_options(self):
diff --git a/src/myfasthtml/core/instances.py b/src/myfasthtml/core/instances.py
index 7b5d394..4d45958 100644
--- a/src/myfasthtml/core/instances.py
+++ b/src/myfasthtml/core/instances.py
@@ -84,7 +84,7 @@ class BaseInstance:
return self._prefix
def get_full_id(self) -> str:
- return f"{InstancesManager.get_session_id(self._session)}-{self._id}"
+ return f"{InstancesManager.get_session_id(self._session)}#{self._id}"
def get_full_parent_id(self) -> Optional[str]:
parent = self.get_parent()
diff --git a/src/myfasthtml/core/network_utils.py b/src/myfasthtml/core/network_utils.py
index 4840cf0..5a9c077 100644
--- a/src/myfasthtml/core/network_utils.py
+++ b/src/myfasthtml/core/network_utils.py
@@ -3,6 +3,7 @@ from collections.abc import Callable
ROOT_COLOR = "#ff9999"
GHOST_COLOR = "#cccccc"
+
def from_nested_dict(trees: list[dict]) -> tuple[list, list]:
"""
Convert a list of nested dictionaries to vis.js nodes and edges format.