| 2026-09-18 |
Keep extension zips out of the repo — make prod builds them
...
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
7 hours ago
|
Download page: extension install guide for Chrome and Firefox
...
- public /download page (nav item «Расширение»): per-browser download
buttons and step-by-step install instructions, setup (server URL is
the panel's own origin) and hotkey reference, en/ru
- npm run package -w @ltt/extension zips dist/{chrome,firefox} into
packages/web/public/ext (stable names, manifest at archive root so
"Load unpacked" works right after unpacking)
- make ext / make prod always build fresh zips; deploy/.env.example
seeds the release address bugtrail.gnexus.space
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
7 hours ago
|
v0.1.0-beta: production transfer guide and parameterized prod stack
...
- docker-compose.prod.yml reads POSTGRES_PASSWORD, HTTP(S)_PORT and
SITE_ADDRESS from .env; caddy persists its config in a volume
- Caddyfile takes the site address from the environment — a DNS name
turns on automatic HTTPS
- README: full first-transfer, domain/HTTPS, update and backup guide
Smoke-tested locally: fresh postgres migrates on boot, panel + /api
serve from one origin, SPA fallback works.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
7 hours ago
|
Report cards: breathing room under the section title
...
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
7 hours ago
|
AI prompt dialog: let the textarea span the full modal width
...
The kit caps GnTextarea's wrapper at 600px; lift it for the prompt dialog
so long prompts are comfortable to review and edit.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
7 hours ago
|
AI prompt generator on the report page
...
The header gains an "AI prompt" button: it builds a ready-to-paste
bug-fix prompt for a developer's AI coding agent — description, page
URL, element context (selector, classes, text, test attributes),
environment, recorded steps and token-scoped attachment links — and
opens it in an editable dialog with a copy button. Labels follow the
panel language via i18n (en/ru).
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
8 hours ago
|
Centered annotation editor canvas; picker UI never lands in note screenshots
...
- ScreenshotViewer: the fullscreen editor stage now centers the editor
block (it sized to the canvas and hugged the left edge on wide windows)
- overlay App: unmount the picker (highlight + tooltip) as soon as an
element is picked, before the pristine capture happens
- RegionSelector: hide the overlay locally around the pristine capture
too, so it no longer depends on the background's message roundtrip
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
8 hours ago
|
BugTrail logo in the app header at 48x48
...
Wire the existing public/bugtrail.svg into GnNavigationShell's logoSrc
and raise the kit's 22px brand-image cap to 48px (global override — the
shell renders the topbar outside the scoped content slot).
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
8 hours ago
|
Report page: swap header buttons, fix back navigation on share links
...
- the copy-link button now comes before "Start replay" in the header
- "Back to project" no longer depends on in-app history: on a cold
share-link visit (note or recording opened by URL) it falls back to
the report's project page — the detail GET now returns
project_share_token; without a project reference it goes to the
projects list
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
8 hours ago
|
Attachments tab shows an empty state when there is nothing to show
...
The tab no longer disappears when a report has no attachments (or when
everything is already displayed in Details) — it renders GnEmptyState
("Вложений нет" / "No attachments here") instead of a blank card.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
9 hours ago
|
GIF fallback raised to the same 768px crop as the video
...
Frame cap lowered 240 -> 150 so the heavier 768px GIF stays under the
server's 25MB upload limit.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
9 hours ago
|
Cursor marker in recordings + higher encode quality
...
- CDP screencast frames don't include the OS pointer, so the GIF and
WebM encoders now paint a classic white arrow (dark outline) at the
interpolated cursor position — the crop centers on it, so it stays
in frame
- screencast JPEG quality 55 -> 70
- VP8 bitrate ~0.3 bits/px/frame (cap 8 Mbps) for sharper output
- verified with the real built offscreen encoder: synthetic green
frames + known track land the arrow within 3px of the true cursor
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
9 hours ago
|
Video in report details + 768px cursor crop quality
...
- web panel: screen recording now plays in the Details tab (large
video card), removed from the Attachments thumbnails to avoid
showing it twice
- screencast cap raised 1280 -> 2560 so wide viewports keep the
cursor crop at native resolution (user viewport 2552 was scaled
down to 1280, halving the video size)
- crop raised 512 -> 768 CSS px; GIF fallback stays at 512
- VP8 bitrate now scales with output size (~0.18 bits/px/frame,
capped at 4 Mbps) instead of a fixed 1 Mbps
- e2e ffprobe assertion updated to the 768px cap
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
10 hours ago
|
Crop recordings in the JPEG's pixel space, not the device space
...
Page.startScreencast frames are scaled to fit maxWidth/maxHeight, but the
frame metadata's deviceWidth/deviceHeight keep the pre-scale device size.
On viewports wider than the 1280px cap the crop was computed in the wrong
coordinate space: shifted toward the bottom-right and, for a cursor there,
hanging off the actual image — drawImage clipped it and the video showed a
squashed corner plus letterbox. Decode the first frame to size the output
and crop each frame by its real bitmap dimensions instead.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
10 hours ago
|
Recording submits at stop; composer edits the report by token
...
An MV3 service worker is killed after ~30 s of idle time, so parking the
recording buffer in background memory (pending + 10-min auto-save timer)
was unreliable: the timer never fired and Save could hit a restarted
worker with an empty buffer. Now recorderStop uploads the media, submits
the report with the auto-title right away, and only then opens the
composer. Save patches title/description (PATCH /api/reports/{token}),
Discard deletes the report — both are token-based and survive worker
restarts. recorder_submit is replaced by recorder_edit/recorder_discard
{token}.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
11 hours ago
|

Project switcher in popup, pending-recording composer, cursor-crop video
...
- Popup: project dropdown (list_projects + settings_save defaultProjectId);
switching refreshes the last-report card and panel link immediately.
- Recording stop no longer submits blindly: the buffer goes pending and a
composer opens in the tab with title (prefilled) + description, Save
(Ctrl+Enter, copies the share link) or two-step Discard; unanswered
pending recordings auto-save after 10 minutes, and starting a new
recording auto-saves a pending one. Composer survives navigation.
- WebM is now a square cursor-following crop (shared cursorCropRect helper
with the GIF path), source dimension raised to 1280 so the 512px crop is
native-resolution at DPR 1; full-frame 800px stays as fallback.
- e2e: second project + popup switcher asserts, composer save (typed
title/description verified on server), keyboard-driven discard flow,
ffprobe square-video assertions (512x512 VP8).
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
11 hours ago
|
| 2026-09-17 |

Record silent WebM screen video instead of GIF during recordings
...
The recorder now captures continuous screencast frames via the CDP
screencast API and encodes them into a silent WebM (VP8, ~15 fps, capped
at 800px wide) uploaded as recording.webm. WebCodecs VideoEncoder is not
exposed to service workers, so encoding runs in an offscreen document
(chrome.offscreen) fed by base64 frame batches over a runtime port; the
result is verified with a local <video> playback probe and falls back to
the cursor-following GIF when encoding or verification fails (Firefox,
old Chrome, broken output).
Root cause of the earlier corrupt output: webm-muxer silently produces a
broken file when the first chunk's timestamp is non-zero — media
timestamps now start at the first frame and the muxer uses
firstTimestampBehavior: "offset".
The e2e now validates the uploaded recording with ffprobe (vp8 codec,
positive duration) and still covers the GIF fallback path, console-step
dump, navigation survival and replay.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
23 hours ago
|

Record screen video around the cursor + console dump per recording
...
The recorder now captures continuous screen video instead of single frames
per event. captureVisibleTab is quota-limited to ~2 shots/sec, so the video
rides a CDP screencast (Page.startScreencast) over the chrome.debugger
connection: frames arrive throttled to ~15 fps, decimated past 600 to bound
memory. At submit, the GIF is cropped to a 512×512 square centered on the
cursor position from the mouse track at each frame's time, paced by real
frame gaps — a "video" of exactly the area the tester was looking at.
Firefox and a busy debugger degrade to the old per-event screenshots; the
video path is a videoCapture setting (default on, like hoverReplay).
Console errors now ride along with recordings: a MAIN-world document_start
tap wraps console.error/warn and the uncaught error/rejection handlers,
posts through the relay content script, and lands as "console" timeline
steps (capped at 200, text truncated at 2000 chars). New consoleCapture
setting; the server schema, shared types and the panel timeline (warning
icon) accept the new step type; replay counts such no-effect steps as
played so the played/total summary stays honest.
E2E generates a console error + warning mid-recording, asserts the console
steps, and validates the GIF header: 512px wide with 3+ frames.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|

Replay hover events: synthetic mouse family + CSS :hover via debugger API
...
The virtual cursor now dispatches the full mouse event family as it walks
the recorded path — mouseover/mouseenter on the new target, mouseout/
mouseleave (non-bubbling) on the previous one, relatedTarget wired up, and
throttled mousemove — so JS hover handlers stay live during replay.
CSS :hover can't be triggered by synthetic events, so when the hoverReplay
setting is on (default) the background attaches chrome.debugger to the
replay tab and mirrors cursor positions as trusted Input.dispatchMouseEvent
mouseMoved commands (~20 Hz), which light up real :hover styles. Firefox has
no debugger API and another debugger may hold the tab — both degrade
gracefully to JS-level hover only. New "debugger" permission; toggle in the
options page.
E2E now latches both hover kinds on the test page (sticky mouseover flag +
rAF-sampled :hover poll) and asserts the synthetic family; the CSS check is
reported but stays non-fatal since the debugger can be legitimately absent.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|
Recorder survives page navigation, cursor clock sync, initial page_url
...
- Background reinstalls the capture listeners (and recorder bar) in
every new document while a recording is active, so a full page
navigation no longer resets the track; pending input debounces and
the mouse-track tail are flushed on pagehide so "type, then
navigate" keeps the input step
- The report's page_url now stays the URL where recording started
(replay opens that page; the rest of the path lives in url_change
steps)
- Replay: after a url_change step the new document gets its content
script and cursor back, resuming the mouse path from the current
step (replay_start now takes a from_t offset); after every executed
step the background nudges the cursor clock toward the gap-capped
step clock (max 400 ms per nudge) so the virtual mouse no longer
drifts behind the actions
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|

Record the mouse path and play it back with a virtual cursor
...
- Recording: content script samples mousemove at ~25 Hz (2 px / 40 ms
thresholds), batches it to the background every second and before
submit; stored as reports.mouse_track (viewport + points, capped and
decimated server-side) via a new alembic migration
- Replay: a virtual cursor (overlay arrow) walks the recorded path,
scaled to the current viewport, dispatching synthetic mousemoves so
JS hover handlers stay live; clicks snap the cursor to the target
- Replay errors: page-load timeout aborts with a clear message, a
closed tab aborts, per-step failures (missing elements) are
collected, reported in the flash and in the replay result
- Panel: "Start replay" button on recording reports talks to the
extension through a new tiny relay content script (window messages,
no extension-id config), with presence check and toasts for
missing extension / no response / partial replay (en + ru)
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|
Add BugTrail logo: extension icons, favicons and popup/options branding
...
SVG mark (brand/bugtrail.svg): a bug at the head of the trail of steps
it walked, Tokyo Night palette. PNG icons 16/32/48/128 are rendered
from it (verify-logo.mjs) and shipped in the extension manifest;
web panel gets an SVG favicon; popup and options headers show the mark
instead of the generic bug glyph.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|
Rename the service to BugTrail
...
Bug + trail: the recorded trail of actions leading to the bug.
User-facing rename in the web panel shell, extension manifest/popup/
options, API title, i18n locales and README; Firefox id is now
bugtrail@gnexus.space. Internal @ltt/* package scopes unchanged.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|
Report page: replace nonexistent ph-cursor-click timeline icon
...
The bundled Phosphor set has no cursor-click glyph, so click steps in
the steps timeline rendered an empty marker. Use ph-mouse-simple, and
audit all other icon names against the bundled icons.css (all valid).
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|
Extension: popup UI, panel URL, icon fix, GIF tracks and track replay
...
- Popup replaces the auto-start picker: mode choice (element note /
record steps), latest report card with open / replay / delete, web
panel link; share links open at panelUrl (default :5173)
- Register the Phosphor font via the FontFace API — constructable
stylesheets ignore @font-face, which left overlay icons empty
- Recorder tracks upload as one animated GIF (gifenc) instead of per-
step screenshots; frames follow event pacing, UI hidden on capture
- Replay a recorded track from the popup: background opens the start
page, re-executes click/input/url_change steps with original pacing
and flashes the result in the overlay
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|
Extension popup: mode choice, latest report, panel link; auto-copied share links
...
- Toolbar click now opens a popup instead of jumping straight into the
picker: two mode buttons (element note / record steps with live step
count), the latest report of the default project with open and
two-step delete, and an "Open web panel" shortcut to the project page.
- New background messages (start_picker, toggle_recorder, latest_report,
delete_report, open_panel) resolve the active tab themselves; the
action.onClicked path stays for keyboard-driven flows.
- Settings gain panelUrl (falls back to serverUrl); options page gets a
"Web panel URL" input.
- After a note is submitted its share link is copied to the clipboard
automatically (with an execCommand fallback) and a brief toast
confirms it; e2e asserts the clipboard content.
- Build: new popup entry (own CSS chunk), manifest default_popup.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|
Bigger editor canvas, larger annotation text, plugin UI hidden in captures
...
- Fullscreen editor: toolbar and Save/Cancel now float above the canvas,
which takes the entire window (1344x896 vs 1147x765 at 1440x900).
- Extension composer widened to 1100px, drawing area raised to 62vh.
- Text tool renders ~2.4x larger (strokeWidth * 12, min 24px); size
slider now goes up to 20.
- Screenshots never contain the plugin's own UI: the background hides the
overlay host around every captureVisibleTab (covers region capture,
full-page tiles and recorder step screenshots); the region selector
keeps it hidden for the whole stitch via a refcounted module.
- e2e saves a step screenshot for inspection; fixed its file download to
resolve attachment id -> file id.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|
Make the annotation editor comfortable to draw in
...
- AnnotationEditor gains a fit="contain" mode: the canvas scales to fit
its box entirely instead of being cropped to container width.
- Report page: "Edit annotations" now opens a fullscreen editor overlay
(toolbar + large canvas + Save/Cancel, Escape cancels) instead of the
small inline editor.
- Extension composer: widened to 920px and the screenshot area now takes
55vh with the canvas contain-fit, so there is actual room to draw.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|

Annotated screenshots in lightbox; region/whole-page capture in extension
...
- Extract shared shapeDraw.ts (pen/arrow/rect/text) used by both the
annotation editor and the viewer.
- ScreenshotViewer lightbox now renders a canvas with the screenshot plus
annotation shapes baked in, so opening a screenshot fullscreen shows the
drawn version instead of the raw image.
- New RegionSelector step in the extension: after picking an element, the
page is captured pristine (before any overlay renders) and the user can
either drag a region on the page (cropped at devicePixelRatio) or take
the whole page via the button / Enter.
- Whole-page capture scrolls and stitches viewport tiles, hiding the
overlay and fixed/sticky elements; per-tile captures retry with backoff
because captureVisibleTab is throttled.
- NoteComposer receives the screenshot from the region selector instead of
capturing on its own; e2e updated (Enter = whole page).
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|
Web: drop avatars, show navigation on shared project/report pages
...
- remove GnAvatar from the shell footer and the settings profile card
- AppShell falls back to a plain centered body when nobody is logged in, so
ProjectPage and ReportPage can wrap it too: logged-in users get the nav
shell on share-token pages, anonymous visitors keep the bare layout
- pages no longer carry their own max-width/padding (the shell body owns it)
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|