Fixed when tab already exists + InstancesDebugger
This commit is contained in:
14
src/app.py
14
src/app.py
@@ -3,11 +3,12 @@ import logging.config
|
|||||||
import yaml
|
import yaml
|
||||||
from fasthtml import serve
|
from fasthtml import serve
|
||||||
|
|
||||||
|
from myfasthtml.controls.InstancesDebugger import InstancesDebugger
|
||||||
from myfasthtml.controls.Layout import Layout
|
from myfasthtml.controls.Layout import Layout
|
||||||
from myfasthtml.controls.TabsManager import TabsManager
|
from myfasthtml.controls.TabsManager import TabsManager
|
||||||
from myfasthtml.controls.helpers import Ids, mk
|
from myfasthtml.controls.helpers import Ids, mk
|
||||||
from myfasthtml.core.commands import Command
|
|
||||||
from myfasthtml.core.instances import InstancesManager, RootInstance
|
from myfasthtml.core.instances import InstancesManager, RootInstance
|
||||||
|
from myfasthtml.icons.carbon import volume_object_storage
|
||||||
from myfasthtml.myfastapp import create_app
|
from myfasthtml.myfastapp import create_app
|
||||||
|
|
||||||
with open('logging.yaml', 'r') as f:
|
with open('logging.yaml', 'r') as f:
|
||||||
@@ -31,14 +32,17 @@ def index(session):
|
|||||||
layout.set_footer("Goodbye World")
|
layout.set_footer("Goodbye World")
|
||||||
|
|
||||||
tabs_manager = TabsManager(layout, _id=f"{Ids.TabsManager}-main")
|
tabs_manager = TabsManager(layout, _id=f"{Ids.TabsManager}-main")
|
||||||
|
instances_debugger = InstancesManager.get(session, Ids.InstancesDebugger, InstancesDebugger, layout)
|
||||||
btn_show_right_drawer = mk.button("show",
|
btn_show_right_drawer = mk.button("show",
|
||||||
command=Command("ShowRightDrawer",
|
command=layout.commands.toggle_drawer("right"),
|
||||||
"Show Right Drawer",
|
|
||||||
layout.toggle_drawer, "right"),
|
|
||||||
id="btn_show_right_drawer_id")
|
id="btn_show_right_drawer_id")
|
||||||
|
|
||||||
|
btn_show_instances_debugger = mk.icon(volume_object_storage,
|
||||||
|
command=tabs_manager.commands.add_tab("Instances", instances_debugger),
|
||||||
|
id=instances_debugger.get_id())
|
||||||
layout.header_left.add(tabs_manager.add_tab_btn())
|
layout.header_left.add(tabs_manager.add_tab_btn())
|
||||||
layout.header_right.add(btn_show_right_drawer)
|
layout.header_right.add(btn_show_right_drawer)
|
||||||
|
layout.left_drawer.add(btn_show_instances_debugger)
|
||||||
layout.set_main(tabs_manager)
|
layout.set_main(tabs_manager)
|
||||||
return layout
|
return layout
|
||||||
|
|
||||||
|
|||||||
26
src/myfasthtml/controls/InstancesDebugger.py
Normal file
26
src/myfasthtml/controls/InstancesDebugger.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
from myfasthtml.controls.VisNetwork import VisNetwork
|
||||||
|
from myfasthtml.controls.helpers import Ids
|
||||||
|
from myfasthtml.core.instances import SingleInstance, InstancesManager
|
||||||
|
from myfasthtml.core.network_utils import from_parent_child_list
|
||||||
|
|
||||||
|
|
||||||
|
class InstancesDebugger(SingleInstance):
|
||||||
|
def __init__(self, session, parent, _id=None):
|
||||||
|
super().__init__(session, Ids.InstancesDebugger, parent)
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
instances = self._get_instances()
|
||||||
|
nodes, edges = from_parent_child_list(instances,
|
||||||
|
id_getter=lambda x: x.get_id(),
|
||||||
|
label_getter=lambda x: x.get_prefix(),
|
||||||
|
parent_getter=lambda x: x.get_parent().get_id() if x.get_parent() else None
|
||||||
|
)
|
||||||
|
|
||||||
|
vis_network = VisNetwork(self, nodes=nodes, edges=edges)
|
||||||
|
return vis_network
|
||||||
|
|
||||||
|
def _get_instances(self):
|
||||||
|
return list(InstancesManager.instances.values())
|
||||||
|
|
||||||
|
def __ft__(self):
|
||||||
|
return self.render()
|
||||||
@@ -35,7 +35,7 @@ class LayoutState(DbObject):
|
|||||||
|
|
||||||
class Commands(BaseCommands):
|
class Commands(BaseCommands):
|
||||||
def toggle_drawer(self, side: Literal["left", "right"]):
|
def toggle_drawer(self, side: Literal["left", "right"]):
|
||||||
return Command("ToggleDrawer", "Toggle main layout drawer", self._owner.toggle_drawer, side)
|
return Command("ToggleDrawer", f"Toggle {side} layout drawer", self._owner.toggle_drawer, side)
|
||||||
|
|
||||||
def update_drawer_width(self, side: Literal["left", "right"]):
|
def update_drawer_width(self, side: Literal["left", "right"]):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -115,6 +115,11 @@ class TabsManager(MultipleInstance):
|
|||||||
if auto_increment:
|
if auto_increment:
|
||||||
label = f"{label}_{self._get_tab_count()}"
|
label = f"{label}_{self._get_tab_count()}"
|
||||||
component = component or VisNetwork(self, nodes=vis_nodes, edges=vis_edges)
|
component = component or VisNetwork(self, nodes=vis_nodes, edges=vis_edges)
|
||||||
|
|
||||||
|
tab_id = self._tab_already_exists(label, component)
|
||||||
|
if tab_id:
|
||||||
|
return self.show_tab(tab_id)
|
||||||
|
|
||||||
tab_id = self.add_tab(label, component)
|
tab_id = self.add_tab(label, component)
|
||||||
return (
|
return (
|
||||||
self._mk_tabs_controller(),
|
self._mk_tabs_controller(),
|
||||||
@@ -145,14 +150,7 @@ class TabsManager(MultipleInstance):
|
|||||||
component_id = component.get_id()
|
component_id = component.get_id()
|
||||||
|
|
||||||
# Check if a tab with the same component_type, component_id AND label already exists
|
# Check if a tab with the same component_type, component_id AND label already exists
|
||||||
existing_tab_id = None
|
existing_tab_id = self._tab_already_exists(label, component)
|
||||||
if component_id is not None:
|
|
||||||
for tab_id, tab_data in state.tabs.items():
|
|
||||||
if (tab_data.get('component_type') == component_type and
|
|
||||||
tab_data.get('component_id') == component_id and
|
|
||||||
tab_data.get('label') == label):
|
|
||||||
existing_tab_id = tab_id
|
|
||||||
break
|
|
||||||
|
|
||||||
if existing_tab_id:
|
if existing_tab_id:
|
||||||
# Update existing tab (only the component instance in memory)
|
# Update existing tab (only the component instance in memory)
|
||||||
@@ -365,6 +363,22 @@ class TabsManager(MultipleInstance):
|
|||||||
hx_swap_oob=f"beforeend:#{self._id}-content-wrapper",
|
hx_swap_oob=f"beforeend:#{self._id}-content-wrapper",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _tab_already_exists(self, label, component):
|
||||||
|
if not isinstance(component, BaseInstance):
|
||||||
|
return None
|
||||||
|
|
||||||
|
component_type = component.get_prefix() if isinstance(component, BaseInstance) else type(component).__name__
|
||||||
|
component_id = component.get_id()
|
||||||
|
|
||||||
|
if component_id is not None:
|
||||||
|
for tab_id, tab_data in self._state.tabs.items():
|
||||||
|
if (tab_data.get('component_type') == component_type and
|
||||||
|
tab_data.get('component_id') == component_id and
|
||||||
|
tab_data.get('label') == label):
|
||||||
|
return tab_id
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
def _get_tab_list(self):
|
def _get_tab_list(self):
|
||||||
return [self._state.tabs[tab_id] for tab_id in self._state.tabs_order if tab_id in self._state.tabs]
|
return [self._state.tabs[tab_id] for tab_id in self._state.tabs_order if tab_id in self._state.tabs]
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class VisNetwork(MultipleInstance):
|
|||||||
state.options = options
|
state.options = options
|
||||||
|
|
||||||
self._state.update(state)
|
self._state.update(state)
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
|
|
||||||
# Serialize nodes and edges to JSON
|
# Serialize nodes and edges to JSON
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class Ids:
|
|||||||
AuthProxy = "mf-auth-proxy"
|
AuthProxy = "mf-auth-proxy"
|
||||||
Boundaries = "mf-boundaries"
|
Boundaries = "mf-boundaries"
|
||||||
DbManager = "mf-dbmanager"
|
DbManager = "mf-dbmanager"
|
||||||
|
InstancesDebugger = "mf-instances-debugger"
|
||||||
Layout = "mf-layout"
|
Layout = "mf-layout"
|
||||||
Root = "mf-root"
|
Root = "mf-root"
|
||||||
Search = "mf-search"
|
Search = "mf-search"
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ class BaseInstance:
|
|||||||
|
|
||||||
def get_prefix(self):
|
def get_prefix(self):
|
||||||
return self._prefix
|
return self._prefix
|
||||||
|
|
||||||
|
def get_parent(self):
|
||||||
|
return self._parent
|
||||||
|
|
||||||
class SingleInstance(BaseInstance):
|
class SingleInstance(BaseInstance):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -385,7 +385,7 @@ down_to_bottom = NotStr('''<svg name="carbon-DownToBottom" xmlns="http://www.w3.
|
|||||||
basketball = NotStr('''<svg name="carbon-Basketball" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32"><path d="M16 2a14 14 0 1 0 14 14A14.016 14.016 0 0 0 16 2zm11.95 13H22.04a14.409 14.409 0 0 1 2.738-7.153A11.94 11.94 0 0 1 27.95 15zM17 15V4.05a11.918 11.918 0 0 1 6.287 2.438A16.265 16.265 0 0 0 20.04 15zm-2 0h-3.04a16.265 16.265 0 0 0-3.247-8.512A11.918 11.918 0 0 1 15 4.051zm0 2v10.95a11.918 11.918 0 0 1-6.287-2.438A16.265 16.265 0 0 0 11.96 17zm2 0h3.04a16.265 16.265 0 0 0 3.248 8.512A11.918 11.918 0 0 1 17 27.949zM7.22 7.847A14.409 14.409 0 0 1 9.96 15H4.051a11.94 11.94 0 0 1 3.17-7.153zM4.05 17H9.96a14.409 14.409 0 0 1-2.738 7.153A11.94 11.94 0 0 1 4.05 17zm20.73 7.153A14.409 14.409 0 0 1 22.04 17h5.908a11.94 11.94 0 0 1-3.17 7.153z" fill="currentColor" /></svg>''')
|
basketball = NotStr('''<svg name="carbon-Basketball" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32"><path d="M16 2a14 14 0 1 0 14 14A14.016 14.016 0 0 0 16 2zm11.95 13H22.04a14.409 14.409 0 0 1 2.738-7.153A11.94 11.94 0 0 1 27.95 15zM17 15V4.05a11.918 11.918 0 0 1 6.287 2.438A16.265 16.265 0 0 0 20.04 15zm-2 0h-3.04a16.265 16.265 0 0 0-3.247-8.512A11.918 11.918 0 0 1 15 4.051zm0 2v10.95a11.918 11.918 0 0 1-6.287-2.438A16.265 16.265 0 0 0 11.96 17zm2 0h3.04a16.265 16.265 0 0 0 3.248 8.512A11.918 11.918 0 0 1 17 27.949zM7.22 7.847A14.409 14.409 0 0 1 9.96 15H4.051a11.94 11.94 0 0 1 3.17-7.153zM4.05 17H9.96a14.409 14.409 0 0 1-2.738 7.153A11.94 11.94 0 0 1 4.05 17zm20.73 7.153A14.409 14.409 0 0 1 22.04 17h5.908a11.94 11.94 0 0 1-3.17 7.153z" fill="currentColor" /></svg>''')
|
||||||
thunderstorm_scattered_night = NotStr('''<svg name="carbon-ThunderstormScatteredNight" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32"> <path d="M13.338 30l-1.736-1l2.287-4H10l3.993-7l1.737 1l-2.284 4h3.891l-3.999 7z" fill="currentColor" /> <path d="M29.844 13.035a1.52 1.52 0 0 0-1.231-.866a5.356 5.356 0 0 1-3.41-1.716A6.465 6.465 0 0 1 23.92 4.06a1.604 1.604 0 0 0-.3-1.546a1.455 1.455 0 0 0-1.36-.492l-.019.004a7.854 7.854 0 0 0-6.105 6.48A7.372 7.372 0 0 0 13.5 8a7.551 7.551 0 0 0-7.15 5.244A5.993 5.993 0 0 0 8 25v-2a3.993 3.993 0 0 1-.673-7.93l.663-.112l.145-.656a5.496 5.496 0 0 1 10.73 0l.145.656l.663.113A3.993 3.993 0 0 1 19 23v2a5.955 5.955 0 0 0 5.88-7.146a7.502 7.502 0 0 0 4.867-3.3a1.537 1.537 0 0 0 .097-1.52zm-5.693 2.918a5.966 5.966 0 0 0-3.502-2.709a7.508 7.508 0 0 0-2.62-3.694a6.008 6.008 0 0 1 3.77-5.333a8.458 8.458 0 0 0 1.939 7.596a7.404 7.404 0 0 0 3.902 2.228a5.442 5.442 0 0 1-3.489 1.912z" fill="currentColor" /></svg>''')
|
thunderstorm_scattered_night = NotStr('''<svg name="carbon-ThunderstormScatteredNight" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32"> <path d="M13.338 30l-1.736-1l2.287-4H10l3.993-7l1.737 1l-2.284 4h3.891l-3.999 7z" fill="currentColor" /> <path d="M29.844 13.035a1.52 1.52 0 0 0-1.231-.866a5.356 5.356 0 0 1-3.41-1.716A6.465 6.465 0 0 1 23.92 4.06a1.604 1.604 0 0 0-.3-1.546a1.455 1.455 0 0 0-1.36-.492l-.019.004a7.854 7.854 0 0 0-6.105 6.48A7.372 7.372 0 0 0 13.5 8a7.551 7.551 0 0 0-7.15 5.244A5.993 5.993 0 0 0 8 25v-2a3.993 3.993 0 0 1-.673-7.93l.663-.112l.145-.656a5.496 5.496 0 0 1 10.73 0l.145.656l.663.113A3.993 3.993 0 0 1 19 23v2a5.955 5.955 0 0 0 5.88-7.146a7.502 7.502 0 0 0 4.867-3.3a1.537 1.537 0 0 0 .097-1.52zm-5.693 2.918a5.966 5.966 0 0 0-3.502-2.709a7.508 7.508 0 0 0-2.62-3.694a6.008 6.008 0 0 1 3.77-5.333a8.458 8.458 0 0 0 1.939 7.596a7.404 7.404 0 0 0 3.902 2.228a5.442 5.442 0 0 1-3.489 1.912z" fill="currentColor" /></svg>''')
|
||||||
letter_uu = NotStr('''<svg name="carbon-LetterUu" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32"> <path d="M23 23h-4a2 2 0 0 1-2-2v-8h2v8h4v-8h2v8a2 2 0 0 1-2 2z" fill="currentColor" /> <path d="M13 23H9a2 2 0 0 1-2-2V9h2v12h4V9h2v12a2 2 0 0 1-2 2z" fill="currentColor" /></svg>''')
|
letter_uu = NotStr('''<svg name="carbon-LetterUu" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32"> <path d="M23 23h-4a2 2 0 0 1-2-2v-8h2v8h4v-8h2v8a2 2 0 0 1-2 2z" fill="currentColor" /> <path d="M13 23H9a2 2 0 0 1-2-2V9h2v12h4V9h2v12a2 2 0 0 1-2 2z" fill="currentColor" /></svg>''')
|
||||||
continue = NotStr('''<svg name="carbon-Continue" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32"> <path d="M10 28a1 1 0 0 1-1-1V5a1 1 0 0 1 1.501-.865l19 11a1 1 0 0 1 0 1.73l-19 11A.998.998 0 0 1 10 28zm1-21.266v18.532L27 16z" fill="currentColor" /> <path d="M4 4h2v24H4z" fill="currentColor" /></svg>''')
|
continue_ = NotStr('''<svg name="carbon-Continue" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32"> <path d="M10 28a1 1 0 0 1-1-1V5a1 1 0 0 1 1.501-.865l19 11a1 1 0 0 1 0 1.73l-19 11A.998.998 0 0 1 10 28zm1-21.266v18.532L27 16z" fill="currentColor" /> <path d="M4 4h2v24H4z" fill="currentColor" /></svg>''')
|
||||||
login = NotStr('''<svg name="carbon-Login" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32"> <path d="M26 30H14a2 2 0 0 1-2-2v-3h2v3h12V4H14v3h-2V4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v24a2 2 0 0 1-2 2z" fill="currentColor" /> <path d="M14.59 20.59L18.17 17H4v-2h14.17l-3.58-3.59L16 10l6 6l-6 6l-1.41-1.41z" fill="currentColor" /></svg>''')
|
login = NotStr('''<svg name="carbon-Login" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32"> <path d="M26 30H14a2 2 0 0 1-2-2v-3h2v3h12V4H14v3h-2V4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v24a2 2 0 0 1-2 2z" fill="currentColor" /> <path d="M14.59 20.59L18.17 17H4v-2h14.17l-3.58-3.59L16 10l6 6l-6 6l-1.41-1.41z" fill="currentColor" /></svg>''')
|
||||||
favorite = NotStr('''<svg name="carbon-Favorite" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32"><path d="M22.45 6a5.47 5.47 0 0 1 3.91 1.64a5.7 5.7 0 0 1 0 8L16 26.13L5.64 15.64a5.7 5.7 0 0 1 0-8a5.48 5.48 0 0 1 7.82 0l2.54 2.6l2.53-2.58A5.44 5.44 0 0 1 22.45 6m0-2a7.47 7.47 0 0 0-5.34 2.24L16 7.36l-1.11-1.12a7.49 7.49 0 0 0-10.68 0a7.72 7.72 0 0 0 0 10.82L16 29l11.79-11.94a7.72 7.72 0 0 0 0-10.82A7.49 7.49 0 0 0 22.45 4z" fill="currentColor" /></svg>''')
|
favorite = NotStr('''<svg name="carbon-Favorite" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32"><path d="M22.45 6a5.47 5.47 0 0 1 3.91 1.64a5.7 5.7 0 0 1 0 8L16 26.13L5.64 15.64a5.7 5.7 0 0 1 0-8a5.48 5.48 0 0 1 7.82 0l2.54 2.6l2.53-2.58A5.44 5.44 0 0 1 22.45 6m0-2a7.47 7.47 0 0 0-5.34 2.24L16 7.36l-1.11-1.12a7.49 7.49 0 0 0-10.68 0a7.72 7.72 0 0 0 0 10.82L16 29l11.79-11.94a7.72 7.72 0 0 0 0-10.82A7.49 7.49 0 0 0 22.45 4z" fill="currentColor" /></svg>''')
|
||||||
cd_archive = NotStr('''<svg name="carbon-CdArchive" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32"> <path d="M16 28a12 12 0 1 1 12-12a12 12 0 0 1-12 12zm0-22a10 10 0 1 0 10 10A10 10 0 0 0 16 6z" fill="currentColor" /> <path d="M16 22a6 6 0 1 1 6-6a6 6 0 0 1-6 6zm0-10a4 4 0 1 0 4 4a4 4 0 0 0-4-4z" fill="currentColor" /> <circle cx="16" cy="16" r="2" fill="currentColor" /></svg>''')
|
cd_archive = NotStr('''<svg name="carbon-CdArchive" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32"> <path d="M16 28a12 12 0 1 1 12-12a12 12 0 0 1-12 12zm0-22a10 10 0 1 0 10 10A10 10 0 0 0 16 6z" fill="currentColor" /> <path d="M16 22a6 6 0 1 1 6-6a6 6 0 0 1-6 6zm0-10a4 4 0 1 0 4 4a4 4 0 0 0-4-4z" fill="currentColor" /> <circle cx="16" cy="16" r="2" fill="currentColor" /></svg>''')
|
||||||
|
|||||||
Reference in New Issue
Block a user