"""World map visualization tab using pyvis + iframe for reliable rendering.""" import html as html_module import gradio as gr from core.world import WorldGraph from ui.i18n import t def _generate_map_html() -> str: """Generate the interactive world map as an iframe with srcdoc.""" world = WorldGraph() vis_data = world.to_vis_data() if not vis_data["nodes"]: return f"""

{t('map_empty_title')}

{t('map_empty_desc')}

""" try: from pyvis.network import Network net = Network( height="600px", width="100%", bgcolor="#0f0f1a", font_color="#e0e0e0", directed=True, ) net.barnes_hut( gravity=-5000, central_gravity=0.3, spring_length=200, spring_strength=0.05, damping=0.09, ) NODE_STYLES = { "location": {"color": "#4a9eff", "shape": "dot", "size": 35, "border": "#2d7dd2"}, "npc": {"color": "#50c878", "shape": "diamond", "size": 25, "border": "#3da35d"}, "quest": {"color": "#ffd700", "shape": "star", "size": 25, "border": "#ccac00"}, "item": {"color": "#ff6b6b", "shape": "triangle", "size": 20, "border": "#cc5555"}, "creature": {"color": "#ff9f43", "shape": "triangleDown", "size": 20, "border": "#cc7700"}, "dream": {"color": "#9b59b6", "shape": "square", "size": 15, "border": "#7d3c98"}, } for node in vis_data["nodes"]: style = NODE_STYLES.get(node["type"], NODE_STYLES["location"]) tooltip = f"{node['label']}
{node.get('title', '')}" net.add_node( node["id"], label=node["label"], color={ "background": style["color"], "border": style["border"], "highlight": {"background": "#ffffff", "border": style["color"]}, }, shape=style["shape"], size=style["size"], title=tooltip, borderWidth=2, shadow=True, ) EDGE_COLORS = { "resides_at": "#50c878", "located_at": "#4a9eff", "given_by": "#ffd700", "from_dream": "#9b59b6", "connects_to": "#666666", "related_to": "#888888", } for edge in vis_data["edges"]: relation = edge.get("label", "") color = EDGE_COLORS.get(relation, "#555555") net.add_edge( edge["from"], edge["to"], label=relation, color={"color": color, "highlight": "#ffffff"}, width=1.5, arrows={"to": {"enabled": True, "scaleFactor": 0.5}}, smooth={"type": "curvedCW", "roundness": 0.2}, ) # Generate full HTML page from pyvis map_html = net.generate_html() # Inject dark theme CSS dark_css = """ """ map_html = map_html.replace("", dark_css + "") # Encode as srcdoc iframe (scripts execute in iframe context) escaped = html_module.escape(map_html) stats = world.stats() return f"""
{t('map_location')} {t('map_npc')} {t('map_quest')} {t('map_item')} {t('map_dream')}
{stats['total_nodes']} {t('map_nodes')} ยท {stats['total_edges']} {t('map_connections')}
""" except Exception as e: stats = world.stats() by_type = stats.get("by_type", {}) return f"""

{t('map_overview')}

{t('map_location')}: {by_type.get('location', 0)} | {t('map_npc')}: {by_type.get('npc', 0)} | {t('map_quest')}: {by_type.get('quest', 0)}

{t('map_render_error')}: {e}

""" def create_map_tab() -> gr.HTML: """Create the world map tab UI.""" map_display = gr.HTML(value=_generate_map_html()) refresh_btn = gr.Button(t("map_refresh"), variant="secondary") refresh_btn.click(fn=_generate_map_html, outputs=[map_display]) return map_display