A drop-in feedback & bug-report widget for Laravel. It adds a floating 🐞 icon to your site; clicking it lets a user write a bug report or a suggestion, visually pick the related block on the page (with a drill-down picker that lets you "see through" to the element behind), and ships the report — its text, a CSS selector, the element's HTML and a screenshot of the area — to Telegram and/or a log file.
- Zero front-end build step — the widget is plain JS served by a route.
- Works out of the box: with no Telegram config it just logs to a file.
- Auto-injects into your pages, or place it manually with
@improveme. - Fully publishable config; rename the env vars; override the route/controller.
- PHP 8.2+
- Laravel 10, 11, 12 or 13
composer require arhx/improvemeThat's it. The service provider is auto-discovered and, by default, the widget
auto-injects before </body> on every HTML page. Open any page and you'll
see the floating icon in the bottom-right corner.
Reports are written to
storage/logs/improveme.logimmediately, even before you configure Telegram.
Add to your .env:
IMPROVEME_TELEGRAM_TOKEN=123456:ABC-your-bot-token
IMPROVEME_TELEGRAM_CHAT_ID=-1001234567890Now every report is sent to that chat (screenshot as a photo with the report as caption). Leave them unset and only the log channel runs.
To send into a forum topic: IMPROVEME_TELEGRAM_THREAD_ID=42.
Prefer to control exactly where the snippet renders? Turn off auto-injection and drop the directive in your layout:
IMPROVEME_INJECT=false<body>
...
@improveme
</body>| Env | Default | Meaning |
|---|---|---|
IMPROVEME_ENABLED |
true |
Master on/off switch. |
IMPROVEME_INJECT |
true |
Auto-inject before </body>. |
IMPROVEME_AUDIENCE |
all |
all | auth | guest. |
IMPROVEME_POSITION |
bottom-right |
Icon corner. |
IMPROVEME_ACCENT |
#ff5a36 |
Icon / primary button colour. |
IMPROVEME_HOVER_COLOR |
#3b82f6 |
Element hover outline. |
IMPROVEME_SELECTED_COLOR |
#22c55e |
Picked element outline. |
IMPROVEME_SCREENSHOT_PADDING |
15 |
Px captured around the selection. |
IMPROVEME_TELEGRAM_TOKEN |
— | Bot token (enables Telegram). |
IMPROVEME_TELEGRAM_CHAT_ID |
— | Target chat id. |
Publish the config to tweak anything (and to rename the env vars — just edit the
env('…') calls in the published file):
php artisan vendor:publish --tag=improveme-configPublish the Blade snippet (e.g. to restyle the bootstrap):
php artisan vendor:publish --tag=improveme-viewsSelf-host the JS asset instead of serving it from a route:
php artisan vendor:publish --tag=improveme-assetsPoint the package at your own controller (extend the bundled one for the easiest path):
// config/improveme.php
'controller' => \App\Http\Controllers\MyReportController::class,use Arhx\Improveme\Http\Controllers\ReportController;
class MyReportController extends ReportController
{
protected function rules(array $config): array
{
return array_merge(parent::rules($config), [
'message' => ['required', 'string', 'min:10', 'max:5000'],
]);
}
}Or take over routing entirely:
// config/improveme.php
'register_routes' => false,// routes/web.php
Route::post('feedback', \App\Http\Controllers\MyReportController::class);- Click 🎯 Pick an element — the page enters picking mode.
- Hovering outlines the block under the cursor (hover colour).
- Click to select it (selected colour). The selection stays highlighted.
- Hovering the selected block now sees through it — the outline previews the block behind it; clicking again drills to that containing block.
- Press Esc (or Done) to finish. A screenshot of the selection
(padded by 15px) is captured client-side via
html2canvas-pro — a
maintained html2canvas fork that understands Tailwind 4's
oklch()colours (lazy-loaded from a CDN; URL configurable viaIMPROVEME_HTML2CANVAS_URL).
The report payload includes a unique CSS selector, the element's outerHTML
(truncated), its bounding rect, the page URL/title, viewport, user agent, the
authenticated user (if any) and the screenshot.
MIT © arhx
{ "type": "bug", // or "idea" "message": "…", "page": { "url": "…", "title": "…", "referrer": "…" }, "viewport": { "w": 1440, "h": 900, "dpr": 2 }, "element": { "selector": "…", "tag": "div", "html": "…", "rect": {…} }, "screenshot": "data:image/png;base64,…" }