Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -129,25 +129,25 @@ upgrade: ## Upgrade all dependencies
example: example-litestar ## Alias for example-litestar

example-asgi: ## Run ASGI example app (http://localhost:8000)
@uv run python examples/asgi_basic/app.py
@uv run python -m examples.asgi_basic.app

example-litestar: ## Run Litestar example app (http://localhost:8001)
@uv run python examples/litestar_basic/app.py
@uv run python -m examples.litestar_basic.app

example-aa: ## Run Litestar + Advanced-Alchemy example (http://localhost:8002)
@uv run python examples/litestar_advanced_alchemy/app.py
@uv run python -m examples.litestar_advanced_alchemy.app

example-graphql: ## Run GraphQL + Strawberry example (http://localhost:8003)
@uv run python examples/graphql_panel_example.py
@uv run python -m examples.graphql_panel_example

example-websocket: ## Run WebSocket example app (http://localhost:8002)
@uv run python examples/websocket_panel_example.py
@uv run python -m examples.websocket_panel_example

example-mcp: ## Run MCP server example (http://localhost:8004)
@uv run python examples/mcp_server_example.py
@uv run python -m examples.mcp_server_example

example-mcp-server: ## Run MCP server (stdio transport)
@uv run python examples/mcp_server_example.py --mcp
@uv run python -m examples.mcp_server_example --mcp

##@ Git Worktrees

Expand Down
1 change: 1 addition & 0 deletions examples/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Example applications for the debug-toolbar library."""
6 changes: 6 additions & 0 deletions src/debug_toolbar/litestar/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,10 @@ def _render_toolbar(self, data: dict[str, Any]) -> str:
<link rel="stylesheet" href="/_debug_toolbar/static/toolbar.css">
<div id="debug-toolbar" data-request-id="{request_id}">
<div class="toolbar-bar">
<button class="toolbar-collapse-btn" title="Collapse toolbar"
aria-label="Collapse toolbar" aria-expanded="true">
<span class="collapse-icon">&laquo;</span>
</button>
<span class="toolbar-brand" title="Click to toggle">Debug Toolbar</span>
<span class="toolbar-time">{total_time:.2f}ms</span>
<div class="toolbar-panels">
Expand All @@ -588,6 +592,8 @@ def _render_toolbar(self, data: dict[str, Any]) -> str:
<a href="/_debug_toolbar/" class="toolbar-history-link" title="View request history">History</a>
</div>
<div class="toolbar-details"></div>
<!-- Reserved for future toolbar content and use by toolbar.js -->
<div class="toolbar-content"></div>
</div>
<script src="/_debug_toolbar/static/toolbar.js"></script>
"""
258 changes: 252 additions & 6 deletions src/debug_toolbar/litestar/routes/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1304,19 +1304,29 @@ def get_toolbar_css() -> str:
flex-direction: column;
}

/* Collapsed state - hide everything except collapse button */
#debug-toolbar.collapsed .toolbar-panels,
#debug-toolbar.collapsed .toolbar-details,
#debug-toolbar.collapsed .toolbar-content {
display: none;
}

/* Collapsed state for side positions */
#debug-toolbar.collapsed .toolbar-content,
#debug-toolbar.collapsed .toolbar-brand,
#debug-toolbar.collapsed .toolbar-time,
#debug-toolbar.collapsed .toolbar-request-id,
#debug-toolbar.collapsed .toolbar-history-link,
#debug-toolbar.collapsed .toolbar-theme-btn,
#debug-toolbar.collapsed .toolbar-position-btn,
#debug-toolbar.collapsed .toolbar-position-controls,
#debug-toolbar.collapsed .toolbar-resize-handle {
display: none !important;
}

/* Collapsed state for side positions - shrink to just the button */
#debug-toolbar.collapsed[data-position="right"],
#debug-toolbar[data-position="right"].collapsed,
#debug-toolbar.collapsed[data-position="left"],
#debug-toolbar[data-position="left"].collapsed {
width: auto;
min-width: 0;
max-width: 50px;
}

#debug-toolbar.collapsed[data-position="top"],
Expand All @@ -1325,6 +1335,13 @@ def get_toolbar_css() -> str:
#debug-toolbar[data-position="bottom"].collapsed {
height: auto;
min-height: 0;
max-height: 50px;
}

/* Collapsed toolbar bar should be minimal */
#debug-toolbar.collapsed .toolbar-bar {
padding: 6px;
justify-content: center;
}

/* Resize handle */
Expand Down Expand Up @@ -1462,6 +1479,63 @@ def get_toolbar_css() -> str:
font-size: 14px;
}

.toolbar-collapse-btn {
background: var(--dt-bg-tertiary);
border: none;
width: 28px;
height: 28px;
border-radius: 4px;
color: var(--dt-text-secondary);
cursor: pointer;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.15s, color 0.15s, transform 0.2s;
flex-shrink: 0;
}

.toolbar-collapse-btn:hover {
background: var(--dt-accent);
color: white;
}

/* Right position (default): expanded Β» (points right to collapse), collapsed Β« (points left to expand) */
#debug-toolbar[data-position="right"] .toolbar-collapse-btn .collapse-icon,
#debug-toolbar:not([data-position]) .toolbar-collapse-btn .collapse-icon {
transform: rotate(180deg);
}

#debug-toolbar[data-position="right"].collapsed .toolbar-collapse-btn .collapse-icon,
#debug-toolbar:not([data-position]).collapsed .toolbar-collapse-btn .collapse-icon {
transform: rotate(0deg);
}

/* Left position: expanded Β« (points left to collapse), collapsed Β» (points right to expand) */
#debug-toolbar[data-position="left"] .toolbar-collapse-btn .collapse-icon {
transform: rotate(0deg);
}

#debug-toolbar[data-position="left"].collapsed .toolbar-collapse-btn .collapse-icon {
transform: rotate(180deg);
}

#debug-toolbar[data-position="top"] .toolbar-collapse-btn .collapse-icon {
transform: rotate(90deg);
}

#debug-toolbar[data-position="top"].collapsed .toolbar-collapse-btn .collapse-icon {
transform: rotate(-90deg);
}

#debug-toolbar[data-position="bottom"] .toolbar-collapse-btn .collapse-icon {
transform: rotate(-90deg);
}

#debug-toolbar[data-position="bottom"].collapsed .toolbar-collapse-btn .collapse-icon {
transform: rotate(90deg);
}

.toolbar-brand {
font-weight: bold;
color: var(--dt-accent);
Expand Down Expand Up @@ -2540,6 +2614,161 @@ def get_toolbar_css() -> str:
border-radius: 4px;
font-size: 11px;
}

/* Mobile Responsiveness */
@media (max-width: 768px) {
/* Automatically position toolbar at bottom on mobile for better UX */
#debug-toolbar,
#debug-toolbar[data-position="right"],
#debug-toolbar[data-position="left"],
#debug-toolbar[data-position="top"] {
top: auto !important;
bottom: 0 !important;
left: 0 !important;
right: 0 !important;
width: 100vw !important;
height: auto;
max-width: 100vw !important;
min-height: 40px;
max-height: 70vh;
border-left: none !important;
border-right: none !important;
border-bottom: none !important;
border-top: 2px solid var(--dt-accent);
flex-direction: column;
}

/* Adjust toolbar bar for mobile */
.toolbar-bar {
flex-wrap: wrap;
padding: 8px 12px;
gap: 8px;
font-size: 12px;
}

/* Make brand smaller on mobile */
.toolbar-brand {
font-size: 13px;
}

/* Adjust time display */
.toolbar-time {
font-size: 11px;
}

/* Stack panels vertically or wrap */
.toolbar-panels {
flex-wrap: wrap;
width: 100%;
gap: 6px;
}

.toolbar-panel-btn {
padding: 6px 10px;
font-size: 11px;
min-height: 32px;
flex: 1 1 auto;
}

/* Touch-friendly buttons - minimum 44px */
.toolbar-collapse-btn,
.toolbar-theme-btn,
.toolbar-position-btn {
min-width: 32px;
min-height: 32px;
width: 32px;
height: 32px;
}

/* Adjust details panel for mobile */
.toolbar-details {
max-height: 50vh;
overflow-y: auto;
font-size: 12px;
}

/* Make content scrollable */
.toolbar-content {
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}

/* Optimize panel tables for mobile */
.panel-table {
font-size: 11px;
}

.panel-table td {
padding: 6px 8px;
}

/* Adjust metadata list for mobile */
.metadata-list {
grid-template-columns: 100px 1fr;
gap: 6px 12px;
font-size: 12px;
}

/* Better spacing for alerts on mobile */
.alert-card {
padding: 10px;
margin-bottom: 8px;
}

/* Memory stats grid adjustment */
.memory-summary,
.async-summary,
.graphql-summary,
.ws-summary {
grid-template-columns: repeat(auto-fit, minmax(70px, 1fr));
gap: 6px;
font-size: 11px;
}

/* Collapsed state on mobile - just show collapse button */
#debug-toolbar.collapsed {
max-height: 50px;
min-height: 40px;
max-width: 60px;
width: auto;
left: auto !important;
right: 10px !important;
bottom: 10px !important;
border-radius: 8px;
}

/* Ensure resize handle is hidden on mobile */
.toolbar-resize-handle {
display: none;
}

/* Position controls - make them more touch-friendly */
.toolbar-position-controls {
gap: 6px;
}
}

/* Extra small screens - hide non-essential elements */
@media (max-width: 480px) {
.toolbar-request-id {
display: none;
}

.panel-subtitle {
display: none;
}
}

/* Landscape mobile / small tablets */
@media (max-width: 1024px) and (max-height: 600px) {
#debug-toolbar {
max-height: 50vh;
}

.toolbar-details {
max-height: 35vh;
}
}
"""


Expand Down Expand Up @@ -2590,7 +2819,7 @@ def get_toolbar_js() -> str:
class DebugToolbar {
constructor(element) {
this.element = element;
this.isCollapsed = false;
this.isCollapsed = localStorage.getItem('debug-toolbar-collapsed') === 'true';
this.activePanel = null;
this.position = localStorage.getItem('debug-toolbar-position') || 'right';
this.theme = localStorage.getItem('debug-toolbar-theme') || 'dark';
Expand All @@ -2607,6 +2836,12 @@ class DebugToolbar {
this.addThemeToggle();
this.addPositionControls();
this.addResizeHandle();
this.addCollapseButton();

// Apply saved collapsed state
if (this.isCollapsed) {
this.element.classList.add('collapsed');
}

const brand = this.element.querySelector('.toolbar-brand');
if (brand) {
Expand All @@ -2620,6 +2855,16 @@ class DebugToolbar {
});
}

addCollapseButton() {
const collapseBtn = this.element.querySelector('.toolbar-collapse-btn');
if (collapseBtn) {
collapseBtn.addEventListener('click', (e) => {
e.stopPropagation();
this.toggle();
});
}
}

addThemeToggle() {
this.themeBtn = document.createElement('button');
this.themeBtn.className = 'toolbar-theme-btn';
Expand Down Expand Up @@ -2760,6 +3005,7 @@ class DebugToolbar {
toggle() {
this.isCollapsed = !this.isCollapsed;
this.element.classList.toggle('collapsed', this.isCollapsed);
localStorage.setItem('debug-toolbar-collapsed', String(this.isCollapsed));
}

showPanel(panelId) {
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/test_litestar_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
import gzip

import pytest
from litestar.status_codes import HTTP_200_OK
from litestar.testing import TestClient

from debug_toolbar.litestar import DebugToolbarPlugin, LitestarDebugToolbarConfig
from litestar import Litestar, MediaType, Response, get
from litestar.status_codes import HTTP_200_OK


@get("/", media_type=MediaType.HTML)
Expand Down Expand Up @@ -277,7 +277,7 @@ def test_works_with_before_after_request(self) -> None:
Note: We only verify before_request hook is called. The after_request
hook timing varies in CI environments due to async execution order.
"""
from litestar import Request, Response
from litestar import Request

hook_state: dict[str, bool] = {"before": False, "after": False}

Expand Down
Loading
Loading