diff --git a/Makefile b/Makefile
index bff7212..56b5905 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/examples/__init__.py b/examples/__init__.py
new file mode 100644
index 0000000..c938aac
--- /dev/null
+++ b/examples/__init__.py
@@ -0,0 +1 @@
+"""Example applications for the debug-toolbar library."""
diff --git a/src/debug_toolbar/litestar/middleware.py b/src/debug_toolbar/litestar/middleware.py
index 53400bd..625f8ef 100644
--- a/src/debug_toolbar/litestar/middleware.py
+++ b/src/debug_toolbar/litestar/middleware.py
@@ -576,6 +576,10 @@ def _render_toolbar(self, data: dict[str, Any]) -> str:
"""
diff --git a/src/debug_toolbar/litestar/routes/handlers.py b/src/debug_toolbar/litestar/routes/handlers.py
index 8be448e..cd9bf47 100644
--- a/src/debug_toolbar/litestar/routes/handlers.py
+++ b/src/debug_toolbar/litestar/routes/handlers.py
@@ -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"],
@@ -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 */
@@ -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);
@@ -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;
+ }
+}
"""
@@ -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';
@@ -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) {
@@ -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';
@@ -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) {
diff --git a/tests/integration/test_litestar_middleware.py b/tests/integration/test_litestar_middleware.py
index 8a92396..fb3c6e8 100644
--- a/tests/integration/test_litestar_middleware.py
+++ b/tests/integration/test_litestar_middleware.py
@@ -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)
@@ -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}
diff --git a/uv.lock b/uv.lock
index 0c8152f..bc3133d 100644
--- a/uv.lock
+++ b/uv.lock
@@ -420,7 +420,7 @@ wheels = [
[[package]]
name = "debug-toolbar"
-version = "0.3.0"
+version = "0.3.1"
source = { editable = "." }
dependencies = [
{ name = "jinja2" },