From 3ea551bc1a9faabf56e3510c838f778e6f702bda Mon Sep 17 00:00:00 2001 From: Kodjo Sossouvi Date: Mon, 23 Mar 2026 22:32:05 +0100 Subject: [PATCH] Fixed wrong full refresh --- src/myfasthtml/controls/Profiler.py | 115 ++++++++++++++++------------ 1 file changed, 66 insertions(+), 49 deletions(-) diff --git a/src/myfasthtml/controls/Profiler.py b/src/myfasthtml/controls/Profiler.py index 71cb7ae..9f7565d 100644 --- a/src/myfasthtml/controls/Profiler.py +++ b/src/myfasthtml/controls/Profiler.py @@ -37,7 +37,7 @@ def _mk_span_rows(span, depth: int, total_ms: float) -> list: """ rows = [] indent = [Div(cls="mf-profiler-span-indent") for _ in range(depth)] - + if isinstance(span, CumulativeSpan): pct = (span.total_ms / total_ms * 100) if total_ms > 0 else 0 duration_cls = _span_duration_cls(span.total_ms) @@ -57,7 +57,7 @@ def _mk_span_rows(span, depth: int, total_ms: float) -> list: cls="mf-profiler-span-row", ) rows.append(row) - + else: pct = (span.duration_ms / total_ms * 100) if total_ms > 0 else 0 duration_cls = _span_duration_cls(span.duration_ms) @@ -75,7 +75,7 @@ def _mk_span_rows(span, depth: int, total_ms: float) -> list: rows.append(row) for child in span.children: rows.extend(_mk_span_rows(child, depth + 1, total_ms)) - + return rows @@ -111,7 +111,7 @@ class Commands(BaseCommands): self._owner, self._owner.handle_toggle_detail_view, ).htmx(target=f"#{self._id}") - + def toggle_enable(self): return Command( "ProfilerToggleEnable", @@ -145,7 +145,7 @@ class Commands(BaseCommands): self._owner, self._owner.handle_select_trace, kwargs={"trace_id": trace_id}, - ).htmx(target=f"#{self._id}") + ).htmx(target=f"#tr_{trace_id}") class Profiler(SingleInstance): @@ -186,15 +186,24 @@ class Profiler(SingleInstance): def handle_select_trace(self, trace_id: str): """Select a trace row and re-render to show it highlighted.""" + if self._selected_id is not None: + old_trace = next(trace for trace in profiler.traces if trace.trace_id == self._selected_id) + else: + old_trace = None + self._selected_id = trace_id - return self + trace = next(trace for trace in profiler.traces if trace.trace_id == trace_id) + + return (self._mk_trace_item(trace), + self._mk_trace_item(old_trace), + self._panel.set_right(self._mk_right_panel(trace))) def handle_toggle_detail_view(self): """Toggle detail panel between tree and pie view.""" self._detail_view = "pie" if self._detail_view == "tree" else "tree" logger.debug(f"Profiler detail view set to {self._detail_view}") return self - + def handle_refresh(self): """Refresh the trace list without changing selection.""" return self @@ -245,32 +254,36 @@ class Profiler(SingleInstance): id=f"tb_{self._id}", ) + def _mk_trace_item(self, trace: ProfilingTrace): + if trace is None: + return None + + ts = trace.timestamp.strftime("%H:%M:%S.") + f"{trace.timestamp.microsecond // 1000:03d}" + duration_cls = self._duration_cls(trace.total_duration_ms) + row_cls = "mf-profiler-row mf-profiler-row-selected" if trace.trace_id == self._selected_id else "mf-profiler-row" + + return mk.mk( + Div( + Div( + Span(trace.command_name, cls="mf-profiler-cmd"), + Span(trace.command_description, cls="mf-profiler-cmd-description"), + cls="mf-profiler-cmd-cell", + ), + Span(f"{trace.total_duration_ms:.1f} ms", cls=f"mf-profiler-duration {duration_cls}"), + Span(ts, cls="mf-profiler-ts"), + cls=row_cls, + id=f"tr_{trace.trace_id}", + ), + command=self.commands.select_trace(trace.trace_id), + ) + def _mk_trace_list(self): """Build the trace list with one clickable row per recorded trace.""" traces = profiler.traces if not traces: return Div("No traces recorded.", cls="mf-profiler-empty") - rows = [] - for trace in reversed(traces): - ts = trace.timestamp.strftime("%H:%M:%S.") + f"{trace.timestamp.microsecond // 1000:03d}" - duration_cls = self._duration_cls(trace.total_duration_ms) - row_cls = "mf-profiler-row mf-profiler-row-selected" if trace.trace_id == self._selected_id else "mf-profiler-row" - - row = mk.mk( - Div( - Div( - Span(trace.command_name, cls="mf-profiler-cmd"), - Span(trace.command_description, cls="mf-profiler-cmd-description"), - cls="mf-profiler-cmd-cell", - ), - Span(f"{trace.total_duration_ms:.1f} ms", cls=f"mf-profiler-duration {duration_cls}"), - Span(ts, cls="mf-profiler-ts"), - cls=row_cls, - ), - command=self.commands.select_trace(trace.trace_id), - ) - rows.append(row) + rows = [self._mk_trace_item(trace) for trace in reversed(traces)] return Div( Div( @@ -286,7 +299,7 @@ class Profiler(SingleInstance): def _mk_detail_placeholder(self): """Placeholder shown in the right panel before a trace is selected.""" return Div("Select a trace to view details.", cls="mf-profiler-empty") - + def _mk_detail_header(self, trace: "ProfilingTrace"): """Build the detail panel header with title and tree/pie toggle. @@ -305,12 +318,14 @@ class Profiler(SingleInstance): tree_cls = "mf-profiler-view-btn mf-profiler-view-btn-active" if self._detail_view == "tree" else "mf-profiler-view-btn" pie_cls = "mf-profiler-view-btn mf-profiler-view-btn-active" if self._detail_view == "pie" else "mf-profiler-view-btn" toggle = Div( - mk.icon(text_bullet_list_tree20_filled, command=self.commands.toggle_detail_view(), tooltip="Span tree", cls=tree_cls), - mk.icon(data_pie24_regular, command=self.commands.toggle_detail_view(), tooltip="Pie chart (coming soon)", cls=pie_cls), + mk.icon(text_bullet_list_tree20_filled, command=self.commands.toggle_detail_view(), tooltip="Span tree", + cls=tree_cls), + mk.icon(data_pie24_regular, command=self.commands.toggle_detail_view(), tooltip="Pie chart (coming soon)", + cls=pie_cls), cls="mf-profiler-view-toggle", ) return Div(title, toggle, cls="mf-profiler-detail-header") - + def _mk_detail_body(self, trace: "ProfilingTrace"): """Build the scrollable detail body: metadata, kwargs and span breakdown. @@ -321,28 +336,28 @@ class Profiler(SingleInstance): A FT element for the detail body. """ from types import SimpleNamespace - + meta_props = Properties( self, conf=PropertiesConf( obj=trace, groups={"Metadata": { - "command": "command_name", - "description": "command_description", - "duration_ms": "total_duration_ms", - "timestamp": "timestamp", + "command": "command_name", + "description": "command_description", + "duration_ms": "total_duration_ms", + "timestamp": "timestamp", }}, ), _id="-detail-meta", ) - + kwargs_obj = SimpleNamespace(**trace.kwargs) if trace.kwargs else SimpleNamespace() kwargs_props = Properties( self, conf=PropertiesConf(obj=kwargs_obj, groups={"kwargs": {"*": ""}}), _id="-detail-kwargs", ) - + span_props = None if trace.root_span is not None: span_props = Properties( @@ -354,13 +369,13 @@ class Profiler(SingleInstance): ), _id="-detail-spans", ) - + if self._detail_view == "pie": pie_placeholder = Div("Pie chart — coming soon.", cls="mf-profiler-empty") return Div(meta_props, kwargs_props, pie_placeholder, cls="mf-profiler-detail-body") - + return Div(meta_props, kwargs_props, span_props, cls="mf-profiler-detail-body") - + def _mk_detail_panel(self, trace: "ProfilingTrace"): """Build the full detail panel for a selected trace. @@ -376,6 +391,14 @@ class Profiler(SingleInstance): cls="mf-profiler-detail", ) + def _mk_right_panel(self, trace: "ProfilingTrace"): + """Build the right panel with a trace detail view.""" + return ( + self._mk_detail_panel(trace) + if trace is not None + else self._mk_detail_placeholder() + ) + # ------------------------------------------------------------------ # Render # ------------------------------------------------------------------ @@ -386,15 +409,9 @@ class Profiler(SingleInstance): selected_trace = next( (t for t in profiler.traces if t.trace_id == self._selected_id), None ) - - right_panel = ( - self._mk_detail_panel(selected_trace) - if selected_trace is not None - else self._mk_detail_placeholder() - ) - + self._panel.set_main(self._mk_trace_list()) - self._panel.set_right(right_panel) + self._panel.set_right(self._mk_right_panel(selected_trace)) return Div( self._mk_toolbar(), self._panel,