Source code for pipeworks_mud_mapper.layout.map_panel
"""Map panel component for the center column.
The map panel displays the interactive Plotly visualization of rooms
and exits, along with filter controls for showing/hiding Z-levels.
The flattened view displays all rooms on a single 2D plane regardless
of their Z coordinate, with visual differentiation by level:
- **z=-1 (Down)**: Black filled circles (smallest)
- **z=0 (Ground)**: Blue filled circles (largest)
- **z=+1 (Up)**: White circles with black border (medium)
Component Structure
-------------------
::
┌─────────────────────────────────────────┐
│ │
│ ●───────● ◐ │
│ │ │ │ │
│ ●───────●─────● Plotly map │
│ ⬤ │
│ │
├─────────────────────────────────────────┤
│ Show Layers: ☑ Down ☑ Ground ☑ Up │
└─────────────────────────────────────────┘
Component IDs
-------------
- ``map-graph``: Plotly Graph component for room visualization
- ``z-level-filter``: Checklist for filtering which Z-levels to display
Filter Behavior
---------------
All levels are shown by default (all checkboxes checked). Unchecking a
level hides rooms at that Z coordinate. This is useful for:
- Reducing visual clutter when working on a single floor
- Selecting stacked rooms (hide upper levels to click lower ones)
- Focusing on specific areas of the map
See Also
--------
- ``components/map_view.py``: Plotly figure creation functions
- ``callbacks/map_callbacks.py``: Callbacks for map interaction
"""
import dash_bootstrap_components as dbc
from dash import dcc, html
from pipeworks_mud_mapper.components.map_view import create_map_figure
from pipeworks_mud_mapper.services.exit_utils import EXIT_SHORT_ORDER
def _create_api_workspace_tab() -> dbc.Tab:
"""Create the Workspace API tab layout.
The API tab provides:
- Service configuration (base URL, auth, headers)
- Command builder (method/path/body)
- Response display area
"""
service_card = dbc.Card(
[
dbc.CardHeader("Services"),
dbc.CardBody(
[
html.Div(id="workspace-api-service-feedback", className="mb-2"),
dbc.Label("Service", html_for="workspace-api-service-select"),
dbc.Row(
[
dbc.Col(
dcc.Dropdown(
id="workspace-api-service-select",
options=[],
placeholder="Select a service...",
clearable=True,
className="small",
),
width=9,
),
dbc.Col(
dbc.Button(
[html.I(className="bi bi-arrow-clockwise me-1"), "Refresh"],
id="workspace-api-service-refresh",
color="secondary",
size="sm",
outline=True,
className="w-100",
),
width=3,
),
],
className="g-2 mb-2",
),
dbc.Label("Name", html_for="workspace-api-service-name"),
dbc.Input(
id="workspace-api-service-name",
type="text",
placeholder="e.g., Name Generation",
className="mb-2",
),
dbc.Label("Base URL", html_for="workspace-api-service-base-url"),
dbc.Input(
id="workspace-api-service-base-url",
type="text",
placeholder="http://localhost:8000",
className="mb-2",
),
dbc.Label("Auth Type", html_for="workspace-api-service-auth-type"),
dbc.Select(
id="workspace-api-service-auth-type",
options=[
{"label": "None", "value": "none"},
{"label": "Bearer", "value": "bearer"},
{"label": "Basic", "value": "basic"},
{"label": "API Key", "value": "api_key"},
],
value="none",
className="mb-2",
),
dbc.Label("Auth Secret", html_for="workspace-api-service-auth-secret"),
dbc.Input(
id="workspace-api-service-auth-secret",
type="password",
placeholder="token or user:pass",
className="mb-2",
),
dbc.Label("Default Headers (JSON)", html_for="workspace-api-service-headers"),
dbc.Textarea(
id="workspace-api-service-headers",
placeholder='{"X-Client": "mapper"}',
style={"height": "90px", "fontFamily": "monospace"},
className="mb-2",
),
dbc.Checklist(
id="workspace-api-service-enabled",
options=[{"label": "Enabled", "value": "enabled"}],
value=["enabled"],
switch=True,
className="mb-2",
),
dbc.Label("Notes", html_for="workspace-api-service-notes"),
dbc.Textarea(
id="workspace-api-service-notes",
placeholder="Optional notes for this service.",
style={"height": "70px"},
className="mb-2",
),
dbc.ButtonGroup(
[
dbc.Button(
[html.I(className="bi bi-plus me-1"), "New"],
id="workspace-api-service-new",
color="secondary",
size="sm",
outline=True,
),
dbc.Button(
[html.I(className="bi bi-save me-1"), "Save"],
id="workspace-api-service-save",
color="primary",
size="sm",
),
dbc.Button(
[html.I(className="bi bi-trash me-1"), "Delete"],
id="workspace-api-service-delete",
color="danger",
size="sm",
outline=True,
),
],
className="w-100",
),
],
className="small",
),
]
)
command_card = dbc.Card(
[
dbc.CardHeader("Commands"),
dbc.CardBody(
[
html.Div(id="workspace-api-command-feedback", className="mb-2"),
dbc.Label("Command", html_for="workspace-api-command-select"),
dcc.Dropdown(
id="workspace-api-command-select",
options=[],
placeholder="Select a command...",
clearable=True,
className="small mb-2",
),
dbc.Label("Name", html_for="workspace-api-command-name"),
dbc.Input(
id="workspace-api-command-name",
type="text",
placeholder="e.g., Generate Names",
className="mb-2",
),
dbc.Label("Method", html_for="workspace-api-command-method"),
dbc.Select(
id="workspace-api-command-method",
options=[
{"label": "GET", "value": "GET"},
{"label": "POST", "value": "POST"},
{"label": "PUT", "value": "PUT"},
{"label": "PATCH", "value": "PATCH"},
{"label": "DELETE", "value": "DELETE"},
],
value="GET",
className="mb-2",
),
dbc.Label("Path", html_for="workspace-api-command-path"),
dbc.Input(
id="workspace-api-command-path",
type="text",
placeholder="/api/generate",
className="mb-2",
),
dbc.Label("Query (JSON)", html_for="workspace-api-command-query"),
dbc.Textarea(
id="workspace-api-command-query",
placeholder='{"limit": 10}',
style={"height": "70px", "fontFamily": "monospace"},
className="mb-2",
),
dbc.Label("Headers (JSON)", html_for="workspace-api-command-headers"),
dbc.Textarea(
id="workspace-api-command-headers",
placeholder='{"X-Request": "mapper"}',
style={"height": "70px", "fontFamily": "monospace"},
className="mb-2",
),
dbc.Label("Body (JSON)", html_for="workspace-api-command-body"),
dbc.Textarea(
id="workspace-api-command-body",
placeholder='{"class": "goblin"}',
style={"height": "90px", "fontFamily": "monospace"},
className="mb-2",
),
dbc.Label("Timeout (seconds)", html_for="workspace-api-command-timeout"),
dbc.Input(
id="workspace-api-command-timeout",
type="number",
min=1,
step=1,
placeholder="30",
className="mb-3",
),
dbc.ButtonGroup(
[
dbc.Button(
[html.I(className="bi bi-play-fill me-1"), "Run"],
id="workspace-api-command-run",
color="primary",
size="sm",
),
dbc.Button(
[html.I(className="bi bi-save me-1"), "Save"],
id="workspace-api-command-save",
color="secondary",
size="sm",
outline=True,
),
dbc.Button(
[html.I(className="bi bi-plus me-1"), "New"],
id="workspace-api-command-new",
color="secondary",
size="sm",
outline=True,
),
dbc.Button(
[html.I(className="bi bi-trash me-1"), "Delete"],
id="workspace-api-command-delete",
color="danger",
size="sm",
outline=True,
),
],
className="w-100",
),
],
className="small",
),
]
)
response_card = dbc.Card(
[
dbc.CardHeader("Response"),
dbc.CardBody(
[
html.Div(id="workspace-api-run-feedback", className="mb-2"),
html.Div(
id="workspace-api-response-view",
className="small",
),
],
className="small",
),
]
)
return dbc.Tab(
[
dcc.Store(id="workspace-api-response", data=None),
dcc.Store(id="workspace-api-jobs", data={"jobs": []}),
dbc.Row(
[
dbc.Col(service_card, xs=12, md=6),
dbc.Col(command_card, xs=12, md=6),
],
className="g-2",
),
html.Hr(className="my-2"),
response_card,
],
label="API",
)
[docs]
def create_map_panel() -> dbc.Card:
"""Create the center column map panel component.
The map panel contains:
- Plotly Graph component for room visualization (flattened multi-level view)
- Z-level filter checkboxes for showing/hiding each level
Returns
-------
dbc.Card
Bootstrap Card containing the map and layer filter controls.
Component IDs
-------------
- ``map-graph``: Plotly Graph for map visualization
- ``z-level-filter``: Checklist for filtering visible Z-levels
Visual Indicators
-----------------
The filter checkboxes include colored circles matching the room styling:
- **Down (z=-1)**: Small black circle
- **Ground (z=0)**: Medium blue circle
- **Up (z=+1)**: Medium white circle with black border
Notes
-----
- Map displays all Z-levels by default (flattened view)
- Uncheck a level to hide rooms at that Z coordinate
- Rooms are rendered in Z-order: down first, then up, then ground on top
- Ground level rooms receive clicks first when stacked
- To select a lower-level room, temporarily uncheck higher levels
- Scroll zoom and pan are enabled via Graph config
- Lasso and select tools are removed from mode bar
Examples
--------
The map panel is typically used within the main layout::
>>> from pipeworks_mud_mapper.layout.map_panel import create_map_panel
>>> panel = create_map_panel()
>>> # Panel contains 'map-graph' and 'z-level-filter' components
"""
# Keep shared dropdown options small and reusable for the workspace editor.
exit_direction_options = [
{"label": direction, "value": direction} for direction in EXIT_SHORT_ORDER
]
zone_exit_help_text = (
"Cross-zone exits hand off players to a room in another zone. "
"Use the editor below or click a row to edit."
)
zone_exit_header_class = "fw-semibold mb-1"
zone_exit_help_class = "small text-muted mb-2"
zone_exit_feedback_class = "small mb-2"
zone_exit_row_class = "g-2 align-items-end mb-2"
zone_exit_save_icon = html.I(className="bi bi-check me-1")
zone_exit_clear_icon = html.I(className="bi bi-x me-1")
world_refresh_icon = html.I(className="bi bi-arrow-clockwise me-1")
return dbc.Card(
[
dbc.CardBody(
[
# ---------------------------------------------------------
# Plotly Map Figure
# ---------------------------------------------------------
# Interactive map showing rooms as nodes and exits as lines.
# Displays all Z-levels simultaneously (flattened view).
# ---------------------------------------------------------------------
# Map + Workspace Split Row
# ---------------------------------------------------------------------
# The Plotly map keeps its fixed 700px width (see create_map_figure),
# which leaves usable space to the right inside the center column.
# We reserve that space for a "Workspace" card with tabs for upcoming
# SQLite tooling and other utilities.
dbc.Row(
[
# Map column: auto-sized to the Plotly figure width.
dbc.Col(
html.Div(
dcc.Graph(
id="map-graph",
figure=create_map_figure(), # No title for flattened view
config={
"displayModeBar": True,
"scrollZoom": True,
"modeBarButtonsToRemove": ["lasso2d", "select2d"],
},
),
# Adds a subtle divider so the workspace feels distinct.
className="map-graph-frame",
),
width="auto",
className="pe-0",
),
# Workspace column: fills remaining horizontal space.
dbc.Col(
dbc.Card(
[
dbc.CardHeader("Workspace"),
dbc.CardBody(
# Tabs provide room for future tooling.
# First tab is a SQLite DB placeholder with a faux
# table.
dbc.Tabs(
[
dbc.Tab(
[
html.Div(
[
html.Span(
"SQLite DB",
className="fw-semibold",
),
dbc.Button(
"Refresh",
id="workspace-db-refresh",
color="link",
size="sm",
className="ms-auto",
),
],
className=(
"d-flex align-items-center mb-2"
),
),
html.Div(
[
# Workspace utility actions.
# Background jobs keep UI fast.
# Tooltips explain each action.
dbc.ButtonGroup(
[
dbc.Button(
"Backup DB",
id="workspace-db-backup-btn",
size="sm",
color="secondary",
outline=True,
),
dbc.Button(
"Export Map JSON",
id="workspace-db-export-map-btn",
size="sm",
color="secondary",
outline=True,
),
dbc.Button(
"Export Zone JSON",
id="workspace-db-export-zone-btn",
size="sm",
color="secondary",
outline=True,
),
dbc.Button(
"Dump SQL",
id="workspace-db-export-sql-btn",
size="sm",
color="secondary",
outline=True,
),
],
size="sm",
className="flex-wrap gap-1",
),
dbc.Tooltip(
"Timestamped DB backup.",
target="workspace-db-backup-btn",
),
dbc.Tooltip(
"Export authoring JSON.",
target="workspace-db-export-map-btn",
),
dbc.Tooltip(
"Export zone JSON.",
target="workspace-db-export-zone-btn",
),
dbc.Tooltip(
"Dump SQL schema + data.",
target="workspace-db-export-sql-btn",
),
],
className="mb-2",
),
html.Div(
id="workspace-db-feedback",
className="small mb-2",
),
html.Div(
id="workspace-db-summary",
className="small",
),
html.Hr(className="my-2"),
html.Div(
id="workspace-db-table",
),
],
label="SQLite DB",
),
# Additional placeholder tabs for upcoming work.
dbc.Tab(
[
html.P(
"Rooms for the selected map.",
className="small text-muted mb-2",
),
html.P(
"Click a row to focus the editor.",
className="small text-muted mb-2",
),
html.Div(
id="workspace-room-table",
),
html.Hr(className="my-2"),
html.Div(
[
html.Div(
"Zone Exits",
className=zone_exit_header_class,
),
html.P(
zone_exit_help_text,
className=zone_exit_help_class,
),
# Feedback
html.Div(
id="workspace-zone-exit-feedback",
className=zone_exit_feedback_class,
),
# Editor row
dbc.Row(
[
dbc.Col(
dcc.Dropdown(
id="workspace-zone-exit-direction",
options=exit_direction_options,
placeholder="Dir",
clearable=True,
className="small",
),
width=2,
),
dbc.Col(
dcc.Dropdown(
id="workspace-zone-exit-zone",
options=[],
placeholder="Zone",
clearable=True,
className="small",
),
width=4,
),
dbc.Col(
dcc.Dropdown(
id="workspace-zone-exit-room",
options=[],
placeholder="Room",
clearable=True,
disabled=True,
className="small",
),
width=4,
),
dbc.Col(
dbc.Button(
[
zone_exit_save_icon,
"Save",
],
id="workspace-zone-exit-save",
color="primary",
size="sm",
className="w-100",
),
width=1,
),
dbc.Col(
dbc.Button(
[
zone_exit_clear_icon,
"Clear",
],
id="workspace-zone-exit-clear",
color="secondary",
size="sm",
outline=True,
className="w-100",
),
width=1,
),
],
className=zone_exit_row_class,
),
# Table view
html.Div(
id="workspace-zone-exit-table",
),
],
className="mt-2",
),
],
label="Rooms",
),
dbc.Tab(
[
dbc.Row(
[
dbc.Col(
html.Div(
"World JSON",
className="fw-semibold",
),
width=8,
),
dbc.Col(
dbc.Button(
[
world_refresh_icon,
"Refresh",
],
id="workspace-world-json-refresh",
color="secondary",
size="sm",
outline=True,
className="w-100",
),
width=4,
),
],
className="align-items-center mb-2",
),
html.Div(
id="workspace-world-json",
),
],
label="World JSON",
),
_create_api_workspace_tab(),
dbc.Tab(
html.P(
"Placeholder: index inspector",
className="small text-muted mb-0",
),
label="Indexes",
),
dbc.Tab(
html.P(
"Placeholder: stats and vacuum",
className="small text-muted mb-0",
),
label="Stats",
),
dbc.Tab(
html.P(
"Placeholder: notes and todos",
className="small text-muted mb-0",
),
label="Notes",
),
]
)
),
],
className="map-side-card",
),
className="ps-0",
style={"minWidth": 0},
),
],
className="g-2 align-items-start",
),
# ---------------------------------------------------------
# Z-Level Filter Checkboxes
# ---------------------------------------------------------
# Allow users to show/hide specific Z-levels. All are shown
# by default. Colored indicators match room styling.
html.Div(
[
html.Label("Show Layers:", className="me-3"),
dbc.Checklist(
id="z-level-filter",
options=[
# Down level: black circle indicator
{
"label": html.Span(
[
html.Span(
"",
style={
"display": "inline-block",
"width": "10px",
"height": "10px",
"backgroundColor": "#282828",
"borderRadius": "50%",
"marginRight": "4px",
"verticalAlign": "middle",
},
),
"Down (z=-1)",
]
),
"value": -1,
},
# Ground level: blue circle indicator
{
"label": html.Span(
[
html.Span(
"",
style={
"display": "inline-block",
"width": "12px",
"height": "12px",
"backgroundColor": "#4682B4",
"borderRadius": "50%",
"marginRight": "4px",
"verticalAlign": "middle",
},
),
"Ground (z=0)",
]
),
"value": 0,
},
# Up level: white circle with black border
{
"label": html.Span(
[
html.Span(
"",
style={
"display": "inline-block",
"width": "11px",
"height": "11px",
"backgroundColor": "white",
"border": "2px solid #282828",
"borderRadius": "50%",
"marginRight": "4px",
"verticalAlign": "middle",
},
),
"Up (z=+1)",
]
),
"value": 1,
},
],
# All levels checked by default (show all rooms)
value=[-1, 0, 1],
inline=True,
),
# ---------------------------------------------------------
# Z-Level Visual Offset Control
# ---------------------------------------------------------
# Allows users to adjust how much stacked rooms are
# visually separated. 0 = no offset (overlap), higher
# values = more separation. Uses -/+/input for easy control.
html.Div(
[
html.Label(
"Stack Offset X:",
className="ms-4 me-2",
style={"whiteSpace": "nowrap"},
),
dbc.InputGroup(
[
dbc.Button(
"-",
id="z-level-offset-x-decrease",
color="secondary",
size="sm",
style={"width": "32px"},
),
dbc.Input(
id="z-level-offset-x",
type="number",
value=0.4,
min=-5,
max=5,
step=0.1,
size="sm",
style={"width": "70px", "textAlign": "center"},
),
dbc.Button(
"+",
id="z-level-offset-x-increase",
color="secondary",
size="sm",
style={"width": "32px"},
),
],
size="sm",
),
html.Label(
"Y:",
className="ms-3 me-2",
style={"whiteSpace": "nowrap"},
),
dbc.InputGroup(
[
dbc.Button(
"-",
id="z-level-offset-y-decrease",
color="secondary",
size="sm",
style={"width": "32px"},
),
dbc.Input(
id="z-level-offset-y",
type="number",
value=0.4,
min=-5,
max=5,
step=0.1,
size="sm",
style={"width": "70px", "textAlign": "center"},
),
dbc.Button(
"+",
id="z-level-offset-y-increase",
color="secondary",
size="sm",
style={"width": "32px"},
),
],
size="sm",
),
],
className="d-flex align-items-center",
),
],
className="d-flex align-items-center mt-2 p-2 bg-body-tertiary rounded",
),
]
),
],
className="h-100",
)