plain.toolbar 0.10.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ .venv
2
+ /.env
3
+ *.egg-info
4
+ *.py[co]
5
+ __pycache__
6
+ *.DS_Store
7
+
8
+ /*.code-workspace
9
+
10
+ # Test apps
11
+ plain*/tests/.plain
12
+
13
+ # Agent scratch files
14
+ /scratch
15
+
16
+ # Plain temp dirs
17
+ .plain
18
+
19
+ .vscode
20
+ /.claude/skills/plain-*
21
+ /.benchmarks
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2023, Dropseed, LLC
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,233 @@
1
+ Metadata-Version: 2.4
2
+ Name: plain.toolbar
3
+ Version: 0.10.0
4
+ Summary: Debug toolbar for Plain applications
5
+ Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
6
+ License-Expression: BSD-3-Clause
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.13
9
+ Requires-Dist: plain-tailwind<1.0.0
10
+ Requires-Dist: plain<1.0.0
11
+ Description-Content-Type: text/markdown
12
+
13
+ # plain.toolbar
14
+
15
+ **A developer toolbar that displays debugging information in your browser.**
16
+
17
+ - [Overview](#overview)
18
+ - [Built-in panels](#built-in-panels)
19
+ - [Request panel](#request-panel)
20
+ - [Exception panel](#exception-panel)
21
+ - [Creating custom toolbar items](#creating-custom-toolbar-items)
22
+ - [Button-only items](#button-only-items)
23
+ - [Visibility](#visibility)
24
+ - [JavaScript API](#javascript-api)
25
+ - [FAQs](#faqs)
26
+ - [Installation](#installation)
27
+
28
+ ## Overview
29
+
30
+ The toolbar appears at the bottom of your browser window and shows useful debugging information about the current request. You can expand it to see detailed panels, switch between tabs, and resize it by dragging the top edge.
31
+
32
+ To render the toolbar, add the `{% toolbar %}` tag to your base template (typically just before the closing `</body>` tag):
33
+
34
+ ```html
35
+ <!DOCTYPE html>
36
+ <html>
37
+ <head>
38
+ <title>My App</title>
39
+ </head>
40
+ <body>
41
+ {% block content %}{% endblock %}
42
+
43
+ {% toolbar %}
44
+ </body>
45
+ </html>
46
+ ```
47
+
48
+ The toolbar automatically hides itself in production unless the user is an admin. In debug mode, it always appears.
49
+
50
+ ## Built-in panels
51
+
52
+ ### Request panel
53
+
54
+ The Request panel shows information about the current HTTP request:
55
+
56
+ - Request ID
57
+ - Query parameters
58
+ - HTTP method
59
+ - View class
60
+ - URL pattern, name, args, and kwargs
61
+ - Template names (if available)
62
+ - Primary object (if the view has an `object` attribute)
63
+
64
+ ### Exception panel
65
+
66
+ When an exception occurs during request handling, the Exception panel automatically appears with:
67
+
68
+ - The exception type and message
69
+ - A color-coded traceback showing frames from your app, Plain, third-party packages, and Python stdlib
70
+ - Source code context around each frame (expandable/collapsible)
71
+ - Local variables for each frame (in debug mode)
72
+ - A "Copy" button to copy the full exception for sharing
73
+ - A "View raw" button to see the standard Python traceback format
74
+ - Clickable file paths that open in VS Code
75
+
76
+ App frames are highlighted in amber and expanded by default, making it easy to spot where the error occurred in your code.
77
+
78
+ ## Creating custom toolbar items
79
+
80
+ You can add your own panels to the toolbar by creating a `ToolbarItem` subclass and registering it with the `@register_toolbar_item` decorator.
81
+
82
+ Create a `toolbar.py` file in any installed app:
83
+
84
+ ```python
85
+ # app/users/toolbar.py
86
+ from plain.toolbar import ToolbarItem, register_toolbar_item
87
+
88
+
89
+ @register_toolbar_item
90
+ class UserToolbarItem(ToolbarItem):
91
+ name = "User"
92
+ panel_template_name = "toolbar/user.html"
93
+
94
+ def get_template_context(self):
95
+ context = super().get_template_context()
96
+ context["current_user"] = getattr(self.request, "user", None)
97
+ return context
98
+ ```
99
+
100
+ Then create the panel template:
101
+
102
+ ```html
103
+ <!-- app/users/templates/toolbar/user.html -->
104
+ <div class="px-6 py-4 text-sm">
105
+ {% if current_user %}
106
+ <dl class="grid grid-cols-[max-content_1fr] gap-x-8 gap-y-2">
107
+ <dt>Email</dt>
108
+ <dd class="text-white/50">{{ current_user.email }}</dd>
109
+ <dt>ID</dt>
110
+ <dd class="text-white/50">{{ current_user.id }}</dd>
111
+ </dl>
112
+ {% else %}
113
+ <p class="text-white/50">No user logged in</p>
114
+ {% endif %}
115
+ </div>
116
+ ```
117
+
118
+ The toolbar uses autodiscovery to find `toolbar.py` files in all installed apps.
119
+
120
+ ### Button-only items
121
+
122
+ You can also create toolbar items that only show a button in the minimized toolbar bar (no expandable panel). Set `button_template_name` instead of `panel_template_name`:
123
+
124
+ ```python
125
+ @register_toolbar_item
126
+ class QuickActionToolbarItem(ToolbarItem):
127
+ name = "QuickAction"
128
+ button_template_name = "toolbar/quick_action_button.html"
129
+
130
+ def is_enabled(self):
131
+ # Only show when a certain condition is met
132
+ return some_condition
133
+ ```
134
+
135
+ Override `is_enabled()` to control when your toolbar item appears.
136
+
137
+ ## Visibility
138
+
139
+ The toolbar only renders when `Toolbar.should_render()` returns `True`. This happens when:
140
+
141
+ 1. `DEBUG` is `True`, or
142
+ 2. The user has `is_admin = True`, or
143
+ 3. An admin is impersonating another user (requires `plain.admin`)
144
+
145
+ You can also temporarily hide the toolbar:
146
+
147
+ - Click the X button to hide it for the current session
148
+ - Click the clock icon to hide it for 1 hour (stored in localStorage)
149
+ - Call `plainToolbar.show()` in the browser console to bring it back
150
+
151
+ ## JavaScript API
152
+
153
+ The toolbar exposes a `window.plainToolbar` object for programmatic control:
154
+
155
+ ```javascript
156
+ // Show/hide the toolbar
157
+ plainToolbar.show();
158
+ plainToolbar.hide();
159
+
160
+ // Expand/collapse the details panel
161
+ plainToolbar.expand();
162
+ plainToolbar.collapse();
163
+ plainToolbar.toggleExpand();
164
+
165
+ // Show a specific tab
166
+ plainToolbar.showTab("Request");
167
+ plainToolbar.showTab("Exception");
168
+
169
+ // Hide for a duration (milliseconds from now)
170
+ plainToolbar.hideUntil(Date.now() + 3600000); // Hide for 1 hour
171
+
172
+ // Reset custom height
173
+ plainToolbar.resetHeight();
174
+ ```
175
+
176
+ ## FAQs
177
+
178
+ #### How do I style my custom panel?
179
+
180
+ The toolbar uses Tailwind CSS classes. Your panel template has access to all Tailwind utilities. The toolbar has a dark theme, so use light text colors like `text-white`, `text-stone-300`, or `text-white/50` for muted text.
181
+
182
+ #### Can I add multiple custom panels?
183
+
184
+ Yes. Create multiple `ToolbarItem` subclasses, each with its own `name` and templates. They will appear as separate tabs in the toolbar.
185
+
186
+ #### Why does the Exception panel open automatically?
187
+
188
+ When an exception occurs, the toolbar automatically expands and shows the Exception panel so you can immediately see what went wrong. This behavior is intentional to surface errors quickly during development.
189
+
190
+ #### How do I disable the toolbar completely?
191
+
192
+ Remove `plain.toolbar` from your `INSTALLED_PACKAGES` setting. Alternatively, remove the `{% toolbar %}` tag from your templates.
193
+
194
+ ## Installation
195
+
196
+ Install the `plain.toolbar` package from PyPI:
197
+
198
+ ```console
199
+ uv add plain.toolbar
200
+ ```
201
+
202
+ Add `plain.toolbar` to your `INSTALLED_PACKAGES` in `app/settings.py`:
203
+
204
+ ```python
205
+ INSTALLED_PACKAGES = [
206
+ # ... other packages
207
+ "plain.toolbar",
208
+ ]
209
+ ```
210
+
211
+ Add the `{% toolbar %}` template tag to your base template, just before the closing `</body>` tag:
212
+
213
+ ```html
214
+ <!DOCTYPE html>
215
+ <html>
216
+ <head>
217
+ <title>My App</title>
218
+ </head>
219
+ <body>
220
+ {% block content %}{% endblock %}
221
+
222
+ {% toolbar %}
223
+ </body>
224
+ </html>
225
+ ```
226
+
227
+ A `VERSION` setting is required in your `app/settings.py` to display in the toolbar:
228
+
229
+ ```python
230
+ VERSION = "1.0.0"
231
+ ```
232
+
233
+ The toolbar should now appear at the bottom of your browser window in debug mode.
@@ -0,0 +1 @@
1
+ plain/toolbar/README.md
@@ -0,0 +1,190 @@
1
+ # plain-toolbar changelog
2
+
3
+ ## [0.10.0](https://github.com/dropseed/plain/releases/plain-toolbar@0.10.0) (2026-01-15)
4
+
5
+ ### What's changed
6
+
7
+ - The toolbar now displays on mobile devices instead of being hidden ([ee7acaa](https://github.com/dropseed/plain/commit/ee7acaa67c))
8
+ - Panel tabs are horizontally scrollable on mobile for better touch navigation ([ee7acaa](https://github.com/dropseed/plain/commit/ee7acaa67c))
9
+ - Panel content area uses a taller height (50vh) on mobile for improved usability ([ee7acaa](https://github.com/dropseed/plain/commit/ee7acaa67c))
10
+ - Resize handle is hidden on mobile since touch-based resizing is not practical ([ee7acaa](https://github.com/dropseed/plain/commit/ee7acaa67c))
11
+ - Buttons and icons are sized larger on mobile for better touch targets ([ee7acaa](https://github.com/dropseed/plain/commit/ee7acaa67c))
12
+
13
+ ### Upgrade instructions
14
+
15
+ - No changes required
16
+
17
+ ## [0.9.0](https://github.com/dropseed/plain/releases/plain-toolbar@0.9.0) (2026-01-13)
18
+
19
+ ### What's changed
20
+
21
+ - Added comprehensive README documentation for the package ([8f1aed3](https://github.com/dropseed/plain/commit/8f1aed34ff))
22
+
23
+ ### Upgrade instructions
24
+
25
+ - No changes required
26
+
27
+ ## [0.8.2](https://github.com/dropseed/plain/releases/plain-toolbar@0.8.2) (2025-12-12)
28
+
29
+ ### What's changed
30
+
31
+ - Exception traceback frame badges now use more distinct colors to improve visual differentiation between app, plain, plainx, python, and third-party frames ([3911c11](https://github.com/dropseed/plain/commit/3911c1114d842aed4f48782a767c589d71f219af))
32
+
33
+ ### Upgrade instructions
34
+
35
+ - No changes required
36
+
37
+ ## [0.8.1](https://github.com/dropseed/plain/releases/plain-toolbar@0.8.1) (2025-12-05)
38
+
39
+ ### What's changed
40
+
41
+ - Toolbar tabs now display in registration order instead of being sorted alphabetically ([ef81fb8](https://github.com/dropseed/plain/commit/ef81fb8ff0b7ab09760d4b63e904e72ee2d4b97d))
42
+ - Toolbar expand and close button icons are now more subtle with improved sizing ([ab038bd](https://github.com/dropseed/plain/commit/ab038bddef6b8f5108efd1c6315f3537898b20b8))
43
+
44
+ ### Upgrade instructions
45
+
46
+ - No changes required
47
+
48
+ ## [0.8.0](https://github.com/dropseed/plain/releases/plain-toolbar@0.8.0) (2025-12-04)
49
+
50
+ ### What's changed
51
+
52
+ - Exception toolbar now displays rich traceback frames with expandable source code context instead of raw traceback text ([9c4415e](https://github.com/dropseed/plain/commit/9c4415ed6266a36014f1fea75033f3bba4a23b7c))
53
+ - Frames are categorized by source (app, plain, plainx, python, third-party) with color-coded badges ([9c4415e](https://github.com/dropseed/plain/commit/9c4415ed6266a36014f1fea75033f3bba4a23b7c))
54
+ - App frames are expanded by default while framework/library frames are collapsed ([9c4415e](https://github.com/dropseed/plain/commit/9c4415ed6266a36014f1fea75033f3bba4a23b7c))
55
+ - Local variables are displayed for each frame when `DEBUG=True` ([9c4415e](https://github.com/dropseed/plain/commit/9c4415ed6266a36014f1fea75033f3bba4a23b7c))
56
+ - Frame filenames link directly to VS Code at the exact line number ([9c4415e](https://github.com/dropseed/plain/commit/9c4415ed6266a36014f1fea75033f3bba4a23b7c))
57
+ - Toggle between rich frame view and raw traceback text with the "View raw" button ([9c4415e](https://github.com/dropseed/plain/commit/9c4415ed6266a36014f1fea75033f3bba4a23b7c))
58
+
59
+ ### Upgrade instructions
60
+
61
+ - No changes required
62
+
63
+ ## [0.7.1](https://github.com/dropseed/plain/releases/plain-toolbar@0.7.1) (2025-10-31)
64
+
65
+ ### What's changed
66
+
67
+ - The main toolbar script now includes `nonce="{{ request.csp_nonce }}"` for better Content Security Policy compliance ([10f642a](https://github.com/dropseed/plain/commit/10f642a097aa487400f2dffd341f595d93218af9))
68
+
69
+ ### Upgrade instructions
70
+
71
+ - No changes required
72
+
73
+ ## [0.7.0](https://github.com/dropseed/plain/releases/plain-toolbar@0.7.0) (2025-10-29)
74
+
75
+ ### What's changed
76
+
77
+ - The toolbar JavaScript now uses CSP-compliant methods by avoiding inline style injection and using CSS classes instead ([784f3dd](https://github.com/dropseed/plain/commit/784f3dd9724c11256cc3aa0a0e15c5c3eae6133c))
78
+ - Exception template uses `data-` attributes and event listeners instead of inline `onclick` handlers for better CSP compliance ([784f3dd](https://github.com/dropseed/plain/commit/784f3dd9724c11256cc3aa0a0e15c5c3eae6133c))
79
+ - Script tags now include `nonce="{{ request.csp_nonce }}"` to work with Content Security Policy ([784f3dd](https://github.com/dropseed/plain/commit/784f3dd9724c11256cc3aa0a0e15c5c3eae6133c))
80
+
81
+ ### Upgrade instructions
82
+
83
+ - No changes required
84
+
85
+ ## [0.6.0](https://github.com/dropseed/plain/releases/plain-toolbar@0.6.0) (2025-10-24)
86
+
87
+ ### What's changed
88
+
89
+ - Added explicit `package_label = "plaintoolbar"` to the package configuration ([d1783dd](https://github.com/dropseed/plain/commit/d1783dd564))
90
+
91
+ ### Upgrade instructions
92
+
93
+ - No changes required
94
+
95
+ ## [0.5.3](https://github.com/dropseed/plain/releases/plain-toolbar@0.5.3) (2025-10-22)
96
+
97
+ ### What's changed
98
+
99
+ - Fixed toolbar visibility check to properly use `get_request_impersonator()` helper instead of accessing the `impersonator` attribute directly ([548a385](https://github.com/dropseed/plain/commit/548a385))
100
+
101
+ ### Upgrade instructions
102
+
103
+ - No changes required
104
+
105
+ ## [0.5.2](https://github.com/dropseed/plain/releases/plain-toolbar@0.5.2) (2025-10-06)
106
+
107
+ ### What's changed
108
+
109
+ - Added comprehensive type annotations to improve IDE support and type checking ([c87ca27](https://github.com/dropseed/plain/commit/c87ca27ed2))
110
+
111
+ ### Upgrade instructions
112
+
113
+ - No changes required
114
+
115
+ ## [0.5.1](https://github.com/dropseed/plain/releases/plain-toolbar@0.5.1) (2025-10-02)
116
+
117
+ ### What's changed
118
+
119
+ - The toolbar now uses `get_request_user()` helper to check user authentication, improving compatibility with different auth implementations ([2663c49](https://github.com/dropseed/plain/commit/2663c49404))
120
+
121
+ ### Upgrade instructions
122
+
123
+ - No changes required
124
+
125
+ ## [0.5.0](https://github.com/dropseed/plain/releases/plain-toolbar@0.5.0) (2025-09-30)
126
+
127
+ ### What's changed
128
+
129
+ - The toolbar now uses `settings.VERSION` instead of the deprecated `settings.APP_VERSION` ([4c5f216](https://github.com/dropseed/plain/commit/4c5f2166c1))
130
+
131
+ ### Upgrade instructions
132
+
133
+ - If you were accessing `settings.APP_VERSION` in any toolbar customizations, update to use `settings.VERSION` instead
134
+
135
+ ## [0.4.0](https://github.com/dropseed/plain/releases/plain-toolbar@0.4.0) (2025-09-30)
136
+
137
+ ### What's changed
138
+
139
+ - Renamed `ToolbarPanel` to `ToolbarItem` and `register_toolbar_panel` to `register_toolbar_item` for better clarity ([79654db](https://github.com/dropseed/plain/commit/79654dbefe))
140
+ - The toolbar now receives the full template context instead of just the request, allowing toolbar items to access context variables like `object` ([821bfc6](https://github.com/dropseed/plain/commit/821bfc6fab))
141
+ - Removed admin URL link from the request panel to reduce clutter ([5e665fd](https://github.com/dropseed/plain/commit/5e665fd4ca))
142
+ - Admin link and impersonation UI moved to a new AdminToolbarItem button ([821bfc6](https://github.com/dropseed/plain/commit/821bfc6fab))
143
+
144
+ ### Upgrade instructions
145
+
146
+ - Replace any usage of `ToolbarPanel` with `ToolbarItem` in your custom toolbar extensions
147
+ - Replace any usage of `@register_toolbar_panel` decorator with `@register_toolbar_item`
148
+ - Update any custom toolbar items to expect `context` instead of `request` in `__init__()`: change `def __init__(self, request)` to `def __init__(self, context)` and add `self.request = context["request"]`
149
+ - The `panel_template_name` attribute replaces `template_name` (though `template_name` still works for backward compatibility)
150
+
151
+ ## [0.3.0](https://github.com/dropseed/plain/releases/plain-toolbar@0.3.0) (2025-09-25)
152
+
153
+ ### What's changed
154
+
155
+ - Updated toolbar module autodiscovery to use the new `packages_registry.autodiscover_modules()` method ([b0b610d](https://github.com/dropseed/plain/commit/b0b610d461))
156
+
157
+ ### Upgrade instructions
158
+
159
+ - No changes required
160
+
161
+ ## [0.2.0](https://github.com/dropseed/plain/releases/plain-toolbar@0.2.0) (2025-09-19)
162
+
163
+ ### What's changed
164
+
165
+ - Minimum Python version raised to 3.13 ([d86e307](https://github.com/dropseed/plain/commit/d86e307efb))
166
+
167
+ ### Upgrade instructions
168
+
169
+ - Upgrade your Python environment to Python 3.13 or later
170
+
171
+ ## [0.1.1](https://github.com/dropseed/plain/releases/plain-toolbar@0.1.1) (2025-08-28)
172
+
173
+ ### What's changed
174
+
175
+ - Improved null safety when checking user and impersonator attributes in toolbar rendering ([90568bd](https://github.com/dropseed/plain/commit/90568bdfa4))
176
+
177
+ ### Upgrade instructions
178
+
179
+ - No changes required
180
+
181
+ ## [0.1.0](https://github.com/dropseed/plain/releases/plain-toolbar@0.1.0) (2025-08-27)
182
+
183
+ ### What's changed
184
+
185
+ - Initial release of plain-toolbar as a standalone package ([e49d54b](https://github.com/dropseed/plain/commit/e49d54bfea162424c73e54bf7ed87e93442af899))
186
+ - Fixed URL pattern and name display to include quotes in the request toolbar ([aa759c7](https://github.com/dropseed/plain/commit/aa759c72cae987ed8b6dd07c2e70f5fb97b6fd09))
187
+
188
+ ### Upgrade instructions
189
+
190
+ - No changes required
@@ -0,0 +1,221 @@
1
+ # plain.toolbar
2
+
3
+ **A developer toolbar that displays debugging information in your browser.**
4
+
5
+ - [Overview](#overview)
6
+ - [Built-in panels](#built-in-panels)
7
+ - [Request panel](#request-panel)
8
+ - [Exception panel](#exception-panel)
9
+ - [Creating custom toolbar items](#creating-custom-toolbar-items)
10
+ - [Button-only items](#button-only-items)
11
+ - [Visibility](#visibility)
12
+ - [JavaScript API](#javascript-api)
13
+ - [FAQs](#faqs)
14
+ - [Installation](#installation)
15
+
16
+ ## Overview
17
+
18
+ The toolbar appears at the bottom of your browser window and shows useful debugging information about the current request. You can expand it to see detailed panels, switch between tabs, and resize it by dragging the top edge.
19
+
20
+ To render the toolbar, add the `{% toolbar %}` tag to your base template (typically just before the closing `</body>` tag):
21
+
22
+ ```html
23
+ <!DOCTYPE html>
24
+ <html>
25
+ <head>
26
+ <title>My App</title>
27
+ </head>
28
+ <body>
29
+ {% block content %}{% endblock %}
30
+
31
+ {% toolbar %}
32
+ </body>
33
+ </html>
34
+ ```
35
+
36
+ The toolbar automatically hides itself in production unless the user is an admin. In debug mode, it always appears.
37
+
38
+ ## Built-in panels
39
+
40
+ ### Request panel
41
+
42
+ The Request panel shows information about the current HTTP request:
43
+
44
+ - Request ID
45
+ - Query parameters
46
+ - HTTP method
47
+ - View class
48
+ - URL pattern, name, args, and kwargs
49
+ - Template names (if available)
50
+ - Primary object (if the view has an `object` attribute)
51
+
52
+ ### Exception panel
53
+
54
+ When an exception occurs during request handling, the Exception panel automatically appears with:
55
+
56
+ - The exception type and message
57
+ - A color-coded traceback showing frames from your app, Plain, third-party packages, and Python stdlib
58
+ - Source code context around each frame (expandable/collapsible)
59
+ - Local variables for each frame (in debug mode)
60
+ - A "Copy" button to copy the full exception for sharing
61
+ - A "View raw" button to see the standard Python traceback format
62
+ - Clickable file paths that open in VS Code
63
+
64
+ App frames are highlighted in amber and expanded by default, making it easy to spot where the error occurred in your code.
65
+
66
+ ## Creating custom toolbar items
67
+
68
+ You can add your own panels to the toolbar by creating a `ToolbarItem` subclass and registering it with the `@register_toolbar_item` decorator.
69
+
70
+ Create a `toolbar.py` file in any installed app:
71
+
72
+ ```python
73
+ # app/users/toolbar.py
74
+ from plain.toolbar import ToolbarItem, register_toolbar_item
75
+
76
+
77
+ @register_toolbar_item
78
+ class UserToolbarItem(ToolbarItem):
79
+ name = "User"
80
+ panel_template_name = "toolbar/user.html"
81
+
82
+ def get_template_context(self):
83
+ context = super().get_template_context()
84
+ context["current_user"] = getattr(self.request, "user", None)
85
+ return context
86
+ ```
87
+
88
+ Then create the panel template:
89
+
90
+ ```html
91
+ <!-- app/users/templates/toolbar/user.html -->
92
+ <div class="px-6 py-4 text-sm">
93
+ {% if current_user %}
94
+ <dl class="grid grid-cols-[max-content_1fr] gap-x-8 gap-y-2">
95
+ <dt>Email</dt>
96
+ <dd class="text-white/50">{{ current_user.email }}</dd>
97
+ <dt>ID</dt>
98
+ <dd class="text-white/50">{{ current_user.id }}</dd>
99
+ </dl>
100
+ {% else %}
101
+ <p class="text-white/50">No user logged in</p>
102
+ {% endif %}
103
+ </div>
104
+ ```
105
+
106
+ The toolbar uses autodiscovery to find `toolbar.py` files in all installed apps.
107
+
108
+ ### Button-only items
109
+
110
+ You can also create toolbar items that only show a button in the minimized toolbar bar (no expandable panel). Set `button_template_name` instead of `panel_template_name`:
111
+
112
+ ```python
113
+ @register_toolbar_item
114
+ class QuickActionToolbarItem(ToolbarItem):
115
+ name = "QuickAction"
116
+ button_template_name = "toolbar/quick_action_button.html"
117
+
118
+ def is_enabled(self):
119
+ # Only show when a certain condition is met
120
+ return some_condition
121
+ ```
122
+
123
+ Override `is_enabled()` to control when your toolbar item appears.
124
+
125
+ ## Visibility
126
+
127
+ The toolbar only renders when `Toolbar.should_render()` returns `True`. This happens when:
128
+
129
+ 1. `DEBUG` is `True`, or
130
+ 2. The user has `is_admin = True`, or
131
+ 3. An admin is impersonating another user (requires `plain.admin`)
132
+
133
+ You can also temporarily hide the toolbar:
134
+
135
+ - Click the X button to hide it for the current session
136
+ - Click the clock icon to hide it for 1 hour (stored in localStorage)
137
+ - Call `plainToolbar.show()` in the browser console to bring it back
138
+
139
+ ## JavaScript API
140
+
141
+ The toolbar exposes a `window.plainToolbar` object for programmatic control:
142
+
143
+ ```javascript
144
+ // Show/hide the toolbar
145
+ plainToolbar.show();
146
+ plainToolbar.hide();
147
+
148
+ // Expand/collapse the details panel
149
+ plainToolbar.expand();
150
+ plainToolbar.collapse();
151
+ plainToolbar.toggleExpand();
152
+
153
+ // Show a specific tab
154
+ plainToolbar.showTab("Request");
155
+ plainToolbar.showTab("Exception");
156
+
157
+ // Hide for a duration (milliseconds from now)
158
+ plainToolbar.hideUntil(Date.now() + 3600000); // Hide for 1 hour
159
+
160
+ // Reset custom height
161
+ plainToolbar.resetHeight();
162
+ ```
163
+
164
+ ## FAQs
165
+
166
+ #### How do I style my custom panel?
167
+
168
+ The toolbar uses Tailwind CSS classes. Your panel template has access to all Tailwind utilities. The toolbar has a dark theme, so use light text colors like `text-white`, `text-stone-300`, or `text-white/50` for muted text.
169
+
170
+ #### Can I add multiple custom panels?
171
+
172
+ Yes. Create multiple `ToolbarItem` subclasses, each with its own `name` and templates. They will appear as separate tabs in the toolbar.
173
+
174
+ #### Why does the Exception panel open automatically?
175
+
176
+ When an exception occurs, the toolbar automatically expands and shows the Exception panel so you can immediately see what went wrong. This behavior is intentional to surface errors quickly during development.
177
+
178
+ #### How do I disable the toolbar completely?
179
+
180
+ Remove `plain.toolbar` from your `INSTALLED_PACKAGES` setting. Alternatively, remove the `{% toolbar %}` tag from your templates.
181
+
182
+ ## Installation
183
+
184
+ Install the `plain.toolbar` package from PyPI:
185
+
186
+ ```console
187
+ uv add plain.toolbar
188
+ ```
189
+
190
+ Add `plain.toolbar` to your `INSTALLED_PACKAGES` in `app/settings.py`:
191
+
192
+ ```python
193
+ INSTALLED_PACKAGES = [
194
+ # ... other packages
195
+ "plain.toolbar",
196
+ ]
197
+ ```
198
+
199
+ Add the `{% toolbar %}` template tag to your base template, just before the closing `</body>` tag:
200
+
201
+ ```html
202
+ <!DOCTYPE html>
203
+ <html>
204
+ <head>
205
+ <title>My App</title>
206
+ </head>
207
+ <body>
208
+ {% block content %}{% endblock %}
209
+
210
+ {% toolbar %}
211
+ </body>
212
+ </html>
213
+ ```
214
+
215
+ A `VERSION` setting is required in your `app/settings.py` to display in the toolbar:
216
+
217
+ ```python
218
+ VERSION = "1.0.0"
219
+ ```
220
+
221
+ The toolbar should now appear at the bottom of your browser window in debug mode.