| name | layout-widgets |
| description | Use when calculating Langfuse dashboard widget grid positions. Trigger phrases: "layout widgets", "widget placement", "grid position", "arrange widgets", "widget positioning". Computes x/y coordinates and sizes for the 12-column dashboard grid.
|
Layout Widgets on Langfuse Dashboard Grid
Calculate x, y, x_size, and y_size values for new Langfuse dashboard
widget placement entries on the 12-column grid.
Grid Rules
- The dashboard grid is 12 columns wide (Langfuse default).
- Maximum 2 half-width widgets per row (each 6 columns).
- Default widget size:
x_size: 6, y_size: 4 (half-width, 4 rows tall).
- Full-width widget:
x_size: 12, y_size: 4.
- Widgets cannot overlap.
- Column positions: 0 through 11 (zero-indexed).
Widget Placement Entry Shape
Each widget placement entry in the dashboard definition.widgets[] array:
{
"type": "widget",
"id": "<uuid4 for placement slot>",
"widgetId": "<cuid of dashboard_widget row>",
"x": 0,
"y": 0,
"x_size": 6,
"y_size": 4
}
Key distinction:
id -- UUID v4 placement slot ID generated via uuid.uuid4().
widgetId -- CUID for the dashboard_widgets.id row.
Placement Algorithm
Step 1: Parse Existing Widget Positions
Read the current dashboard definition:
SELECT definition FROM dashboards
WHERE id = %(dashboard_id)s AND project_id = %(project_id)s;
Extract all widget placement entries from definition["widgets"]. Each entry
has x, y, x_size, and y_size.
Step 2: Find the Bottom of the Grid
Use the maximum y + y_size across existing widgets; use 0 when the dashboard is empty.
Step 3: Analyze the Last Row
Use the largest existing y as last_row_y, collect widgets at that y, and sum their x_size values.
Step 4: Determine New Widget Position
Apply the placement rules:
def calculate_position(
existing_widgets: list[dict],
new_x_size: int = 6,
new_y_size: int = 4,
) -> dict:
"""Calculate the grid position for a new widget."""
if not existing_widgets:
return {"x": 0, "y": 0, "x_size": new_x_size, "y_size": new_y_size}
max_y = max(w["y"] + w["y_size"] for w in existing_widgets)
last_row_y = max(w["y"] for w in existing_widgets)
last_row_widgets = [w for w in existing_widgets if w["y"] == last_row_y]
last_row_width = sum(w["x_size"] for w in last_row_widgets)
remaining_space = 12 - last_row_width
if remaining_space >= new_x_size and len(last_row_widgets) < 2:
new_x = last_row_width
new_y = last_row_y
else:
new_x = 0
new_y = max_y
return {"x": new_x, "y": new_y, "x_size": new_x_size, "y_size": new_y_size}
Step 5: Construct the Placement Entry
Generate a UUID v4 for the placement slot and build the entry:
import uuid
placement = calculate_position(existing_widgets)
entry = {
"type": "widget",
"id": str(uuid.uuid4()),
"widgetId": widget_id,
**placement,
}
Layout Examples
Empty Dashboard -- First Widget
Position: x=0, y=0, x_size=6, y_size=4
| Widget A (6 cols) | |
| | |
One Half-Width Widget -- Second Widget
Position: x=6, y=0, x_size=6, y_size=4
| Widget A (6 cols) | Widget B (6 cols) |
| | |
Two Half-Width Widgets -- Third Widget (New Row)
Position: x=0, y=4, x_size=6, y_size=4
| Widget A (6 cols) | Widget B (6 cols) |
| | |
| Widget C (6 cols) | |
| | |
Full-Width Widget on Last Row -- Next Widget (New Row)
When the last row has a full-width widget (x_size=12), always start a new row:
Position: x=0, y=8, x_size=6, y_size=4
| Full-Width Widget (12 cols) |
| |
| Widget D (6 cols) | |
| | |
Batch Layout
For batch placement (e.g., from suggest-widgets), append each new entry to
existing_widgets before calculating the next position:
new_entries = []
all_widgets = list(existing_widgets)
for widget_id in widget_ids_to_place:
position = calculate_position(all_widgets)
entry = {
"type": "widget",
"id": str(uuid.uuid4()),
"widgetId": widget_id,
**position,
}
new_entries.append(entry)
all_widgets.append(entry)
Size Recommendations
| Widget Type | Recommended Size |
|---|
| Number (big stat) | x_size: 3, y_size: 2 |
| Standard chart | x_size: 6, y_size: 4 |
| Wide chart | x_size: 12, y_size: 4 |
| Tall pivot table | x_size: 12, y_size: 6 |
| Histogram | x_size: 6, y_size: 4 |
Adjust sizes based on the user's preference. The algorithm handles any valid
size within the 12-column constraint.